private void PopulateDescendantsInternal(ParentedEXmlObject currentScannedParent)
 {
     foreach (ParentedEXmlObject obj in currentScannedParent.Children)
     {
         DescendantsInternal.Add(obj);
         PopulateDescendantsInternal(obj);
     }
 }
        /// <summary>
        /// Converts an EXmlBase and all of its child elements into an XXMLBase. Do not implicitly or explicitly cast EXmlBase into XXMLObject as it will not populate the parent data.
        /// </summary>
        /// <param name="obj">The EXmlBase to convert.</param>
        /// <param name="parent">The parent object of the XXMLObject. If you are converting an EXmlBase manually, it is advised that you do not specify this.</param>
        public static ParentedEXmlObject TransformEntireElementTree(EXmlBase obj, ParentedEXmlObject parent = null)
        {
            ParentedEXmlObject root = new ParentedEXmlObject(obj);

            root.Parent = parent;
            foreach (EXmlBase exml in obj.Elements)
            {
                root.Children.Add(TransformEntireElementTree(exml, root));
            }
            return(root);
        }
        /// <summary>
        /// Converts this custom object back into an EXmlBase so that it can be serialized back into an MBIN.
        /// </summary>
        /// <returns></returns>
        public EXmlBase ConvertToSerializableEXML(EXmlBase parent = null, ParentedEXmlObject parentAsNative = null)
        {
            EXmlBase root;

            if (parent == null)
            {
                // Attribute is gonna be template. If it's not, there's a problem.
                if (Attribute.AttributeName != "template")
                {
                    throw new Exception("Top level attribute is not a template!");
                }
                EXmlData newData = new EXmlData();
                newData.Name     = Name;
                newData.Template = Attribute.Value;
                root             = newData;
            }
            else
            {
                root = parent;
            }

            //EXmlBase root = parent ?? this;
            parentAsNative = parentAsNative ?? this;
            root.Elements  = new List <EXmlBase>();
            foreach (ParentedEXmlObject child in parentAsNative.Children)
            {
                EXmlBase childAsBase = child;
                if (child.Attribute?.AttributeName == "template")
                {
                    EXmlData childAsTyped = new EXmlData();
                    childAsTyped.Name     = childAsBase.Name;
                    childAsTyped.Elements = childAsBase.Elements;
                    childAsTyped.Template = child.Attribute.Value;
                    root.Elements.Add(childAsTyped);
                    childAsBase = childAsTyped;
                }
                else if (child.Attribute?.AttributeName == "value")
                {
                    EXmlProperty childAsTyped = new EXmlProperty();
                    childAsTyped.Name     = childAsBase.Name;
                    childAsTyped.Elements = childAsBase.Elements;
                    childAsTyped.Value    = child.Attribute.Value;
                    root.Elements.Add(childAsTyped);
                    childAsBase = childAsTyped;
                }
                else if (child.Attribute?.AttributeName == "comment")
                {
                    EXmlMeta childAsTyped = new EXmlMeta();
                    childAsTyped.Name     = childAsBase.Name;
                    childAsTyped.Elements = childAsBase.Elements;
                    childAsTyped.Comment  = child.Attribute.Value;
                    root.Elements.Add(childAsTyped);
                    childAsBase = childAsTyped;
                }
                else
                {
                    root.Elements.Add(childAsBase);
                }

                ConvertToSerializableEXML(childAsBase, child);
            }
            return(root);
        }
        /// <summary>
        /// Returns a ParentedEXmlObject within the specified parent that can be found with the specified path.
        /// </summary>
        /// <param name="parent">The parent EXML node.</param>
        /// <param name="path">The path to the desired node.</param>
        /// <returns></returns>
        public ParentedEXmlObject GetObjectFromParentAndPath(ParentedEXmlObject parent, string path)
        {
            if (parent == null || path == null)
            {
                return(null);
            }

            string[] pathElements = path.Split('.');
            if (pathElements.Length > 1)
            {
                pathElements = pathElements.Skip(1).ToArray();
            }
            else
            {
                return(null);
            }

            foreach (string target in pathElements)
            {
                if (target.StartsWith("DataContainer["))
                {
                    // This is a custom container with no name. Don't search by name.
                    string valueName = target.Replace("DataContainer[", "");
                    valueName = valueName.Substring(0, valueName.Length - 1);                     // Cuts off the ending ]
                    foreach (ParentedEXmlObject child in parent.Children)
                    {
                        if (child.Attribute.Value.Replace(".xml", "") == valueName)
                        {
                            parent = child;                             // Update the parent and then break the loop.
                            break;
                        }
                    }
                }
                else
                {
                    // Normal value.
                    foreach (ParentedEXmlObject child in parent.Children)
                    {
                        if (child.Name == target)
                        {
                            parent = child;                             // Update the parent and then break the loop.
                            break;
                        }
                    }
                }
            }

            // Lastly, check if we got the right child. If we got lost somewhere in the tree, the name of the child won't be the same as the last path element.
            string lastElement       = pathElements.Last();
            string intendedChildName = null;

            if (lastElement.StartsWith("DataContainer["))
            {
                // It's a container. Strip the garbage.
                intendedChildName = parent.Attribute.Value.Replace(".xml", "");
                lastElement       = lastElement.Replace("DataContainer[", "");
                lastElement       = lastElement.Substring(0, intendedChildName.Length - 1);
            }
            else
            {
                intendedChildName = parent.Name;
            }

            if (lastElement == intendedChildName)
            {
                return(parent);
            }

            // Report the error.
            ConsoleColor oldColor = Console.ForegroundColor;

            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("Unable to find child from path {0} (last child was {1})", path, parent.Name);
            Console.ForegroundColor = oldColor;
            return(null);
        }