2 //------------------------------------------------------------------------------
3 // <copyright file="XmlRawTextWriterGenerator.cxx" company="Microsoft">
4 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // <owner current="true" primary="true">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 // WARNING: This file is generated and should not be modified directly. Instead,
10 // modify XmlTextWriterGenerator.cxx and run gen.bat in the same directory.
11 // This batch file will execute the following commands:
13 // cl.exe /C /EP /D _XML_UTF8_TEXT_WRITER XmlRawTextWriterGenerator.cxx > XmlUtf8RawTextWriter.cs
14 // cl.exe /C /EP /D _XML_ENCODED_TEXT_WRITER XmlRawTextWriterGenerator.cxx > XmlEncodedRawTextWriter.cs
16 // Because these two implementations of XmlTextWriter are so similar, the C++ preprocessor
17 // is used to generate each implementation from one template file, using macros and ifdefs.
19 // Note: This file was generated without #define SILVERLIGHT
25 using System.Diagnostics;
26 using System.Globalization;
28 namespace System.Xml {
30 // Concrete implementation of XmlWriter abstract class that serializes events as encoded XML
31 // text. The general-purpose XmlEncodedTextWriter uses the Encoder class to output to any
32 // encoding. The XmlUtf8TextWriter class combined the encoding operation with serialization
33 // in order to achieve better performance.
34 internal partial class XmlEncodedRawTextWriter : XmlRawWriter {
40 private readonly bool useAsync;
44 protected byte[] bufBytes;
47 protected Stream stream;
49 // encoding of the stream or text writer
50 protected Encoding encoding;
53 protected XmlCharType xmlCharType = XmlCharType.Instance;
56 protected int bufPos = 1; // buffer position starts at 1, because we need to be able to safely step back -1 in case we need to
57 // close an empty element or in CDATA section detection of double ]; _BUFFER[0] will always be 0
58 protected int textPos = 1; // text end position; don't indent first element, pi, or comment
59 protected int contentPos; // element content end position
60 protected int cdataPos; // cdata end position
61 protected int attrEndPos; // end of the last attribute
62 protected int bufLen = BUFSIZE;
65 protected bool writeToNull;
66 protected bool hadDoubleBracket;
67 protected bool inAttributeValue;
69 protected int bufBytesUsed;
70 protected char[] bufChars;
72 // encoder for encoding chars in specified encoding when writing to stream
73 protected Encoder encoder;
76 protected TextWriter writer;
78 // escaping of characters invalid in the output encoding
79 protected bool trackTextContent;
80 protected bool inTextContent;
81 private int lastMarkPos;
82 private int[] textContentMarks; // even indices contain text content start positions
83 // odd indices contain markup start positions
84 private CharEntityEncoderFallback charEntityFallback;
87 protected NewLineHandling newLineHandling;
88 protected bool closeOutput;
89 protected bool omitXmlDeclaration;
90 protected string newLineChars;
91 protected bool checkCharacters;
93 protected XmlStandalone standalone;
94 protected XmlOutputMethod outputMethod;
96 protected bool autoXmlDeclaration;
97 protected bool mergeCDataSections;
102 private const int BUFSIZE = 2048 * 3; // Should be greater than default FileStream size (4096), otherwise the FileStream will try to cache the data
103 private const int ASYNCBUFSIZE = 64 * 1024; // Set async buffer size to 64KB
104 private const int OVERFLOW = 32; // Allow overflow in order to reduce checks when writing out constant size markup
105 private const int INIT_MARKS_COUNT = 64;
110 // Construct and initialize an instance of this class.
111 protected XmlEncodedRawTextWriter( XmlWriterSettings settings ) {
114 useAsync = settings.Async;
118 newLineHandling = settings.NewLineHandling;
119 omitXmlDeclaration = settings.OmitXmlDeclaration;
120 newLineChars = settings.NewLineChars;
121 checkCharacters = settings.CheckCharacters;
122 closeOutput = settings.CloseOutput;
124 standalone = settings.Standalone;
125 outputMethod = settings.OutputMethod;
126 mergeCDataSections = settings.MergeCDataSections;
128 if ( checkCharacters && newLineHandling == NewLineHandling.Replace ) {
129 ValidateContentChars( newLineChars, "NewLineChars", false );
133 // Construct an instance of this class that outputs text to the TextWriter interface.
134 public XmlEncodedRawTextWriter( TextWriter writer, XmlWriterSettings settings ) : this( settings ) {
135 Debug.Assert( writer != null && settings != null );
137 this.writer = writer;
138 this.encoding = writer.Encoding;
139 // the buffer is allocated will OVERFLOW in order to reduce checks when writing out constant size markup
140 if (settings.Async) {
141 bufLen = ASYNCBUFSIZE;
143 this.bufChars = new char[bufLen + OVERFLOW];
145 // Write the xml declaration
146 if (settings.AutoXmlDeclaration ) {
147 WriteXmlDeclaration( standalone );
148 autoXmlDeclaration = true;
153 // Construct an instance of this class that serializes to a Stream interface.
154 public XmlEncodedRawTextWriter( Stream stream, XmlWriterSettings settings ) : this( settings ) {
155 Debug.Assert( stream != null && settings != null );
157 this.stream = stream;
158 this.encoding = settings.Encoding;
160 // the buffer is allocated will OVERFLOW in order to reduce checks when writing out constant size markup
161 if (settings.Async) {
162 bufLen = ASYNCBUFSIZE;
164 bufChars = new char[bufLen + OVERFLOW];
166 bufBytes = new byte[ bufChars.Length ];
169 // Init escaping of characters not fitting into the target encoding
170 trackTextContent = true;
171 inTextContent = false;
173 textContentMarks = new int[INIT_MARKS_COUNT];
174 textContentMarks[0] = 1;
176 charEntityFallback = new CharEntityEncoderFallback();
177 this.encoding = (Encoding)settings.Encoding.Clone();
178 encoding.EncoderFallback = charEntityFallback;
180 encoder = encoding.GetEncoder();
182 if ( !stream.CanSeek || stream.Position == 0 ) {
183 byte[] bom = encoding.GetPreamble();
184 if ( bom.Length != 0 ) {
185 this.stream.Write( bom, 0, bom.Length );
189 // Write the xml declaration
190 if ( settings.AutoXmlDeclaration ) {
191 WriteXmlDeclaration( standalone );
192 autoXmlDeclaration = true;
198 // XmlWriter implementation
200 // Returns settings the writer currently applies.
201 public override XmlWriterSettings Settings {
203 XmlWriterSettings settings = new XmlWriterSettings();
205 settings.Encoding = this.encoding;
206 settings.OmitXmlDeclaration = this.omitXmlDeclaration;
207 settings.NewLineHandling = this.newLineHandling;
208 settings.NewLineChars = this.newLineChars;
209 settings.CloseOutput = this.closeOutput;
210 settings.ConformanceLevel = ConformanceLevel.Auto;
211 settings.CheckCharacters = checkCharacters;
213 settings.AutoXmlDeclaration = autoXmlDeclaration;
214 settings.Standalone = standalone;
215 settings.OutputMethod = outputMethod;
217 settings.ReadOnly = true;
223 // Write the xml declaration. This must be the first call.
224 internal override void WriteXmlDeclaration( XmlStandalone standalone ) {
225 // Output xml declaration only if user allows it and it was not already output
226 if ( !omitXmlDeclaration && !autoXmlDeclaration ) {
228 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
230 RawText( "<?xml version=\"" );
236 if ( encoding != null ) {
237 RawText( "\" encoding=\"" );
238 RawText( encoding.WebName );
242 if ( standalone != XmlStandalone.Omit ) {
243 RawText( "\" standalone=\"" );
244 RawText( standalone == XmlStandalone.Yes ? "yes" : "no" );
251 internal override void WriteXmlDeclaration( string xmldecl ) {
252 // Output xml declaration only if user allows it and it was not already output
253 if ( !omitXmlDeclaration && !autoXmlDeclaration ) {
254 WriteProcessingInstruction( "xml", xmldecl );
259 // Serialize the document type declaration.
260 public override void WriteDocType( string name, string pubid, string sysid, string subset ) {
261 Debug.Assert( name != null && name.Length > 0 );
263 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
265 RawText( "<!DOCTYPE ");
267 if ( pubid != null ) {
268 RawText( " PUBLIC \"" );
271 if ( sysid != null ) {
274 bufChars[bufPos++] = (char) '"';
276 else if ( sysid != null ) {
277 RawText( " SYSTEM \"" );
279 bufChars[bufPos++] = (char) '"';
282 bufChars[bufPos++] = (char) ' ';
285 if ( subset != null ) {
286 bufChars[bufPos++] = (char) '[';
288 bufChars[bufPos++] = (char) ']';
291 bufChars[this.bufPos++] = (char) '>';
294 // Serialize the beginning of an element start tag: "<prefix:localName"
295 public override void WriteStartElement( string prefix, string localName, string ns) {
296 Debug.Assert( localName != null && localName.Length > 0 );
297 Debug.Assert( prefix != null );
299 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
301 bufChars[bufPos++] = (char) '<';
302 if ( prefix != null && prefix.Length != 0 ) {
304 bufChars[this.bufPos++] = (char) ':';
307 RawText( localName );
312 // Serialize the end of an element start tag in preparation for content serialization: ">"
313 internal override void StartElementContent() {
314 bufChars[bufPos++] = (char) '>';
316 // StartElementContent is always called; therefore, in order to allow shortcut syntax, we save the
317 // position of the '>' character. If WriteEndElement is called and no other characters have been
318 // output, then the '>' character can be be overwritten with the shortcut syntax " />".
322 // Serialize an element end tag: "</prefix:localName>", if content was output. Otherwise, serialize
323 // the shortcut syntax: " />".
324 internal override void WriteEndElement( string prefix, string localName, string ns ) {
325 Debug.Assert( localName != null && localName.Length > 0 );
326 Debug.Assert( prefix != null );
328 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
330 if ( contentPos != bufPos ) {
331 // Content has been output, so can't use shortcut syntax
332 bufChars[bufPos++] = (char) '<';
333 bufChars[bufPos++] = (char) '/';
335 if ( prefix != null && prefix.Length != 0) {
337 bufChars[bufPos++] = (char) ':';
339 RawText( localName );
340 bufChars[bufPos++] = (char) '>';
343 // Use shortcut syntax; overwrite the already output '>' character
345 bufChars[bufPos++] = (char) ' ';
346 bufChars[bufPos++] = (char) '/';
347 bufChars[bufPos++] = (char) '>';
351 // Serialize a full element end tag: "</prefix:localName>"
352 internal override void WriteFullEndElement( string prefix, string localName, string ns ) {
353 Debug.Assert( localName != null && localName.Length > 0 );
354 Debug.Assert( prefix != null );
356 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
358 bufChars[bufPos++] = (char) '<';
359 bufChars[bufPos++] = (char) '/';
361 if ( prefix != null && prefix.Length != 0) {
363 bufChars[bufPos++] = (char) ':';
365 RawText( localName );
366 bufChars[bufPos++] = (char) '>';
369 // Serialize an attribute tag using double quotes around the attribute value: 'prefix:localName="'
370 public override void WriteStartAttribute( string prefix, string localName, string ns ) {
371 Debug.Assert( localName != null && localName.Length > 0 );
372 Debug.Assert( prefix != null );
374 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
376 if ( attrEndPos == bufPos ) {
377 bufChars[bufPos++] = (char) ' ';
380 if ( prefix != null && prefix.Length > 0 ) {
382 bufChars[bufPos++] = (char) ':';
384 RawText( localName );
385 bufChars[bufPos++] = (char) '=';
386 bufChars[bufPos++] = (char) '"';
388 inAttributeValue = true;
391 // Serialize the end of an attribute value using double quotes: '"'
392 public override void WriteEndAttribute() {
393 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
394 bufChars[bufPos++] = (char) '"';
395 inAttributeValue = false;
400 internal override void WriteNamespaceDeclaration( string prefix, string namespaceName ) {
401 Debug.Assert( prefix != null && namespaceName != null );
403 this.WriteStartNamespaceDeclaration( prefix );
404 this.WriteString( namespaceName );
405 this.WriteEndNamespaceDeclaration();
408 internal override bool SupportsNamespaceDeclarationInChunks {
414 internal override void WriteStartNamespaceDeclaration(string prefix) {
415 Debug.Assert( prefix != null );
417 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
422 if ( prefix.Length == 0 ) {
423 RawText( " xmlns=\"" );
426 RawText( " xmlns:" );
428 bufChars[bufPos++] = (char)'=';
429 bufChars[bufPos++] = (char)'"';
432 inAttributeValue = true;
433 if ( trackTextContent && inTextContent != true ) { ChangeTextContentMark( true ); }
436 internal override void WriteEndNamespaceDeclaration() {
437 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
438 inAttributeValue = false;
440 bufChars[bufPos++] = (char)'"';
445 // Serialize a CData section. If the "]]>" pattern is found within
446 // the text, replace it with "]]><![CDATA[>".
447 public override void WriteCData( string text ) {
448 Debug.Assert( text != null );
450 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
452 if ( mergeCDataSections && bufPos == cdataPos ) {
453 // Merge adjacent cdata sections - overwrite the "]]>" characters
454 Debug.Assert( bufPos >= 4 );
458 // Start a new cdata section
459 bufChars[bufPos++] = (char) '<';
460 bufChars[bufPos++] = (char) '!';
461 bufChars[bufPos++] = (char) '[';
462 bufChars[bufPos++] = (char) 'C';
463 bufChars[bufPos++] = (char) 'D';
464 bufChars[bufPos++] = (char) 'A';
465 bufChars[bufPos++] = (char) 'T';
466 bufChars[bufPos++] = (char) 'A';
467 bufChars[bufPos++] = (char) '[';
470 WriteCDataSection( text );
472 bufChars[bufPos++] = (char) ']';
473 bufChars[bufPos++] = (char) ']';
474 bufChars[bufPos++] = (char) '>';
480 // Serialize a comment.
481 public override void WriteComment( string text ) {
482 Debug.Assert( text != null );
484 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
486 bufChars[bufPos++] = (char) '<';
487 bufChars[bufPos++] = (char) '!';
488 bufChars[bufPos++] = (char) '-';
489 bufChars[bufPos++] = (char) '-';
491 WriteCommentOrPi( text, '-' );
493 bufChars[bufPos++] = (char) '-';
494 bufChars[bufPos++] = (char) '-';
495 bufChars[bufPos++] = (char) '>';
498 // Serialize a processing instruction.
499 public override void WriteProcessingInstruction( string name, string text ) {
500 Debug.Assert( name != null && name.Length > 0 );
501 Debug.Assert( text != null );
503 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
505 bufChars[bufPos++] = (char) '<';
506 bufChars[bufPos++] = (char) '?';
509 if ( text.Length > 0 ) {
510 bufChars[bufPos++] = (char) ' ';
511 WriteCommentOrPi( text, '?' );
514 bufChars[bufPos++] = (char) '?';
515 bufChars[bufPos++] = (char) '>';
518 // Serialize an entity reference.
519 public override void WriteEntityRef( string name ) {
520 Debug.Assert( name != null && name.Length > 0 );
522 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
524 bufChars[bufPos++] = (char) '&';
526 bufChars[bufPos++] = (char) ';';
528 if ( bufPos > bufLen ) {
535 // Serialize a character entity reference.
536 public override void WriteCharEntity( char ch ) {
537 string strVal = ((int)ch).ToString( "X", NumberFormatInfo.InvariantInfo );
539 if ( checkCharacters && !xmlCharType.IsCharData( ch ) ) {
540 // we just have a single char, not a surrogate, therefore we have to pass in '\0' for the second char
541 throw XmlConvert.CreateInvalidCharException( ch, '\0' );
544 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
546 bufChars[bufPos++] = (char)'&';
547 bufChars[bufPos++] = (char)'#';
548 bufChars[bufPos++] = (char)'x';
550 bufChars[bufPos++] = (char)';';
552 if ( bufPos > bufLen ) {
559 // Serialize a whitespace node.
561 public override unsafe void WriteWhitespace( string ws ) {
562 Debug.Assert( ws != null );
563 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
565 fixed ( char * pSrc = ws ) {
566 char * pSrcEnd = pSrc + ws.Length;
567 if ( inAttributeValue) {
568 WriteAttributeTextBlock( pSrc, pSrcEnd );
571 WriteElementTextBlock( pSrc, pSrcEnd );
577 // Serialize either attribute or element text using XML rules.
579 public override unsafe void WriteString( string text ) {
580 Debug.Assert( text != null );
581 if ( trackTextContent && inTextContent != true ) { ChangeTextContentMark( true ); }
583 fixed ( char * pSrc = text ) {
584 char * pSrcEnd = pSrc + text.Length;
585 if ( inAttributeValue) {
586 WriteAttributeTextBlock( pSrc, pSrcEnd );
589 WriteElementTextBlock( pSrc, pSrcEnd );
595 // Serialize surrogate character entity.
596 public override void WriteSurrogateCharEntity( char lowChar, char highChar ) {
597 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
598 int surrogateChar = XmlCharType.CombineSurrogateChar( lowChar, highChar );
600 bufChars[bufPos++] = (char)'&';
601 bufChars[bufPos++] = (char)'#';
602 bufChars[bufPos++] = (char)'x';
603 RawText( surrogateChar.ToString( "X", NumberFormatInfo.InvariantInfo ) );
604 bufChars[bufPos++] = (char)';';
608 // Serialize either attribute or element text using XML rules.
609 // Arguments are validated in the XmlWellformedWriter layer.
611 public override unsafe void WriteChars( char[] buffer, int index, int count ) {
612 Debug.Assert( buffer != null );
613 Debug.Assert( index >= 0 );
614 Debug.Assert( count >= 0 && index + count <= buffer.Length );
616 if ( trackTextContent && inTextContent != true ) { ChangeTextContentMark( true ); }
618 fixed ( char * pSrcBegin = &buffer[index] ) {
619 if ( inAttributeValue ) {
620 WriteAttributeTextBlock( pSrcBegin, pSrcBegin + count );
623 WriteElementTextBlock( pSrcBegin, pSrcBegin + count );
629 // Serialize raw data.
630 // Arguments are validated in the XmlWellformedWriter layer
632 public override unsafe void WriteRaw( char[] buffer, int index, int count ) {
633 Debug.Assert( buffer != null );
634 Debug.Assert( index >= 0 );
635 Debug.Assert( count >= 0 && index + count <= buffer.Length );
637 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
639 fixed ( char * pSrcBegin = &buffer[index] ) {
640 WriteRawWithCharChecking( pSrcBegin, pSrcBegin + count );
646 // Serialize raw data.
648 public override unsafe void WriteRaw( string data ) {
649 Debug.Assert( data != null );
651 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
653 fixed ( char * pSrcBegin = data ) {
654 WriteRawWithCharChecking( pSrcBegin, pSrcBegin + data.Length );
660 // Flush all bytes in the buffer to output and close the output stream or writer.
661 public override void Close() {
667 // Future calls to Close or Flush shouldn't write to Stream or Writer
670 if ( stream != null ) {
686 else if ( writer != null ) {
705 // Flush all characters in the buffer to output and call Flush() on the output object.
706 public override void Flush() {
710 if ( stream != null ) {
713 else if ( writer != null ) {
720 // Implementation methods
722 // Flush all characters in the buffer to output. Do not flush the output object.
723 protected virtual void FlushBuffer() {
725 // Output all characters (except for previous characters stored at beginning of buffer)
726 if ( !writeToNull ) {
728 Debug.Assert( stream != null || writer != null );
730 if ( stream != null ) {
731 if ( trackTextContent ) {
732 charEntityFallback.Reset( textContentMarks, lastMarkPos );
733 // reset text content tracking
735 if ((lastMarkPos & 1) != 0) {
736 // If the previous buffer ended inside a text content we need to preserve that info
737 // which means the next index to which we write has to be even
738 textContentMarks[1] = 1;
744 Debug.Assert( textContentMarks[0] == 1 );
746 EncodeChars( 1, bufPos, true );
749 // Write text to TextWriter
750 writer.Write( bufChars, 1, bufPos - 1 );
756 // Future calls to flush (i.e. when Close() is called) don't attempt to write to stream
761 // Move last buffer character to the beginning of the buffer (so that previous character can always be determined)
762 bufChars[0] = bufChars[bufPos - 1];
764 // Reset buffer position
765 textPos = (textPos == bufPos) ? 1 : 0;
766 attrEndPos = (attrEndPos == bufPos) ? 1 : 0;
767 contentPos = 0; // Needs to be zero, since overwriting '>' character is no longer possible
768 cdataPos = 0; // Needs to be zero, since overwriting ']]>' characters is no longer possible
769 bufPos = 1; // Buffer position starts at 1, because we need to be able to safely step back -1 in case we need to
770 // close an empty element or in CDATA section detection of double ]; _BUFFER[0] will always be 0
774 private void EncodeChars( int startOffset, int endOffset, bool writeAllToStream ) {
775 // Write encoded text to stream
779 while ( startOffset < endOffset ) {
780 if ( charEntityFallback != null ) {
781 charEntityFallback.StartOffset = startOffset;
783 encoder.Convert( bufChars, startOffset, endOffset - startOffset, bufBytes, bufBytesUsed, bufBytes.Length - bufBytesUsed, false, out chEnc, out bEnc, out completed );
784 startOffset += chEnc;
785 bufBytesUsed += bEnc;
786 if ( bufBytesUsed >= ( bufBytes.Length - 16 ) ) {
787 stream.Write( bufBytes, 0, bufBytesUsed );
791 if ( writeAllToStream && bufBytesUsed > 0 ) {
792 stream.Write( bufBytes, 0, bufBytesUsed );
797 private void FlushEncoder() {
798 Debug.Assert( bufPos == 1 );
799 if ( stream != null ) {
803 // decode no chars, just flush
804 encoder.Convert( bufChars, 1, 0, bufBytes, 0, bufBytes.Length, true, out chEnc, out bEnc, out completed );
806 stream.Write( bufBytes, 0, bEnc );
812 // Serialize text that is part of an attribute value. The '&', '<', '>', and '"' characters
815 protected unsafe void WriteAttributeTextBlock( char *pSrc, char *pSrcEnd ) {
817 fixed ( char * pDstBegin = bufChars ) {
818 char * pDst = pDstBegin + this.bufPos;
822 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
823 if ( pDstEnd > pDstBegin + bufLen ) {
824 pDstEnd = pDstBegin + bufLen;
827 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fAttrValue ) != 0 ) ) ) {
833 Debug.Assert( pSrc <= pSrcEnd );
836 if ( pSrc >= pSrcEnd ) {
841 if ( pDst >= pDstEnd ) {
843 bufPos = (int)(pDst - pDstBegin);
845 pDst = pDstBegin + 1;
850 // some character needs to be escaped
853 pDst = AmpEntity( pDst );
856 pDst = LtEntity( pDst );
859 pDst = GtEntity( pDst );
862 pDst = QuoteEntity( pDst );
869 if ( newLineHandling == NewLineHandling.None ) {
874 // escape tab in attributes
875 pDst = TabEntity( pDst );
879 if ( newLineHandling == NewLineHandling.None ) {
884 // escape new lines in attributes
885 pDst = CarriageReturnEntity( pDst );
889 if ( newLineHandling == NewLineHandling.None ) {
894 // escape new lines in attributes
895 pDst = LineFeedEntity( pDst );
899 if ( XmlCharType.IsSurrogate( ch ) ) { pDst = EncodeSurrogate( pSrc, pSrcEnd, pDst ); pSrc += 2; } else if ( ch <= 0x7F || ch >= 0xFFFE ) { pDst = InvalidXmlChar( ch, pDst, true ); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
904 bufPos = (int)(pDst - pDstBegin);
909 // Serialize text that is part of element content. The '&', '<', and '>' characters
912 protected unsafe void WriteElementTextBlock( char *pSrc, char *pSrcEnd ) {
914 fixed ( char * pDstBegin = bufChars ) {
915 char * pDst = pDstBegin + this.bufPos;
919 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
920 if ( pDstEnd > pDstBegin + bufLen ) {
921 pDstEnd = pDstBegin + bufLen;
924 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fAttrValue ) != 0 ) ) ) {
930 Debug.Assert( pSrc <= pSrcEnd );
933 if ( pSrc >= pSrcEnd ) {
938 if ( pDst >= pDstEnd ) {
940 bufPos = (int)(pDst - pDstBegin);
942 pDst = pDstBegin + 1;
947 // some character needs to be escaped
950 pDst = AmpEntity( pDst );
953 pDst = LtEntity( pDst );
956 pDst = GtEntity( pDst );
965 if ( newLineHandling == NewLineHandling.Replace ) {
967 pDst = WriteNewLine( pDst );
976 switch ( newLineHandling ) {
977 case NewLineHandling.Replace:
978 // Replace "\r\n", or "\r" with NewLineChars
979 if ( pSrc[1] == '\n' ) {
983 pDst = WriteNewLine( pDst );
986 case NewLineHandling.Entitize:
988 pDst = CarriageReturnEntity( pDst );
990 case NewLineHandling.None:
997 if ( XmlCharType.IsSurrogate( ch ) ) { pDst = EncodeSurrogate( pSrc, pSrcEnd, pDst ); pSrc += 2; } else if ( ch <= 0x7F || ch >= 0xFFFE ) { pDst = InvalidXmlChar( ch, pDst, true ); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1002 bufPos = (int)(pDst - pDstBegin);
1009 protected unsafe void RawText( string s ) {
1010 Debug.Assert( s != null );
1012 fixed ( char * pSrcBegin = s ) {
1013 RawText( pSrcBegin, pSrcBegin + s.Length );
1017 protected unsafe void RawText( char * pSrcBegin, char * pSrcEnd ) {
1019 fixed ( char * pDstBegin = bufChars ) {
1020 char * pDst = pDstBegin + this.bufPos;
1021 char * pSrc = pSrcBegin;
1025 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
1026 if ( pDstEnd > pDstBegin + this.bufLen ) {
1027 pDstEnd = pDstBegin + this.bufLen;
1030 while ( pDst < pDstEnd && ( ( ch = *pSrc ) < XmlCharType.SurHighStart ) ) {
1036 Debug.Assert( pSrc <= pSrcEnd );
1039 if ( pSrc >= pSrcEnd ) {
1044 if ( pDst >= pDstEnd ) {
1046 bufPos = (int)(pDst - pDstBegin);
1048 pDst = pDstBegin + 1;
1053 if ( XmlCharType.IsSurrogate( ch ) ) { pDst = EncodeSurrogate( pSrc, pSrcEnd, pDst ); pSrc += 2; } else if ( ch <= 0x7F || ch >= 0xFFFE ) { pDst = InvalidXmlChar( ch, pDst, false ); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1056 bufPos = (int)(pDst - pDstBegin);
1061 protected unsafe void WriteRawWithCharChecking( char * pSrcBegin, char * pSrcEnd ) {
1063 fixed ( char * pDstBegin = bufChars ) {
1064 char * pSrc = pSrcBegin;
1065 char * pDst = pDstBegin + bufPos;
1069 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
1070 if ( pDstEnd > pDstBegin + bufLen ) {
1071 pDstEnd = pDstBegin + bufLen;
1074 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fText ) != 0 ) ) ) {
1081 Debug.Assert( pSrc <= pSrcEnd );
1084 if ( pSrc >= pSrcEnd ) {
1089 if ( pDst >= pDstEnd ) {
1091 bufPos = (int)(pDst - pDstBegin);
1093 pDst = pDstBegin + 1;
1098 // handle special characters
1108 if ( newLineHandling == NewLineHandling.Replace ) {
1109 // Normalize "\r\n", or "\r" to NewLineChars
1110 if ( pSrc[1] == '\n' ) {
1114 pDst = WriteNewLine( pDst );
1123 if ( newLineHandling == NewLineHandling.Replace ) {
1125 pDst = WriteNewLine( pDst );
1134 if ( XmlCharType.IsSurrogate( ch ) ) { pDst = EncodeSurrogate( pSrc, pSrcEnd, pDst ); pSrc += 2; } else if ( ch <= 0x7F || ch >= 0xFFFE ) { pDst = InvalidXmlChar( ch, pDst, false ); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1139 bufPos = (int)(pDst - pDstBegin);
1144 protected unsafe void WriteCommentOrPi( string text, int stopChar ) {
1146 if ( text.Length == 0 ) {
1147 if ( bufPos >= bufLen ) {
1153 fixed ( char * pSrcBegin = text )
1155 fixed ( char * pDstBegin = bufChars ) {
1156 char * pSrc = pSrcBegin;
1158 char * pSrcEnd = pSrcBegin + text.Length;
1160 char * pDst = pDstBegin + bufPos;
1164 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
1165 if ( pDstEnd > pDstBegin + bufLen ) {
1166 pDstEnd = pDstBegin + bufLen;
1169 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fText ) != 0 ) && ch != stopChar ) ) {
1176 Debug.Assert( pSrc <= pSrcEnd );
1179 if ( pSrc >= pSrcEnd ) {
1184 if ( pDst >= pDstEnd ) {
1186 bufPos = (int)(pDst - pDstBegin);
1188 pDst = pDstBegin + 1;
1193 // handle special characters
1198 if ( ch == stopChar ) {
1199 // Insert space between adjacent dashes or before comment's end dashes
1200 if ( pSrc + 1 == pSrcEnd || *(pSrc + 1)== '-' ) {
1209 if ( ch == stopChar ) {
1210 // Processing instruction: insert space between adjacent '?' and '>'
1211 if ( pSrc + 1 < pSrcEnd && *(pSrc + 1)== '>' ) {
1222 if ( newLineHandling == NewLineHandling.Replace ) {
1223 // Normalize "\r\n", or "\r" to NewLineChars
1224 if ( pSrc[1] == '\n' ) {
1228 pDst = WriteNewLine( pDst );
1237 if ( newLineHandling == NewLineHandling.Replace ) {
1239 pDst = WriteNewLine( pDst );
1254 if ( XmlCharType.IsSurrogate( ch ) ) { pDst = EncodeSurrogate( pSrc, pSrcEnd, pDst ); pSrc += 2; } else if ( ch <= 0x7F || ch >= 0xFFFE ) { pDst = InvalidXmlChar( ch, pDst, false ); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1259 bufPos = (int)(pDst - pDstBegin);
1264 protected unsafe void WriteCDataSection( string text ) {
1265 if ( text.Length == 0 ) {
1266 if ( bufPos >= bufLen ) {
1274 fixed ( char * pSrcBegin = text )
1276 fixed ( char * pDstBegin = bufChars ) {
1277 char * pSrc = pSrcBegin;
1279 char * pSrcEnd = pSrcBegin + text.Length;
1281 char * pDst = pDstBegin + bufPos;
1285 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
1286 if ( pDstEnd > pDstBegin + bufLen ) {
1287 pDstEnd = pDstBegin + bufLen;
1290 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fAttrValue ) != 0 ) && ch != ']' ) ) {
1297 Debug.Assert( pSrc <= pSrcEnd );
1300 if ( pSrc >= pSrcEnd ) {
1305 if ( pDst >= pDstEnd ) {
1307 bufPos = (int)(pDst - pDstBegin);
1309 pDst = pDstBegin + 1;
1314 // handle special characters
1317 if ( hadDoubleBracket && pDst[-1] == (char) ']') { // pDst[-1] will always correct - there is a padding character at _BUFFER[0]
1318 // The characters "]]>" were found within the CData text
1319 pDst = RawEndCData( pDst );
1320 pDst = RawStartCData( pDst );
1326 if ( pDst[-1] == (char)']' ) { // pDst[-1] will always correct - there is a padding character at _BUFFER[0]
1327 hadDoubleBracket = true;
1330 hadDoubleBracket = false;
1336 if ( newLineHandling == NewLineHandling.Replace ) {
1337 // Normalize "\r\n", or "\r" to NewLineChars
1338 if ( pSrc[1] == '\n' ) {
1342 pDst = WriteNewLine( pDst );
1351 if ( newLineHandling == NewLineHandling.Replace ) {
1353 pDst = WriteNewLine( pDst );
1370 if ( XmlCharType.IsSurrogate( ch ) ) { pDst = EncodeSurrogate( pSrc, pSrcEnd, pDst ); pSrc += 2; } else if ( ch <= 0x7F || ch >= 0xFFFE ) { pDst = InvalidXmlChar( ch, pDst, false ); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1375 bufPos = (int)(pDst - pDstBegin);
1380 private static unsafe char* EncodeSurrogate( char* pSrc, char* pSrcEnd, char* pDst ) {
1381 Debug.Assert( XmlCharType.IsSurrogate( *pSrc ) );
1384 if ( ch <= XmlCharType.SurHighEnd ) {
1385 if ( pSrc + 1 < pSrcEnd ) {
1386 int lowChar = pSrc[1];
1387 if ( lowChar >= XmlCharType.SurLowStart &&
1388 (LocalAppContextSwitches.DontThrowOnInvalidSurrogatePairs || lowChar <= XmlCharType.SurLowEnd)) {
1391 pDst[1] = (char)lowChar;
1396 throw XmlConvert.CreateInvalidSurrogatePairException( (char)lowChar, (char)ch );
1398 throw new ArgumentException( Res.GetString( Res.Xml_InvalidSurrogateMissingLowChar ) );
1400 throw XmlConvert.CreateInvalidHighSurrogateCharException( (char)ch );
1403 private unsafe char* InvalidXmlChar( int ch, char* pDst, bool entitize ) {
1404 Debug.Assert( !xmlCharType.IsWhiteSpace( (char)ch ) );
1405 Debug.Assert( !xmlCharType.IsAttributeValueChar( (char)ch ) );
1407 if ( checkCharacters ) {
1408 // This method will never be called on surrogates, so it is ok to pass in '\0' to the CreateInvalidCharException
1409 throw XmlConvert.CreateInvalidCharException( (char)ch, '\0' );
1413 return CharEntity( pDst, (char)ch );
1425 internal unsafe void EncodeChar(ref char* pSrc, char*pSrcEnd, ref char* pDst) {
1427 if ( XmlCharType.IsSurrogate( ch ) ) { pDst = EncodeSurrogate( pSrc, pSrcEnd, pDst ); pSrc += 2; } else if ( ch <= 0x7F || ch >= 0xFFFE ) { pDst = InvalidXmlChar( ch, pDst, false ); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1430 protected void ChangeTextContentMark( bool value ) {
1431 Debug.Assert( inTextContent != value );
1432 Debug.Assert( inTextContent || ((lastMarkPos & 1) == 0) );
1433 inTextContent = value;
1434 if ( lastMarkPos + 1 == textContentMarks.Length ) {
1435 GrowTextContentMarks();
1437 textContentMarks[++lastMarkPos] = this.bufPos;
1440 private void GrowTextContentMarks() {
1441 Debug.Assert( lastMarkPos + 1 == textContentMarks.Length );
1442 int[] newTextContentMarks = new int[ textContentMarks.Length * 2 ];
1443 Array.Copy( textContentMarks, newTextContentMarks, textContentMarks.Length );
1444 textContentMarks = newTextContentMarks;
1447 // Write NewLineChars to the specified buffer position and return an updated position.
1449 protected unsafe char * WriteNewLine( char * pDst ) {
1450 fixed ( char * pDstBegin = bufChars ) {
1451 bufPos = (int) (pDst - pDstBegin);
1452 // Let RawText do the real work
1453 RawText( newLineChars );
1454 return pDstBegin + bufPos;
1458 // Following methods do not check whether pDst is beyond the bufSize because the buffer was allocated with a OVERFLOW to accomodate
1459 // for the writes of small constant-length string as below.
1461 // Entitize '<' as "<". Return an updated pointer.
1463 protected static unsafe char * LtEntity( char * pDst ) {
1464 pDst[0] = (char)'&';
1465 pDst[1] = (char)'l';
1466 pDst[2] = (char)'t';
1467 pDst[3] = (char)';';
1471 // Entitize '>' as ">". Return an updated pointer.
1473 protected static unsafe char * GtEntity( char * pDst ) {
1474 pDst[0] = (char)'&';
1475 pDst[1] = (char)'g';
1476 pDst[2] = (char)'t';
1477 pDst[3] = (char)';';
1481 // Entitize '&' as "&". Return an updated pointer.
1483 protected static unsafe char * AmpEntity( char * pDst ) {
1484 pDst[0] = (char)'&';
1485 pDst[1] = (char)'a';
1486 pDst[2] = (char)'m';
1487 pDst[3] = (char)'p';
1488 pDst[4] = (char)';';
1492 // Entitize '"' as """. Return an updated pointer.
1494 protected static unsafe char * QuoteEntity( char * pDst ) {
1495 pDst[0] = (char)'&';
1496 pDst[1] = (char)'q';
1497 pDst[2] = (char)'u';
1498 pDst[3] = (char)'o';
1499 pDst[4] = (char)'t';
1500 pDst[5] = (char)';';
1504 // Entitize '\t' as "	". Return an updated pointer.
1506 protected static unsafe char * TabEntity( char * pDst ) {
1507 pDst[0] = (char)'&';
1508 pDst[1] = (char)'#';
1509 pDst[2] = (char)'x';
1510 pDst[3] = (char)'9';
1511 pDst[4] = (char)';';
1515 // Entitize 0xa as "
". Return an updated pointer.
1517 protected static unsafe char * LineFeedEntity( char * pDst ) {
1518 pDst[0] = (char)'&';
1519 pDst[1] = (char)'#';
1520 pDst[2] = (char)'x';
1521 pDst[3] = (char)'A';
1522 pDst[4] = (char)';';
1526 // Entitize 0xd as "
". Return an updated pointer.
1528 protected static unsafe char * CarriageReturnEntity( char * pDst ) {
1529 pDst[0] = (char)'&';
1530 pDst[1] = (char)'#';
1531 pDst[2] = (char)'x';
1532 pDst[3] = (char)'D';
1533 pDst[4] = (char)';';
1537 private static unsafe char * CharEntity( char * pDst, char ch ) {
1538 string s = ((int)ch).ToString( "X",NumberFormatInfo.InvariantInfo );
1539 pDst[0] = (char)'&';
1540 pDst[1] = (char)'#';
1541 pDst[2] = (char)'x';
1544 fixed ( char *pSrc = s ) {
1546 while ( ( *pDst++ = (char)*pS++ ) != 0 );
1549 pDst[-1] = (char)';';
1553 // Write "<![CDATA[" to the specified buffer. Return an updated pointer.
1555 protected static unsafe char * RawStartCData( char * pDst ) {
1556 pDst[0] = (char)'<';
1557 pDst[1] = (char)'!';
1558 pDst[2] = (char)'[';
1559 pDst[3] = (char)'C';
1560 pDst[4] = (char)'D';
1561 pDst[5] = (char)'A';
1562 pDst[6] = (char)'T';
1563 pDst[7] = (char)'A';
1564 pDst[8] = (char)'[';
1568 // Write "]]>" to the specified buffer. Return an updated pointer.
1570 protected static unsafe char * RawEndCData( char * pDst ) {
1571 pDst[0] = (char)']';
1572 pDst[1] = (char)']';
1573 pDst[2] = (char)'>';
1577 protected unsafe void ValidateContentChars( string chars, string propertyName, bool allowOnlyWhitespace ) {
1579 if ( allowOnlyWhitespace ) {
1580 if ( !xmlCharType.IsOnlyWhitespace( chars ) ) {
1581 throw new ArgumentException( Res.GetString( Res.Xml_IndentCharsNotWhitespace, propertyName ) );
1585 string error = null;
1586 for ( int i = 0; i < chars.Length; i++ ) {
1587 if ( !xmlCharType.IsTextChar( chars[i] ) ) {
1588 switch ( chars[i] ) {
1596 error = Res.GetString( Res.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs( chars, i ) );
1599 if ( XmlCharType.IsHighSurrogate(chars[i]) ) {
1600 if ( i + 1 < chars.Length ) {
1601 if ( XmlCharType.IsLowSurrogate(chars[i + 1]) ) {
1606 error = Res.GetString( Res.Xml_InvalidSurrogateMissingLowChar );
1609 else if ( XmlCharType.IsLowSurrogate(chars[i]) ) {
1610 error = Res.GetString( Res.Xml_InvalidSurrogateHighChar, ((uint)chars[i]).ToString( "X", CultureInfo.InvariantCulture ) );
1620 throw new ArgumentException( Res.GetString( Res.Xml_InvalidCharsInIndent, new string[] { propertyName, error } ) );
1626 // Same as base text writer class except that elements, attributes, comments, and pi's are indented.
1627 internal partial class XmlEncodedRawTextWriterIndent : XmlEncodedRawTextWriter {
1632 protected int indentLevel;
1633 protected bool newLineOnAttributes;
1634 protected string indentChars;
1636 protected bool mixedContent;
1637 private BitStack mixedContentStack;
1639 protected ConformanceLevel conformanceLevel = ConformanceLevel.Auto;
1645 public XmlEncodedRawTextWriterIndent( TextWriter writer, XmlWriterSettings settings ) : base( writer, settings ) {
1649 public XmlEncodedRawTextWriterIndent( Stream stream, XmlWriterSettings settings ) : base( stream, settings ) {
1654 // XmlWriter methods
1656 public override XmlWriterSettings Settings {
1658 XmlWriterSettings settings = base.Settings;
1660 settings.ReadOnly = false;
1661 settings.Indent = true;
1662 settings.IndentChars = indentChars;
1663 settings.NewLineOnAttributes = newLineOnAttributes;
1664 settings.ReadOnly = true;
1670 public override void WriteDocType( string name, string pubid, string sysid, string subset ) {
1672 if ( !mixedContent && base.textPos != base.bufPos) {
1675 base.WriteDocType( name, pubid, sysid, subset );
1678 public override void WriteStartElement( string prefix, string localName, string ns ) {
1679 Debug.Assert( localName != null && localName.Length != 0 && prefix != null && ns != null );
1682 if ( !mixedContent && base.textPos != base.bufPos) {
1686 mixedContentStack.PushBit( mixedContent );
1688 base.WriteStartElement( prefix, localName, ns );
1691 internal override void StartElementContent() {
1692 // If this is the root element and we're writing a document
1693 // do not inherit the mixedContent flag into the root element.
1694 // This is to allow for whitespace nodes on root level
1695 // without disabling indentation for the whole document.
1696 if (indentLevel == 1 && conformanceLevel == ConformanceLevel.Document) {
1697 mixedContent = false;
1700 mixedContent = mixedContentStack.PeekBit();
1702 base.StartElementContent();
1705 internal override void OnRootElement(ConformanceLevel currentConformanceLevel) {
1706 // Just remember the current conformance level
1707 conformanceLevel = currentConformanceLevel;
1710 internal override void WriteEndElement(string prefix, string localName, string ns) {
1713 if ( !mixedContent && base.contentPos != base.bufPos ) {
1714 // There was content, so try to indent
1715 if ( base.textPos != base.bufPos ) {
1719 mixedContent = mixedContentStack.PopBit();
1721 base.WriteEndElement( prefix, localName, ns );
1724 internal override void WriteFullEndElement(string prefix, string localName, string ns) {
1727 if ( !mixedContent && base.contentPos != base.bufPos ) {
1728 // There was content, so try to indent
1729 if ( base.textPos != base.bufPos ) {
1733 mixedContent = mixedContentStack.PopBit();
1735 base.WriteFullEndElement( prefix, localName, ns );
1738 // Same as base class, plus possible indentation.
1739 public override void WriteStartAttribute( string prefix, string localName, string ns ) {
1741 if ( newLineOnAttributes ) {
1745 base.WriteStartAttribute( prefix, localName, ns );
1748 public override void WriteCData( string text ) {
1749 mixedContent = true;
1750 base.WriteCData( text );
1753 public override void WriteComment( string text ) {
1754 if ( !mixedContent && base.textPos != base.bufPos ) {
1758 base.WriteComment( text );
1761 public override void WriteProcessingInstruction( string target, string text ) {
1762 if ( !mixedContent && base.textPos != base.bufPos ) {
1766 base.WriteProcessingInstruction( target, text );
1769 public override void WriteEntityRef( string name ) {
1770 mixedContent = true;
1771 base.WriteEntityRef( name );
1774 public override void WriteCharEntity( char ch ) {
1775 mixedContent = true;
1776 base.WriteCharEntity( ch );
1779 public override void WriteSurrogateCharEntity( char lowChar, char highChar ) {
1780 mixedContent = true;
1781 base.WriteSurrogateCharEntity( lowChar, highChar );
1784 public override void WriteWhitespace( string ws ) {
1785 mixedContent = true;
1786 base.WriteWhitespace( ws );
1789 public override void WriteString( string text ) {
1790 mixedContent = true;
1791 base.WriteString( text );
1794 public override void WriteChars( char[] buffer, int index, int count ) {
1795 mixedContent = true;
1796 base.WriteChars( buffer, index, count );
1799 public override void WriteRaw( char[] buffer, int index, int count ) {
1800 mixedContent = true;
1801 base.WriteRaw( buffer, index, count );
1804 public override void WriteRaw( string data ) {
1805 mixedContent = true;
1806 base.WriteRaw( data );
1809 public override void WriteBase64( byte[] buffer, int index, int count ) {
1810 mixedContent = true;
1811 base.WriteBase64( buffer, index, count );
1817 private void Init( XmlWriterSettings settings ) {
1819 indentChars = settings.IndentChars;
1820 newLineOnAttributes = settings.NewLineOnAttributes;
1821 mixedContentStack = new BitStack();
1823 // check indent characters that they are valid XML characters
1824 if ( base.checkCharacters ) {
1825 if ( newLineOnAttributes ) {
1826 base.ValidateContentChars( indentChars, "IndentChars", true );
1827 base.ValidateContentChars( newLineChars, "NewLineChars", true );
1830 base.ValidateContentChars( indentChars, "IndentChars", false );
1831 if ( base.newLineHandling != NewLineHandling.Replace ) {
1832 base.ValidateContentChars( newLineChars, "NewLineChars", false );
1838 // Add indentation to output. Write newline and then repeat IndentChars for each indent level.
1839 private void WriteIndent() {
1840 RawText( base.newLineChars );
1841 for ( int i = indentLevel; i > 0; i-- ) {
1842 RawText( indentChars );