Common CustomDataSource
Common_CustomDataSource_CSharp\CustomDataSource_CSharp\REXMLDataSource_CSharp\QueryFunctionality.cs
// Copyright 2007 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Web.UI;
using ESRI.ArcGIS.ADF.Web.Geometry;
using ESRI.ArcGIS.ADF.Web.DataSources;
using ESRI.ArcGIS.ADF.Web.Display.Graphics;
using System.Collections;
using ESRI.ArcGIS.ADF.Web;

namespace REXMLDataSource_CSharp
{
    public class QueryFunctionality : IQueryFunctionality
    {
        public QueryFunctionality(string name, MapResource resource)
        {
            this.name = name;
            this.resource = resource;
        }

        private GraphicsDataSet GetGraphicsDataSet(string mapFunctionalityName)
        {
            MapResource mapResource = resource as MapResource;
            if (mapResource == null)
                return null;

            if (mapFunctionalityName == null)
            {
                return mapResource.Graphics;
            }
            else
            {
                MapFunctionality mapFunctionality = mapResource.Functionalities.Find(mapFunctionalityName) as MapFunctionality;
                return mapFunctionality == null ? null : mapFunctionality.GraphicsDataSet;
            }
        }

        private GraphicsLayer GetGraphicsLayer(string mapFunctionalityName, string layerID)
        {
            GraphicsDataSet gds = GetGraphicsDataSet(mapFunctionalityName);
            if (gds == null)
                return null;

            string tableName = layerID;
            if (tableName == null)
                return null;

            return gds.Tables[tableName] as GraphicsLayer;
        }

        #region IQueryFunctionality implementation

        public DataTable[] Find(string mapFunctionalityName, FindParameters findParameters)
        {
            List<DataTable> datatables = new List<DataTable>();
            GraphicsDataSet graphicsDataSet = GetGraphicsDataSet(mapFunctionalityName);
            if (graphicsDataSet == null) return null;

            QueryFilter filter = new QueryFilter();
            filter.MaxRecords = findParameters.MaxRecords;
            filter.ReturnADFGeometries = findParameters.ReturnADFGeometries;

            IDictionaryEnumerator en = findParameters.LayersAndFields.GetEnumerator();
            while (en.MoveNext())
            {
                string layerID = en.Key as string;
                string[] searchFields = en.Value as string[];

                // Build where clause
                StringBuilder whereExpression = new StringBuilder();
                for (int i = 0; i < searchFields.Length; i++)
                {
                    if (findParameters.UseSqlContains)
                    {
                        // todo: change to use SQL CONTAINS statement
                        whereExpression.Append(String.Format("{0} like '%{1}%'", searchFields[i], findParameters.FindString));
                    }
                    else
                    {
                        whereExpression.Append(String.Format("{0} like '%{1}%'", searchFields[i], findParameters.FindString));
                    }
                    if (i != searchFields.Length - 1) whereExpression.Append(" OR ");
                }

                filter.WhereClause = whereExpression.ToString();

                GraphicsLayer glayer = graphicsDataSet.Tables[layerID] as GraphicsLayer;
                if (glayer != null)
                {
                    if (findParameters.FindOption != FindOption.VisibleLayers || (findParameters.FindOption == FindOption.VisibleLayers && glayer.Visible == true))
                    {
                        datatables.Add(Query(mapFunctionalityName, glayer.TableName, filter));
                    }
                }
            }


            return datatables.ToArray();
        }

        public DataTable[] Identify(string mapFunctionalityName, Geometry geometry, int tolerance, IdentifyOption option, string[] layers)
        {
            List<DataTable> datatables = new List<DataTable>();

            GraphicsDataSet graphicsDataSource = GetGraphicsDataSet(mapFunctionalityName);
            if (graphicsDataSource == null)
                return null;

            SpatialFilter sf = new SpatialFilter();
            sf.SearchOrder = SearchOrder.Spatial;
            if (geometry is Point)
            {
                Point point = geometry as Point;
                MapResource mr = Resource as MapResource;
                sf.Geometry = point.Expand(tolerance, graphicsDataSource.FullExtent);
            }
            else
            {
                throw new NotSupportedException("GraphicsLayer only supports Points in the Identify method.");
            }

            if (layers == null)
            {
                foreach (GraphicsLayer glb in graphicsDataSource.Tables)
                {
                    datatables.Add(Query(mapFunctionalityName, glb.TableName, sf));
                }
            }
            else
            {
                foreach (string o in layers)
                {
                    datatables.Add(Query(mapFunctionalityName, o, sf));
                }
            }

            return datatables.ToArray();
        }

