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