#region Disclaimer / License // Copyright (C) 2010, Jackie Ng // http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie@gmail.com // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.Text; using System.Xml; using System.IO; namespace OSGeo.MapGuide.MaestroAPI { public class XmlFeatureSetReader : FeatureSetReader { private XmlTextReader m_reader; //TODO: Make internal public XmlFeatureSetReader(Stream m_source) : base() { m_reader = new XmlTextReader(m_source); m_reader.WhitespaceHandling = WhitespaceHandling.Significant; //First we extract the response layout m_reader.Read(); if (m_reader.Name != "xml") throw new Exception("Bad document"); m_reader.Read(); if (m_reader.Name != "FeatureSet" && m_reader.Name != "PropertySet" && m_reader.Name != "RowSet") throw new Exception("Bad document"); m_reader.Read(); if (m_reader.Name != "xs:schema" && m_reader.Name != "PropertyDefinitions" && m_reader.Name != "ColumnDefinitions") throw new Exception("Bad document"); XmlDocument doc = new XmlDocument(); doc.LoadXml(m_reader.ReadOuterXml()); XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable); mgr.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema"); mgr.AddNamespace("gml", "http://www.opengis.net/gml"); mgr.AddNamespace("fdo", "http://fdo.osgeo.org/schemas"); //TODO: Assumes there is only one type returned... perhaps more can be returned.... XmlNodeList lst = doc.SelectNodes("xs:schema/xs:complexType/xs:complexContent/xs:extension/xs:sequence/xs:element", mgr); if (lst.Count == 0) lst = doc.SelectNodes("xs:schema/xs:complexType/xs:sequence/xs:element", mgr); if (lst.Count == 0) lst = doc.SelectNodes("PropertyDefinitions/PropertyDefinition"); if (lst.Count == 0) lst = doc.SelectNodes("ColumnDefinitions/Column"); FeatureSetColumn[] cols = new FeatureSetColumn[lst.Count]; for(int i = 0;i<lst.Count;i++) cols[i] = new XmlFeatureSetColumn(lst[i]); InitColumns(cols); m_row = null; if (m_reader.Name != "Features" && m_reader.Name != "Properties" && m_reader.Name != "Rows") throw new Exception("Bad document"); m_reader.Read(); if (m_reader.NodeType != XmlNodeType.EndElement) { if (m_reader.Name == "Features") m_reader = null; //No features :( else if (m_reader.Name == "PropertyCollection" || m_reader.Name == "Row") { //OK } else if (m_reader.Name != "Feature") throw new Exception("Bad document"); } } protected override bool ReadInternal() { if (m_reader == null || (m_reader.Name != "Feature" && m_reader.Name != "PropertyCollection" && m_reader.Name != "Row")) { m_row = null; return false; } return true; } protected override FeatureSetRow ProcessFeatureRow() { string xmlfragment = m_reader.ReadOuterXml(); XmlDocument doc = new XmlDocument(); doc.LoadXml(xmlfragment); FeatureSetRow row = null; if (doc["Row"] != null) row = new XmlFeatureSetRow(this, doc["Row"]); else if (doc["Feature"] == null) row = new XmlFeatureSetRow(this, doc["PropertyCollection"]); else row = new XmlFeatureSetRow(this, doc["Feature"]); if (m_reader.Name != "Feature" && m_reader.Name != "PropertyCollection" && m_reader.Name != "Row") { m_reader.Close(); m_reader = null; } return row; } protected override void CloseInternal() { } public override int Depth { get { throw new NotImplementedException(); } } public override System.Data.DataTable GetSchemaTable() { throw new NotImplementedException(); } public override int RecordsAffected { get { throw new NotImplementedException(); } } } public class XmlFeatureSetRow : FeatureSetRow { internal XmlFeatureSetRow(FeatureSetReader parent, XmlNode node) : base(parent) { string nodeName = "Property"; if (node.Name == "Row") nodeName = "Column"; foreach (XmlNode p in node.SelectNodes(nodeName)) { int ordinal = GetOrdinal(p["Name"].InnerText); if (!m_nulls[ordinal]) throw new Exception("Bad document, multiple: " + p["Name"].InnerText + " values in a single feature"); m_nulls[ordinal] = false; XmlNode valueNode = p["Value"]; if (valueNode == null) { m_nulls[ordinal] = true; } else { if (parent.Columns[ordinal].Type == typeof(string) || parent.Columns[ordinal].Type == Utility.UnmappedType) m_items[ordinal] = valueNode.InnerText; else if (parent.Columns[ordinal].Type == typeof(int)) m_items[ordinal] = XmlConvert.ToInt32(valueNode.InnerText); else if (parent.Columns[ordinal].Type == typeof(long)) m_items[ordinal] = XmlConvert.ToInt64(valueNode.InnerText); else if (parent.Columns[ordinal].Type == typeof(short)) m_items[ordinal] = XmlConvert.ToInt16(valueNode.InnerText); else if (parent.Columns[ordinal].Type == typeof(double)) m_items[ordinal] = XmlConvert.ToDouble(valueNode.InnerText); else if (parent.Columns[ordinal].Type == typeof(bool)) m_items[ordinal] = XmlConvert.ToBoolean(valueNode.InnerText); else if (parent.Columns[ordinal].Type == typeof(DateTime)) { try { //Fix for broken ODBC provider string v = valueNode.InnerText; if (v.Trim().ToUpper().StartsWith("TIMESTAMP")) v = v.Trim().Substring("TIMESTAMP".Length).Trim(); else if (v.Trim().ToUpper().StartsWith("DATE")) v = v.Trim().Substring("DATE".Length).Trim(); else if (v.Trim().ToUpper().StartsWith("TIME")) v = v.Trim().Substring("TIME".Length).Trim(); if (v != valueNode.InnerText) { if (v.StartsWith("'")) v = v.Substring(1); if (v.EndsWith("'")) v = v.Substring(0, v.Length - 1); m_items[ordinal] = DateTime.Parse(v, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.NoCurrentDateDefault); } else m_items[ordinal] = XmlConvert.ToDateTime(v, XmlDateTimeSerializationMode.Unspecified); } catch (Exception ex) { //Unfortunately FDO supports invalid dates, such as the 30th feb m_nulls[ordinal] = true; m_items[ordinal] = ex; } } else if (parent.Columns[ordinal].Type == Utility.GeometryType) { m_items[ordinal] = valueNode.InnerText; if (string.IsNullOrEmpty(valueNode.InnerText)) { m_nulls[ordinal] = true; m_items[ordinal] = null; } else m_lazyloadGeometry[ordinal] = true; } else throw new Exception("Unknown type: " + parent.Columns[ordinal].Type.FullName); } } } } public class XmlFeatureSetColumn : FeatureSetColumn { internal XmlFeatureSetColumn(XmlNode node) : base() { if (node.Name == "PropertyDefinition" || node.Name == "Column") { m_name = node["Name"].InnerText; m_allowNull = true; switch (node["Type"].InnerText.ToLower().Trim()) { case "string": m_type = typeof(string); break; case "byte": m_type = typeof(Byte); break; case "int32": case "int": case "integer": m_type = typeof(int); break; case "int16": m_type = typeof(short); break; case "int64": case "long": m_type = typeof(long); break; case "float": case "single": m_type = typeof(float); break; case "double": case "decimal": m_type = typeof(double); break; case "boolean": case "bool": m_type = typeof(bool); return; case "datetime": case "date": m_type = typeof(DateTime); break; case "raster": m_type = Utility.RasterType; break; case "geometry": m_type = Utility.GeometryType; break; default: //throw new Exception("Failed to find appropriate type for: " + node["xs:simpleType"]["xs:restriction"].Attributes["base"].Value); m_type = Utility.UnmappedType; break; } } else { m_name = node.Attributes["name"].Value; m_allowNull = node.Attributes["minOccurs"] != null && node.Attributes["minOccurs"].Value == "0"; if (node.Attributes["type"] != null && node.Attributes["type"].Value == "gml:AbstractGeometryType") m_type = Utility.GeometryType; else if (node["xs:simpleType"] == null) m_type = Utility.RasterType; else switch (node["xs:simpleType"]["xs:restriction"].Attributes["base"].Value.ToLower()) { case "xs:string": m_type = typeof(string); break; case "fdo:byte": m_type = typeof(Byte); break; case "fdo:int32": m_type = typeof(int); break; case "fdo:int16": m_type = typeof(short); break; case "fdo:int64": m_type = typeof(long); break; case "xs:float": case "xs:single": case "fdo:single": m_type = typeof(float); break; case "xs:double": case "xs:decimal": m_type = typeof(double); break; case "xs:boolean": m_type = typeof(bool); return; case "xs:datetime": m_type = typeof(DateTime); break; default: //throw new Exception("Failed to find appropriate type for: " + node["xs:simpleType"]["xs:restriction"].Attributes["base"].Value); m_type = Utility.UnmappedType; break; } } } } }