private static string GetLineInfo(XmlReader reader)
        {
            var lineInfo = reader as IXmlLineInfo;

            return(lineInfo == null ?  string.Empty :
                   Resources.FormatMsg_LineInfo(lineInfo.LineNumber, lineInfo.LinePosition));
        }
Esempio n. 2
0
        public void CommitOperationThrowsExceptionWhenFindNewlyAddedKeyAfterLoadOperation()
        {
            var xml               = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<?xml-stylesheet type=""text/xsl"" href=""style1.xsl""?>
<settings>
    <?xml-stylesheet type=""text/xsl"" href=""style2.xsl""?>
    <Data>
        <DefaultConnection>
            <ConnectionString>TestConnectionString</ConnectionString>
            <Provider>SqlClient</Provider>
        </DefaultConnection>
        <Inventory>
            <ConnectionString>AnotherTestConnectionString</ConnectionString>
            <Provider>MySql</Provider>
        </Inventory>
    </Data>
</settings>";
            var modifiedXml       = @"<?xml version=""1.0"" encoding=""UTF-8""?>
<?xml-stylesheet type=""text/xsl"" href=""style1.xsl""?>
<settings>
    <?xml-stylesheet type=""text/xsl"" href=""style2.xsl""?>
    <Data>
        <DefaultConnection>
            <ConnectionString>TestConnectionString</ConnectionString>
            <Provider>SqlClient</Provider>
            <NewKey>NewValue</NewKey>
        </DefaultConnection>
        <Inventory>
            <ConnectionString>AnotherTestConnectionString</ConnectionString>
            <Provider>MySql</Provider>
        </Inventory>
    </Data>
</settings>";
            var xmlConfigSrc      = new XmlConfigurationSource(ArbitraryFilePath);
            var outputCacheStream = new MemoryStream();

            xmlConfigSrc.Load(StringToStream(xml));

            var exception = Assert.Throws <InvalidOperationException>(
                () => xmlConfigSrc.Commit(StringToStream(modifiedXml), outputCacheStream));

            Assert.Equal(
                Resources.FormatError_CommitWhenNewKeyFound("Data:DefaultConnection:NewKey"), exception.Message);
        }
        // Common attributes contribute to key-value pairs
        // This method adds a key-value pair if current node in reader represents a common attribute
        private static void AddAttributePair(XmlReader reader, Stack <string> prefixStack,
                                             IDictionary <string, string> data, XmlWriter writer)
        {
            if (string.Equals(reader.LocalName, NameAttributeKey, StringComparison.OrdinalIgnoreCase))
            {
                return;
            }

            prefixStack.Push(reader.LocalName);
            var key = string.Join(Constants.KeyDelimiter, prefixStack.Reverse <string>());

            if (data.ContainsKey(key))
            {
                throw new FormatException(Resources.FormatError_KeyIsDuplicated(key, GetLineInfo(reader)));
            }

            data[key] = reader.Value;
            prefixStack.Pop();
        }
        private void ProcessAttributes(XmlReader reader, Stack <string> prefixStack, IDictionary <string, string> data,
                                       Action <XmlReader, Stack <string>, IDictionary <string, string>, XmlWriter> act, XmlWriter writer = null)
        {
            for (int i = 0; i < reader.AttributeCount; i++)
            {
                reader.MoveToAttribute(i);

                // If there is a namespace attached to current attribute
                if (!string.IsNullOrEmpty(reader.NamespaceURI))
                {
                    throw new FormatException(Resources.FormatError_NamespaceIsNotSupported(GetLineInfo(reader)));
                }

                act(reader, prefixStack, data, writer);
            }

            // Go back to the element containing the attributes we just processed
            reader.MoveToElement();
        }
