public bool LoadSchema(string schemaXml, List<string> excludePaths)
        {
            if (excludePaths == null) excludePaths = new List<string>();

            XDocument doc = XDocument.Parse(schemaXml);

            //set the root element and try to get a root element name
            _root = new SchemaElement();

            var namespaceAttribute = doc.Root.Attribute(XName.Get("targetNamespace"));
            if (namespaceAttribute != null)
                _root.Namespace = namespaceAttribute.Value;
            else
                _root.Namespace = "";

            _root.Root = _root;

            //recursively extract information about all child nodes
            foreach (XElement child in doc.Root.Elements())
                processNodes(child, _root, excludePaths);

            explodeChildren(_root, "", null, 0);

            //find the real root - in syspro the root is actually a sub-node but some of the
            //schemas are actually incorrect. these schemas define child items in the root
            //but then instead of referencing in their correct location, they are defined again.
            //as a result, the only reliable way of determining the correct root is to use
            //the node with the highest number of children, evluated recursively
            if (_root.Name == "")
            {
                SchemaElement newRoot = null;
                int highestChildren = 0;
                foreach (KeyValuePair<string, SchemaElement> kvp in _root.Children)
                    if (kvp.Value.TotalChildren > highestChildren)
                    {
                        newRoot = kvp.Value;
                        highestChildren = kvp.Value.TotalChildren;
                    }

                if (newRoot != null)
                    _root = newRoot;
            }

            return true;
        }
        public void ReplaceChild(SchemaElement element, string key)
        {
            RemoveChild(key);

            AddChild(element, key);
        }
        /// <summary>
        /// Clones the current object but does not clone all child properties
        /// </summary>
        /// <returns></returns>
        public SchemaElement CreateDuplicate()
        {
            SchemaElement el = new SchemaElement()
            {
                Nillable = this.Nillable,

                MinOccurs = this.MinOccurs,
                MaxOccurs = this.MaxOccurs,
                MinLength = this.MinLength,
                MaxLength = this.MaxLength,

                EnumerationValues = this.EnumerationValues,

                DefaultValue = this.DefaultValue,

                //not cloned
                Attributes = this.Attributes,

                FieldType = this.FieldType,

                Name = this.Name,

                Reference = this.Reference,

                Documentation = this.Documentation,

                //not cloned
                Parent = this.Parent,

                //not cloned
                Root = this.Root,

                Namespace = this.Namespace,

                Children = new Dictionary<string, SchemaElement>()
            };

            foreach (var child in this.Children)
            {
                var duplicateChild = child.Value.CreateDuplicate();
                duplicateChild.Parent = el;
                el.Children.Add(child.Key, duplicateChild);
            }

            return el;
        }
 public void AddChild(SchemaElement element, string key)
 {
     element.Parent = this;
     try
     {
         _children.Add(key, element);
     }
     catch (Exception ex)
     {
         throw new Exception(ex.Message + " (Key '" + key + "')");
     }
 }
 public void AddChild(SchemaElement element)
 {
     AddChild(element, element.Name);
 }
 private void buildNodePath(SchemaElement element, ref string path)
 {
     path = element.Name + (path.Length == 0 ? "" : "/") + path;
     if (element.Parent != null) buildNodePath(element.Parent, ref path);
 }
        private void processNodes(XElement node, SchemaElement curParent, List<string> excludePaths)
        {
            string partName = node.Name.LocalName.ToLower();

            SchemaElement curEl;

            switch (partName)
            {
                case "element":
                    curEl = new SchemaElement()
                    {
                        Root = _root,
                        Name = (node.Attribute(XName.Get("name")) == null) ? "" : node.Attribute(XName.Get("name")).Value,
                        Reference = node.Attribute(XName.Get("ref")) == null ? "" : node.Attribute(XName.Get("ref")).Value,
                        DefaultValue = node.Attribute(XName.Get("default")) == null ? "" : node.Attribute(XName.Get("default")).Value,
                    };

                    //changed assumption of "nillable" to true when not specified
                    curEl.Nillable = node.Attribute(XName.Get("nillable")) == null ? false : ((node.Attribute(XName.Get("nillable")).Value.ToLower() == "true") || (node.Attribute(XName.Get("nillable")).Value == "1"));

                    if (node.Attribute(XName.Get("minOccurs")) != null)
                    {
                        if (!Int32.TryParse(node.Attribute(XName.Get("minOccurs")).Value, out curEl.MinOccurs)) curEl.MinOccurs = SchemaElement.UndefinedValue;
                    }

                    if (node.Attribute(XName.Get("maxOccurs")) != null)
                    {
                        if (!Int32.TryParse(node.Attribute(XName.Get("maxOccurs")).Value, out curEl.MaxOccurs)) curEl.MaxOccurs = SchemaElement.UndefinedValue;
                    }

                    string type = node.Attribute(XName.Get("type")) == null ? "" : node.Attribute(XName.Get("type")).Value;

                    curEl.FieldType = type.IndexOf(":") > -1 ? type.Substring(type.IndexOf(":") + 1) : type;

                    if ((curEl.Reference == "") && (type.Length > 0) && (type.IndexOf(":") == -1))
                        curEl.Reference = type;

                    //cache all attributes
                    curEl.Attributes = node.Attributes();

                    //apply exclusion paths
                    if (excludePaths.Contains(getNodePath(curParent) + "/" + curEl.Name)) return;

                    curParent.AddChild(curEl);

                    //process all child elements - curEl is the parent
                    foreach (XElement childNode in node.Elements())
                        processNodes(childNode, curEl, excludePaths);
                    break;

                case "complextype":
                // 2015/09/02 - allowing simple types to be declared as references too
                case "simpletype":
                    if (node.Attribute(XName.Get("name")) == null)
                    {
                        foreach (XElement childNode in node.Elements())
                            processNodes(childNode, curParent, excludePaths);
                    }
                    else
                    {
                        curEl = new SchemaElement()
                        {
                            Root = _root,
                            Name = (node.Attribute(XName.Get("name")) == null) ? "" : node.Attribute(XName.Get("name")).Value,
                        };

                        //apply exclusion paths
                        if (excludePaths.Contains(getNodePath(curParent) + "/" + curEl.Name)) return;

                        curParent.AddChild(curEl);

                        //process all child elements - curEl is the parent
                        foreach (XElement childNode in node.Elements())
                            processNodes(childNode, curEl, excludePaths);
                    }
                    break;

                case "documentation":
                    //write documentation for the current parent
                    curParent.Documentation = cleanDocumentation(node.Value);
                    break;

                case "enumeration":
                    if (node.Attribute(XName.Get("value")) != null)
                        curParent.EnumerationValues.Add(node.Attribute(XName.Get("value")).Value);
                    break;

                case "minlength":
                    if ((node.Parent.Name.LocalName.ToLower().EndsWith("restriction")) && (node.Attribute(XName.Get("value")) != null))
                    {
                        if (!Int32.TryParse(node.Attribute(XName.Get("value")).Value, out curParent.MinLength)) curParent.MinLength = SchemaElement.UndefinedValue;
                    }
                    break;

                case "maxlength":
                    if ((node.Parent.Name.LocalName.ToLower().EndsWith("restriction")) && (node.Attribute(XName.Get("value")) != null))
                    {
                        if (!Int32.TryParse(node.Attribute(XName.Get("value")).Value, out curParent.MaxLength)) curParent.MaxLength = SchemaElement.UndefinedValue;
                    }
                    break;
            }

            if ((partName == "annotation") ||
                (partName == "restriction") ||
                (partName == "sequence") ||
                (partName == "all"))
            {
                foreach (XElement childNode in node.Elements())
                    processNodes(childNode, curParent, excludePaths);
            }
        }
 private string getNodePath(SchemaElement element)
 {
     string path = "";
     buildNodePath(element, ref path);
     return path;
 }
        private void explodeChildren(SchemaElement element, string elementKey, SchemaElement parent, int currentDepth)
        {
            if (currentDepth >= MAX_SCHEMA_DEPTH) return;

            foreach (var child in new Dictionary<string, SchemaElement>(element.Children))
            {
                SchemaElement childElement = child.Value;
                if (childElement.IsReference)
                {
                    if (!_root.Children.ContainsKey(childElement.Reference))
                        throw new Exception("Referenced element '" + childElement.Reference + "' is not defined!");

                    if (_root.Children[childElement.Reference].IsReference)
                        throw new Exception("Referenced element '" + childElement.Reference + "' is a reference!");

                    //remove self and add the referenced item instead
                    childElement = _root.Children[childElement.Reference].CreateDuplicate();
                    childElement.Parent = element;
                    childElement.Name = child.Key;
                    element.ReplaceChild(childElement, child.Key);
                }

                explodeChildren(childElement, child.Key, element, currentDepth + 1);
            }
        }