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
)
59 Init (value, xmlType
);
62 private void Init (bool value, XmlSchemaType xmlType
)
65 throw new ArgumentNullException ("xmlType");
66 xmlTypeCode
= XmlTypeCode
.Boolean
;
67 this.booleanValue
= value;
72 public XPathAtomicValue (DateTime
value, XmlSchemaType xmlType
)
74 Init (value, xmlType
);
77 private void Init (DateTime
value, XmlSchemaType xmlType
)
80 throw new ArgumentNullException ("xmlType");
81 xmlTypeCode
= XmlTypeCode
.DateTime
;
82 this.dateTimeValue
= value;
87 public XPathAtomicValue (decimal value, XmlSchemaType xmlType
)
89 Init (value, xmlType
);
92 private void Init (decimal value, XmlSchemaType xmlType
)
95 throw new ArgumentNullException ("xmlType");
96 xmlTypeCode
= XmlTypeCode
.Decimal
;
97 this.decimalValue
= value;
102 public XPathAtomicValue (double value, XmlSchemaType xmlType
)
104 Init (value, xmlType
);
107 private void Init (double value, XmlSchemaType xmlType
)
110 throw new ArgumentNullException ("xmlType");
111 xmlTypeCode
= XmlTypeCode
.Double
;
112 this.doubleValue
= value;
113 schemaType
= xmlType
;
117 public XPathAtomicValue (int value, XmlSchemaType xmlType
)
119 Init (value, xmlType
);
122 private void Init (int value, XmlSchemaType xmlType
)
125 throw new ArgumentNullException ("xmlType");
126 xmlTypeCode
= XmlTypeCode
.Int
;
127 this.intValue
= value;
128 schemaType
= xmlType
;
132 public XPathAtomicValue (long value, XmlSchemaType xmlType
)
134 Init (value, xmlType
);
137 private void Init (long value, XmlSchemaType xmlType
)
140 throw new ArgumentNullException ("xmlType");
141 xmlTypeCode
= XmlTypeCode
.Long
;
142 this.longValue
= value;
143 schemaType
= xmlType
;
147 public XPathAtomicValue (float value, XmlSchemaType xmlType
)
149 Init (value, xmlType
);
152 private void Init (float value, XmlSchemaType xmlType
)
155 throw new ArgumentNullException ("xmlType");
156 xmlTypeCode
= XmlTypeCode
.Float
;
157 this.floatValue
= value;
158 schemaType
= xmlType
;
162 public XPathAtomicValue (string value, XmlSchemaType xmlType
)
164 Init (value, xmlType
);
167 private void Init (string value, XmlSchemaType xmlType
)
170 throw new ArgumentNullException ("value");
172 throw new ArgumentNullException ("xmlType");
173 xmlTypeCode
= XmlTypeCode
.String
;
174 this.stringValue
= value;
175 schemaType
= xmlType
;
179 public XPathAtomicValue (object value, XmlSchemaType xmlType
)
181 Init (value, xmlType
);
184 private void Init (object value, XmlSchemaType xmlType
)
186 // It accepts any kind of object, but will be rejected on each value properties.
188 throw new ArgumentNullException ("value");
190 throw new ArgumentNullException ("xmlType");
192 switch (Type
.GetTypeCode (value.GetType ())) {
194 case TypeCode
.UInt16
:
196 Init ((int) value, xmlType
);
198 case TypeCode
.Decimal
:
199 Init ((decimal) value, xmlType
);
201 case TypeCode
.Double
:
202 Init ((double) value, xmlType
);
204 case TypeCode
.Single
:
205 Init ((float) value, xmlType
);
208 case TypeCode
.UInt32
:
209 Init ((long) value, xmlType
);
211 case TypeCode
.String
:
212 Init ((string) value, xmlType
);
214 case TypeCode
.DateTime
:
215 Init ((DateTime
) value, xmlType
);
217 case TypeCode
.Boolean
:
218 Init ((bool) value, xmlType
);
222 ICollection col
= value as ICollection
;
223 if (col
!= null && col
.Count
== 1) {
225 Init (((IList
) col
) [0], xmlType
);
227 IEnumerator en
= col
.GetEnumerator ();
230 if (en
.Current
is DictionaryEntry
)
231 Init (((DictionaryEntry
) en
.Current
).Value
, xmlType
);
233 Init (en
.Current
, xmlType
);
238 XPathAtomicValue another
= value as XPathAtomicValue
;
239 if (another
!= null) {
240 switch (another
.xmlTypeCode
) {
241 case XmlTypeCode
.Boolean
:
242 Init (another
.booleanValue
, xmlType
);
244 case XmlTypeCode
.DateTime
:
245 Init (another
.dateTimeValue
, xmlType
);
247 case XmlTypeCode
.Decimal
:
248 Init (another
.decimalValue
, xmlType
);
250 case XmlTypeCode
.Double
:
251 Init (another
.doubleValue
, xmlType
);
253 case XmlTypeCode
.Int
:
254 Init (another
.intValue
, xmlType
);
256 case XmlTypeCode
.Long
:
257 Init (another
.longValue
, xmlType
);
259 case XmlTypeCode
.Float
:
260 Init (another
.floatValue
, xmlType
);
262 case XmlTypeCode
.String
:
263 Init (another
.stringValue
, xmlType
);
266 objectValue
= another
.objectValue
;
272 schemaType
= xmlType
;
279 object ICloneable
.Clone ()
281 return this.Clone ();
285 public XPathAtomicValue
Clone ()
287 return new XPathAtomicValue (this, schemaType
);
291 public override object ValueAs (Type type
, IXmlNamespaceResolver nsResolver
)
293 switch (XmlTypeCodeFromRuntimeType (type
, false)) {
294 case XmlTypeCode
.Int
:
295 case XmlTypeCode
.Short
:
296 case XmlTypeCode
.UnsignedShort
:
298 case XmlTypeCode
.Decimal
:
299 return ValueAsDecimal
;
300 case XmlTypeCode
.Double
:
301 return ValueAsDouble
;
302 case XmlTypeCode
.Float
:
303 return ValueAsSingle
;
304 case XmlTypeCode
.Long
:
305 case XmlTypeCode
.UnsignedInt
:
307 case XmlTypeCode
.String
:
309 case XmlTypeCode
.DateTime
:
310 return ValueAsDateTime
;
311 case XmlTypeCode
.Boolean
:
312 return ValueAsBoolean
;
313 case XmlTypeCode
.Item
:
315 case XmlTypeCode
.QName
:
316 return XmlQualifiedName
.Parse (Value
, nsResolver
);
318 if (type
.GetInterface ("System.Collections.ICollection") != null)
320 throw new NotImplementedException ();
327 // As long as I tried, neither of such XPathAtomicValue created
328 // with XmlText that contains atomic value, XmlElement that
329 // contains such XmlText, XmlDocument nor XPathNavigator
330 // created from such nodes returned false. So it won't be
331 // true on this class. Apparently this class needs more
333 public override bool IsNode
{
334 get { return false; }
337 internal XmlTypeCode ResolvedTypeCode
{
339 if (schemaType
!= XmlSchemaComplexType
.AnyType
)
340 return schemaType
.TypeCode
;
347 public override object TypedValue
{
349 switch (ResolvedTypeCode
) {
350 case XmlTypeCode
.Boolean
:
351 return ValueAsBoolean
;
352 case XmlTypeCode
.DateTime
:
353 return ValueAsDateTime
;
354 case XmlTypeCode
.Decimal
:
355 return ValueAsDecimal
;
356 case XmlTypeCode
.Double
:
357 return ValueAsDouble
;
358 case XmlTypeCode
.Long
:
360 case XmlTypeCode
.Int
:
362 case XmlTypeCode
.Float
:
363 return ValueAsSingle
;
364 case XmlTypeCode
.String
:
372 // This method works like ValueAsString.
373 public override string Value
{
375 switch (ResolvedTypeCode
) {
376 case XmlTypeCode
.Boolean
:
377 stringValue
= XQueryConvert
.BooleanToString (ValueAsBoolean
);
379 case XmlTypeCode
.DateTime
:
380 stringValue
= XQueryConvert
.DateTimeToString (ValueAsDateTime
);
382 case XmlTypeCode
.Decimal
:
383 stringValue
= XQueryConvert
.DecimalToString (ValueAsDecimal
);
385 case XmlTypeCode
.Double
:
386 stringValue
= XQueryConvert
.DoubleToString (ValueAsDouble
);
388 case XmlTypeCode
.Long
:
389 stringValue
= XQueryConvert
.IntegerToString (ValueAsInt64
);
391 case XmlTypeCode
.Int
:
392 stringValue
= XQueryConvert
.IntToString (ValueAsInt32
);
394 case XmlTypeCode
.Float
:
395 stringValue
= XQueryConvert
.FloatToString (ValueAsSingle
);
397 case XmlTypeCode
.String
:
400 case XmlTypeCode
.None
:
401 case XmlTypeCode
.Item
:
402 case XmlTypeCode
.AnyAtomicType
:
403 switch (XmlTypeCodeFromRuntimeType (objectValue
.GetType (), false)) {
404 case XmlTypeCode
.String
:
405 stringValue
= (string) objectValue
;
407 case XmlTypeCode
.DateTime
:
408 stringValue
= XQueryConvert
.DateTimeToString ((DateTime
) objectValue
);
410 case XmlTypeCode
.Boolean
:
411 stringValue
= XQueryConvert
.BooleanToString ((bool) objectValue
);
413 case XmlTypeCode
.Float
:
414 stringValue
= XQueryConvert
.FloatToString ((float) objectValue
);
416 case XmlTypeCode
.Double
:
417 stringValue
= XQueryConvert
.DoubleToString ((double) objectValue
);
419 case XmlTypeCode
.Decimal
:
420 stringValue
= XQueryConvert
.DecimalToString ((decimal) objectValue
);
422 case XmlTypeCode
.Long
:
423 stringValue
= XQueryConvert
.IntegerToString ((long) objectValue
);
425 case XmlTypeCode
.Int
:
426 stringValue
= XQueryConvert
.IntToString ((int) objectValue
);
431 if (stringValue
!= null)
434 if (objectValue
!= null)
435 throw new InvalidCastException (String
.Format ("Conversion from runtime type {0} to {1} is not supported", objectValue
.GetType (), XmlTypeCode
.String
));
437 throw new InvalidCastException (String
.Format ("Conversion from schema type {0} (type code {1}) to {2} is not supported", schemaType
.QualifiedName
, xmlTypeCode
, XmlTypeCode
.String
));
442 public override bool ValueAsBoolean
{
444 switch (xmlTypeCode
) {
445 case XmlTypeCode
.Boolean
:
447 case XmlTypeCode
.Decimal
:
448 return XQueryConvert
.DecimalToBoolean (decimalValue
);
449 case XmlTypeCode
.Double
:
450 return XQueryConvert
.DoubleToBoolean (doubleValue
);
451 case XmlTypeCode
.Long
:
452 return XQueryConvert
.IntegerToBoolean (longValue
);
453 case XmlTypeCode
.Int
:
454 return XQueryConvert
.IntToBoolean (intValue
);
455 case XmlTypeCode
.Float
:
456 return XQueryConvert
.FloatToBoolean (floatValue
);
457 case XmlTypeCode
.String
:
458 return XQueryConvert
.StringToBoolean (stringValue
);
460 case XmlTypeCode
.None
:
461 case XmlTypeCode
.Item
:
462 case XmlTypeCode
.AnyAtomicType
:
463 if (objectValue
is bool)
464 return (bool) objectValue
;
469 throw new InvalidCastException (String
.Format ("Conversion from {0} to {1} is not supported", schemaType
.QualifiedName
, XmlSchemaSimpleType
.XsBoolean
.QualifiedName
));
474 public override DateTime ValueAsDateTime
{
476 switch (xmlTypeCode
) {
477 case XmlTypeCode
.DateTime
:
478 return dateTimeValue
;
479 case XmlTypeCode
.String
:
480 return XQueryConvert
.StringToDateTime (stringValue
);
481 case XmlTypeCode
.None
:
482 case XmlTypeCode
.Item
:
483 case XmlTypeCode
.AnyAtomicType
:
484 if (objectValue
is DateTime
)
485 return (DateTime
) objectValue
;
490 throw new InvalidCastException (String
.Format ("Conversion from {0} to {1} is not supported", schemaType
.QualifiedName
, XmlSchemaSimpleType
.XsDateTime
.QualifiedName
));
495 public override decimal ValueAsDecimal
{
497 switch (xmlTypeCode
) {
498 case XmlTypeCode
.Boolean
:
499 return XQueryConvert
.BooleanToDecimal (booleanValue
);
500 case XmlTypeCode
.Decimal
:
502 case XmlTypeCode
.Double
:
503 return XQueryConvert
.DoubleToDecimal (doubleValue
);
504 case XmlTypeCode
.Long
:
505 return XQueryConvert
.IntegerToDecimal (longValue
);
506 case XmlTypeCode
.Int
:
507 return XQueryConvert
.IntToDecimal (intValue
);
508 case XmlTypeCode
.Float
:
509 return XQueryConvert
.FloatToDecimal (floatValue
);
510 case XmlTypeCode
.String
:
511 return XQueryConvert
.StringToDecimal (stringValue
);
512 case XmlTypeCode
.None
:
513 case XmlTypeCode
.Item
:
514 case XmlTypeCode
.AnyAtomicType
:
515 if (objectValue
is decimal)
516 return (decimal) objectValue
;
521 throw new InvalidCastException (String
.Format ("Conversion from {0} to {1} is not supported", schemaType
.QualifiedName
, XmlSchemaSimpleType
.XsDecimal
.QualifiedName
));
526 public override double ValueAsDouble
{
528 switch (xmlTypeCode
) {
529 case XmlTypeCode
.Boolean
:
530 return XQueryConvert
.BooleanToDouble (booleanValue
);
531 case XmlTypeCode
.Decimal
:
532 return XQueryConvert
.DecimalToDouble (decimalValue
);
533 case XmlTypeCode
.Double
:
535 case XmlTypeCode
.Long
:
536 return XQueryConvert
.IntegerToDouble (longValue
);
537 case XmlTypeCode
.Int
:
538 return XQueryConvert
.IntToDouble (intValue
);
539 case XmlTypeCode
.Float
:
540 return XQueryConvert
.FloatToDouble (floatValue
);
541 case XmlTypeCode
.String
:
542 return XQueryConvert
.StringToDouble (stringValue
);
543 case XmlTypeCode
.None
:
544 case XmlTypeCode
.Item
:
545 case XmlTypeCode
.AnyAtomicType
:
546 if (objectValue
is double)
547 return (double) objectValue
;
552 throw new InvalidCastException (String
.Format ("Conversion from {0} to {1} is not supported", schemaType
.QualifiedName
, XmlSchemaSimpleType
.XsDouble
.QualifiedName
));
557 public override int ValueAsInt32
{
559 switch (xmlTypeCode
) {
560 case XmlTypeCode
.Boolean
:
561 return XQueryConvert
.BooleanToInt (booleanValue
);
562 case XmlTypeCode
.Decimal
:
563 return XQueryConvert
.DecimalToInt (decimalValue
);
564 case XmlTypeCode
.Double
:
565 return XQueryConvert
.DoubleToInt (doubleValue
);
566 case XmlTypeCode
.Long
:
567 return XQueryConvert
.IntegerToInt (longValue
);
568 case XmlTypeCode
.Int
:
570 case XmlTypeCode
.Float
:
571 return XQueryConvert
.FloatToInt (floatValue
);
572 case XmlTypeCode
.String
:
573 return XQueryConvert
.StringToInt (stringValue
);
574 case XmlTypeCode
.None
:
575 case XmlTypeCode
.Item
:
576 case XmlTypeCode
.AnyAtomicType
:
577 if (objectValue
is int)
578 return (int) objectValue
;
583 throw new InvalidCastException (String
.Format ("Conversion from {0} to {1} is not supported", schemaType
.QualifiedName
, XmlSchemaSimpleType
.XsInt
.QualifiedName
));
588 public override long ValueAsInt64
{
590 switch (xmlTypeCode
) {
591 case XmlTypeCode
.Boolean
:
592 return XQueryConvert
.BooleanToInteger (booleanValue
);
593 case XmlTypeCode
.Decimal
:
594 return XQueryConvert
.DecimalToInteger (decimalValue
);
595 case XmlTypeCode
.Double
:
596 return XQueryConvert
.DoubleToInteger (doubleValue
);
597 case XmlTypeCode
.Long
:
599 case XmlTypeCode
.Int
:
600 return XQueryConvert
.IntegerToInt (intValue
);
601 case XmlTypeCode
.Float
:
602 return XQueryConvert
.FloatToInteger (floatValue
);
603 case XmlTypeCode
.String
:
604 return XQueryConvert
.StringToInteger (stringValue
);
605 case XmlTypeCode
.None
:
606 case XmlTypeCode
.Item
:
607 case XmlTypeCode
.AnyAtomicType
:
608 if (objectValue
is long)
609 return (long) objectValue
;
614 throw new InvalidCastException (String
.Format ("Conversion from {0} to {1} is not supported", schemaType
.QualifiedName
, XmlSchemaSimpleType
.XsLong
.QualifiedName
));
619 public override float ValueAsSingle
{
621 switch (xmlTypeCode
) {
622 case XmlTypeCode
.Boolean
:
623 return XQueryConvert
.BooleanToFloat (booleanValue
);
624 case XmlTypeCode
.Decimal
:
625 return XQueryConvert
.DecimalToFloat (decimalValue
);
626 case XmlTypeCode
.Double
:
627 return XQueryConvert
.DoubleToFloat (doubleValue
);
628 case XmlTypeCode
.Float
:
630 case XmlTypeCode
.Int
:
631 return XQueryConvert
.FloatToInt (intValue
);
632 case XmlTypeCode
.Long
:
633 return XQueryConvert
.IntegerToFloat (longValue
);
634 case XmlTypeCode
.String
:
635 return XQueryConvert
.StringToFloat (stringValue
);
636 case XmlTypeCode
.None
:
637 case XmlTypeCode
.Item
:
638 case XmlTypeCode
.AnyAtomicType
:
639 if (objectValue
is float)
640 return (float) objectValue
;
645 throw new InvalidCastException (String
.Format ("Conversion from {0} to {1} is not supported", schemaType
.QualifiedName
, XmlSchemaSimpleType
.XsFloat
.QualifiedName
));
650 public override ICollection ValueAsList
{
652 if (valueAsList
!= null)
654 if (objectValue
is ICollection
)
655 valueAsList
= objectValue
as ICollection
;
656 else if (objectValue
is Array
)
657 valueAsList
= new ArrayList ((Array
) objectValue
);
658 else if (xmlTypeCode
!= XmlTypeCode
.None
) {
659 ArrayList al
= new ArrayList ();
664 throw new NotImplementedException ();
670 public override Type ValueType
{
671 get { return schemaType.Datatype.ValueType; }
675 public override XmlSchemaType XmlType
{
676 get { return schemaType; }
681 #region internal static members
683 internal static Type
RuntimeTypeFromXmlTypeCode (XmlTypeCode typeCode
)
686 case XmlTypeCode
.Int
:
688 case XmlTypeCode
.Decimal
:
689 return typeof (decimal);
690 case XmlTypeCode
.Double
:
691 return typeof (double);
692 case XmlTypeCode
.Float
:
693 return typeof (float);
694 case XmlTypeCode
.Long
:
695 return typeof (long);
696 case XmlTypeCode
.Short
:
697 return typeof (short);
698 case XmlTypeCode
.UnsignedShort
:
699 return typeof (ushort);
700 case XmlTypeCode
.UnsignedInt
:
701 return typeof (uint);
702 case XmlTypeCode
.String
:
703 return typeof (string);
704 case XmlTypeCode
.DateTime
:
705 return typeof (DateTime
);
706 case XmlTypeCode
.Boolean
:
707 return typeof (bool);
708 case XmlTypeCode
.Item
:
709 return typeof (object);
711 throw new NotSupportedException (String
.Format ("XQuery internal error: Cannot infer Runtime Type from XmlTypeCode {0}.", typeCode
));
714 internal static XmlTypeCode
XmlTypeCodeFromRuntimeType (Type cliType
, bool raiseError
)
716 switch (Type
.GetTypeCode (cliType
)) {
718 return XmlTypeCode
.Int
;
719 case TypeCode
.Decimal
:
720 return XmlTypeCode
.Decimal
;
721 case TypeCode
.Double
:
722 return XmlTypeCode
.Double
;
723 case TypeCode
.Single
:
724 return XmlTypeCode
.Float
;
726 return XmlTypeCode
.Long
;
728 return XmlTypeCode
.Short
;
729 case TypeCode
.UInt16
:
730 return XmlTypeCode
.UnsignedShort
;
731 case TypeCode
.UInt32
:
732 return XmlTypeCode
.UnsignedInt
;
733 case TypeCode
.String
:
734 return XmlTypeCode
.String
;
735 case TypeCode
.DateTime
:
736 return XmlTypeCode
.DateTime
;
737 case TypeCode
.Boolean
:
738 return XmlTypeCode
.Boolean
;
739 case TypeCode
.Object
:
740 return XmlTypeCode
.Item
;
743 throw new NotSupportedException (String
.Format ("XQuery internal error: Cannot infer XmlTypeCode from Runtime Type {0}", cliType
));
745 return XmlTypeCode
.None
;