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