        public DataTable Query(string mapFunctionalityName, string layerID, QueryFilter queryFilter)
        {
            GraphicsLayer glayer = GetGraphicsLayer(mapFunctionalityName, layerID);
            if (glayer == null)
                return null;

            #region Create Table
            DataTable dataTable = null;
            List<DataColumn> columns = new List<DataColumn>();
            foreach (DataColumn col in glayer.Columns)
            {
                if (queryFilter.SubFields.Count == 0 || queryFilter.SubFields.IndexOf(col.ColumnName) != -1)
                    columns.Add(CloneColumn(col));
                else if (queryFilter.ReturnADFGeometries && (col.DataType == typeof(GraphicElement) || col.DataType == typeof(Geometry)))
                    columns.Add(CloneColumn(col));
            }
            if (queryFilter.ReturnADFGeometries)
            {
                if (glayer is FeatureGraphicsLayer)
                {
                    FeatureGraphicsLayer flayer = glayer as FeatureGraphicsLayer;
                    dataTable = new FeatureGraphicsLayer(flayer.TableName, columns.ToArray(), flayer.GeometryColumnName,
                        flayer.GraphicsIDColumn.ColumnName, flayer.FeatureType);
                }
                else
                {
                    ElementGraphicsLayer elayer = glayer as ElementGraphicsLayer;
                    dataTable = new ElementGraphicsLayer(elayer.TableName, columns.ToArray(), elayer.GraphicsColumn.ColumnName,
                        elayer.GraphicsIDColumn.ColumnName);
                }
            }
            if (dataTable == null)
            {
                dataTable = new DataTable(glayer.TableName);
                dataTable.Columns.AddRange(columns.ToArray());
            }
            #endregion

            // Do query
            if (queryFilter is SpatialFilter)
            {
                SpatialFilter sf = queryFilter as SpatialFilter;
                if (sf.SearchOrder == SearchOrder.Spatial)
                {
                    InitialSpatialQuery(glayer, dataTable, sf);
                    AttributeQueryOnSpatialResults(dataTable, queryFilter);
                }
                else
                {
                    List<Geometry> geometries = InitialAttributeQuery(glayer, dataTable, queryFilter);
                    SpatialQueryOnAttributeResults(dataTable, sf, geometries);
                }
            }
            else
            {
                InitialAttributeQuery(glayer, dataTable, queryFilter);
            }

            if (dataTable.Rows.Count > queryFilter.MaxRecords && queryFilter.MaxRecords > 0)
            {
                while (dataTable.Rows.Count > queryFilter.MaxRecords)
                    dataTable.Rows.RemoveAt(dataTable.Rows.Count - 1);
            }

            return dataTable;
        }

        private List<Geometry> InitialAttributeQuery(GraphicsLayer glayer, DataTable resultsDataTable, QueryFilter queryFilter)
        {
            List<Geometry> geometries = new List<Geometry>();
            DataRow[] rows = glayer.Select(queryFilter.WhereClause);
            foreach (DataRow row in rows)
            {
                resultsDataTable.ImportRow(row);
                geometries.Add(glayer.GeometryFromRow(row));
            }

            return geometries;
        }

        private void InitialSpatialQuery(GraphicsLayer glayer, DataTable resultsDataTable, SpatialFilter sf)
        {
            foreach (DataRow row in glayer.Rows)
            {
                Geometry geometry = glayer.GeometryFromRow(row);

                // To implemement, evaluate feature geometry and spatial filter geometry
                // if (geometry != null && GeometriesIntersect(geometry, sf.Geometry))
                
                if (geometry != null)
                {
                    resultsDataTable.ImportRow(row);
                }
                
            }
        }

        private void AttributeQueryOnSpatialResults(DataTable dataTable, QueryFilter queryFilter)
        {
            if (queryFilter.WhereClause == "")
                return;

            DataRow[] rows = dataTable.Select(queryFilter.WhereClause);
            foreach (DataRow row in dataTable.Rows)
            {
                if (Array.IndexOf(rows, row) == -1)
                    dataTable.Rows.Remove(row);
            }
        }

        private void SpatialQueryOnAttributeResults(DataTable dataTable, SpatialFilter sf, List<Geometry> geometries)
        {
            if (geometries == null || geometries.Count == 0)
                return;

            for (int i = 0, j = 0; i < dataTable.Rows.Count; ++i, ++j)
            {
                // To implement, evaluate if feature geometry intersects spatial filter geometry
                
                /*if (!GeometriesIntersect(geometries[j], sf.Geometry))
                {
                    dataTable.Rows.Remove(dataTable.Rows[i]);
                    --i;
                }*/
            }
        }

