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