#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(); } /// /// Initializes a new instance of the class. /// /// The name. /// The description. public ClassDefinition(string name, string description) : this() { this.Name = name; this.Description = description; } /// /// Gets or sets the base class /// public ClassDefinition BaseClass { get; set; } /// /// Gets the property definition at the specified index /// /// /// public PropertyDefinition this[int index] { get { return _properties[index]; } } /// /// Gets the ordinal of the specified property name /// /// The property name. /// 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; } /// /// Gets the parent schema /// public FeatureSchema Parent { get; internal set; } /// /// Gets a Property Definition by its name /// /// /// The matching property definition. null if none found public PropertyDefinition FindProperty(string name) { foreach (var prop in _properties) { if (prop.Name.Equals(name)) return prop; } return null; } /// /// Gets the qualified name of this class. The qualified name takes the form [Schema Name]:[Class Name] /// public string QualifiedName { get { return this.Parent != null ? this.Parent.Name + ":" + this.Name : this.Name; } } /// /// Writes the current element's content /// /// /// public void WriteXml(XmlDocument doc, XmlNode currentNode) { XmlElement id = null; var en = Utility.EncodeFDOName(this.Name); if (_identity.Count > 0) { id = doc.CreateElement("xs", "element", XmlNamespaces.XS); id.SetAttribute("name", en); //TODO: May need encoding id.SetAttribute("type", this.Parent.Name + ":" + en + "Type"); id.SetAttribute("abstract", this.IsAbstract.ToString().ToLower()); id.SetAttribute("substitutionGroup", "gml:_Feature"); var key = doc.CreateElement("xs", "key", XmlNamespaces.XS); key.SetAttribute("name", en + "Key"); var selector = doc.CreateElement("xs", "selector", XmlNamespaces.XS); selector.SetAttribute("xpath", ".//" + en); 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", en + "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); } } else { ctype.SetAttribute("hasGeometry", XmlNamespaces.FDO, "false"); } //Write description node var anno = doc.CreateElement("xs", "annotation", XmlNamespaces.XS); //NOXLATE var docN = doc.CreateElement("xs", "documentation", XmlNamespaces.XS); //NOXLATE docN.InnerText = this.Description; ctype.AppendChild(anno); anno.AppendChild(docN); var cnt = doc.CreateElement("xs", "complexContent", XmlNamespaces.XS); //NOXLATE 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); } /// /// Set the current element's content from the current XML node /// /// /// public void ReadXml(XmlNode node, XmlNamespaceManager mgr) { var en = Utility.EncodeFDOName(this.Name); var abn = node.Attributes["abstract"]; if (abn != null) this.IsAbstract = Convert.ToBoolean(abn.Value); //Description var docNode = node.SelectSingleNode("xs:annotation/xs:documentation", mgr); //NOXLATE if (docNode != null) this.Description = docNode.InnerText; //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 = Utility.GetFdoAttribute(node, "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=\"" + en + "\"]/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); } } } } }