5 // Atsushi Enomoto <atsushi@ximian.com>
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections;
35 using System.Xml.Schema;
37 namespace System.Xml.XPath
39 public sealed class XPathAtomicValue : XPathItem, ICloneable
42 DateTime dateTimeValue;
50 XmlSchemaType schemaType;
51 XmlTypeCode xmlTypeCode;
52 ICollection valueAsList;
57 public XPathAtomicValue (bool value, XmlSchemaType xmlType)
60 throw new ArgumentNullException ("xmlType");
61 xmlTypeCode = XmlTypeCode.Boolean;
62 this.booleanValue = value;
67 public XPathAtomicValue (DateTime value, XmlSchemaType xmlType)
70 throw new ArgumentNullException ("xmlType");
71 xmlTypeCode = XmlTypeCode.DateTime;
72 this.dateTimeValue = value;
77 public XPathAtomicValue (decimal value, XmlSchemaType xmlType)
80 throw new ArgumentNullException ("xmlType");
81 xmlTypeCode = XmlTypeCode.Decimal;
82 this.decimalValue = value;
87 public XPathAtomicValue (double value, XmlSchemaType xmlType)
90 throw new ArgumentNullException ("xmlType");
91 xmlTypeCode = XmlTypeCode.Double;
92 this.doubleValue = value;
97 public XPathAtomicValue (int value, XmlSchemaType xmlType)
100 throw new ArgumentNullException ("xmlType");
101 xmlTypeCode = XmlTypeCode.Int;
102 this.intValue = value;
103 schemaType = xmlType;
107 public XPathAtomicValue (long value, XmlSchemaType xmlType)
110 throw new ArgumentNullException ("xmlType");
111 xmlTypeCode = XmlTypeCode.Long;
112 this.longValue = value;
113 schemaType = xmlType;
117 public XPathAtomicValue (object value, XmlSchemaType xmlType)
119 // It accepts any kind of object, but will be rejected on each value properties.
121 throw new ArgumentNullException ("value");
123 throw new ArgumentNullException ("xmlType");
125 if (value is XPathAtomicValue)
126 objectValue = ((XPathAtomicValue) value).TypedValue;
129 schemaType = xmlType;
133 public XPathAtomicValue (float value, XmlSchemaType xmlType)
136 throw new ArgumentNullException ("xmlType");
137 xmlTypeCode = XmlTypeCode.Float;
138 this.floatValue = value;
139 schemaType = xmlType;
143 public XPathAtomicValue (string value, XmlSchemaType xmlType)
146 throw new ArgumentNullException ("value");
148 throw new ArgumentNullException ("xmlType");
149 xmlTypeCode = XmlTypeCode.String;
150 this.stringValue = value;
151 schemaType = xmlType;
158 object ICloneable.Clone ()
160 return this.Clone ();
164 public XPathAtomicValue Clone ()
166 return new XPathAtomicValue (this, schemaType);
170 public override object ValueAs (Type type, IXmlNamespaceResolver nsResolver)
172 switch (XmlTypeCodeFromRuntimeType (type, false)) {
173 case XmlTypeCode.Int:
174 case XmlTypeCode.Short:
175 case XmlTypeCode.UnsignedShort:
177 case XmlTypeCode.Decimal:
178 return ValueAsDecimal;
179 case XmlTypeCode.Double:
180 return ValueAsDouble;
181 case XmlTypeCode.Float:
182 return ValueAsSingle;
183 case XmlTypeCode.Long:
184 case XmlTypeCode.UnsignedInt:
186 case XmlTypeCode.String:
188 case XmlTypeCode.DateTime:
189 return ValueAsDateTime;
190 case XmlTypeCode.Boolean:
191 return ValueAsBoolean;
192 case XmlTypeCode.Item:
194 case XmlTypeCode.QName:
195 return XmlQualifiedName.Parse (Value, nsResolver);
197 if (type.GetInterface ("System.Collections.ICollection") != null)
199 throw new NotImplementedException ();
206 // As long as I tried, neither of such XPathAtomicValue created
207 // with XmlText that contains atomic value, XmlElement that
208 // contains such XmlText, XmlDocument nor XPathNavigator
209 // created from such nodes returned false. So it won't be
210 // true on this class. Apparently this class needs more
212 public override bool IsNode {
213 get { return false; }
217 public override object TypedValue {
219 switch (xmlTypeCode) {
220 case XmlTypeCode.Boolean:
221 return ValueAsBoolean;
222 case XmlTypeCode.DateTime:
223 return ValueAsDateTime;
224 case XmlTypeCode.Decimal:
225 return ValueAsDecimal;
226 case XmlTypeCode.Double:
227 return ValueAsDouble;
228 case XmlTypeCode.Long:
230 case XmlTypeCode.Int:
232 case XmlTypeCode.Float:
233 return ValueAsSingle;
234 case XmlTypeCode.String:
242 // This method works like ValueAsString.
243 public override string Value {
245 if (stringValue != null)
247 switch (xmlTypeCode) {
248 case XmlTypeCode.Boolean:
249 stringValue = XQueryConvert.BooleanToString (booleanValue);
251 case XmlTypeCode.DateTime:
252 stringValue = XQueryConvert.DateTimeToString (dateTimeValue);
254 case XmlTypeCode.Decimal:
255 stringValue = XQueryConvert.DecimalToString (decimalValue);
257 case XmlTypeCode.Double:
258 stringValue = XQueryConvert.DoubleToString (doubleValue);
260 case XmlTypeCode.Long:
261 stringValue = XQueryConvert.IntegerToString (longValue);
263 case XmlTypeCode.Int:
264 stringValue = XQueryConvert.IntToString (intValue);
266 case XmlTypeCode.Float:
267 stringValue = XQueryConvert.FloatToString (floatValue);
269 case XmlTypeCode.String:
272 case XmlTypeCode.None:
273 case XmlTypeCode.Item:
274 case XmlTypeCode.AnyAtomicType:
275 switch (XmlTypeCodeFromRuntimeType (objectValue.GetType (), false)) {
276 case XmlTypeCode.String:
277 stringValue = (string) objectValue;
279 case XmlTypeCode.DateTime:
280 stringValue = XQueryConvert.DateTimeToString ((DateTime) objectValue);
282 case XmlTypeCode.Boolean:
283 stringValue = XQueryConvert.BooleanToString ((bool) objectValue);
285 case XmlTypeCode.Float:
286 stringValue = XQueryConvert.FloatToString ((float) objectValue);
288 case XmlTypeCode.Double:
289 stringValue = XQueryConvert.DoubleToString ((double) objectValue);
291 case XmlTypeCode.Decimal:
292 stringValue = XQueryConvert.DecimalToString ((decimal) objectValue);
294 case XmlTypeCode.Long:
295 stringValue = XQueryConvert.IntegerToString ((long) objectValue);
297 case XmlTypeCode.Int:
298 stringValue = XQueryConvert.IntToString ((int) objectValue);
303 if (stringValue != null)
306 if (objectValue != null)
307 throw new InvalidOperationException (String.Format ("Conversion from runtime type {0} to {1} is not supported", objectValue.GetType (), XmlTypeCode.String));
309 throw new InvalidOperationException (String.Format ("Conversion from schema type {0} (type code {1}) to {2} is not supported", schemaType.QualifiedName, xmlTypeCode, XmlTypeCode.String));
314 public override bool ValueAsBoolean {
316 switch (xmlTypeCode) {
317 case XmlTypeCode.Boolean:
319 case XmlTypeCode.Decimal:
320 return XQueryConvert.DecimalToBoolean (decimalValue);
321 case XmlTypeCode.Double:
322 return XQueryConvert.DoubleToBoolean (doubleValue);
323 case XmlTypeCode.Long:
324 return XQueryConvert.IntegerToBoolean (longValue);
325 case XmlTypeCode.Int:
326 return XQueryConvert.IntToBoolean (intValue);
327 case XmlTypeCode.Float:
328 return XQueryConvert.FloatToBoolean (floatValue);
329 case XmlTypeCode.String:
330 return XQueryConvert.StringToBoolean (stringValue);
332 case XmlTypeCode.None:
333 case XmlTypeCode.Item:
334 case XmlTypeCode.AnyAtomicType:
335 if (objectValue is bool)
336 return (bool) objectValue;
341 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Boolean));
346 public override DateTime ValueAsDateTime {
348 switch (xmlTypeCode) {
349 case XmlTypeCode.DateTime:
350 return dateTimeValue;
351 case XmlTypeCode.String:
352 return XQueryConvert.StringToDateTime (stringValue);
353 case XmlTypeCode.None:
354 case XmlTypeCode.Item:
355 case XmlTypeCode.AnyAtomicType:
356 if (objectValue is DateTime)
357 return (DateTime) objectValue;
362 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.DateTime));
367 public override decimal ValueAsDecimal {
369 switch (xmlTypeCode) {
370 case XmlTypeCode.Boolean:
371 return XQueryConvert.BooleanToDecimal (booleanValue);
372 case XmlTypeCode.Decimal:
374 case XmlTypeCode.Double:
375 return XQueryConvert.DoubleToDecimal (doubleValue);
376 case XmlTypeCode.Long:
377 return XQueryConvert.IntegerToDecimal (longValue);
378 case XmlTypeCode.Int:
379 return XQueryConvert.IntToDecimal (intValue);
380 case XmlTypeCode.Float:
381 return XQueryConvert.FloatToDecimal (floatValue);
382 case XmlTypeCode.String:
383 return XQueryConvert.StringToDecimal (stringValue);
384 case XmlTypeCode.None:
385 case XmlTypeCode.Item:
386 case XmlTypeCode.AnyAtomicType:
387 if (objectValue is decimal)
388 return (decimal) objectValue;
393 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Decimal));
398 public override double ValueAsDouble {
400 switch (xmlTypeCode) {
401 case XmlTypeCode.Boolean:
402 return XQueryConvert.BooleanToDouble (booleanValue);
403 case XmlTypeCode.Decimal:
404 return XQueryConvert.DecimalToDouble (decimalValue);
405 case XmlTypeCode.Double:
407 case XmlTypeCode.Long:
408 return XQueryConvert.IntegerToDouble (longValue);
409 case XmlTypeCode.Int:
410 return XQueryConvert.IntToDouble (intValue);
411 case XmlTypeCode.Float:
412 return XQueryConvert.FloatToDouble (floatValue);
413 case XmlTypeCode.String:
414 return XQueryConvert.StringToDouble (stringValue);
415 case XmlTypeCode.None:
416 case XmlTypeCode.Item:
417 case XmlTypeCode.AnyAtomicType:
418 if (objectValue is double)
419 return (double) objectValue;
424 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Double));
429 public override int ValueAsInt32 {
431 switch (xmlTypeCode) {
432 case XmlTypeCode.Boolean:
433 return XQueryConvert.BooleanToInt (booleanValue);
434 case XmlTypeCode.Decimal:
435 return XQueryConvert.DecimalToInt (decimalValue);
436 case XmlTypeCode.Double:
437 return XQueryConvert.DoubleToInt (doubleValue);
438 case XmlTypeCode.Long:
439 return XQueryConvert.IntegerToInt (longValue);
440 case XmlTypeCode.Int:
442 case XmlTypeCode.Float:
443 return XQueryConvert.FloatToInt (floatValue);
444 case XmlTypeCode.String:
445 return XQueryConvert.StringToInt (stringValue);
446 case XmlTypeCode.None:
447 case XmlTypeCode.Item:
448 case XmlTypeCode.AnyAtomicType:
449 if (objectValue is int)
450 return (int) objectValue;
455 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Int));
460 public override long ValueAsInt64 {
462 switch (xmlTypeCode) {
463 case XmlTypeCode.Boolean:
464 return XQueryConvert.BooleanToInteger (booleanValue);
465 case XmlTypeCode.Decimal:
466 return XQueryConvert.DecimalToInteger (decimalValue);
467 case XmlTypeCode.Double:
468 return XQueryConvert.DoubleToInteger (doubleValue);
469 case XmlTypeCode.Long:
471 case XmlTypeCode.Int:
472 return XQueryConvert.IntegerToInt (intValue);
473 case XmlTypeCode.Float:
474 return XQueryConvert.FloatToInteger (floatValue);
475 case XmlTypeCode.String:
476 return XQueryConvert.StringToInteger (stringValue);
477 case XmlTypeCode.None:
478 case XmlTypeCode.Item:
479 case XmlTypeCode.AnyAtomicType:
480 if (objectValue is long)
481 return (long) objectValue;
486 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Long));
491 public override float ValueAsSingle {
493 switch (xmlTypeCode) {
494 case XmlTypeCode.Boolean:
495 return XQueryConvert.BooleanToFloat (booleanValue);
496 case XmlTypeCode.Decimal:
497 return XQueryConvert.DecimalToFloat (decimalValue);
498 case XmlTypeCode.Double:
499 return XQueryConvert.DoubleToFloat (doubleValue);
500 case XmlTypeCode.Float:
502 case XmlTypeCode.Int:
503 return XQueryConvert.FloatToInt (intValue);
504 case XmlTypeCode.Long:
505 return XQueryConvert.IntegerToFloat (longValue);
506 case XmlTypeCode.String:
507 return XQueryConvert.StringToFloat (stringValue);
508 case XmlTypeCode.None:
509 case XmlTypeCode.Item:
510 case XmlTypeCode.AnyAtomicType:
511 if (objectValue is float)
512 return (float) objectValue;
517 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Float));
522 public override ICollection ValueAsList {
524 if (valueAsList != null)
526 if (objectValue is ICollection)
527 valueAsList = objectValue as ICollection;
528 else if (objectValue is Array)
529 valueAsList = new ArrayList ((Array) objectValue);
530 else if (xmlTypeCode != XmlTypeCode.None) {
531 ArrayList al = new ArrayList ();
536 throw new NotImplementedException ();
542 public override Type ValueType {
543 get { return schemaType.Datatype.ValueType; }
547 public override XmlSchemaType XmlType {
548 get { return schemaType; }
553 #region internal static members
555 internal static Type RuntimeTypeFromXmlTypeCode (XmlTypeCode typeCode)
558 case XmlTypeCode.Int:
560 case XmlTypeCode.Decimal:
561 return typeof (decimal);
562 case XmlTypeCode.Double:
563 return typeof (double);
564 case XmlTypeCode.Float:
565 return typeof (float);
566 case XmlTypeCode.Long:
567 return typeof (long);
568 case XmlTypeCode.Short:
569 return typeof (short);
570 case XmlTypeCode.UnsignedShort:
571 return typeof (ushort);
572 case XmlTypeCode.UnsignedInt:
573 return typeof (uint);
574 case XmlTypeCode.String:
575 return typeof (string);
576 case XmlTypeCode.DateTime:
577 return typeof (DateTime);
578 case XmlTypeCode.Boolean:
579 return typeof (bool);
580 case XmlTypeCode.Item:
581 return typeof (object);
583 throw new NotSupportedException (String.Format ("XQuery internal error: Cannot infer Runtime Type from XmlTypeCode {0}.", typeCode));
586 internal static XmlTypeCode XmlTypeCodeFromRuntimeType (Type cliType, bool raiseError)
588 switch (Type.GetTypeCode (cliType)) {
590 return XmlTypeCode.Int;
591 case TypeCode.Decimal:
592 return XmlTypeCode.Decimal;
593 case TypeCode.Double:
594 return XmlTypeCode.Double;
595 case TypeCode.Single:
596 return XmlTypeCode.Float;
598 return XmlTypeCode.Long;
600 return XmlTypeCode.Short;
601 case TypeCode.UInt16:
602 return XmlTypeCode.UnsignedShort;
603 case TypeCode.UInt32:
604 return XmlTypeCode.UnsignedInt;
605 case TypeCode.String:
606 return XmlTypeCode.String;
607 case TypeCode.DateTime:
608 return XmlTypeCode.DateTime;
609 case TypeCode.Boolean:
610 return XmlTypeCode.Boolean;
611 case TypeCode.Object:
612 return XmlTypeCode.Item;
615 throw new NotSupportedException (String.Format ("XQuery internal error: Cannot infer XmlTypeCode from Runtime Type {0}", cliType));
617 return XmlTypeCode.None;