private static XmNodeOptions GetNodeOptions(XmlElement xe, string strXPath,
                                                    XmContext ctx)
        {
            XmNodeOptions o = new XmNodeOptions();

            ctx.GetNodeOptions(o, strXPath);
            o.LoadElementAttributes(xe);
            return(o);
        }
            public static XmElementCollection FromChildNodes(XmlElement xeParent,
                                                             string strParentXPath, XmContext ctx)
            {
                XmElementCollection c = new XmElementCollection();

                if (xeParent == null)
                {
                    Debug.Assert(false); return(c);
                }
                if (strParentXPath == null)
                {
                    Debug.Assert(false); return(c);
                }
                if (ctx == null)
                {
                    Debug.Assert(false); return(c);
                }

                Dictionary <string, XmlElement> d = c.m_d;

                foreach (XmlNode xn in xeParent.ChildNodes)
                {
                    XmlElement xe = (xn as XmlElement);
                    if (xe == null)
                    {
                        continue;
                    }

                    string strNameC      = xe.Name;
                    string strXPathC     = strParentXPath + "/" + strNameC;
                    string strKeyCustomC = ctx.GetNodeKey(xe, strXPathC);

                    string strKey = BuildNodeKey(strNameC, strKeyCustomC, 0);
                    if (d.ContainsKey(strKey))
                    {
                        // In general, custom node keys should be unique
                        Debug.Assert(string.IsNullOrEmpty(strKeyCustomC));

                        for (int i = d.Count; i >= 0; --i)
                        {
                            string strKeyTest = BuildNodeKey(strNameC, strKeyCustomC, i);
                            if (d.ContainsKey(strKeyTest))
                            {
                                Debug.Assert(i != d.Count);                                 // Key must change in first iteration
                                break;
                            }

                            strKey = strKeyTest;
                        }
                        Debug.Assert(!d.ContainsKey(strKey));
                    }

                    c.Add(strKey, xe);
                }

                return(c);
            }
        // Old, for plugins
        public static void MergeNodes(XmlDocument xd, XmlNode xn, XmlNode xnOverride)
        {
            XmlElement xeBase     = (xn as XmlElement);
            XmlElement xeOverride = (xnOverride as XmlElement);
            string     strXPath   = ((xn != null) ? xn.Name : string.Empty);

            XmContext ctx = new XmContext(xd,
                                          delegate(XmNodeOptions oP, string strXPathP) { },
                                          delegate(XmlNode xnP, string strXPathP) { return(null); });

            MergeElements(xeBase, xeOverride, strXPath, null, ctx);
        }
        internal static bool IsAlwaysEnforced(XmlNode xn, string strXPath,
                                              XmContext ctx)
        {
            if (xn == null)
            {
                Debug.Assert(false); return(false);
            }
            if (xn.NodeType == XmlNodeType.Document)
            {
                return(true);
            }
            // If xn is the document node, strXPath is empty
            if (string.IsNullOrEmpty(strXPath))
            {
                Debug.Assert(false); return(false);
            }
            if (ctx == null)
            {
                Debug.Assert(false); return(false);
            }

            XmlElement xe = (xn as XmlElement);

            if (xe == null)
            {
                Debug.Assert(false); return(false);
            }

            XmNodeOptions o = GetNodeOptions(xe, strXPath, ctx);

            if ((o.NodeMode == XmNodeMode.None) ||
                (o.NodeMode == XmNodeMode.Create))
            {
                return(false);
            }
            if (o.ContentMode == XmContentMode.None)
            {
                return(false);
            }

            XmlNode xnP = xe.ParentNode;

            if (xnP == null)
            {
                Debug.Assert(false); return(false);
            }
            if (object.ReferenceEquals(xnP, xn))
            {
                Debug.Assert(false); return(false);
            }

            int iSep = strXPath.LastIndexOf('/');

            if (iSep < 0)
            {
                Debug.Assert(false); return(false);
            }
            string strXPathP = strXPath.Substring(0, iSep);

            return(IsAlwaysEnforced(xnP, strXPathP, ctx));
        }
        internal static XmlElement MergeElements(XmlElement xeBase, XmlElement xeOverride,
                                                 string strXPath, XmlElement xeBaseParent, XmContext ctx)
        {
            if (xeOverride == null)
            {
                throw new ArgumentNullException("xeOverride");
            }
            if (strXPath == null)
            {
                throw new ArgumentNullException("strXPath");
            }
            if (ctx == null)
            {
                throw new ArgumentNullException("ctx");
            }

            string strName = xeOverride.Name;

            Debug.Assert((xeBase == null) || (xeBase.Name == strName));
            Debug.Assert((strXPath == strName) || strXPath.EndsWith("/" + strName));
            Debug.Assert((xeBase == null) || (xeBaseParent == null) ||
                         object.ReferenceEquals(xeBase.ParentNode, xeBaseParent));

            XmNodeOptions o = GetNodeOptions(xeOverride, strXPath, ctx);

            bool bContinue = false;

            switch (o.NodeMode)
            {
            case XmNodeMode.None: break;

            case XmNodeMode.Create:
                if ((xeBase == null) && (xeBaseParent != null))
                {
                    xeBase = ctx.BaseDocument.CreateElement(strName);
                    xeBaseParent.AppendChild(xeBase);
                    bContinue = true;
                }
                break;

            case XmNodeMode.Open:
                if (xeBase != null)
                {
                    bContinue = true;
                }
                break;

            case XmNodeMode.OpenOrCreate:
                if (xeBase != null)
                {
                    bContinue = true;
                }
                else if (xeBaseParent != null)
                {
                    xeBase = ctx.BaseDocument.CreateElement(strName);
                    xeBaseParent.AppendChild(xeBase);
                    bContinue = true;
                }
                break;

            case XmNodeMode.Remove:
                if (xeBase != null)
                {
                    if (xeBaseParent != null)
                    {
                        xeBaseParent.RemoveChild(xeBase);
                        xeBase = null;                                 // Return value, indicate removal
                    }
                    else
                    {
                        // Cannot remove element; clear it instead
                        xeBase.InnerXml = string.Empty;
                    }
                }
                break;

            default: Debug.Assert(false); break;
            }
            if (!bContinue)
            {
                return(xeBase);
            }
            if (xeBase == null)
            {
                Debug.Assert(false); return(null);
            }

            XmContentMode cm = o.ContentMode;

            if ((cm == XmContentMode.Merge) && !HasChildElement(xeBase) &&
                !HasChildElement(xeOverride))
            {
                cm = XmContentMode.Replace;
            }

            XmElementCollection xcBase = null, xcOverride = null;

            if (cm == XmContentMode.Merge)
            {
                xcBase     = XmElementCollection.FromChildNodes(xeBase, strXPath, ctx);
                xcOverride = XmElementCollection.FromChildNodes(xeOverride, strXPath, ctx);

                if (o.ChildrenOtherMode == XmChildrenOtherMode.Remove)
                {
                    List <string> lRemove = new List <string>();
                    foreach (KeyValuePair <string, XmlElement> kvpBaseC in xcBase)
                    {
                        if (xcOverride[kvpBaseC.Key] == null)
                        {
                            xeBase.RemoveChild(kvpBaseC.Value);
                            lRemove.Add(kvpBaseC.Key);
                        }
                    }
                    // Do not simply rebuild xcBase, because indices in
                    // node keys would change
                    xcBase = xcBase.Except(lRemove);
                }
            }

            switch (cm)
            {
            case XmContentMode.None: Debug.Assert(false); break;                     // Currently unused

            case XmContentMode.Merge:
                foreach (KeyValuePair <string, XmlElement> kvpOverrideC in xcOverride)
                {
                    string     strKeyC = kvpOverrideC.Key;
                    XmlElement xeBaseC = xcBase[strKeyC];

                    XmlElement xeOverrideC = kvpOverrideC.Value;
                    string     strXPathC   = strXPath + "/" + xeOverrideC.Name;

                    XmlElement xeBaseCNew = MergeElements(xeBaseC,
                                                          xeOverrideC, strXPathC, xeBase, ctx);

                    if (!object.ReferenceEquals(xeBaseCNew, xeBaseC))
                    {
                        Debug.Assert((xeBaseCNew == null) || (xeBaseC == null));
                        if (xeBaseCNew == null)
                        {
                            xcBase.Remove(strKeyC);
                        }
                        else
                        {
                            xcBase.Add(strKeyC, xeBaseCNew);
                        }
                    }
                }
                break;

            case XmContentMode.Replace:
                xeBase.InnerXml = SafeInnerXml(xeOverride);
                break;

            default: Debug.Assert(false); break;
            }

            if ((cm == XmContentMode.Merge) && (o.ChildrenSortOrder ==
                                                XmChildrenSortOrder.This))
            {
                xcBase.SortBy(xcOverride);
                xcBase.ReorderElementsOf(xeBase);
            }

            return(xeBase);
        }