Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / System.Xml / System / Xml / Core / HtmlUtf8RawTextWriter.cs
blob6cca7607cfb56542381f55998c094726f6c0081b
2 //------------------------------------------------------------------------------
3 // <copyright file="HtmlRawTextWriterGenerator.cxx" company="Microsoft">
4 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // </copyright>
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 HtmlTextWriterGenerator.cxx > HtmlUtf8TextWriter.cs
14 // cl.exe /C /EP /D _XML_ENCODED_TEXT_WRITER HtmlTextWriterGenerator.cxx > HtmlEncodedTextWriter.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.
43 using System;
44 using System.IO;
45 using System.Text;
46 using System.Xml;
47 using System.Xml.Schema;
48 //using System.Xml.Query;
49 using System.Diagnostics;
50 using MS.Internal.Xml;
52 namespace System.Xml {
54 internal class HtmlUtf8RawTextWriter : XmlUtf8RawTextWriter {
56 // Fields
58 protected ByteStack elementScope;
59 protected ElementProperties currentElementProperties;
60 private AttributeProperties currentAttributeProperties;
62 private bool endsWithAmpersand;
63 private byte [] uriEscapingBuffer;
65 // settings
66 private string mediaType;
67 private bool doNotEscapeUriAttributes;
70 // Static fields
72 protected static TernaryTreeReadOnly elementPropertySearch;
73 protected static TernaryTreeReadOnly attributePropertySearch;
76 // Constants
78 private const int StackIncrement = 10;
81 // Constructors
91 public HtmlUtf8RawTextWriter( Stream stream, XmlWriterSettings settings ) : base( stream, settings ) {
92 Init( settings );
96 // XmlRawWriter implementation
98 internal override void WriteXmlDeclaration( XmlStandalone standalone ) {
99 // Ignore xml declaration
102 internal override void WriteXmlDeclaration( string xmldecl ) {
103 // Ignore xml declaration
106 /// Html rules allow public ID without system ID and always output "html"
107 public override void WriteDocType( string name, string pubid, string sysid, string subset ) {
108 Debug.Assert( name != null && name.Length > 0 );
112 RawText( "<!DOCTYPE ");
114 // Bug 114337: Always output "html" or "HTML" in doc-type, even if "name" is something else
115 if ( name == "HTML" )
116 RawText( "HTML" );
117 else
118 RawText( "html" );
120 if ( pubid != null ) {
121 RawText( " PUBLIC \"" );
122 RawText( pubid );
123 if ( sysid != null ) {
124 RawText( "\" \"" );
125 RawText( sysid );
127 bufBytes[bufPos++] = (byte) '"';
129 else if ( sysid != null ) {
130 RawText( " SYSTEM \"" );
131 RawText( sysid );
132 bufBytes[bufPos++] = (byte) '"';
134 else {
135 bufBytes[bufPos++] = (byte) ' ';
138 if ( subset != null ) {
139 bufBytes[bufPos++] = (byte) '[';
140 RawText( subset );
141 bufBytes[bufPos++] = (byte) ']';
144 bufBytes[this.bufPos++] = (byte) '>';
147 // For the HTML element, it should call this method with ns and prefix as String.Empty
148 public override void WriteStartElement( string prefix, string localName, string ns ) {
149 Debug.Assert( localName != null && localName.Length != 0 && prefix != null && ns != null );
151 elementScope.Push( (byte)currentElementProperties );
153 if ( ns.Length == 0 ) {
154 Debug.Assert( prefix.Length == 0 );
157 currentElementProperties = (ElementProperties)elementPropertySearch.FindCaseInsensitiveString( localName );
158 base.bufBytes[bufPos++] = (byte) '<';
159 base.RawText( localName );
160 base.attrEndPos = bufPos;
162 else {
163 // Since the HAS_NS has no impact to the ElementTextBlock behavior,
164 // we don't need to push it into the stack.
165 currentElementProperties = ElementProperties.HAS_NS;
166 base.WriteStartElement( prefix, localName, ns );
170 // Output >. For HTML needs to output META info
171 internal override void StartElementContent() {
172 base.bufBytes[base.bufPos++] = (byte) '>';
174 // Detect whether content is output
175 this.contentPos = this.bufPos;
177 if ( ( currentElementProperties & ElementProperties.HEAD ) != 0 ) {
178 WriteMetaElement();
182 // end element with />
183 // for HTML(ns.Length == 0)
184 // not an empty tag <h1></h1>
185 // empty tag <basefont>
186 internal override void WriteEndElement( string prefix, string localName, string ns ) {
187 if ( ns.Length == 0 ) {
188 Debug.Assert( prefix.Length == 0 );
192 if ( ( currentElementProperties & ElementProperties.EMPTY ) == 0 ) {
193 bufBytes[base.bufPos++] = (byte) '<';
194 bufBytes[base.bufPos++] = (byte) '/';
195 base.RawText( localName );
196 bufBytes[base.bufPos++] = (byte) '>';
199 else {
200 //xml content
201 base.WriteEndElement( prefix, localName, ns );
204 currentElementProperties = (ElementProperties)elementScope.Pop();
207 internal override void WriteFullEndElement( string prefix, string localName, string ns ) {
208 if ( ns.Length == 0 ) {
209 Debug.Assert( prefix.Length == 0 );
213 if ( ( currentElementProperties & ElementProperties.EMPTY ) == 0 ) {
214 bufBytes[base.bufPos++] = (byte) '<';
215 bufBytes[base.bufPos++] = (byte) '/';
216 base.RawText( localName );
217 bufBytes[base.bufPos++] = (byte) '>';
220 else {
221 //xml content
222 base.WriteFullEndElement( prefix, localName, ns );
225 currentElementProperties = (ElementProperties)elementScope.Pop();
228 // 1. How the outputBooleanAttribute(fBOOL) and outputHtmlUriText(fURI) being set?
229 // When SA is called.
231 // BOOL_PARENT URI_PARENT Others
232 // fURI
233 // URI att false true false
235 // fBOOL
236 // BOOL att true false false
238 // How they change the attribute output behaviors?
240 // 1) fURI=true fURI=false
241 // SA a=" a="
242 // AT HtmlURIText HtmlText
243 // EA " "
245 // 2) fBOOL=true fBOOL=false
246 // SA a a="
247 // AT HtmlText output nothing
248 // EA output nothing "
250 // When they get reset?
251 // At the end of attribute.
253 // 2. How the outputXmlTextElementScoped(fENs) and outputXmlTextattributeScoped(fANs) are set?
254 // fANs is in the scope of the fENs.
256 // SE(localName) SE(ns, pre, localName) SA(localName) SA(ns, pre, localName)
257 // fENs false(default) true(action)
258 // fANs false(default) false(default) false(default) true(action)
260 // how they get reset?
262 // EE(localName) EE(ns, pre, localName) EENC(ns, pre, localName) EA(localName) EA(ns, pre, localName)
263 // fENs false(action)
264 // fANs false(action)
266 // How they change the TextOutput?
268 // fENs | fANs Else
269 // AT XmlText HtmlText
272 // 3. Flags for processing &{ split situations
274 // When the flag is set?
276 // AT src[lastchar]='&' flag&{ = true;
278 // when it get result?
280 // AT method.
282 // How it changes the behaviors?
284 // flag&{=true
286 // AT if (src[0] == '{') {
287 // output "&{"
288 // }
289 // else {
290 // output &amp;
291 // }
293 // EA output amp;
296 // SA if (flagBOOL == false) { output =";}
298 // AT if (flagBOOL) { return};
299 // if (flagNS) {XmlText;} {
300 // }
301 // else if (flagURI) {
302 // HtmlURIText;
303 // }
304 // else {
305 // HtmlText;
306 // }
309 // AT if (flagNS) {XmlText;} {
310 // }
311 // else if (flagURI) {
312 // HtmlURIText;
313 // }
314 // else if (!flagBOOL) {
315 // HtmlText; //flag&{ handling
316 // }
319 // EA if (flagBOOL == false) { output "
320 // }
321 // else if (flag&{) {
322 // output amp;
323 // }
325 public override void WriteStartAttribute( string prefix, string localName, string ns ) {
326 Debug.Assert( localName != null && localName.Length != 0 && prefix != null && ns != null );
328 if ( ns.Length == 0 ) {
329 Debug.Assert( prefix.Length == 0 );
332 if ( base.attrEndPos == bufPos ) {
333 base.bufBytes[bufPos++] = (byte) ' ';
335 base.RawText( localName );
337 if ( ( currentElementProperties & ( ElementProperties.BOOL_PARENT | ElementProperties.URI_PARENT | ElementProperties.NAME_PARENT ) ) != 0 ) {
338 currentAttributeProperties = (AttributeProperties)attributePropertySearch.FindCaseInsensitiveString( localName ) &
339 (AttributeProperties)currentElementProperties;
341 if ( ( currentAttributeProperties & AttributeProperties.BOOLEAN ) != 0 ) {
342 base.inAttributeValue = true;
343 return;
346 else {
347 currentAttributeProperties = AttributeProperties.DEFAULT;
350 base.bufBytes[bufPos++] = (byte) '=';
351 base.bufBytes[bufPos++] = (byte) '"';
353 else {
354 base.WriteStartAttribute( prefix, localName, ns );
355 currentAttributeProperties = AttributeProperties.DEFAULT;
358 base.inAttributeValue = true;
361 // Output the amp; at end of EndAttribute
362 public override void WriteEndAttribute() {
364 if ( ( currentAttributeProperties & AttributeProperties.BOOLEAN ) != 0 ) {
365 base.attrEndPos = bufPos;
367 else {
368 if ( endsWithAmpersand ) {
369 OutputRestAmps();
370 endsWithAmpersand = false;
375 base.bufBytes[bufPos++] = (byte) '"';
377 base.inAttributeValue = false;
378 base.attrEndPos = bufPos;
381 // HTML PI's use ">" to terminate rather than "?>".
382 public override void WriteProcessingInstruction( string target, string text ) {
383 Debug.Assert( target != null && target.Length != 0 && text != null );
387 bufBytes[base.bufPos++] = (byte) '<';
388 bufBytes[base.bufPos++] = (byte) '?';
389 base.RawText( target );
390 bufBytes[base.bufPos++] = (byte) ' ';
392 base.WriteCommentOrPi( text, '?' );
394 base.bufBytes[base.bufPos++] = (byte) '>';
396 if ( base.bufPos > base.bufLen ) {
397 FlushBuffer();
401 // Serialize either attribute or element text using HTML rules.
402 public override unsafe void WriteString( string text ) {
403 Debug.Assert( text != null );
407 fixed ( char * pSrc = text ) {
408 char * pSrcEnd = pSrc + text.Length;
409 if ( base.inAttributeValue) {
410 WriteHtmlAttributeTextBlock( pSrc, pSrcEnd );
412 else {
413 WriteHtmlElementTextBlock( pSrc, pSrcEnd );
418 public override void WriteEntityRef( string name ) {
419 throw new InvalidOperationException( Res.GetString( Res.Xml_InvalidOperation ) );
422 public override void WriteCharEntity( char ch ) {
423 throw new InvalidOperationException( Res.GetString( Res.Xml_InvalidOperation ) );
426 public override void WriteSurrogateCharEntity( char lowChar, char highChar ) {
427 throw new InvalidOperationException( Res.GetString( Res.Xml_InvalidOperation ) );
430 public override unsafe void WriteChars( char[] buffer, int index, int count ) {
431 Debug.Assert( buffer != null );
432 Debug.Assert( index >= 0 );
433 Debug.Assert( count >= 0 && index + count <= buffer.Length );
437 fixed ( char * pSrcBegin = &buffer[index] ) {
438 if ( inAttributeValue ) {
439 WriteAttributeTextBlock( pSrcBegin, pSrcBegin + count );
441 else {
442 WriteElementTextBlock( pSrcBegin, pSrcBegin + count );
448 // Private methods
451 private void Init( XmlWriterSettings settings ) {
452 Debug.Assert( (int)ElementProperties.URI_PARENT == (int)AttributeProperties.URI );
453 Debug.Assert( (int)ElementProperties.BOOL_PARENT == (int)AttributeProperties.BOOLEAN );
454 Debug.Assert( (int)ElementProperties.NAME_PARENT == (int)AttributeProperties.NAME );
456 if ( elementPropertySearch == null ) {
457 //elementPropertySearch should be init last for the mutli thread safe situation.
458 attributePropertySearch = new TernaryTreeReadOnly(HtmlTernaryTree.htmlAttributes );
459 elementPropertySearch = new TernaryTreeReadOnly( HtmlTernaryTree.htmlElements );
462 elementScope = new ByteStack( StackIncrement );
463 uriEscapingBuffer = new byte[5];
464 currentElementProperties = ElementProperties.DEFAULT;
466 mediaType = settings.MediaType;
467 doNotEscapeUriAttributes = settings.DoNotEscapeUriAttributes;
470 protected void WriteMetaElement() {
471 base.RawText("<META http-equiv=\"Content-Type\"");
473 if ( mediaType == null ) {
474 mediaType = "text/html";
477 base.RawText( " content=\"" );
478 base.RawText( mediaType );
479 base.RawText( "; charset=" );
480 base.RawText( base.encoding.WebName );
481 base.RawText( "\">" );
484 // Justify the stack usage:
486 // Nested elements has following possible position combinations
487 // 1. <E1>Content1<E2>Content2</E2></E1>
488 // 2. <E1><E2>Content2</E2>Content1</E1>
489 // 3. <E1>Content<E2>Cotent2</E2>Content1</E1>
491 // In the situation 2 and 3, the stored currentElementProrperties will be E2's,
492 // only the top of the stack is the real E1 element properties.
493 protected unsafe void WriteHtmlElementTextBlock( char * pSrc, char * pSrcEnd ) {
494 if ( ( currentElementProperties & ElementProperties.NO_ENTITIES ) != 0 ) {
495 base.RawText( pSrc, pSrcEnd );
496 } else {
497 base.WriteElementTextBlock( pSrc, pSrcEnd );
502 protected unsafe void WriteHtmlAttributeTextBlock( char * pSrc, char * pSrcEnd ) {
503 if ( ( currentAttributeProperties & ( AttributeProperties.BOOLEAN | AttributeProperties.URI | AttributeProperties.NAME ) ) != 0 ) {
504 if ( ( currentAttributeProperties & AttributeProperties.BOOLEAN ) != 0 ) {
505 //if output boolean attribute, ignore this call.
506 return;
509 if ( ( currentAttributeProperties & ( AttributeProperties.URI | AttributeProperties.NAME ) ) != 0 && !doNotEscapeUriAttributes ) {
510 WriteUriAttributeText( pSrc, pSrcEnd );
512 else {
513 WriteHtmlAttributeText( pSrc, pSrcEnd );
516 else if ( ( currentElementProperties & ElementProperties.HAS_NS ) != 0 ) {
517 base.WriteAttributeTextBlock( pSrc, pSrcEnd );
519 else {
520 WriteHtmlAttributeText( pSrc, pSrcEnd );
525 // &{ split cases
526 // 1). HtmlAttributeText("a&");
527 // HtmlAttributeText("{b}");
529 // 2). HtmlAttributeText("a&");
530 // EndAttribute();
532 // 3).split with Flush by the user
533 // HtmlAttributeText("a&");
534 // FlushBuffer();
535 // HtmlAttributeText("{b}");
538 // Solutions:
539 // case 1)hold the &amp; output as &
540 // if the next income character is {, output {
541 // else output amp;
544 private unsafe void WriteHtmlAttributeText( char * pSrc, char *pSrcEnd ) {
545 if ( endsWithAmpersand ) {
546 if ( pSrcEnd - pSrc > 0 && pSrc[0] != '{' ) {
547 OutputRestAmps();
549 endsWithAmpersand = false;
552 fixed ( byte * pDstBegin = bufBytes ) {
553 byte * pDst = pDstBegin + this.bufPos;
555 char ch = (char)0;
556 for (;;) {
557 byte * pDstEnd = pDst + ( pSrcEnd - pSrc );
558 if ( pDstEnd > pDstBegin + bufLen ) {
559 pDstEnd = pDstBegin + bufLen;
563 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fAttrValue ) != 0 ) && ch <= 0x7F ) ) {
567 *pDst++ = (byte)ch;
568 pSrc++;
570 Debug.Assert( pSrc <= pSrcEnd );
572 // end of value
573 if ( pSrc >= pSrcEnd ) {
574 break;
577 // end of buffer
578 if ( pDst >= pDstEnd ) {
579 bufPos = (int)(pDst - pDstBegin);
580 FlushBuffer();
581 pDst = pDstBegin + 1;
582 continue;
585 // some character needs to be escaped
586 switch ( ch ) {
587 case '&':
588 if ( pSrc + 1 == pSrcEnd ) {
589 endsWithAmpersand = true;
591 else if ( pSrc[1] != '{' ) {
592 pDst = XmlUtf8RawTextWriter.AmpEntity(pDst);
593 break;
595 *pDst++ = (byte)ch;
596 break;
597 case '"':
598 pDst = QuoteEntity( pDst );
599 break;
600 case '<':
601 case '>':
602 case '\'':
603 case (char)0x9:
604 *pDst++ = (byte)ch;
605 break;
606 case (char)0xD:
607 // do not normalize new lines in attributes - just escape them
608 pDst = CarriageReturnEntity( pDst );
609 break;
610 case (char)0xA:
611 // do not normalize new lines in attributes - just escape them
612 pDst = LineFeedEntity( pDst );
613 break;
614 default:
615 EncodeChar( ref pSrc, pSrcEnd, ref pDst);
616 continue;
618 pSrc++;
620 bufPos = (int)(pDst - pDstBegin);
624 private unsafe void WriteUriAttributeText( char * pSrc, char * pSrcEnd ) {
625 if ( endsWithAmpersand ) {
626 if ( pSrcEnd - pSrc > 0 && pSrc[0] != '{' ) {
627 OutputRestAmps();
629 this.endsWithAmpersand = false;
632 fixed ( byte * pDstBegin = bufBytes ) {
633 byte * pDst = pDstBegin + this.bufPos;
635 char ch = (char)0;
636 for (;;) {
637 byte * pDstEnd = pDst + ( pSrcEnd - pSrc );
638 if ( pDstEnd > pDstBegin + bufLen ) {
639 pDstEnd = pDstBegin + bufLen;
642 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fAttrValue ) != 0 ) && ch < 0x80 ) ) {
643 *pDst++ = (byte)ch;
644 pSrc++;
646 Debug.Assert( pSrc <= pSrcEnd );
648 // end of value
649 if ( pSrc >= pSrcEnd ) {
650 break;
653 // end of buffer
654 if ( pDst >= pDstEnd ) {
655 bufPos = (int)(pDst - pDstBegin);
656 FlushBuffer();
657 pDst = pDstBegin + 1;
658 continue;
661 // some character needs to be escaped
662 switch ( ch ) {
663 case '&':
664 if ( pSrc + 1 == pSrcEnd ) {
665 endsWithAmpersand = true;
667 else if ( pSrc[1] != '{' ) {
668 pDst = XmlUtf8RawTextWriter.AmpEntity(pDst);
669 break;
671 *pDst++ = (byte)ch;
672 break;
673 case '"':
674 pDst = QuoteEntity( pDst );
675 break;
676 case '<':
677 case '>':
678 case '\'':
679 case (char)0x9:
680 *pDst++ = (byte)ch;
681 break;
682 case (char)0xD:
683 // do not normalize new lines in attributes - just escape them
684 pDst = CarriageReturnEntity( pDst );
685 break;
686 case (char)0xA:
687 // do not normalize new lines in attributes - just escape them
688 pDst = LineFeedEntity( pDst );
689 break;
690 default:
691 const string hexDigits = "0123456789ABCDEF";
692 fixed ( byte * pUriEscapingBuffer = uriEscapingBuffer ) {
693 byte * pByte = pUriEscapingBuffer;
694 byte * pEnd = pByte;
696 XmlUtf8RawTextWriter.CharToUTF8( ref pSrc, pSrcEnd, ref pEnd );
698 while ( pByte < pEnd ) {
699 *pDst++ = (byte) '%';
700 *pDst++ = (byte) hexDigits[*pByte >> 4];
701 *pDst++ = (byte) hexDigits[*pByte & 0xF];
702 pByte++;
705 continue;
707 pSrc++;
709 bufPos = (int)(pDst - pDstBegin);
713 // For handling &{ in Html text field. If & is not followed by {, it still needs to be escaped.
714 private void OutputRestAmps() {
715 base.bufBytes[bufPos++] = (byte)'a';
716 base.bufBytes[bufPos++] = (byte)'m';
717 base.bufBytes[bufPos++] = (byte)'p';
718 base.bufBytes[bufPos++] = (byte)';';
724 // Indentation HtmlWriter only indent <BLOCK><BLOCK> situations
726 // Here are all the cases:
727 // ELEMENT1 actions ELEMENT2 actions SC EE
728 // 1). SE SC store SE blockPro SE a). check ELEMENT1 blockPro <A> </A>
729 // EE if SE, EE are blocks b). true: check ELEMENT2 blockPro <B> <B>
730 // c). detect ELEMENT is SE, SC
731 // d). increase the indexlevel
733 // 2). SE SC, Store EE blockPro EE a). check stored blockPro <A></A> </A>
734 // EE if SE, EE are blocks b). true: indexLevel same </B>
739 // This is an alternative way to make the output looks better
741 // Indentation HtmlWriter only indent <BLOCK><BLOCK> situations
743 // Here are all the cases:
744 // ELEMENT1 actions ELEMENT2 actions Samples
745 // 1). SE SC store SE blockPro SE a). check ELEMENT1 blockPro <A>(blockPos)
746 // b). true: check ELEMENT2 blockPro <B>
747 // c). detect ELEMENT is SE, SC
748 // d). increase the indentLevel
750 // 2). EE Store EE blockPro SE a). check stored blockPro </A>
751 // b). true: indentLevel same <B>
752 // c). output block2
754 // 3). EE same as above EE a). check stored blockPro </A>
755 // b). true: --indentLevel </B>
756 // c). output block2
758 // 4). SE SC same as above EE a). check stored blockPro <A></A>
759 // b). true: indentLevel no change
760 internal class HtmlUtf8RawTextWriterIndent : HtmlUtf8RawTextWriter {
762 // Fields
764 int indentLevel;
766 // for detecting SE SC sitution
767 int endBlockPos;
769 // settings
770 string indentChars;
771 bool newLineOnAttributes;
774 // Constructors
783 public HtmlUtf8RawTextWriterIndent( Stream stream, XmlWriterSettings settings ) : base( stream, settings ) {
784 Init( settings );
788 // XmlRawWriter overrides
790 /// <summary>
791 /// Serialize the document type declaration.
792 /// </summary>
793 public override void WriteDocType( string name, string pubid, string sysid, string subset ) {
794 base.WriteDocType( name, pubid, sysid, subset );
796 // Allow indentation after DocTypeDecl
797 endBlockPos = base.bufPos;
800 public override void WriteStartElement(string prefix, string localName, string ns ) {
801 Debug.Assert( localName != null && localName.Length != 0 && prefix != null && ns != null );
805 base.elementScope.Push( (byte)base.currentElementProperties );
807 if ( ns.Length == 0 ) {
808 Debug.Assert( prefix.Length == 0 );
810 base.currentElementProperties = (ElementProperties)elementPropertySearch.FindCaseInsensitiveString( localName );
812 if ( endBlockPos == base.bufPos && ( base.currentElementProperties & ElementProperties.BLOCK_WS ) != 0 ) {
813 WriteIndent();
815 indentLevel++;
817 base.bufBytes[bufPos++] = (byte) '<';
819 else {
820 base.currentElementProperties = ElementProperties.HAS_NS | ElementProperties.BLOCK_WS;
822 if ( endBlockPos == base.bufPos ) {
823 WriteIndent();
825 indentLevel++;
827 base.bufBytes[base.bufPos++] = (byte) '<';
828 if ( prefix.Length != 0 ) {
829 base.RawText( prefix );
830 base.bufBytes[base.bufPos++] = (byte) ':';
833 base.RawText( localName );
834 base.attrEndPos = bufPos;
837 internal override void StartElementContent() {
838 base.bufBytes[base.bufPos++] = (byte) '>';
840 // Detect whether content is output
841 base.contentPos = base.bufPos;
843 if ( ( currentElementProperties & ElementProperties.HEAD ) != 0) {
844 WriteIndent();
845 WriteMetaElement();
846 endBlockPos = base.bufPos;
848 else if ( ( base.currentElementProperties & ElementProperties.BLOCK_WS ) != 0 ) {
849 // store the element block position
850 endBlockPos = base.bufPos;
854 internal override void WriteEndElement( string prefix, string localName, string ns ) {
855 bool isBlockWs;
856 Debug.Assert( localName != null && localName.Length != 0 && prefix != null && ns != null );
858 indentLevel--;
860 // If this element has block whitespace properties,
861 isBlockWs = ( base.currentElementProperties & ElementProperties.BLOCK_WS ) != 0;
862 if ( isBlockWs ) {
863 // And if the last node to be output had block whitespace properties,
864 // And if content was output within this element,
865 if ( endBlockPos == base.bufPos && base.contentPos != base.bufPos ) {
866 // Then indent
867 WriteIndent();
871 base.WriteEndElement(prefix, localName, ns);
873 // Reset contentPos in case of empty elements
874 base.contentPos = 0;
876 // Mark end of element in buffer for element's with block whitespace properties
877 if ( isBlockWs ) {
878 endBlockPos = base.bufPos;
882 public override void WriteStartAttribute( string prefix, string localName, string ns ) {
883 if ( newLineOnAttributes ) {
884 RawText( base.newLineChars );
885 indentLevel++;
886 WriteIndent();
887 indentLevel--;
889 base.WriteStartAttribute( prefix, localName, ns );
892 protected override void FlushBuffer() {
893 // Make sure the buffer will reset the block position
894 endBlockPos = ( endBlockPos == base.bufPos ) ? 1 : 0;
895 base.FlushBuffer();
899 // Private methods
901 private void Init( XmlWriterSettings settings ) {
902 indentLevel = 0;
903 indentChars = settings.IndentChars;
904 newLineOnAttributes = settings.NewLineOnAttributes;
907 private void WriteIndent() {
908 // <block><inline> -- suppress ws betw <block> and <inline>
909 // <block><block> -- don't suppress ws betw <block> and <block>
910 // <block>text -- suppress ws betw <block> and text (handled by wcharText method)
911 // <block><?PI?> -- suppress ws betw <block> and PI
912 // <block><!-- --> -- suppress ws betw <block> and comment
914 // <inline><block> -- suppress ws betw <inline> and <block>
915 // <inline><inline> -- suppress ws betw <inline> and <inline>
916 // <inline>text -- suppress ws betw <inline> and text (handled by wcharText method)
917 // <inline><?PI?> -- suppress ws betw <inline> and PI
918 // <inline><!-- --> -- suppress ws betw <inline> and comment
920 RawText( base.newLineChars );
921 for ( int i = indentLevel; i > 0; i-- ) {
922 RawText( indentChars );