예제 #1
0
        private XmlWriterSettings GetWriterSettings(Stream readerStream, XmlStreamFormatterOptions options)
        {
            var xmlDelcarationParser = new XmlDeclarationParser();
            var parseResults         = xmlDelcarationParser.Parse(readerStream);

            readerStream.Position = 0;

            var writerSettings = new XmlWriterSettings
            {
                CloseOutput        = false,
                OmitXmlDeclaration = options.XmlDeclarationBehaviour == XmlDeclarationBehaviour.MatchSourceDocument
                    ? !parseResults.XmlDeclarationPresent
                    : options.XmlDeclarationBehaviour == XmlDeclarationBehaviour.AlwaysOmit,
                Indent      = options.Indent,
                IndentChars = options.IndentChars,
                Encoding    = parseResults.StatedEncoding ?? parseResults.ActualEncoding ?? Encoding.UTF8,
            };

            return(writerSettings);
        }
예제 #2
0
        /// <summary>
        /// Copies the source xml stream supplied at construction time, to the destination stream, applying the formatting specified in the
        /// supplied options.
        /// </summary>
        /// <param name="readerStream">The reader stream.</param>
        /// <param name="writerStream">The writer stream.</param>
        /// <param name="options">The formatting options.</param>
        /// <exception cref="ArgumentNullException">Raised if any of the supplied arguments are null.</exception>
        /// <exception cref="InvalidOperationException">Raised if the <paramref name="readerStream"/> is not readable and seekable,
        /// if the <paramref name="writerStream"/> is not writeable, or if this method is called more than once.</exception>
        public void Format([NotNull] Stream readerStream, [NotNull] Stream writerStream, [NotNull] XmlStreamFormatterOptions options)
        {
            if (readerStream == null)
            {
                throw new ArgumentNullException(nameof(readerStream));
            }
            if (writerStream == null)
            {
                throw new ArgumentNullException(nameof(writerStream));
            }
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            if (!readerStream.CanRead || !readerStream.CanSeek)
            {
                throw new InvalidOperationException("readerStream must be both readable and seekable");
            }

            if (!writerStream.CanWrite)
            {
                throw new InvalidOperationException("writerStream must be writeable");
            }

            var pausableWriterStream = new PauseableWriteableStream(writerStream);

            var copier = new XmlReaderToWriterCopier();

            var readerSettings = new XmlReaderSettings
            {
                CloseInput       = false,
                IgnoreWhitespace = true
            };

            var writerSettings = GetWriterSettings(readerStream, options);

            var context = new XmlStreamFormatterContext
            {
                Encoding = writerSettings.Encoding
            };

            if (options.Indent &&
                options.WrapLongElementLines &&
                options.IndentChars != null)
            {
                context.IndentByteSequence = context.Encoding.GetBytes(options.IndentChars);
            }

            using (var reader = XmlReader.Create(readerStream, readerSettings))
                using (var writer = XmlWriter.Create(pausableWriterStream, writerSettings))
                {
                    copier.Copy(reader, writer, (nodeType, xmlReader, xmlWriter) => OnCopyCallback(nodeType, xmlReader, xmlWriter, pausableWriterStream, options, context));
                }
        }
예제 #3
0
        private bool OnCopyCallback(
            XmlNodeType nodeType,
            XmlReader xmlReader,
            XmlWriter xmlWriter,
            PauseableWriteableStream writerStream,
            [NotNull] XmlStreamFormatterOptions options,
            [NotNull] XmlStreamFormatterContext context)
        {
            if (!options.WrapLongElementLines)
            {
                return(true);
            }

            if (nodeType == XmlNodeType.Element)
            {
                context.CurrentLineLength =
                    xmlReader.Prefix.Length                            // prefix - if any
                    + (string.IsNullOrEmpty(xmlReader.Prefix) ? 0 : 1) // colon between prefix and local name, if any
                    + xmlReader.LocalName.Length                       // local name
                    + 1;                                               // opening triangular brace
                context.CurrentElementDepth++;
                return(true);
            }

            if (nodeType == XmlNodeType.EndElement)
            {
                context.CurrentAttributeCountOnElement = 0;
                context.CurrentLineLength = null;
                context.CurrentElementDepth--;
                return(true);
            }

            if (nodeType == XmlNodeType.Attribute)
            {
                context.CurrentAttributeCountOnElement++;
                Debug.Assert(context.CurrentLineLength.HasValue, "_currentLineLength cannot be null at attribute");

                // we want the underlying writer to think the attributes have been written, even though we will write them out "raw"
                // to the underlying stream, so we will "pause" the underlying stream, writer the xmlns attribute, and resume. The writer thinks
                // the attribute has been written, but it doesn't appear in the output!

                // This is especially important of xmlns attributes as if we don't write these out, the writer won't think they've has been written
                // and will start appending namespace declarations to child nodes explicitly as it encounters nodes that use them.

                xmlWriter.Flush();
                writerStream.PauseWriting();
                xmlWriter.WriteAttributeString(xmlReader.Prefix, xmlReader.LocalName, xmlReader.NamespaceURI, xmlReader.Value);
                xmlWriter.Flush();
                writerStream.ResumeWriting();

                xmlWriter.Flush();
                var sb = new StringBuilder();
                if (!string.IsNullOrEmpty(xmlReader.Prefix))
                {
                    sb.Append(xmlReader.Prefix);
                    sb.Append(":");
                }

                sb.AppendFormat(@"{0}=""{1}""", xmlReader.LocalName, SecurityElement.Escape(xmlReader.Value));

                if (context.CurrentLineLength + sb.Length + 1 > options.MaxElementLineLength)
                {
                    InsertNewlineToUnderlyingStream(writerStream, context);

                    var attributeAsBytes = context.Encoding.GetBytes(sb.ToString());
                    writerStream.Write(attributeAsBytes, 0, attributeAsBytes.Length);

                    context.CurrentLineLength = sb.Length;

                    if (sb.Length > options.MaxElementLineLength &&
                        context.CurrentAttributeCountOnElement < xmlReader.AttributeCount)
                    {
                        // this single attribute itself was bigger than the line length, and there are more attributes remaining.
                        // Insert another newline.
                        InsertNewlineToUnderlyingStream(writerStream, context);
                        context.CurrentLineLength = 0;
                    }

                    return(false);
                }
                else
                {
                    if (context.CurrentLineLength != 0)
                    {
                        sb.Insert(0, " ");
                    }
                    var attributeAsBytes = context.Encoding.GetBytes(sb.ToString());
                    writerStream.Write(attributeAsBytes, 0, attributeAsBytes.Length);

                    context.CurrentLineLength += sb.Length;
                    return(false);
                }
            }

            return(true);
        }