1 //------------------------------------------------------------------------------
2 // <copyright file="XmlTextWriter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
11 using System.Diagnostics;
12 using System.Globalization;
14 namespace System.Xml {
18 // This class does special handling of text content for XML. For example
19 // it will replace special characters with entities whenever necessary.
20 internal class XmlTextEncoder {
25 TextWriter textWriter;
27 // true when writing out the content of attribute value
30 // quote char of the attribute (when inAttribute)
33 // caching of attribute value
34 StringBuilder attrValue;
38 XmlCharType xmlCharType;
43 internal XmlTextEncoder( TextWriter textWriter ) {
44 this.textWriter = textWriter;
46 this.xmlCharType = XmlCharType.Instance;
50 // Internal methods and properties
52 internal char QuoteChar {
54 this.quoteChar = value;
58 internal void StartAttribute( bool cacheAttrValue ) {
59 this.inAttribute = true;
60 this.cacheAttrValue = cacheAttrValue;
61 if ( cacheAttrValue ) {
62 if ( attrValue == null ) {
63 attrValue = new StringBuilder();
71 internal void EndAttribute() {
72 if ( cacheAttrValue ) {
75 this.inAttribute = false;
76 this.cacheAttrValue = false;
79 internal string AttributeValue {
81 if ( cacheAttrValue ) {
82 return attrValue.ToString();
90 internal void WriteSurrogateChar( char lowChar, char highChar ) {
91 if ( !XmlCharType.IsLowSurrogate(lowChar) ||
92 !XmlCharType.IsHighSurrogate( highChar ) ) {
93 throw XmlConvert.CreateInvalidSurrogatePairException( lowChar, highChar );
96 textWriter.Write( highChar );
97 textWriter.Write( lowChar );
101 [System.Security.SecurityCritical]
103 internal void Write( char[] array, int offset, int count ) {
104 if ( null == array ) {
105 throw new ArgumentNullException("array");
109 throw new ArgumentOutOfRangeException("offset");
113 throw new ArgumentOutOfRangeException("count");
116 if ( count > array.Length - offset ) {
117 throw new ArgumentOutOfRangeException("count");
120 if ( cacheAttrValue ) {
121 attrValue.Append( array, offset, count );
124 int endPos = offset + count;
130 while ( i < endPos && ( xmlCharType.charProperties[ch = array[i]] & XmlCharType.fAttrValue ) != 0 ) { // ( xmlCharType.IsAttributeValueChar( ( ch = array[i] ) ) ) ) {
135 if ( startPos < i ) {
136 textWriter.Write( array, startPos, i - startPos );
144 textWriter.Write( ch );
149 WriteCharEntityImpl( ch );
152 textWriter.Write( ch );
157 WriteEntityRefImpl( "lt" );
160 WriteEntityRefImpl( "gt" );
163 WriteEntityRefImpl( "amp" );
166 if ( inAttribute && quoteChar == ch ) {
167 WriteEntityRefImpl( "apos" );
170 textWriter.Write( '\'' );
174 if ( inAttribute && quoteChar == ch ) {
175 WriteEntityRefImpl( "quot" );
178 textWriter.Write( '"' );
182 if ( XmlCharType.IsHighSurrogate( ch ) ) {
183 if ( i + 1 < endPos ) {
184 WriteSurrogateChar( array[++i], ch );
187 throw new ArgumentException( Res.GetString( Res.Xml_SurrogatePairSplit ) );
190 else if ( XmlCharType.IsLowSurrogate( ch ) ) {
191 throw XmlConvert.CreateInvalidHighSurrogateCharException( ch );
194 Debug.Assert( ( ch < 0x20 && !xmlCharType.IsWhiteSpace( ch ) ) || ( ch > 0xFFFD ) );
195 WriteCharEntityImpl( ch );
203 internal void WriteSurrogateCharEntity( char lowChar, char highChar ) {
204 if ( !XmlCharType.IsLowSurrogate( lowChar ) ||
205 !XmlCharType.IsHighSurrogate( highChar ) ) {
206 throw XmlConvert.CreateInvalidSurrogatePairException( lowChar, highChar );
209 int surrogateChar = XmlCharType.CombineSurrogateChar( lowChar, highChar );
211 if ( cacheAttrValue ) {
212 attrValue.Append( highChar );
213 attrValue.Append( lowChar );
216 textWriter.Write( "&#x" );
217 textWriter.Write( surrogateChar.ToString( "X", NumberFormatInfo.InvariantInfo ) );
218 textWriter.Write( ';' );
222 [System.Security.SecurityCritical]
224 internal void Write( string text ) {
225 if ( text == null ) {
229 if ( cacheAttrValue ) {
230 attrValue.Append( text );
233 // scan through the string to see if there are any characters to be escaped
234 int len = text.Length;
240 while ( i < len && ( xmlCharType.charProperties[ch = text[i]] & XmlCharType.fAttrValue ) != 0 ) { // ( xmlCharType.IsAttributeValueChar( ( ch = text[i] ) ) ) ) {
245 // reached the end of the string -> write it whole out
246 textWriter.Write( text );
256 if ( ch == 0x9 || ch == 0xA || ch == 0xD || ch == '"' || ch == '\'' ) {
261 // some character that needs to be escaped is found:
265 char[] helperBuffer = new char[256];
267 if ( startPos < i ) {
268 WriteStringFragment( text, startPos, i - startPos, helperBuffer );
276 textWriter.Write( ch );
281 WriteCharEntityImpl( ch );
284 textWriter.Write( ch );
288 WriteEntityRefImpl( "lt" );
291 WriteEntityRefImpl( "gt" );
294 WriteEntityRefImpl( "amp" );
297 if ( inAttribute && quoteChar == ch ) {
298 WriteEntityRefImpl( "apos" );
301 textWriter.Write( '\'' );
305 if ( inAttribute && quoteChar == ch ) {
306 WriteEntityRefImpl( "quot" );
309 textWriter.Write( '"' );
313 if ( XmlCharType.IsHighSurrogate( ch ) ) {
315 WriteSurrogateChar( text[++i], ch );
318 throw XmlConvert.CreateInvalidSurrogatePairException( text[i], ch );
321 else if ( XmlCharType.IsLowSurrogate( ch ) ) {
322 throw XmlConvert.CreateInvalidHighSurrogateCharException( ch );
325 Debug.Assert( ( ch < 0x20 && !xmlCharType.IsWhiteSpace( ch ) ) || ( ch > 0xFFFD ) );
326 WriteCharEntityImpl( ch );
333 while ( i < len && ( xmlCharType.charProperties[ch = text[i]] & XmlCharType.fAttrValue ) != 0 ) { // ( xmlCharType.IsAttributeValueChar( ( text[i] ) ) ) ) {
341 [System.Security.SecurityCritical]
343 internal void WriteRawWithSurrogateChecking( string text ) {
344 if ( text == null ) {
347 if ( cacheAttrValue ) {
348 attrValue.Append( text );
351 int len = text.Length;
358 ( ( xmlCharType.charProperties[ch = text[i]] & XmlCharType.fCharData ) != 0 // ( xmlCharType.IsCharData( ( ch = text[i] ) )
366 if ( XmlCharType.IsHighSurrogate( ch ) ) {
368 char lowChar = text[i+1];
369 if ( XmlCharType.IsLowSurrogate( lowChar ) ) {
374 throw XmlConvert.CreateInvalidSurrogatePairException( lowChar, ch );
377 throw new ArgumentException( Res.GetString( Res.Xml_InvalidSurrogateMissingLowChar ) );
379 else if ( XmlCharType.IsLowSurrogate( ch ) ) {
380 throw XmlConvert.CreateInvalidHighSurrogateCharException( ch );
387 textWriter.Write( text );
391 internal void WriteRaw( string value ) {
392 if ( cacheAttrValue ) {
393 attrValue.Append( value );
395 textWriter.Write( value );
398 internal void WriteRaw( char[] array, int offset, int count ) {
399 if ( null == array ) {
400 throw new ArgumentNullException("array");
404 throw new ArgumentOutOfRangeException("count");
408 throw new ArgumentOutOfRangeException("offset");
411 if ( count > array.Length - offset ) {
412 throw new ArgumentOutOfRangeException("count");
415 if ( cacheAttrValue ) {
416 attrValue.Append( array, offset, count );
418 textWriter.Write( array, offset, count );
423 internal void WriteCharEntity( char ch ) {
424 if ( XmlCharType.IsSurrogate(ch) ) {
425 throw new ArgumentException( Res.GetString( Res.Xml_InvalidSurrogateMissingLowChar ) );
428 string strVal = ((int)ch).ToString( "X", NumberFormatInfo.InvariantInfo );
429 if ( cacheAttrValue ) {
430 attrValue.Append( "&#x" );
431 attrValue.Append( strVal );
432 attrValue.Append( ';' );
434 WriteCharEntityImpl( strVal );
437 internal void WriteEntityRef( string name ) {
438 if ( cacheAttrValue ) {
439 attrValue.Append( '&' );
440 attrValue.Append( name );
441 attrValue.Append( ';' );
443 WriteEntityRefImpl( name );
446 internal void Flush() {
451 // Private implementation methods
453 // This is a helper method to woraround the fact that TextWriter does not have a Write method
454 // for fragment of a string such as Write( string, offset, count).
455 // The string fragment will be written out by copying into a small helper buffer and then
456 // calling textWriter to write out the buffer.
457 private void WriteStringFragment( string str, int offset, int count, char[] helperBuffer ) {
458 int bufferSize = helperBuffer.Length;
459 while ( count > 0 ) {
460 int copyCount = count;
461 if ( copyCount > bufferSize ) {
462 copyCount = bufferSize;
465 str.CopyTo( offset, helperBuffer, 0, copyCount );
466 textWriter.Write( helperBuffer, 0, copyCount );
472 private void WriteCharEntityImpl( char ch ) {
473 WriteCharEntityImpl( ((int)ch).ToString( "X", NumberFormatInfo.InvariantInfo ) );
476 private void WriteCharEntityImpl( string strVal ) {
477 textWriter.Write( "&#x" );
478 textWriter.Write( strVal );
479 textWriter.Write( ';' );
482 private void WriteEntityRefImpl( string name ) {
483 textWriter.Write( '&' );
484 textWriter.Write( name );
485 textWriter.Write( ';' );