Esempio n. 5
0
        public void CommitOperationThrowsExceptionWhenFindInvalidModificationAfterLoadOperation()
        {
            var xml               = @"<?xml version=""1.0"" encoding=""UTF-8""?>
                    <?xml-stylesheet type=""text/xsl"" href=""style1.xsl""?>
                    <settings>
                        <?xml-stylesheet type=""text/xsl"" href=""style2.xsl""?>
                        <Data>
                            <DefaultConnection>
                                <ConnectionString>TestConnectionString</ConnectionString>
                                <Provider>SqlClient</Provider>
                            </DefaultConnection>
                            <Inventory>
                                <ConnectionString>AnotherTestConnectionString</ConnectionString>
                                <Provider>MySql</Provider>
                            </Inventory>
                        </Data>
                    </settings>";
            var modifiedXml       = @"<?xml version=""1.0"" encoding=""UTF-8""?>
                    <?xml-stylesheet type=""text/xsl"" href=""style1.xsl""?>
                    <settings xmlns:MyNameSpace=""http://microsoft.com/wwa/mynamespace"">
                        <?xml-stylesheet type=""text/xsl"" href=""style2.xsl""?>
                        <MyNameSpace:Data>
                            <DefaultConnection>
                                <ConnectionString>TestConnectionString</ConnectionString>
                                <Provider>SqlClient</Provider>
                            </DefaultConnection>
                            <Inventory>
                                <ConnectionString>AnotherTestConnectionString</ConnectionString>
                                <Provider>MySql</Provider>
                            </Inventory>
                        </MyNameSpace:Data>
                    </settings>";
            var xmlConfigSrc      = new XmlConfigurationSource(ArbitraryFilePath);
            var outputCacheStream = new MemoryStream();

            xmlConfigSrc.Load(StringToStream(xml));

            var exception = Assert.Throws <FormatException>(
                () => xmlConfigSrc.Commit(StringToStream(modifiedXml), outputCacheStream));

            Assert.Equal(Resources.FormatError_NamespaceIsNotSupported(Resources.FormatMsg_LineInfo(3, 31)),
                         exception.Message);
        }
        // Read an attribute key-value pair and write it to output destination
        // When writting, the value in memory (i.e. data) is used as new value of current attribute
        private static void CommitAttributePair(XmlReader reader, Stack <string> prefixStack,
                                                IDictionary <string, string> data, XmlWriter writer)
        {
            if (string.Equals(reader.LocalName, NameAttributeKey, StringComparison.OrdinalIgnoreCase))
            {
                writer.WriteAttributeString(reader.LocalName, reader.Value);
                return;
            }

            prefixStack.Push(reader.LocalName);
            var key = string.Join(Constants.KeyDelimiter, prefixStack.Reverse <string>());

            if (!data.ContainsKey(key))
            {
                throw new InvalidOperationException(Resources.FormatError_CommitWhenNewKeyFound(key));
            }

            writer.WriteAttributeString(reader.LocalName, data[key]);
            data.Remove(key);
            prefixStack.Pop();
        }
Esempio n. 7
0
        public void ThrowExceptionWhenKeyIsDuplicated()
        {
            var xml =
                @"<settings>
                    <Data>
                        <DefaultConnection>
                            <ConnectionString>TestConnectionString</ConnectionString>
                            <Provider>SqlClient</Provider>
                        </DefaultConnection>
                    </Data>
                    <Data Name='DefaultConnection' ConnectionString='NewConnectionString'>
                        <Provider>NewProvider</Provider>
                    </Data>
                </settings>";
            var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath);
            var expectedMsg  = Resources.FormatError_KeyIsDuplicated("Data:DefaultConnection:ConnectionString",
                                                                     Resources.FormatMsg_LineInfo(8, 52));

            var exception = Assert.Throws <FormatException>(() => xmlConfigSrc.Load(StringToStream(xml)));

            Assert.Equal(expectedMsg, exception.Message);
        }