        public void GetQueryableLayers(string mapFunctionalityName, out string[] layerIDs, out string[] layerNames, FeatureType featureType)
        {
            layerIDs = null;
            layerNames = null;

            List<DataTable> tables = new List<DataTable>();

            GraphicsDataSet gds = GetGraphicsDataSet(mapFunctionalityName);
            if (gds == null)
                return;

            foreach (GraphicsLayer glayer in gds.Tables)
            {
                FeatureGraphicsLayer gl = glayer as FeatureGraphicsLayer;
                if (gl != null && gl.FeatureType != featureType)
                    continue;

                tables.Add(glayer);
            }

            layerIDs = new string[tables.Count];
            layerNames = new string[tables.Count];
            for (int i = 0; i < tables.Count; ++i)
            {
                layerIDs[i] = tables[i].TableName;
                layerNames[i] = tables[i].TableName;
            }
        }

        public void GetQueryableLayers(string mapFunctionalityName, out string[] layerIDs, out string[] layerNames)
        {
            layerIDs = null;
            layerNames = null;

            GraphicsDataSet gds = GetGraphicsDataSet(mapFunctionalityName);
            if (gds == null)
                return;

            layerIDs = new string[gds.Tables.Count];
            layerNames = new string[gds.Tables.Count];
            for (int i = 0; i < gds.Tables.Count; ++i)
            {
                layerIDs[i] = gds.Tables[i].TableName;
                layerNames[i] = gds.Tables[i].TableName;
            }
        }

        public string[] GetFields(string mapFunctionalityName, string layerID)
        {
            GraphicsLayer glayer = GetGraphicsLayer(mapFunctionalityName, layerID);
            if (glayer == null)
                return null;

            string[] fields = new string[glayer.Columns.Count];
            for (int i = 0; i < glayer.Columns.Count; ++i)
            {
                fields[i] = glayer.Columns[i].ColumnName;
            }

            return fields;
        }

        public string[] GetFields(string mapFunctionalityName, string layerID, out Type[] fieldTypes)
        {
            GraphicsLayer glayer = GetGraphicsLayer(mapFunctionalityName, layerID);
            if (glayer == null)
            {
                fieldTypes = null;
                return null;
            }

            string[] fields = new string[glayer.Columns.Count];
            fieldTypes = new Type[glayer.Columns.Count];

            for (int i = 0; i < glayer.Columns.Count; ++i)
            {
                fields[i] = glayer.Columns[i].ColumnName;
                fieldTypes[i] = glayer.Columns[i].DataType;
            }

            return fields;
        }
        #endregion

        #region IGISFunctionality implementation

        private string name = string.Empty;
        private IGISResource resource = null;
        [NonSerialized]
        System.Web.UI.WebControls.WebControl _webControl;

        public System.Web.UI.WebControls.WebControl WebControl
        {
            get { return _webControl; }
            set { _webControl = value; }
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public IGISResource Resource
        {
            get { return resource; }
            set { resource = value; }
        }

        bool _initialized = false;

        public bool Initialized
        {
            get { return _initialized; }
        }

        public void LoadState() { }

        public void Initialize() { _initialized = true; }

        public void SaveState() { }

        public void Dispose() { _initialized = false; }

        public bool Supports(string operation)
        {
            return true;
        }
        #endregion

        public static DataColumn CloneColumn(DataColumn toClone)
        {
            DataColumn column1 = (DataColumn)Activator.CreateInstance(toClone.GetType());
            column1.AllowDBNull = toClone.AllowDBNull;
            column1.AutoIncrement = toClone.AutoIncrement;
            column1.AutoIncrementStep = toClone.AutoIncrementStep;
            column1.AutoIncrementSeed = toClone.AutoIncrementSeed;
            column1.Caption = toClone.Caption;
            column1.ColumnName = toClone.ColumnName;
            column1.DataType = toClone.DataType;
            column1.DefaultValue = toClone.DefaultValue;
            column1.ColumnMapping = toClone.ColumnMapping;
            column1.ReadOnly = toClone.ReadOnly;
            column1.MaxLength = toClone.MaxLength;
            column1.DateTimeMode = toClone.DateTimeMode;
            column1.Namespace = toClone.Namespace;
            column1.Prefix = toClone.Prefix;
            column1.Unique = toClone.Unique;
            if (toClone.ExtendedProperties != null)
            {
                foreach (object obj1 in toClone.ExtendedProperties.Keys)
                {
                    column1.ExtendedProperties[obj1] = toClone.ExtendedProperties[obj1];
                }
            }
            return column1;
        }
    
    }
}