#region Disclaimer / License // Copyright (C) 2009, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // 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.Collections.ObjectModel; namespace OSGeo.MapGuide.MaestroAPI.Schema { //TODO: Expand on documentation as this is an important class /// /// Represents a FDO class definition /// public class ClassDefinition : SchemaElement, IFdoSerializable { private List _identity; private List _properties; private Dictionary _ordinalMap; private ClassDefinition() { _ordinalMap = new Dictionary(); _identity = new List(); _properties = new List(); } public ClassDefinition(string name, string description) : this() { this.Name = name; this.Description = description; } public ClassDefinition BaseClass { get; set; } /// /// Gets the property definition at the specified index /// /// /// public PropertyDefinition this[int index] { get { return _properties[index]; } } public int GetOrdinal(string name) { if (_ordinalMap.ContainsKey(name)) return _ordinalMap[name]; for (int i = 0; i < this.Properties.Count; i++) { if (this[i].Name.Equals(name)) { _ordinalMap[name] = i; return i; } } throw new ArgumentException("Property not found: " + name); //LOCALIZEME } /// /// Gets or sets whether this is abstract /// public bool IsAbstract { get; set; } /// /// Gets or sets whether this is computed. Computed classes should have its properties /// checked out (and possibly modified) before serving as a basis for a new class definition /// public bool IsComputed { get; set; } /// /// Gets or sets the name of the default geometry property. /// public string DefaultGeometryPropertyName { get; set; } /// /// Gets the identity properties /// public ReadOnlyCollection IdentityProperties { get { return _identity.AsReadOnly(); } } /// /// Removes the assigned identity properties /// public void ClearIdentityProperties() { _identity.Clear(); } /// /// Gets the properties /// public ReadOnlyCollection Properties { get { return _properties.AsReadOnly(); } } /// /// Adds the specified data property, with an option to include it as an identity property /// /// /// public void AddProperty(DataPropertyDefinition prop, bool identity) { if (!_properties.Contains(prop)) _properties.Add(prop); if (identity && !_identity.Contains(prop)) _identity.Add(prop); prop.Parent = this; } /// /// Adds the specified property definition /// /// public void AddProperty(PropertyDefinition prop) { if (!_properties.Contains(prop)) _properties.Add(prop); prop.Parent = this; } /// /// Gets the index of the specified property /// /// /// public int IndexOfProperty(PropertyDefinition prop) { return _properties.IndexOf(prop); } /// /// Removes the property definition at the specified index. If it is a data property /// is is also removed from the identity properties (if it is specified as one) /// /// public void RemovePropertyAt(int index) { if (index < _properties.Count) { var prop = _properties[index]; _properties.RemoveAt(index); if (prop.Type == PropertyDefinitionType.Data) { _identity.Remove((DataPropertyDefinition)prop); prop.Parent = null; } } } /// /// Removes the specified property from the properties collection. If it is a data property definition, it is also /// removed from the identity properties collection /// /// /// public bool RemoveProperty(PropertyDefinition prop) { bool removed = _properties.Remove(prop); if (removed && prop.Type == PropertyDefinitionType.Data) { _identity.Remove((DataPropertyDefinition)prop); prop.Parent = null; } return removed; } #region old impl /* private string m_name; private string m_schema; private FeatureSetColumn[] m_columns; internal ClassDefinition(XmlNode node, XmlNamespaceManager mgr) { XmlNode root = node.ParentNode; if (root.NodeType == XmlNodeType.XmlDeclaration) root = root.NextSibling; m_schema = root.Attributes["targetNamespace"] == null ? null : root.Attributes["targetNamespace"].Value; if (m_schema != null && m_schema.IndexOf("/") > 0) m_schema = m_schema.Substring(m_schema.LastIndexOf("/") + 1); m_name = node.Attributes["name"].Value; if (m_name.EndsWith("Type")) m_name = m_name.Substring(0, m_name.Length - "Type".Length); XmlNodeList lst; if (node.ChildNodes.Count == 0) { m_columns = new FeatureSetColumn[0]; return; } else if (node.FirstChild.Name == "xs:sequence") lst = node.SelectNodes("xs:sequence/xs:element", mgr); else lst = node.SelectNodes("xs:complexContent/xs:extension/xs:sequence/xs:element", mgr); m_columns = new FeatureSetColumn[lst.Count]; for (int i = 0; i < lst.Count; i++) m_columns[i] = new ClassPropertyColumn(lst[i]); XmlNode extension = node.SelectSingleNode("xs:complexContent/xs:extension", mgr); if (extension != null && extension.Attributes["base"] != null) { string extTypeName = extension.Attributes["base"].Value; extTypeName = extTypeName.Substring(extTypeName.IndexOf(":") + 1); XmlNode baseEl = node.ParentNode.SelectSingleNode("xs:complexType[@name='" + extTypeName + "']", mgr); if (baseEl != null) { ClassDefinition tmpScm = new ClassDefinition(baseEl, mgr); FeatureSetColumn[] tmpCol = new FeatureSetColumn[m_columns.Length + tmpScm.m_columns.Length]; Array.Copy(m_columns, tmpCol, m_columns.Length); Array.Copy(tmpScm.m_columns, 0, tmpCol, m_columns.Length, tmpScm.m_columns.Length); m_columns = tmpCol; } } } /// /// Gets the name of this class definition /// public string Name { get { return m_name; } } /// /// Gets the name of the schema which this class definition belongs to /// public string SchemaName { get { return m_schema; } } /// /// Gets the fully qualified name of this class definition ([schema_name]:[name]) /// public string QualifiedName { get { return m_schema == null ? m_name : m_schema + ":" + m_name; } } /// /// Gets the decoded fully qualified name of this class definition ([schema_name]:[name]) /// public string QualifiedNameDecoded { get { return Utility.DecodeFDOName(this.QualifiedName); } } /// /// Gets an array of columns defining the properties in this class definition /// public FeatureSetColumn[] Columns { get { return m_columns; } } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return this.QualifiedName; } internal void MarkIdentityProperties(IEnumerable keyFieldNames) { foreach (var name in keyFieldNames) { foreach (var col in m_columns) { if (col.Name.Equals(name)) { col.IsIdentity = true; } } } } /// /// Gets an array of names of the identity properties /// /// public string[] GetIdentityProperties() { List keys = new List(); foreach (var col in m_columns) { if (col.IsIdentity) keys.Add(col.Name); } return keys.ToArray(); } */ #endregion public FeatureSchema Parent { get; internal set; } public PropertyDefinition FindProperty(string name) { foreach (var prop in _properties) { if (prop.Name.Equals(name)) return prop; } return null; } public string QualifiedName { get { return this.Parent != null ? this.Parent.Name + ":" + this.Name : this.Name; } } public void WriteXml(XmlDocument doc, XmlNode currentNode) { XmlElement id = null; if (_identity.Count > 0) { id = doc.CreateElement("xs", "element", XmlNamespaces.XS); id.SetAttribute("name", this.Name); id.SetAttribute("type", this.Parent.Name + ":" + this.Name + "Type"); id.SetAttribute("abstract", this.IsAbstract.ToString().ToLower()); id.SetAttribute("substitutionGroup", "gml:_Feature"); var key = doc.CreateElement("xs", "key", XmlNamespaces.XS); key.SetAttribute("name", this.Name + "Key"); var selector = doc.CreateElement("xs", "selector", XmlNamespaces.XS); selector.SetAttribute("xpath", ".//" + this.Name); key.AppendChild(selector); foreach (var prop in _identity) { var field = doc.CreateElement("xs", "field", XmlNamespaces.XS); field.SetAttribute("xpath", prop.Name); key.AppendChild(field); } id.AppendChild(key); } //Now write class body var ctype = doc.CreateElement("xs", "complexType", XmlNamespaces.XS); ctype.SetAttribute("name", this.Name + "Type"); //TODO: This may have been decoded. Should it be re-encoded? ctype.SetAttribute("abstract", this.IsAbstract.ToString().ToLower()); if (!string.IsNullOrEmpty(this.DefaultGeometryPropertyName)) { var geom = FindProperty(this.DefaultGeometryPropertyName) as GeometricPropertyDefinition; if (geom != null) { ctype.SetAttribute("geometryName", XmlNamespaces.FDO, geom.Name); } } var cnt = doc.CreateElement("xs", "complexContent", XmlNamespaces.XS); ctype.AppendChild(cnt); var ext = doc.CreateElement("xs", "extension", XmlNamespaces.XS); if (this.BaseClass != null) ext.SetAttribute("base", this.BaseClass.QualifiedName); else ext.SetAttribute("base", "gml:AbstractFeatureType"); cnt.AppendChild(ext); var seq = doc.CreateElement("xs", "sequence", XmlNamespaces.XS); ext.AppendChild(seq); foreach (var prop in _properties) { prop.WriteXml(doc, seq); } if (id != null) currentNode.AppendChild(id); currentNode.AppendChild(ctype); } public void ReadXml(XmlNode node, XmlNamespaceManager mgr) { var abn = node.Attributes["abstract"]; if (abn != null) this.IsAbstract = Convert.ToBoolean(abn.Value); //Process properties XmlNodeList propNodes = node.SelectNodes("xs:complexContent/xs:extension/xs:sequence/xs:element", mgr); if (propNodes.Count == 0) propNodes = node.SelectNodes("xs:sequence/xs:element", mgr); foreach (XmlNode propNode in propNodes) { var prop = PropertyDefinition.Parse(propNode, mgr); this.AddProperty(prop); } //Set designated geometry property var geom = node.Attributes["fdo:geometryName"]; if (geom != null) this.DefaultGeometryPropertyName = geom.Value; //TODO: Base class //Process identity properties var parent = node.ParentNode; var key = parent.SelectSingleNode("xs:element[@name='" + this.Name + "']/xs:key", mgr); if (key != null) { var fields = key.SelectNodes("xs:field", mgr); foreach (XmlNode f in fields) { var idpropa = f.Attributes["xpath"]; if (idpropa == null) throw new Exception("Bad document. Expected attribute: xpath"); //LOCALIZEME var prop = FindProperty(idpropa.Value); if (prop != null && prop.Type == PropertyDefinitionType.Data) _identity.Add((DataPropertyDefinition)prop); } } } } }