private Node CreateNode([NotNull] string key)
        {
            var content = new XElement(_valueNodeName);

            content.Add(new XText(string.Empty));

            var entry = new XElement(_dataNodeName, new XAttribute(_nameAttributeName, key), new XAttribute(_spaceAttributeName, @"preserve"));

            entry.Add(content, new XText("\n  "));

            var fileContentSorting = _configuration.EffectiveResXSortingComparison;

            if (fileContentSorting.HasValue)
            {
                SortAndAdd(fileContentSorting.Value, entry);
            }
            else
            {
                DocumentRoot.Add(entry);
            }

            UpdateNodes();

            try
            {
                new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()).StartNew(() => Container.OnItemOrderChanged(this));
            }
            catch (InvalidOperationException)
            {
                // for scripting deferred notifications are not needed, so if the current thread does not support a synchronization context, just go without.
            }

            return(_nodes[key]);
        }
        private Node CreateNode([NotNull] string key)
        {
            var content = new XElement(_valueNodeName);

            content.Add(new XText(string.Empty));

            var entry = new XElement(_dataNodeName, new XAttribute(_nameAttributeName, key), new XAttribute(_spaceAttributeName, @"preserve"));

            entry.Add(content, new XText("\n  "));

            var fileContentSorting = _configuration.EffectiveResXSortingComparison;

            if (fileContentSorting.HasValue)
            {
                SortAndAdd(fileContentSorting.Value, entry);
            }
            else
            {
                DocumentRoot.Add(entry);
            }

            UpdateNodes();

            Dispatcher.CurrentDispatcher.BeginInvoke(() => Container.OnItemOrderChanged(this));

            return(_nodes[key]);
        }
        private bool SortAndAdd(StringComparison stringComparison, XElement?newNode)
        {
            var comparer = new DelegateComparer <string>((left, right) => string.Compare(left, right, stringComparison));

            string GetName(XElement node) => node.Attribute(_nameAttributeName)?.Value.TrimStart('>') ?? string.Empty;

            var nodes = DocumentRoot
                        .Elements(_dataNodeName)
                        .ToArray();

            var sortedNodes = nodes
                              .OrderBy(GetName, comparer)
                              .ToArray();

            var hasContentChanged = SortNodes(nodes, sortedNodes);

            if (newNode == null)
            {
                return(hasContentChanged);
            }

            var newNodeName = GetName(newNode);
            var nextNode    = sortedNodes.FirstOrDefault(node => comparer.Compare(GetName(node), newNodeName) > 0);

            if (nextNode != null)
            {
                nextNode.AddBeforeSelf(newNode);
            }
            else
            {
                DocumentRoot.Add(newNode);
            }

            return(true);
        }
        /// <summary>
        /// An undocumented "feature" of XElement.Add is that it *SOMETIMES* (and only sometimes) adds a copy of the passed XElement, not the element itself.
        /// Nothing we can do about that. However, this method returns the created copy for further reference.
        /// Also assigns a unique ID to the shape, if the existing ID is taken.
        /// </summary>
        public XElement AddShape(XElement shape)
        {
            VerifyState(ModelState.Ready, ModelState.Busy);

            String idString = shape.GetStringAttribute("Id");

            if (idString != "" && GetShapeById(idString) != null)
            {
                shape.SetAttributeValue("Id", GetUniqueId());
            }

            DocumentRoot.Add(shape);
            XElement copy = (XElement)DocumentRoot.LastNode;

            return(copy);
        }
        private bool SortNodes([NotNull, ItemNotNull] XElement[] nodes, [NotNull, ItemNotNull] XElement[] sortedNodes)
        {
            if (nodes.SequenceEqual(sortedNodes))
            {
                return(false);
            }

            foreach (var item in nodes)
            {
                item.Remove();
            }

            foreach (var item in sortedNodes)
            {
                DocumentRoot.Add(item);
            }

            return(true);
        }