Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / XmlException.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlException.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
7
8 namespace System.Xml {
9     using System;
10     using System.IO;
11     using System.Resources;
12     using System.Text;
13     using System.Diagnostics;
14     using System.Security.Permissions;
15     using System.Globalization;
16     using System.Threading;
17 #if !SILVERLIGHT
18     using System.Runtime.Serialization;
19 #endif
20
21     /// <devdoc>
22     ///    <para>Returns detailed information about the last parse error, including the error
23     ///       number, line number, character position, and a text description.</para>
24     /// </devdoc>
25 #if !SILVERLIGHT
26     [Serializable]
27 #endif
28     public class XmlException : SystemException {
29         string res;
30         string[] args; // this field is not used, it's here just V1.1 serialization compatibility
31         int lineNumber;
32         int linePosition; 
33
34 #if !SILVERLIGHT
35         [OptionalField] 
36 #endif
37         string sourceUri;
38
39         // message != null for V1 exceptions deserialized in Whidbey
40         // message == null for V2 or higher exceptions; the exception message is stored on the base class (Exception._message)
41         string message;
42
43 #if !SILVERLIGHT
44         protected XmlException(SerializationInfo info, StreamingContext context) : base(info, context) {
45             res                 = (string)  info.GetValue("res"  , typeof(string));
46             args                = (string[])info.GetValue("args", typeof(string[]));
47             lineNumber          = (int)     info.GetValue("lineNumber", typeof(int));
48             linePosition        = (int)     info.GetValue("linePosition", typeof(int));
49
50             // deserialize optional members
51             sourceUri = string.Empty;
52             string version = null;
53             foreach ( SerializationEntry e in info ) {
54                 switch ( e.Name ) {
55                     case "sourceUri":
56                         sourceUri = (string)e.Value;
57                         break;
58                     case "version":
59                         version = (string)e.Value;
60                         break;
61                 }
62             }
63
64             if ( version == null ) {
65                 // deserializing V1 exception
66                 message = CreateMessage( res, args, lineNumber, linePosition );
67             }
68             else {
69                 // deserializing V2 or higher exception -> exception message is serialized by the base class (Exception._message)
70                 message = null;
71             }
72         }
73
74         [SecurityPermissionAttribute(SecurityAction.LinkDemand,SerializationFormatter=true)]
75         public override void GetObjectData(SerializationInfo info, StreamingContext context) {
76             base.GetObjectData(info, context);
77             info.AddValue("res",                res);
78             info.AddValue("args",               args);
79             info.AddValue("lineNumber",         lineNumber);
80             info.AddValue("linePosition",       linePosition);
81             info.AddValue("sourceUri",          sourceUri);
82             info.AddValue("version",            "2.0");
83         }
84 #endif
85
86         //provided to meet the ECMA standards
87         public XmlException() : this(null) {
88         }
89
90         //provided to meet the ECMA standards
91         public XmlException(String message) : this (message, ((Exception)null), 0, 0) {
92 #if DEBUG
93             Debug.Assert(message == null || !message.StartsWith("Xml_", StringComparison.Ordinal), "Do not pass a resource here!");
94 #endif
95         }
96         
97         //provided to meet ECMA standards
98         public XmlException(String message, Exception innerException) : this (message, innerException, 0, 0) {
99         } 
100
101         //provided to meet ECMA standards
102         public XmlException(String message, Exception innerException, int lineNumber, int linePosition) : 
103             this( message, innerException, lineNumber, linePosition, null ) {
104         }
105
106         internal XmlException(String message, Exception innerException, int lineNumber, int linePosition, string sourceUri) :
107             base(FormatUserMessage(message, lineNumber, linePosition), innerException) {
108
109             HResult = HResults.Xml;
110             this.res = (message == null ? Res.Xml_DefaultException : Res.Xml_UserException);
111             this.args = new string[] { message };
112             this.sourceUri = sourceUri;
113             this.lineNumber = lineNumber;
114             this.linePosition = linePosition;
115         }
116
117         internal XmlException(string res, string[] args) :
118             this(res, args, null, 0, 0, null) {}
119
120         internal XmlException(string res, string[] args, string sourceUri) :
121             this(res, args, null, 0, 0, sourceUri) {}
122
123         internal XmlException(string res, string arg) :
124             this(res, new string[] { arg }, null, 0, 0, null) {}
125
126         internal XmlException(string res, string arg, string sourceUri) :
127             this(res, new string[] { arg }, null, 0, 0, sourceUri) {}
128
129         internal XmlException(string res, String arg,  IXmlLineInfo lineInfo) :
130             this(res, new string[] { arg }, lineInfo, null) {}
131
132         internal XmlException(string res, String arg, Exception innerException, IXmlLineInfo lineInfo) :
133             this(res, new string[] { arg }, innerException, (lineInfo == null ? 0 : lineInfo.LineNumber), (lineInfo == null ? 0 : lineInfo.LinePosition), null) {}
134
135         internal XmlException(string res, String arg,  IXmlLineInfo lineInfo, string sourceUri) :
136             this(res, new string[] { arg }, lineInfo, sourceUri) {}
137
138         internal XmlException(string res, string[] args,  IXmlLineInfo lineInfo) :
139             this(res, args, lineInfo, null) {}
140
141         internal XmlException(string res, string[] args,  IXmlLineInfo lineInfo, string sourceUri) :
142             this (res, args, null, (lineInfo == null ? 0 : lineInfo.LineNumber), (lineInfo == null ? 0 : lineInfo.LinePosition), sourceUri) {
143         }
144
145         internal XmlException(string res,  int lineNumber, int linePosition) :
146             this(res, (string[])null, null, lineNumber, linePosition) {}
147
148         internal XmlException(string res, string arg, int lineNumber, int linePosition) :
149             this(res,  new string[] { arg }, null, lineNumber, linePosition, null) {}
150
151         internal XmlException(string res, string arg, int lineNumber, int linePosition, string sourceUri) :
152             this(res,  new string[] { arg }, null, lineNumber, linePosition, sourceUri) {}
153
154         internal XmlException(string res, string[] args, int lineNumber, int linePosition) :
155             this( res, args, null, lineNumber, linePosition, null ) {}
156
157         internal XmlException(string res, string[] args, int lineNumber, int linePosition, string sourceUri) :
158             this( res, args, null, lineNumber, linePosition, sourceUri ) {}
159
160         internal XmlException(string res, string[] args, Exception innerException, int lineNumber, int linePosition) : 
161             this( res, args, innerException, lineNumber, linePosition, null ) {}
162
163         internal XmlException(string res, string[] args, Exception innerException, int lineNumber, int linePosition, string sourceUri) :
164             base( CreateMessage(res, args, lineNumber, linePosition), innerException ) {
165             HResult = HResults.Xml;
166             this.res = res;
167             this.args = args;
168             this.sourceUri = sourceUri;
169             this.lineNumber = lineNumber;
170             this.linePosition = linePosition;
171         }
172
173         private static string FormatUserMessage(string message, int lineNumber, int linePosition) {
174             if (message == null) {
175                 return CreateMessage(Res.Xml_DefaultException, null, lineNumber, linePosition);
176             }
177             else {
178                 if (lineNumber == 0 && linePosition == 0) {
179                     // do not reformat the message when not needed
180                     return message;
181                 }
182                 else {
183                     // add line information
184                     return CreateMessage(Res.Xml_UserException, new string[] { message }, lineNumber, linePosition);
185                 }
186             }
187         }
188
189         private static string CreateMessage(string res, string[] args, int lineNumber, int linePosition) {
190             try {
191                 string message;
192
193                 // No line information -> get resource string and return
194                 if (lineNumber == 0) {
195                     message = Res.GetString(res, args);
196                 }
197                 // Line information is available -> we need to append it to the error message
198                 else {
199                     string lineNumberStr = lineNumber.ToString(CultureInfo.InvariantCulture);
200                     string linePositionStr = linePosition.ToString(CultureInfo.InvariantCulture);
201
202 #if SILVERLIGHT
203                     // get the error message from resources
204                     bool fallbackUsed;
205                     message = Res.GetString(res, out fallbackUsed, args);
206
207                     // If debug resources are available, append the line information
208                     if (!fallbackUsed) {
209                         message = Res.GetString(Res.Xml_MessageWithErrorPosition, new string[] { message, lineNumberStr, linePositionStr } );
210                     }
211                     // Debug resources are not available -> add line information to the args and call the GetString to get the default 
212                     // fallback message with the updated arguments. We need to handle the the case when the debug resources are not 
213                     // available like this; otherwise we would end up with two fallback messages in the final string.
214                     else {
215                         int origArgCount = args.Length;
216                         Array.Resize<string>(ref args, origArgCount + 2);
217
218                         args[origArgCount] = lineNumberStr;
219                         args[origArgCount + 1] = linePositionStr;
220                         
221                         message = Res.GetString(res, args);
222                     }
223 #else
224                     message = Res.GetString(res, args);
225                     message = Res.GetString(Res.Xml_MessageWithErrorPosition, new string[] { message, lineNumberStr, linePositionStr });
226 #endif
227                 }
228                 return message;
229             }
230             catch ( MissingManifestResourceException ) {
231                 return "UNKNOWN("+res+")";
232             }
233         }
234
235         internal static string[] BuildCharExceptionArgs(string data, int invCharIndex) {
236             return BuildCharExceptionArgs(data[invCharIndex], invCharIndex + 1 < data.Length ? data[invCharIndex + 1] : '\0');
237         }
238
239         internal static string[] BuildCharExceptionArgs(char[] data, int invCharIndex) {
240             return BuildCharExceptionArgs(data, data.Length, invCharIndex);
241         }
242
243         internal static string[] BuildCharExceptionArgs(char[] data, int length, int invCharIndex) {
244             Debug.Assert(invCharIndex < data.Length);
245             Debug.Assert(invCharIndex < length);
246             Debug.Assert(length <= data.Length);
247
248             return BuildCharExceptionArgs(data[invCharIndex], invCharIndex + 1 < length ? data[invCharIndex + 1] : '\0');
249         }
250
251         internal static string[] BuildCharExceptionArgs(char invChar, char nextChar) {
252             string[] aStringList = new string[2];
253
254             // for surrogate characters include both high and low char in the message so that a full character is displayed
255             if (XmlCharType.IsHighSurrogate(invChar) && nextChar != 0) {
256                 int combinedChar = XmlCharType.CombineSurrogateChar(nextChar, invChar);
257                 aStringList[0] = new string(new char[] { invChar, nextChar } );
258                 aStringList[1] = string.Format(CultureInfo.InvariantCulture, "0x{0:X2}", combinedChar);
259             }
260             else {
261                 // don't include 0 character in the string - in means eof-of-string in native code, where this may bubble up to
262                 if ((int)invChar == 0) {
263                     aStringList[0] = ".";
264                 }
265                 else {
266                     aStringList[0] = invChar.ToString(CultureInfo.InvariantCulture);
267                 }
268                 aStringList[1] = string.Format(CultureInfo.InvariantCulture, "0x{0:X2}", (int)invChar);
269             }
270             return aStringList;
271         }
272
273         public int LineNumber {
274             get { return this.lineNumber; }
275         }
276
277         public int LinePosition {
278             get { return this.linePosition; }
279         }
280
281         public string SourceUri {
282             get { return this.sourceUri; }
283         }
284
285         public override string Message {
286             get { 
287                 return ( message == null ) ? base.Message : message;
288             }
289         }
290
291         internal string ResString {
292             get {
293                 return res;
294             }
295         }
296
297 #if !SILVERLIGHT
298         internal static bool IsCatchableException(Exception e) {
299             Debug.Assert(e != null, "Unexpected null exception");
300             return !(
301                 e is StackOverflowException ||
302                 e is OutOfMemoryException ||
303                 e is ThreadAbortException ||
304                 e is ThreadInterruptedException ||
305                 e is NullReferenceException ||
306                 e is AccessViolationException
307             );
308         }
309 #endif
310     };
311 } // namespace System.Xml