Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / Core / CharEntityEncoderFallback.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="CharEntitiesEncodingFallback.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
7
8 using System.Text;
9 using System.Diagnostics;
10 using System.Globalization;
11
12 namespace System.Xml {
13
14 //
15 // CharEntityEncoderFallback
16 //
17
18     internal class CharEntityEncoderFallback : EncoderFallback {
19         private CharEntityEncoderFallbackBuffer fallbackBuffer;
20
21         private int[]   textContentMarks;
22         private int     endMarkPos;
23         private int     curMarkPos;
24         private int     startOffset;
25
26         internal CharEntityEncoderFallback() {
27         }
28
29         public override EncoderFallbackBuffer CreateFallbackBuffer() {
30             if ( fallbackBuffer == null ) { 
31                 fallbackBuffer = new CharEntityEncoderFallbackBuffer( this );
32             }
33             return fallbackBuffer;
34         }
35  
36         public override int MaxCharCount {
37             get {
38                 return 12;
39             }
40         }
41
42         internal int StartOffset {
43             get {
44                 return startOffset;
45             }
46             set {
47                 startOffset = value;
48             }
49         }
50
51         internal void Reset( int[] textContentMarks, int endMarkPos ) {
52             this.textContentMarks = textContentMarks;
53             this.endMarkPos = endMarkPos;
54             curMarkPos = 0;
55         }
56
57         internal bool CanReplaceAt( int index ) {
58             int mPos = curMarkPos;
59             int charPos = startOffset + index;
60             while ( mPos < endMarkPos && charPos >= textContentMarks[mPos+1] ) {
61                 mPos++;
62             }
63             curMarkPos = mPos;
64
65             return (mPos & 1) != 0;
66         }
67     }
68  
69 //
70 // CharEntityFallbackBuffer
71 //
72     internal class CharEntityEncoderFallbackBuffer : EncoderFallbackBuffer {
73         private CharEntityEncoderFallback parent;
74
75         private string  charEntity = string.Empty;
76         private int     charEntityIndex = -1;
77
78         internal CharEntityEncoderFallbackBuffer( CharEntityEncoderFallback parent ) {
79             this.parent = parent;
80         }
81  
82         public override bool Fallback( char charUnknown, int index ) {
83             // If we are already in fallback, throw, it's probably at the suspect character in charEntity
84             if ( charEntityIndex >= 0 ) {
85                 (new EncoderExceptionFallback()).CreateFallbackBuffer().Fallback( charUnknown, index );
86             }
87  
88             // find out if we can replace the character with entity
89             if ( parent.CanReplaceAt( index ) ) {
90                 // Create the replacement character entity
91                 charEntity = string.Format( CultureInfo.InvariantCulture, "&#x{0:X};", new object[] { (int)charUnknown } );
92                 charEntityIndex = 0;
93                 return true;
94             }
95             else {
96                 EncoderFallbackBuffer errorFallbackBuffer = ( new EncoderExceptionFallback() ).CreateFallbackBuffer();
97                 errorFallbackBuffer.Fallback( charUnknown, index );
98                 return false;
99             }
100         }
101  
102         public override bool Fallback( char charUnknownHigh, char charUnknownLow, int index ) {
103             // check input surrogate pair
104             if ( !char.IsSurrogatePair( charUnknownHigh, charUnknownLow ) ) {
105                 throw XmlConvert.CreateInvalidSurrogatePairException( charUnknownHigh, charUnknownLow );
106             }
107
108             // If we are already in fallback, throw, it's probably at the suspect character in charEntity
109             if ( charEntityIndex >= 0 ) {
110                 (new EncoderExceptionFallback()).CreateFallbackBuffer().Fallback( charUnknownHigh, charUnknownLow, index );
111             }
112  
113             if ( parent.CanReplaceAt( index ) ) {
114                 // Create the replacement character entity
115                 charEntity = string.Format( CultureInfo.InvariantCulture, "&#x{0:X};", new object[] { SurrogateCharToUtf32( charUnknownHigh, charUnknownLow ) } );
116                 charEntityIndex = 0;
117                 return true;
118             }
119             else {
120                 EncoderFallbackBuffer errorFallbackBuffer = ( new EncoderExceptionFallback() ).CreateFallbackBuffer();
121                 errorFallbackBuffer.Fallback( charUnknownHigh, charUnknownLow, index );
122                 return false;
123             }
124         }
125  
126         public override char GetNextChar() {
127             // Bug fix: 35637. The protocol using GetNextChar() and MovePrevious() called by Encoder is not well documented.
128             // Here we have to to signal to Encoder that the previous read was last character. Only AFTER we can 
129             // mark ourself as done (-1). Otherwise MovePrevious() can still be called, but -1 is already incorrectly set
130             // and return false from MovePrevious(). Then Encoder ----ing the rest of the bytes.
131             if (charEntityIndex == charEntity.Length)
132             {
133                 charEntityIndex = -1;
134             }
135             if ( charEntityIndex == -1 ) {
136                 return (char)0;
137             }
138             else {
139                 Debug.Assert( charEntityIndex < charEntity.Length );
140                 char ch = charEntity[charEntityIndex++];
141                 return ch;
142             }
143         }
144   
145         public override bool MovePrevious() {
146             if ( charEntityIndex == -1 ) {
147                 return false;
148             }
149             else {
150                 // Could be == length if just read the last character
151                 Debug.Assert(charEntityIndex <= charEntity.Length);
152                 if (charEntityIndex > 0)
153                 {
154                     charEntityIndex--;
155                     return true;
156                 }
157                 else {
158                     return false;
159                 }
160             }
161         }
162           
163
164         public override int Remaining {
165             get {
166                 if ( charEntityIndex == -1 ) {
167                     return 0;
168                 }
169                 else {
170                     return charEntity.Length - charEntityIndex;
171                 }
172             }
173         }
174  
175         public override void Reset() {
176             charEntityIndex = -1;
177         }
178
179         private int SurrogateCharToUtf32(char highSurrogate, char lowSurrogate) {
180             return XmlCharType.CombineSurrogateChar(lowSurrogate, highSurrogate);
181         }
182     }
183 }