2004-08-25 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml.XPath / XPathAtomicValue.cs
1 //
2 // XPathAtomicValue.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // (C)2004 Novell Inc.
8 //
9
10 //
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:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
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.
29 //
30
31 #if NET_2_0
32
33 using System.Collections;
34 using System.Xml;
35 using System.Xml.Schema;
36
37 namespace System.Xml.XPath
38 {
39         public sealed class XPathAtomicValue : XPathItem, ICloneable
40         {
41                 bool booleanValue;
42                 DateTime dateTimeValue;
43                 decimal decimalValue;
44                 double doubleValue;
45                 int intValue;
46                 long longValue;
47                 object objectValue;
48                 float floatValue;
49                 string stringValue;
50                 XmlSchemaType schemaType;
51                 XmlTypeCode xmlTypeCode;
52                 ICollection valueAsList;
53
54                 #region Constructors
55
56                 [MonoTODO]
57                 public XPathAtomicValue (bool value, XmlSchemaType xmlType)
58                 {
59                         if (xmlType == null)
60                                 throw new ArgumentNullException ("xmlType");
61                         xmlTypeCode = XmlTypeCode.Boolean;
62                         this.booleanValue = value;
63                         schemaType = xmlType;
64                 }
65
66                 [MonoTODO]
67                 public XPathAtomicValue (DateTime value, XmlSchemaType xmlType)
68                 {
69                         if (xmlType == null)
70                                 throw new ArgumentNullException ("xmlType");
71                         xmlTypeCode = XmlTypeCode.DateTime;
72                         this.dateTimeValue = value;
73                         schemaType = xmlType;
74                 }
75
76                 [MonoTODO]
77                 public XPathAtomicValue (decimal value, XmlSchemaType xmlType)
78                 {
79                         if (xmlType == null)
80                                 throw new ArgumentNullException ("xmlType");
81                         xmlTypeCode = XmlTypeCode.Decimal;
82                         this.decimalValue = value;
83                         schemaType = xmlType;
84                 }
85
86                 [MonoTODO]
87                 public XPathAtomicValue (double value, XmlSchemaType xmlType)
88                 {
89                         if (xmlType == null)
90                                 throw new ArgumentNullException ("xmlType");
91                         xmlTypeCode = XmlTypeCode.Double;
92                         this.doubleValue = value;
93                         schemaType = xmlType;
94                 }
95
96                 [MonoTODO]
97                 public XPathAtomicValue (int value, XmlSchemaType xmlType)
98                 {
99                         if (xmlType == null)
100                                 throw new ArgumentNullException ("xmlType");
101                         xmlTypeCode = XmlTypeCode.Int;
102                         this.intValue = value;
103                         schemaType = xmlType;
104                 }
105
106                 [MonoTODO]
107                 public XPathAtomicValue (long value, XmlSchemaType xmlType)
108                 {
109                         if (xmlType == null)
110                                 throw new ArgumentNullException ("xmlType");
111                         xmlTypeCode = XmlTypeCode.Long;
112                         this.longValue = value;
113                         schemaType = xmlType;
114                 }
115
116                 [MonoTODO]
117                 public XPathAtomicValue (object value, XmlSchemaType xmlType)
118                 {
119                         // It accepts any kind of object, but will be rejected on each value properties.
120                         if (value == null)
121                                 throw new ArgumentNullException ("value");
122                         if (xmlType == null)
123                                 throw new ArgumentNullException ("xmlType");
124
125                         if (value is XPathAtomicValue)
126                                 objectValue = ((XPathAtomicValue) value).TypedValue;
127                         else
128                                 objectValue = value;
129                         schemaType = xmlType;
130                 }
131
132                 [MonoTODO]
133                 public XPathAtomicValue (float value, XmlSchemaType xmlType)
134                 {
135                         if (xmlType == null)
136                                 throw new ArgumentNullException ("xmlType");
137                         xmlTypeCode = XmlTypeCode.Float;
138                         this.floatValue = value;
139                         schemaType = xmlType;
140                 }
141
142                 [MonoTODO]
143                 public XPathAtomicValue (string value, XmlSchemaType xmlType)
144                 {
145                         if (value == null)
146                                 throw new ArgumentNullException ("value");
147                         if (xmlType == null)
148                                 throw new ArgumentNullException ("xmlType");
149                         xmlTypeCode = XmlTypeCode.String;
150                         this.stringValue = value;
151                         schemaType = xmlType;
152                 }
153
154                 #endregion
155
156                 #region Methods
157
158                 object ICloneable.Clone ()
159                 {
160                         return this.Clone ();
161                 }
162
163                 [MonoTODO]
164                 public XPathAtomicValue Clone ()
165                 {
166                         return new XPathAtomicValue (this, schemaType);
167                 }
168
169                 [MonoTODO]
170                 public override object ValueAs (Type type, IXmlNamespaceResolver nsResolver)
171                 {
172                         switch (XmlTypeCodeFromRuntimeType (type, false)) {
173                         case XmlTypeCode.Int:
174                         case XmlTypeCode.Short:
175                         case XmlTypeCode.UnsignedShort:
176                                 return ValueAsInt32;
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:
185                                 return ValueAsInt64;
186                         case XmlTypeCode.String:
187                                 return Value;
188                         case XmlTypeCode.DateTime:
189                                 return ValueAsDateTime;
190                         case XmlTypeCode.Boolean:
191                                 return ValueAsBoolean;
192                         case XmlTypeCode.Item:
193                                 return TypedValue;
194                         case XmlTypeCode.QName:
195                                 return XmlQualifiedName.Parse (Value, nsResolver);
196                         }
197                         if (type.GetInterface ("System.Collections.ICollection") != null)
198                                 return ValueAsList;
199                         throw new NotImplementedException ();
200                 }
201
202                 #endregion
203
204                 #region Properties
205
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
211                 // documentation.
212                 public override bool IsNode {
213                         get { return false; }
214                 }
215
216                 [MonoTODO]
217                 public override object TypedValue {
218                         get {
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:
229                                         return ValueAsInt64;
230                                 case XmlTypeCode.Int:
231                                         return ValueAsInt32;
232                                 case XmlTypeCode.Float:
233                                         return ValueAsSingle;
234                                 case XmlTypeCode.String:
235                                         return Value;
236                                 }
237                                 return objectValue;
238                         }
239                 }
240
241                 [MonoTODO]
242                 // This method works like ValueAsString.
243                 public override string Value {
244                         get {
245                                 if (stringValue != null)
246                                         return stringValue;
247                                 switch (xmlTypeCode) {
248                                 case XmlTypeCode.Boolean:
249                                         stringValue = XQueryConvert.BooleanToString (booleanValue);
250                                         break;
251                                 case XmlTypeCode.DateTime:
252                                         stringValue = XQueryConvert.DateTimeToString (dateTimeValue);
253                                         break;
254                                 case XmlTypeCode.Decimal:
255                                         stringValue = XQueryConvert.DecimalToString (decimalValue);
256                                         break;
257                                 case XmlTypeCode.Double:
258                                         stringValue = XQueryConvert.DoubleToString (doubleValue);
259                                         break;
260                                 case XmlTypeCode.Long:
261                                         stringValue = XQueryConvert.IntegerToString (longValue);
262                                         break;
263                                 case XmlTypeCode.Int:
264                                         stringValue = XQueryConvert.IntToString (intValue);
265                                         break;
266                                 case XmlTypeCode.Float:
267                                         stringValue = XQueryConvert.FloatToString (floatValue);
268                                         break;
269                                 case XmlTypeCode.String:
270                                         return stringValue;
271
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;
278                                                 break;
279                                         case XmlTypeCode.DateTime:
280                                                 stringValue = XQueryConvert.DateTimeToString ((DateTime) objectValue);
281                                                 break;
282                                         case XmlTypeCode.Boolean:
283                                                 stringValue = XQueryConvert.BooleanToString ((bool) objectValue);
284                                                 break;
285                                         case XmlTypeCode.Float:
286                                                 stringValue = XQueryConvert.FloatToString ((float) objectValue);
287                                                 break;
288                                         case XmlTypeCode.Double:
289                                                 stringValue = XQueryConvert.DoubleToString ((double) objectValue);
290                                                 break;
291                                         case XmlTypeCode.Decimal:
292                                                 stringValue = XQueryConvert.DecimalToString ((decimal) objectValue);
293                                                 break;
294                                         case XmlTypeCode.Long:
295                                                 stringValue = XQueryConvert.IntegerToString ((long) objectValue);
296                                                 break;
297                                         case XmlTypeCode.Int:
298                                                 stringValue = XQueryConvert.IntToString ((int) objectValue);
299                                                 break;
300                                         }
301                                         break;
302                                 }
303                                 if (stringValue != null)
304                                         return stringValue;
305
306                                 if (objectValue != null)
307                                         throw new InvalidOperationException (String.Format ("Conversion from runtime type {0} to {1} is not supported", objectValue.GetType (), XmlTypeCode.String));
308                                 else
309                                         throw new InvalidOperationException (String.Format ("Conversion from schema type {0} (type code {1}) to {2} is not supported", schemaType.QualifiedName, xmlTypeCode, XmlTypeCode.String));
310                         }
311                 }
312
313                 [MonoTODO]
314                 public override bool ValueAsBoolean {
315                         get {
316                                 switch (xmlTypeCode) {
317                                 case XmlTypeCode.Boolean:
318                                         return booleanValue;
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);
331
332                                 case XmlTypeCode.None:
333                                 case XmlTypeCode.Item:
334                                 case XmlTypeCode.AnyAtomicType:
335                                         if (objectValue is bool)
336                                                 return (bool) objectValue;
337                                         break;
338
339                                 }
340
341                                 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Boolean));
342                         }
343                 }
344
345                 [MonoTODO]
346                 public override DateTime ValueAsDateTime {
347                         get {
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;
358                                         break;
359
360                                 }
361
362                                 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.DateTime));
363                         }
364                 }
365
366                 [MonoTODO]
367                 public override decimal ValueAsDecimal {
368                         get {
369                                 switch (xmlTypeCode) {
370                                 case XmlTypeCode.Boolean:
371                                         return XQueryConvert.BooleanToDecimal (booleanValue);
372                                 case XmlTypeCode.Decimal:
373                                         return decimalValue;
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;
389                                         break;
390
391                                 }
392
393                                 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Decimal));
394                         }
395                 }
396
397                 [MonoTODO]
398                 public override double ValueAsDouble {
399                         get {
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:
406                                         return doubleValue;
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;
420                                         break;
421
422                                 }
423
424                                 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Double));
425                         }
426                 }
427
428                 [MonoTODO]
429                 public override int ValueAsInt32 {
430                         get {
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:
441                                         return intValue;
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;
451                                         break;
452
453                                 }
454
455                                 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Int));
456                         }
457                 }
458
459                 [MonoTODO]
460                 public override long ValueAsInt64 {
461                         get {
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:
470                                         return longValue;
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;
482                                         break;
483
484                                 }
485
486                                 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Long));
487                         }
488                 }
489
490                 [MonoTODO]
491                 public override float ValueAsSingle {
492                         get {
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:
501                                         return floatValue;
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;
513                                         break;
514
515                                 }
516
517                                 throw new InvalidOperationException (String.Format ("Conversion from {0} to {1} is not supported", schemaType, XmlTypeCode.Float));
518                         }
519                 }
520
521                 [MonoTODO]
522                 public override ICollection ValueAsList {
523                         get {
524                                 if (valueAsList != null)
525                                         return valueAsList;
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 ();
532                                         al.Add (TypedValue);
533                                         valueAsList = al;
534                                 }
535                                 else
536                                         throw new NotImplementedException ();
537                                 return valueAsList;
538                         }
539                 }
540
541                 [MonoTODO]
542                 public override Type ValueType {
543                         get { return schemaType.Datatype.ValueType; }
544                 }
545
546                 [MonoTODO]
547                 public override XmlSchemaType XmlType {
548                         get { return schemaType; }
549                 }
550
551                 #endregion
552
553                 #region internal static members
554
555                 internal static Type RuntimeTypeFromXmlTypeCode (XmlTypeCode typeCode)
556                 {
557                         switch (typeCode) {
558                         case XmlTypeCode.Int:
559                                 return typeof (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);
582                         }
583                         throw new NotSupportedException (String.Format ("XQuery internal error: Cannot infer Runtime Type from XmlTypeCode {0}.", typeCode));
584                 }
585
586                 internal static XmlTypeCode XmlTypeCodeFromRuntimeType (Type cliType, bool raiseError)
587                 {
588                         switch (Type.GetTypeCode (cliType)) {
589                         case TypeCode.Int32:
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;
597                         case TypeCode.Int64:
598                                 return XmlTypeCode.Long;
599                         case TypeCode.Int16:
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;
613                         }
614                         if (raiseError)
615                                 throw new NotSupportedException (String.Format ("XQuery internal error: Cannot infer XmlTypeCode from Runtime Type {0}", cliType));
616                         else
617                                 return XmlTypeCode.None;
618                 }
619                 #endregion
620         }
621 }
622
623 #endif