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