private static string GetLineInfo(XmlReader reader) { var lineInfo = reader as IXmlLineInfo; return(lineInfo == null ? string.Empty : Resources.FormatMsg_LineInfo(lineInfo.LineNumber, lineInfo.LinePosition)); }
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(); }
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(); }
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); }
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)); } }