Esempio n. 8
0
        public void ThrowExceptionWhenFindNamespace()
        {
            var xml =
                @"<settings xmlns:MyNameSpace='http://microsoft.com/wwa/mynamespace'>
                    <MyNameSpace:Data>
                        <DefaultConnection>
                            <ConnectionString>TestConnectionString</ConnectionString>
                            <Provider>SqlClient</Provider>
                        </DefaultConnection>
                        <Inventory>
                            <ConnectionString>AnotherTestConnectionString</ConnectionString>
                            <Provider>MySql</Provider>
                        </Inventory>
                    </MyNameSpace:Data>
                </settings>";
            var xmlConfigSrc = new XmlConfigurationSource(ArbitraryFilePath);
            var expectedMsg  = Resources.FormatError_NamespaceIsNotSupported(Resources.FormatMsg_LineInfo(1, 11));

            var exception = Assert.Throws <FormatException>(() => xmlConfigSrc.Load(StringToStream(xml)));

            Assert.Equal(expectedMsg, exception.Message);
        }
        internal void Load(Stream stream)
        {
            var data = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);

            var readerSettings = new XmlReaderSettings()
            {
                DtdProcessing    = DtdProcessing.Prohibit,
                IgnoreComments   = true,
                IgnoreWhitespace = true
            };

            using (var reader = XmlReader.Create(stream, readerSettings))
            {
                var prefixStack = new Stack <string>();

                SkipUntilRootElement(reader);

                // We process the root element individually since it doesn't contribute to prefix
                ProcessAttributes(reader, prefixStack, data, AddNamePrefix);
                ProcessAttributes(reader, prefixStack, data, AddAttributePair);

                var preNodeType = reader.NodeType;
                while (reader.Read())
                {
                    switch (reader.NodeType)
                    {
                    case XmlNodeType.Element:
                        prefixStack.Push(reader.LocalName);
                        ProcessAttributes(reader, prefixStack, data, AddNamePrefix);
                        ProcessAttributes(reader, prefixStack, data, AddAttributePair);

                        // If current element is self-closing
                        if (reader.IsEmptyElement)
                        {
                            prefixStack.Pop();
                        }
                        break;

                    case XmlNodeType.EndElement:
                        if (prefixStack.Any())
                        {
                            // If this EndElement node comes right after an Element node,
                            // it means there is no text/CDATA node in current element
                            if (preNodeType == XmlNodeType.Element)
                            {
                                var key = string.Join(Constants.KeyDelimiter, prefixStack.Reverse());
                                data[key] = string.Empty;
                            }

                            prefixStack.Pop();
                        }
                        break;

                    case XmlNodeType.CDATA:
                    case XmlNodeType.Text:
                    {
                        var key = string.Join(Constants.KeyDelimiter, prefixStack.Reverse());

                        if (data.ContainsKey(key))
                        {
                            throw new FormatException(Resources.FormatError_KeyIsDuplicated(key,
                                                                                            GetLineInfo(reader)));
                        }

                        data[key] = reader.Value;
                        break;
                    }

                    case XmlNodeType.XmlDeclaration:
                    case XmlNodeType.ProcessingInstruction:
                    case XmlNodeType.Comment:
                    case XmlNodeType.Whitespace:
                        // Ignore certain types of nodes
                        break;

                    default:
                        throw new FormatException(Resources.FormatError_UnsupportedNodeType(reader.NodeType,
                                                                                            GetLineInfo(reader)));
                    }
                    preNodeType = reader.NodeType;
                    // If this element is a self-closing element,
                    // we pretend that we just processed an EndElement node
                    // because a self-closing element contains an end within itself
                    if (preNodeType == XmlNodeType.Element &&
                        reader.IsEmptyElement)
                    {
                        preNodeType = XmlNodeType.EndElement;
                    }
                }
            }

            ReplaceData(data);
        }
        // Use the original file as a template while generating new file contents
        // to make sure the format is consistent and comments are not lost
        internal void Commit(Stream inputStream, Stream outputStream)
        {
            var dataCopy = new Dictionary <string, string>(Data, StringComparer.OrdinalIgnoreCase);

            var writerSettings = new XmlWriterSettings()
            {
                Indent           = false,
                ConformanceLevel = ConformanceLevel.Auto
            };

            var outputWriter = XmlWriter.Create(outputStream, writerSettings);

            var readerSettings = new XmlReaderSettings()
            {
                DtdProcessing                = DtdProcessing.Prohibit,
                IgnoreWhitespace             = false,
                IgnoreComments               = false,
                IgnoreProcessingInstructions = false
            };

            using (var inputReader = XmlReader.Create(inputStream, readerSettings))
            {
                var prefixStack = new Stack <string>();

                CopyUntilRootElement(inputReader, outputWriter);

                // We process the root element individually since it doesn't contribute to prefix
                outputWriter.WriteStartElement(inputReader.LocalName);
                ProcessAttributes(inputReader, prefixStack, dataCopy, AddNamePrefix);
                ProcessAttributes(inputReader, prefixStack, dataCopy, CommitAttributePair, outputWriter);

                var preNodeType = inputReader.NodeType;
                while (inputReader.Read())
                {
                    switch (inputReader.NodeType)
                    {
                    case XmlNodeType.Element:
                        prefixStack.Push(inputReader.LocalName);
                        outputWriter.WriteStartElement(inputReader.LocalName);

                        ProcessAttributes(inputReader, prefixStack, dataCopy, AddNamePrefix);
                        ProcessAttributes(inputReader, prefixStack, dataCopy, CommitAttributePair, outputWriter);

                        // If current element is self-closing
                        if (inputReader.IsEmptyElement)
                        {
                            outputWriter.WriteEndElement();
                            prefixStack.Pop();
                        }
                        break;

                    case XmlNodeType.EndElement:
                        if (prefixStack.Any())
                        {
                            // If this EndElement node comes right after an Element node,
                            // it means there is no text/CDATA node in current element
                            if (preNodeType == XmlNodeType.Element)
                            {
                                var key = string.Join(Constants.KeyDelimiter, prefixStack.Reverse());
                                if (!dataCopy.ContainsKey(key))
                                {
                                    throw new InvalidOperationException(Resources.FormatError_CommitWhenNewKeyFound(key));
                                }
                                outputWriter.WriteValue(dataCopy[key]);
                                dataCopy.Remove(key);
                            }
                            outputWriter.WriteFullEndElement();
                            prefixStack.Pop();
                        }
                        break;

                    case XmlNodeType.CDATA:
                    case XmlNodeType.Text:
                    {
                        var key = string.Join(Constants.KeyDelimiter, prefixStack.Reverse());

                        if (!dataCopy.ContainsKey(key))
                        {
                            throw new InvalidOperationException(Resources.FormatError_CommitWhenNewKeyFound(key));
                        }

                        if (inputReader.NodeType == XmlNodeType.CDATA)
                        {
                            outputWriter.WriteCData(dataCopy[key]);
                        }
                        else
                        {
                            outputWriter.WriteValue(dataCopy[key]);
                        }
                        dataCopy.Remove(key);
                        break;
                    }

                    case XmlNodeType.ProcessingInstruction:
                        outputWriter.WriteProcessingInstruction(inputReader.LocalName, inputReader.Value);
                        break;

                    case XmlNodeType.Comment:
                        outputWriter.WriteComment(inputReader.Value);
                        break;

                    case XmlNodeType.Whitespace:
                        outputWriter.WriteWhitespace(inputReader.Value);
                        break;

                    default:
                        throw new FormatException(Resources.FormatError_UnsupportedNodeType(inputReader.NodeType,
                                                                                            GetLineInfo(inputReader)));
                    }
                    preNodeType = inputReader.NodeType;
                    // If this element is a self-closing element,
                    // we pretend that we just processed an EndElement node
                    // because a self-closing element contains an end within itself
                    if (preNodeType == XmlNodeType.Element &&
                        inputReader.IsEmptyElement)
                    {
                        preNodeType = XmlNodeType.EndElement;
                    }
                }

                // Close the root element
                outputWriter.WriteEndElement();
                outputWriter.Flush();
            }

            if (dataCopy.Any())
            {
                var missingKeys = string.Join(", ", dataCopy.Keys);
                throw new InvalidOperationException(Resources.FormatError_CommitWhenKeyMissing(missingKeys));
            }
        }