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.Collections; 7 using System.Collections.Generic; 8 using System.Runtime.InteropServices; 9 using System.Xml.XPath; 10 using System.Diagnostics; 11 12 namespace System.Xml.Schema 13 { 14 /// <summary> 15 /// This class contains a (CLR Object, XmlType) pair that represents an instance of an Xml atomic value. 16 /// It is optimized to avoid boxing. 17 /// </summary> 18 public sealed class XmlAtomicValue : XPathItem, ICloneable 19 { 20 private XmlSchemaType _xmlType; 21 private object _objVal; 22 private TypeCode _clrType; 23 private Union _unionVal; 24 private NamespacePrefixForQName _nsPrefix; 25 26 [StructLayout(LayoutKind.Explicit, Size = 8)] 27 private struct Union 28 { 29 [FieldOffset(0)] 30 public bool boolVal; 31 [FieldOffset(0)] 32 public double dblVal; 33 [FieldOffset(0)] 34 public long i64Val; 35 [FieldOffset(0)] 36 public int i32Val; 37 [FieldOffset(0)] 38 public DateTime dtVal; 39 } 40 41 private class NamespacePrefixForQName : IXmlNamespaceResolver 42 { 43 public string prefix; 44 public string ns; 45 NamespacePrefixForQName(string prefix, string ns)46 public NamespacePrefixForQName(string prefix, string ns) 47 { 48 this.ns = ns; 49 this.prefix = prefix; 50 } LookupNamespace(string prefix)51 public string LookupNamespace(string prefix) 52 { 53 if (prefix == this.prefix) 54 { 55 return ns; 56 } 57 return null; 58 } 59 LookupPrefix(string namespaceName)60 public string LookupPrefix(string namespaceName) 61 { 62 if (ns == namespaceName) 63 { 64 return prefix; 65 } 66 return null; 67 } 68 GetNamespacesInScope(XmlNamespaceScope scope)69 public IDictionary<string, string> GetNamespacesInScope(XmlNamespaceScope scope) 70 { 71 Dictionary<string, string> dict = new Dictionary<string, string>(1); 72 dict[prefix] = ns; 73 return dict; 74 } 75 } 76 77 //----------------------------------------------- 78 // XmlAtomicValue constructors and methods 79 //----------------------------------------------- 80 XmlAtomicValue(XmlSchemaType xmlType, bool value)81 internal XmlAtomicValue(XmlSchemaType xmlType, bool value) 82 { 83 if (xmlType == null) throw new ArgumentNullException(nameof(xmlType)); 84 _xmlType = xmlType; 85 _clrType = TypeCode.Boolean; 86 _unionVal.boolVal = value; 87 } 88 XmlAtomicValue(XmlSchemaType xmlType, DateTime value)89 internal XmlAtomicValue(XmlSchemaType xmlType, DateTime value) 90 { 91 if (xmlType == null) throw new ArgumentNullException(nameof(xmlType)); 92 _xmlType = xmlType; 93 _clrType = TypeCode.DateTime; 94 _unionVal.dtVal = value; 95 } 96 XmlAtomicValue(XmlSchemaType xmlType, double value)97 internal XmlAtomicValue(XmlSchemaType xmlType, double value) 98 { 99 if (xmlType == null) throw new ArgumentNullException(nameof(xmlType)); 100 _xmlType = xmlType; 101 _clrType = TypeCode.Double; 102 _unionVal.dblVal = value; 103 } 104 XmlAtomicValue(XmlSchemaType xmlType, int value)105 internal XmlAtomicValue(XmlSchemaType xmlType, int value) 106 { 107 if (xmlType == null) throw new ArgumentNullException(nameof(xmlType)); 108 _xmlType = xmlType; 109 _clrType = TypeCode.Int32; 110 _unionVal.i32Val = value; 111 } 112 XmlAtomicValue(XmlSchemaType xmlType, long value)113 internal XmlAtomicValue(XmlSchemaType xmlType, long value) 114 { 115 if (xmlType == null) throw new ArgumentNullException(nameof(xmlType)); 116 _xmlType = xmlType; 117 _clrType = TypeCode.Int64; 118 _unionVal.i64Val = value; 119 } 120 XmlAtomicValue(XmlSchemaType xmlType, string value)121 internal XmlAtomicValue(XmlSchemaType xmlType, string value) 122 { 123 if (value == null) throw new ArgumentNullException(nameof(value)); 124 if (xmlType == null) throw new ArgumentNullException(nameof(xmlType)); 125 _xmlType = xmlType; 126 _objVal = value; 127 } 128 XmlAtomicValue(XmlSchemaType xmlType, string value, IXmlNamespaceResolver nsResolver)129 internal XmlAtomicValue(XmlSchemaType xmlType, string value, IXmlNamespaceResolver nsResolver) 130 { 131 if (value == null) throw new ArgumentNullException(nameof(value)); 132 if (xmlType == null) throw new ArgumentNullException(nameof(xmlType)); 133 _xmlType = xmlType; 134 _objVal = value; 135 if (nsResolver != null && (_xmlType.TypeCode == XmlTypeCode.QName || _xmlType.TypeCode == XmlTypeCode.Notation)) 136 { 137 string prefix = GetPrefixFromQName(value); 138 _nsPrefix = new NamespacePrefixForQName(prefix, nsResolver.LookupNamespace(prefix)); 139 } 140 } 141 XmlAtomicValue(XmlSchemaType xmlType, object value)142 internal XmlAtomicValue(XmlSchemaType xmlType, object value) 143 { 144 if (value == null) throw new ArgumentNullException(nameof(value)); 145 if (xmlType == null) throw new ArgumentNullException(nameof(xmlType)); 146 _xmlType = xmlType; 147 _objVal = value; 148 } 149 XmlAtomicValue(XmlSchemaType xmlType, object value, IXmlNamespaceResolver nsResolver)150 internal XmlAtomicValue(XmlSchemaType xmlType, object value, IXmlNamespaceResolver nsResolver) 151 { 152 if (value == null) throw new ArgumentNullException(nameof(value)); 153 if (xmlType == null) throw new ArgumentNullException(nameof(xmlType)); 154 _xmlType = xmlType; 155 _objVal = value; 156 157 if (nsResolver != null && (_xmlType.TypeCode == XmlTypeCode.QName || _xmlType.TypeCode == XmlTypeCode.Notation)) 158 { //Its a qualifiedName 159 XmlQualifiedName qname = _objVal as XmlQualifiedName; 160 Debug.Assert(qname != null); //string representation is handled in a different overload 161 string ns = qname.Namespace; 162 _nsPrefix = new NamespacePrefixForQName(nsResolver.LookupPrefix(ns), ns); 163 } 164 } 165 166 /// <summary> 167 /// Since XmlAtomicValue is immutable, clone simply returns this. 168 /// </summary> Clone()169 public XmlAtomicValue Clone() 170 { 171 return this; 172 } 173 174 175 //----------------------------------------------- 176 // ICloneable methods 177 //----------------------------------------------- 178 179 /// <summary> 180 /// Since XmlAtomicValue is immutable, clone simply returns this. 181 /// </summary> ICloneable.Clone()182 object ICloneable.Clone() 183 { 184 return this; 185 } 186 187 188 //----------------------------------------------- 189 // XPathItem methods 190 //----------------------------------------------- 191 192 public override bool IsNode 193 { 194 get { return false; } 195 } 196 197 public override XmlSchemaType XmlType 198 { 199 get { return _xmlType; } 200 } 201 202 public override Type ValueType 203 { 204 get { return _xmlType.Datatype.ValueType; } 205 } 206 207 public override object TypedValue 208 { 209 get 210 { 211 XmlValueConverter valueConverter = _xmlType.ValueConverter; 212 213 if (_objVal == null) 214 { 215 switch (_clrType) 216 { 217 case TypeCode.Boolean: return valueConverter.ChangeType(_unionVal.boolVal, ValueType); 218 case TypeCode.Int32: return valueConverter.ChangeType(_unionVal.i32Val, ValueType); 219 case TypeCode.Int64: return valueConverter.ChangeType(_unionVal.i64Val, ValueType); 220 case TypeCode.Double: return valueConverter.ChangeType(_unionVal.dblVal, ValueType); 221 case TypeCode.DateTime: return valueConverter.ChangeType(_unionVal.dtVal, ValueType); 222 default: Debug.Assert(false, "Should never get here"); break; 223 } 224 } 225 return valueConverter.ChangeType(_objVal, ValueType, _nsPrefix); 226 } 227 } 228 229 public override bool ValueAsBoolean 230 { 231 get 232 { 233 XmlValueConverter valueConverter = _xmlType.ValueConverter; 234 235 if (_objVal == null) 236 { 237 switch (_clrType) 238 { 239 case TypeCode.Boolean: return _unionVal.boolVal; 240 case TypeCode.Int32: return valueConverter.ToBoolean(_unionVal.i32Val); 241 case TypeCode.Int64: return valueConverter.ToBoolean(_unionVal.i64Val); 242 case TypeCode.Double: return valueConverter.ToBoolean(_unionVal.dblVal); 243 case TypeCode.DateTime: return valueConverter.ToBoolean(_unionVal.dtVal); 244 default: Debug.Assert(false, "Should never get here"); break; 245 } 246 } 247 248 return valueConverter.ToBoolean(_objVal); 249 } 250 } 251 252 public override DateTime ValueAsDateTime 253 { 254 get 255 { 256 XmlValueConverter valueConverter = _xmlType.ValueConverter; 257 258 if (_objVal == null) 259 { 260 switch (_clrType) 261 { 262 case TypeCode.Boolean: return valueConverter.ToDateTime(_unionVal.boolVal); 263 case TypeCode.Int32: return valueConverter.ToDateTime(_unionVal.i32Val); 264 case TypeCode.Int64: return valueConverter.ToDateTime(_unionVal.i64Val); 265 case TypeCode.Double: return valueConverter.ToDateTime(_unionVal.dblVal); 266 case TypeCode.DateTime: return _unionVal.dtVal; 267 default: Debug.Assert(false, "Should never get here"); break; 268 } 269 } 270 271 return valueConverter.ToDateTime(_objVal); 272 } 273 } 274 275 276 public override double ValueAsDouble 277 { 278 get 279 { 280 XmlValueConverter valueConverter = _xmlType.ValueConverter; 281 282 if (_objVal == null) 283 { 284 switch (_clrType) 285 { 286 case TypeCode.Boolean: return valueConverter.ToDouble(_unionVal.boolVal); 287 case TypeCode.Int32: return valueConverter.ToDouble(_unionVal.i32Val); 288 case TypeCode.Int64: return valueConverter.ToDouble(_unionVal.i64Val); 289 case TypeCode.Double: return _unionVal.dblVal; 290 case TypeCode.DateTime: return valueConverter.ToDouble(_unionVal.dtVal); 291 default: Debug.Assert(false, "Should never get here"); break; 292 } 293 } 294 295 return valueConverter.ToDouble(_objVal); 296 } 297 } 298 299 public override int ValueAsInt 300 { 301 get 302 { 303 XmlValueConverter valueConverter = _xmlType.ValueConverter; 304 305 if (_objVal == null) 306 { 307 switch (_clrType) 308 { 309 case TypeCode.Boolean: return valueConverter.ToInt32(_unionVal.boolVal); 310 case TypeCode.Int32: return _unionVal.i32Val; 311 case TypeCode.Int64: return valueConverter.ToInt32(_unionVal.i64Val); 312 case TypeCode.Double: return valueConverter.ToInt32(_unionVal.dblVal); 313 case TypeCode.DateTime: return valueConverter.ToInt32(_unionVal.dtVal); 314 default: Debug.Assert(false, "Should never get here"); break; 315 } 316 } 317 318 return valueConverter.ToInt32(_objVal); 319 } 320 } 321 322 public override long ValueAsLong 323 { 324 get 325 { 326 XmlValueConverter valueConverter = _xmlType.ValueConverter; 327 328 if (_objVal == null) 329 { 330 switch (_clrType) 331 { 332 case TypeCode.Boolean: return valueConverter.ToInt64(_unionVal.boolVal); 333 case TypeCode.Int32: return valueConverter.ToInt64(_unionVal.i32Val); 334 case TypeCode.Int64: return _unionVal.i64Val; 335 case TypeCode.Double: return valueConverter.ToInt64(_unionVal.dblVal); 336 case TypeCode.DateTime: return valueConverter.ToInt64(_unionVal.dtVal); 337 default: Debug.Assert(false, "Should never get here"); break; 338 } 339 } 340 341 return valueConverter.ToInt64(_objVal); 342 } 343 } 344 ValueAs(Type type, IXmlNamespaceResolver nsResolver)345 public override object ValueAs(Type type, IXmlNamespaceResolver nsResolver) 346 { 347 XmlValueConverter valueConverter = _xmlType.ValueConverter; 348 349 if (type == typeof(XPathItem) || type == typeof(XmlAtomicValue)) 350 return this; 351 352 if (_objVal == null) 353 { 354 switch (_clrType) 355 { 356 case TypeCode.Boolean: return valueConverter.ChangeType(_unionVal.boolVal, type); 357 case TypeCode.Int32: return valueConverter.ChangeType(_unionVal.i32Val, type); 358 case TypeCode.Int64: return valueConverter.ChangeType(_unionVal.i64Val, type); 359 case TypeCode.Double: return valueConverter.ChangeType(_unionVal.dblVal, type); 360 case TypeCode.DateTime: return valueConverter.ChangeType(_unionVal.dtVal, type); 361 default: Debug.Assert(false, "Should never get here"); break; 362 } 363 } 364 365 return valueConverter.ChangeType(_objVal, type, nsResolver); 366 } 367 368 public override string Value 369 { 370 get 371 { 372 XmlValueConverter valueConverter = _xmlType.ValueConverter; 373 374 if (_objVal == null) 375 { 376 switch (_clrType) 377 { 378 case TypeCode.Boolean: return valueConverter.ToString(_unionVal.boolVal); 379 case TypeCode.Int32: return valueConverter.ToString(_unionVal.i32Val); 380 case TypeCode.Int64: return valueConverter.ToString(_unionVal.i64Val); 381 case TypeCode.Double: return valueConverter.ToString(_unionVal.dblVal); 382 case TypeCode.DateTime: return valueConverter.ToString(_unionVal.dtVal); 383 default: Debug.Assert(false, "Should never get here"); break; 384 } 385 } 386 return valueConverter.ToString(_objVal, _nsPrefix); 387 } 388 } 389 ToString()390 public override string ToString() 391 { 392 return Value; 393 } 394 GetPrefixFromQName(string value)395 private string GetPrefixFromQName(string value) 396 { 397 int colonOffset; 398 int len = ValidateNames.ParseQName(value, 0, out colonOffset); 399 400 if (len == 0 || len != value.Length) 401 { 402 return null; 403 } 404 if (colonOffset != 0) 405 { 406 return value.Substring(0, colonOffset); 407 } 408 else 409 { 410 return string.Empty; 411 } 412 } 413 } 414 } 415 416