| File: XmlFileInfoDocument.cs | Web Access |
| Project: src\xdt\src\Microsoft.Web.XmlTransform\Microsoft.Web.XmlTransform.csproj (Microsoft.Web.XmlTransform) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.Text; using System.Xml; using System.IO; using System.Diagnostics; namespace Microsoft.Web.XmlTransform { public class XmlFileInfoDocument : XmlDocument, IDisposable { private Encoding _textEncoding = null; private XmlTextReader _reader = null; private XmlAttributePreservationProvider _preservationProvider = null; private bool _firstLoad = true; private string _fileName = null; private int _lineNumberOffset = 0; private int _linePositionOffset = 0; public override void Load(string filename) { LoadFromFileName(filename); _firstLoad = false; } public override void Load(XmlReader reader) { _reader = reader as XmlTextReader; if (_reader != null) { _fileName = _reader.BaseURI; } base.Load(reader); if (_reader != null) { _textEncoding = _reader.Encoding; } _firstLoad = false; } private void LoadFromFileName(string filename) { _fileName = filename; StreamReader reader = null; try { if (PreserveWhitespace) { _preservationProvider = new XmlAttributePreservationProvider(filename); } reader = new StreamReader(filename, true); LoadFromTextReader(reader); } finally { if (_preservationProvider != null) { _preservationProvider.Close(); _preservationProvider = null; } if (reader != null) { reader.Close(); } } } private void LoadFromTextReader(TextReader textReader) { StreamReader streamReader = textReader as StreamReader; if (streamReader != null) { FileStream fileStream = streamReader.BaseStream as FileStream; if (fileStream != null) { _fileName = fileStream.Name; } _textEncoding = GetEncodingFromStream(streamReader.BaseStream); } _reader = new XmlTextReader(_fileName, textReader); base.Load(_reader); if (_textEncoding == null) { _textEncoding = _reader.Encoding; } } private Encoding GetEncodingFromStream(Stream stream) { Encoding encoding = null; if (stream.CanSeek) { byte[] buffer = new byte[3]; #if NET stream.ReadExactly(buffer, 0, buffer.Length); #else int offset = 0; int count = buffer.Length; while (count > 0) { int read = stream.Read(buffer, offset, count); if (read <= 0) { throw new EndOfStreamException(); } offset += read; count -= read; } #endif if (buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) encoding = Encoding.UTF8; else if (buffer[0] == 0xFE && buffer[1] == 0xFF) encoding = Encoding.BigEndianUnicode; else if (buffer[0] == 0xFF && buffer[1] == 0xFE) encoding = Encoding.Unicode; else if (buffer[0] == 0x2B && buffer[1] == 0x2F && buffer[2] == 0x76) // 'Encoding.UTF7' is obsolete: 'The UTF-7 encoding is insecure and should not be used. Consider using UTF-8 instead.' #pragma warning disable SYSLIB0001 encoding = Encoding.UTF7; #pragma warning restore SYSLIB0001 // Reset the stream stream.Seek(0, SeekOrigin.Begin); } return encoding; } internal XmlNode CloneNodeFromOtherDocument(XmlNode element) { XmlTextReader oldReader = _reader; string oldFileName = _fileName; XmlNode clone = null; try { IXmlLineInfo lineInfo = element as IXmlLineInfo; if (lineInfo != null) { _reader = new XmlTextReader(new StringReader(element.OuterXml)); _lineNumberOffset = lineInfo.LineNumber - 1; _linePositionOffset = lineInfo.LinePosition - 2; _fileName = element.OwnerDocument.BaseURI; clone = ReadNode(_reader); } else { _fileName = null; _reader = null; clone = ReadNode(new XmlTextReader(new StringReader(element.OuterXml))); } } finally { _lineNumberOffset = 0; _linePositionOffset = 0; _fileName = oldFileName; _reader = oldReader; } return clone; } internal bool HasErrorInfo { get { return _reader != null; } } internal string FileName { get { return _fileName; } } private int CurrentLineNumber { get { return _reader != null ? _reader.LineNumber + _lineNumberOffset : 0; } } private int CurrentLinePosition { get { return _reader != null ? _reader.LinePosition + _linePositionOffset : 0; } } private bool FirstLoad { get { return _firstLoad; } } private XmlAttributePreservationProvider PreservationProvider { get { return _preservationProvider; } } private Encoding TextEncoding { get { if (_textEncoding != null) { return _textEncoding; } else { // Copied from base implementation of XmlDocument if (HasChildNodes) { XmlDeclaration declaration = FirstChild as XmlDeclaration; if (declaration != null) { string value = declaration.Encoding; if (value.Length > 0) { return System.Text.Encoding.GetEncoding(value); } } } } return null; } } public override void Save(string filename) { XmlWriter xmlWriter = null; using (Stream file = File.Create(filename)) { try { if (PreserveWhitespace) { XmlFormatter.Format(this); xmlWriter = new XmlAttributePreservingWriter(file, TextEncoding); } else { XmlTextWriter textWriter = new XmlTextWriter(file, TextEncoding); textWriter.Formatting = Formatting.Indented; xmlWriter = textWriter; } WriteTo(xmlWriter); } finally { if (xmlWriter != null) { xmlWriter.Flush(); xmlWriter.Close(); } } } } public override void Save(Stream w) { XmlWriter xmlWriter = null; try { if (PreserveWhitespace) { XmlFormatter.Format(this); xmlWriter = new XmlAttributePreservingWriter(w, TextEncoding); } else { XmlTextWriter textWriter = new XmlTextWriter(w, TextEncoding); textWriter.Formatting = Formatting.Indented; xmlWriter = textWriter; } WriteTo(xmlWriter); } finally { if (xmlWriter != null) { xmlWriter.Flush(); } } } public override XmlElement CreateElement(string prefix, string localName, string namespaceURI) { if (HasErrorInfo) { return new XmlFileInfoElement(prefix, localName, namespaceURI, this); } else { return base.CreateElement(prefix, localName, namespaceURI); } } public override XmlAttribute CreateAttribute(string prefix, string localName, string namespaceURI) { if (HasErrorInfo) { return new XmlFileInfoAttribute(prefix, localName, namespaceURI, this); } else { return base.CreateAttribute(prefix, localName, namespaceURI); } } internal bool IsNewNode(XmlNode node) { // The transformation engine will only add elements. Anything // else that gets added must be contained by a new element. // So to determine what's new, we search up the tree for a new // element that contains this node. XmlFileInfoElement element = FindContainingElement(node) as XmlFileInfoElement; return element != null && !element.IsOriginal; } private XmlElement FindContainingElement(XmlNode node) { while (node != null && !(node is XmlElement)) { node = node.ParentNode; } return node as XmlElement; } #region XmlElement override private class XmlFileInfoElement : XmlElement, IXmlLineInfo, IXmlFormattableAttributes { private int lineNumber; private int linePosition; private bool isOriginal; private XmlAttributePreservationDict preservationDict = null; internal XmlFileInfoElement(string prefix, string localName, string namespaceUri, XmlFileInfoDocument document) : base(prefix, localName, namespaceUri, document) { lineNumber = document.CurrentLineNumber; linePosition = document.CurrentLinePosition; isOriginal = document.FirstLoad; if (document.PreservationProvider != null) { preservationDict = document.PreservationProvider.GetDictAtPosition(lineNumber, linePosition - 1); } if (preservationDict == null) { preservationDict = new XmlAttributePreservationDict(); } } public override void WriteTo(XmlWriter w) { string prefix = Prefix; if (!String.IsNullOrEmpty(NamespaceURI)) { prefix = w.LookupPrefix(NamespaceURI); if (prefix == null) { prefix = Prefix; } } w.WriteStartElement(prefix, LocalName, NamespaceURI); XmlAttributePreservingWriter preservingWriter = w as XmlAttributePreservingWriter; if (preservingWriter == null || preservationDict == null) { WriteAttributesTo(w); } else { WritePreservedAttributesTo(preservingWriter); } if (IsEmpty) { w.WriteEndElement(); } else { WriteContentTo(w); w.WriteFullEndElement(); } } private void WriteAttributesTo(XmlWriter w) { XmlAttributeCollection attrs = Attributes; for (int i = 0; i < attrs.Count; i += 1) { XmlAttribute attr = attrs[i]; attr.WriteTo(w); } } private void WritePreservedAttributesTo(XmlAttributePreservingWriter preservingWriter) { preservationDict.WritePreservedAttributes(preservingWriter, Attributes); } #region IXmlLineInfo Members public bool HasLineInfo() { return true; } public int LineNumber { get { return lineNumber; } } public int LinePosition { get { return linePosition; } } public bool IsOriginal { get { return isOriginal; } } #endregion #region IXmlFormattableNode Members void IXmlFormattableAttributes.FormatAttributes(XmlFormatter formatter) { preservationDict.UpdatePreservationInfo(Attributes, formatter); } string IXmlFormattableAttributes.AttributeIndent { get { return preservationDict.GetAttributeNewLineString(null); } } #endregion } #endregion #region XmlAttribute override private class XmlFileInfoAttribute : XmlAttribute, IXmlLineInfo { private int lineNumber; private int linePosition; internal XmlFileInfoAttribute(string prefix, string localName, string namespaceUri, XmlFileInfoDocument document) : base(prefix, localName, namespaceUri, document) { lineNumber = document.CurrentLineNumber; linePosition = document.CurrentLinePosition; } #region IXmlLineInfo Members public bool HasLineInfo() { return true; } public int LineNumber { get { return lineNumber; } } public int LinePosition { get { return linePosition; } } #endregion } #endregion #region Dispose Pattern protected virtual void Dispose(bool disposing) { if (_reader != null) { _reader.Close(); _reader = null; } if (_preservationProvider != null) { _preservationProvider.Close(); _preservationProvider = null; } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ~XmlFileInfoDocument() { Debug.Fail("call dispose please"); Dispose(false); } #endregion } }