Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data / System / Data / SQLTypes / SQLMoney.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SqlMoney.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 //  </copyright>
5 // <owner current="true" primary="true">junfang</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 // <owner current="true" primary="false">[....]</owner>
8 //------------------------------------------------------------------------------
9
10 //**************************************************************************
11 // @File: SqlMoney.cs
12 //
13 // Create by:    JunFang
14 //
15 // Purpose: Implementation of SqlMoney which is equivalent to
16 //            data type "money" in SQL Server
17 //
18 // Notes:
19 //
20 // History:
21 //
22 //   09/17/99  JunFang    Created and implemented as first drop.
23 //
24 // @EndHeader@
25 //**************************************************************************
26
27 using System;
28 using System.Data.Common;
29 using System.Diagnostics;
30 using System.Runtime.InteropServices;
31 using System.Globalization;
32 using System.Xml;
33 using System.Xml.Schema;
34 using System.Xml.Serialization;
35
36 namespace System.Data.SqlTypes {
37
38     /// <devdoc>
39     ///    <para>
40     ///       Represents a currency value ranging from
41     ///       -2<superscript term='63'/> (or -922,337,203,685,477.5808) to 2<superscript term='63'/> -1 (or
42     ///       +922,337,203,685,477.5807) with an accuracy to
43     ///       a ten-thousandth of currency unit to be stored in or retrieved from a
44     ///       database.
45     ///    </para>
46     /// </devdoc>
47     [Serializable]
48     [StructLayout(LayoutKind.Sequential)]
49     [XmlSchemaProvider("GetXsdType")]
50     public struct SqlMoney : INullable, IComparable, IXmlSerializable {
51         private bool m_fNotNull; // false if null
52         private long m_value;
53
54         // SQL Server stores money8 as ticks of 1/10000.
55         internal static readonly int x_iMoneyScale = 4;
56         private static readonly long x_lTickBase = 10000;
57         private static readonly double x_dTickBase = (double)x_lTickBase;
58
59         private static readonly long MinLong = unchecked((long)0x8000000000000000L) / x_lTickBase;
60         private static readonly long MaxLong = 0x7FFFFFFFFFFFFFFFL / x_lTickBase;
61
62         // constructor
63         // construct a Null
64         private SqlMoney(bool fNull) {
65             m_fNotNull = false;
66             m_value = 0;
67         }
68
69         // Constructs from a long value without scaling. The ignored parameter exists
70         // only to distinguish this constructor from the constructor that takes a long.
71         // Used only internally.
72         internal SqlMoney(long value, int ignored) {
73             m_value = value;
74             m_fNotNull = true;
75         }
76
77         /// <devdoc>
78         ///    <para>
79         ///       Initializes a new instance of the <see cref='System.Data.SqlTypes.SqlMoney'/> class with the value given.
80         ///    </para>
81         /// </devdoc>
82         public SqlMoney(int value) {
83             m_value = (long)value * x_lTickBase;
84             m_fNotNull = true;
85         }
86
87         /// <devdoc>
88         ///    <para>
89         ///       Initializes a new instance of the <see cref='System.Data.SqlTypes.SqlMoney'/> class with the value given.
90         ///    </para>
91         /// </devdoc>
92         public SqlMoney(long value) {
93             if (value < MinLong || value > MaxLong)
94                 throw new OverflowException(SQLResource.ArithOverflowMessage);
95             m_value = value * x_lTickBase;
96             m_fNotNull = true;
97         }
98
99         /// <devdoc>
100         ///    <para>
101         ///       Initializes a new instance of the <see cref='System.Data.SqlTypes.SqlMoney'/> class with the value given.
102         ///    </para>
103         /// </devdoc>
104         public SqlMoney(Decimal value) {
105             // Since Decimal is a value type, operate directly on value, don't worry about changing it.
106             SqlDecimal snum = new SqlDecimal(value);
107             snum.AdjustScale(x_iMoneyScale - snum.Scale, true);
108             Debug.Assert(snum.Scale == x_iMoneyScale);
109
110             if (snum.m_data3 != 0 || snum.m_data4 != 0)
111                 throw new OverflowException(SQLResource.ArithOverflowMessage);
112
113             bool fPositive = snum.IsPositive;
114             ulong ulValue = (ulong)snum.m_data1 + ( ((ulong)snum.m_data2) << 32 );
115             if (fPositive && ulValue > (ulong)(Int64.MaxValue) ||
116                 !fPositive && ulValue > unchecked((ulong)(Int64.MinValue)))
117                 throw new OverflowException(SQLResource.ArithOverflowMessage);
118
119             m_value = fPositive ? (long)ulValue : unchecked(- (long)ulValue);
120             m_fNotNull = true;
121         }
122
123         /// <devdoc>
124         ///    <para>
125         ///       Initializes a new instance of the <see cref='System.Data.SqlTypes.SqlMoney'/> class with the value given.
126         ///    </para>
127         /// </devdoc>
128         public SqlMoney(double value) : this(new Decimal(value)) {
129         }
130
131
132         // INullable
133         /// <devdoc>
134         ///    <para>
135         ///       Gets a value indicating whether the <see cref='System.Data.SqlTypes.SqlMoney.Value'/>
136         ///       property is assigned to null.
137         ///    </para>
138         /// </devdoc>
139         public bool IsNull {
140             get { return !m_fNotNull;}
141         }
142
143         // property: Value
144         /// <devdoc>
145         ///    <para>
146         ///       Gets or sets the monetary value of an instance of the <see cref='System.Data.SqlTypes.SqlMoney'/>
147         ///       class.
148         ///    </para>
149         /// </devdoc>
150         public Decimal Value {
151             get {
152                 if (m_fNotNull)
153                     return ToDecimal();
154                 else
155                     throw new SqlNullValueException();
156             }
157         }
158
159         /// <devdoc>
160         ///    <para>[To be supplied.]</para>
161         /// </devdoc>
162         public Decimal ToDecimal() {
163             if(IsNull)
164                 throw new SqlNullValueException();
165
166             bool fNegative = false;
167             long value = m_value;
168             if (m_value < 0) {
169                 fNegative = true;
170                 value = - m_value;
171             }
172
173             return new Decimal(unchecked((int)value), unchecked((int)(value >> 32)), 0, fNegative, (byte)x_iMoneyScale);
174         }
175
176         /// <devdoc>
177         ///    <para>[To be supplied.]</para>
178         /// </devdoc>
179         public long ToInt64() {
180             if(IsNull)
181                 throw new SqlNullValueException();
182
183             long ret = m_value / (x_lTickBase / 10);
184             bool fPositive = (ret >= 0);
185             long remainder = ret % 10;
186             ret = ret / 10;
187
188             if (remainder >= 5) {
189                 if (fPositive)
190                     ret ++;
191                 else
192                     ret --;
193             }
194
195             return ret;
196         }
197
198         internal long ToSqlInternalRepresentation() {
199             if(IsNull)
200                 throw new SqlNullValueException();
201
202             return m_value;
203         }
204
205         /// <devdoc>
206         ///    <para>[To be supplied.]</para>
207         /// </devdoc>
208         public int ToInt32() {
209             return checked((int)(ToInt64()));
210         }
211
212         /// <devdoc>
213         ///    <para>[To be supplied.]</para>
214         /// </devdoc>
215         public double ToDouble() {
216             return Decimal.ToDouble(ToDecimal());
217         }
218
219         // Implicit conversion from Decimal to SqlMoney
220         /// <devdoc>
221         ///    <para>[To be supplied.]</para>
222         /// </devdoc>
223         public static implicit operator SqlMoney(Decimal x) {
224             return new SqlMoney(x);
225         }
226
227         // Explicit conversion from Double to SqlMoney
228         public static explicit operator SqlMoney(double x) {
229             return new SqlMoney(x);
230         }
231
232         // Implicit conversion from long to SqlMoney
233         public static implicit operator SqlMoney(long x) {
234             return new SqlMoney(new Decimal(x));
235         }
236
237         // Explicit conversion from SqlMoney to Decimal. Throw exception if x is Null.
238         /// <devdoc>
239         ///    <para>[To be supplied.]</para>
240         /// </devdoc>
241         public static explicit operator Decimal(SqlMoney x) {
242             return x.Value;
243         }
244
245         /// <devdoc>
246         ///    <para>[To be supplied.]</para>
247         /// </devdoc>
248         public override String ToString() {
249             if (this.IsNull) {
250                 return SQLResource.NullString;
251             }
252             Decimal money = ToDecimal();
253             // Formatting of SqlMoney: At least two digits after decimal point
254             return money.ToString("#0.00##", (IFormatProvider)null);
255         }
256
257         /// <devdoc>
258         ///    <para>[To be supplied.]</para>
259         /// </devdoc>
260         public static SqlMoney Parse(String s) {
261             // Try parsing the format of '#0.00##' generated by ToString() by using the
262             // culture invariant NumberFormatInfo as well as the current culture's format
263             //
264             Decimal d;
265             SqlMoney money;
266
267             const NumberStyles SqlNumberStyle = 
268                      NumberStyles.AllowCurrencySymbol |
269                      NumberStyles.AllowDecimalPoint |
270                      NumberStyles.AllowParentheses | 
271                      NumberStyles.AllowTrailingSign |
272                      NumberStyles.AllowLeadingSign |
273                      NumberStyles.AllowTrailingWhite |
274                      NumberStyles.AllowLeadingWhite;
275
276             if ( s == SQLResource.NullString) {
277                 money = SqlMoney.Null;
278             }
279             else if (Decimal.TryParse(s, SqlNumberStyle, NumberFormatInfo.InvariantInfo, out d)) {
280                 money = new SqlMoney(d);
281             }
282             else {
283                 money = new SqlMoney(Decimal.Parse(s, NumberStyles.Currency, NumberFormatInfo.CurrentInfo));
284             }
285             
286             return money;
287         }
288
289         // Unary operators
290         /// <devdoc>
291         ///    <para>[To be supplied.]</para>
292         /// </devdoc>
293         public static SqlMoney operator -(SqlMoney x) {
294             if (x.IsNull)
295                 return Null;
296             if (x.m_value == MinLong)
297                 throw new OverflowException(SQLResource.ArithOverflowMessage);
298             return new SqlMoney(-x.m_value, 0);
299         }
300
301
302         // Binary operators
303
304         // Arithmetic operators
305         /// <devdoc>
306         ///    <para>[To be supplied.]</para>
307         /// </devdoc>
308         public static SqlMoney operator +(SqlMoney x, SqlMoney y) {
309             try {
310                 return(x.IsNull || y.IsNull) ? Null : new SqlMoney(checked(x.m_value + y.m_value), 0);
311             }
312             catch (OverflowException) {
313                 throw new OverflowException(SQLResource.ArithOverflowMessage);
314             }
315         }
316
317         /// <devdoc>
318         ///    <para>[To be supplied.]</para>
319         /// </devdoc>
320         public static SqlMoney operator -(SqlMoney x, SqlMoney y) {
321             try {
322                 return(x.IsNull || y.IsNull) ? Null : new SqlMoney(checked(x.m_value - y.m_value), 0);
323             }
324             catch (OverflowException) {
325                 throw new OverflowException(SQLResource.ArithOverflowMessage);
326             }
327         }
328
329         /// <devdoc>
330         ///    <para>[To be supplied.]</para>
331         /// </devdoc>
332         public static SqlMoney operator *(SqlMoney x, SqlMoney y) {
333             return (x.IsNull || y.IsNull) ? Null :
334                 new SqlMoney(Decimal.Multiply(x.ToDecimal(), y.ToDecimal()));
335         }
336
337         /// <devdoc>
338         ///    <para>[To be supplied.]</para>
339         /// </devdoc>
340         public static SqlMoney operator /(SqlMoney x, SqlMoney y) {
341             return (x.IsNull || y.IsNull) ? Null :
342                 new SqlMoney(Decimal.Divide(x.ToDecimal(), y.ToDecimal()));
343         }
344
345
346         // Implicit conversions
347
348         // Implicit conversion from SqlBoolean to SqlMoney
349         /// <devdoc>
350         ///    <para>[To be supplied.]</para>
351         /// </devdoc>
352         public static explicit operator SqlMoney(SqlBoolean x) {
353             return x.IsNull ? Null : new SqlMoney((int)x.ByteValue);
354         }
355
356         // Implicit conversion from SqlByte to SqlMoney
357         /// <devdoc>
358         ///    <para>[To be supplied.]</para>
359         /// </devdoc>
360         public static implicit operator SqlMoney(SqlByte x) {
361             return x.IsNull ? Null : new SqlMoney((int)x.Value);
362         }
363
364         // Implicit conversion from SqlInt16 to SqlMoney
365         /// <devdoc>
366         ///    <para>[To be supplied.]</para>
367         /// </devdoc>
368         public static implicit operator SqlMoney(SqlInt16 x) {
369             return x.IsNull ? Null : new SqlMoney((int)x.Value);
370         }
371
372         // Implicit conversion from SqlInt32 to SqlMoney
373         /// <devdoc>
374         ///    <para>[To be supplied.]</para>
375         /// </devdoc>
376         public static implicit operator SqlMoney(SqlInt32 x) {
377             return x.IsNull ? Null : new SqlMoney(x.Value);
378         }
379
380         // Implicit conversion from SqlInt64 to SqlMoney
381         /// <devdoc>
382         ///    <para>[To be supplied.]</para>
383         /// </devdoc>
384         public static implicit operator SqlMoney(SqlInt64 x) {
385             return x.IsNull ? Null : new SqlMoney(x.Value);
386         }
387
388
389         // Explicit conversions
390
391         // Explicit conversion from SqlSingle to SqlMoney
392         /// <devdoc>
393         ///    <para>[To be supplied.]</para>
394         /// </devdoc>
395         public static explicit operator SqlMoney(SqlSingle x) {
396             return x.IsNull ? Null : new SqlMoney((double)x.Value);
397         }
398
399         // Explicit conversion from SqlDouble to SqlMoney
400         /// <devdoc>
401         ///    <para>[To be supplied.]</para>
402         /// </devdoc>
403         public static explicit operator SqlMoney(SqlDouble x) {
404             return x.IsNull ? Null : new SqlMoney(x.Value);
405         }
406
407         // Explicit conversion from SqlDecimal to SqlMoney
408         /// <devdoc>
409         ///    <para>[To be supplied.]</para>
410         /// </devdoc>
411         public static explicit operator SqlMoney(SqlDecimal x) {
412             return x.IsNull ? SqlMoney.Null : new SqlMoney(x.Value);
413         }
414
415         // Explicit conversion from SqlString to SqlMoney
416         // Throws FormatException or OverflowException if necessary.
417         /// <devdoc>
418         ///    <para>[To be supplied.]</para>
419         /// </devdoc>
420         public static explicit operator SqlMoney(SqlString x) {
421             return x.IsNull ? Null : new SqlMoney(Decimal.Parse(x.Value,NumberStyles.Currency,null));
422         }
423
424
425         // Builtin functions
426
427         // Overloading comparison operators
428         /// <devdoc>
429         ///    <para>[To be supplied.]</para>
430         /// </devdoc>
431         public static SqlBoolean operator==(SqlMoney x, SqlMoney y) {
432             return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.m_value == y.m_value);
433         }
434
435         /// <devdoc>
436         ///    <para>[To be supplied.]</para>
437         /// </devdoc>
438         public static SqlBoolean operator!=(SqlMoney x, SqlMoney y) {
439             return ! (x == y);
440         }
441
442         /// <devdoc>
443         ///    <para>[To be supplied.]</para>
444         /// </devdoc>
445         public static SqlBoolean operator<(SqlMoney x, SqlMoney y) {
446             return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.m_value < y.m_value);
447         }
448
449         /// <devdoc>
450         ///    <para>[To be supplied.]</para>
451         /// </devdoc>
452         public static SqlBoolean operator>(SqlMoney x, SqlMoney y) {
453             return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.m_value > y.m_value);
454         }
455
456         /// <devdoc>
457         ///    <para>[To be supplied.]</para>
458         /// </devdoc>
459         public static SqlBoolean operator<=(SqlMoney x, SqlMoney y) {
460             return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.m_value <= y.m_value);
461         }
462
463         /// <devdoc>
464         ///    <para>[To be supplied.]</para>
465         /// </devdoc>
466         public static SqlBoolean operator>=(SqlMoney x, SqlMoney y) {
467             return(x.IsNull || y.IsNull) ? SqlBoolean.Null : new SqlBoolean(x.m_value >= y.m_value);
468         }
469
470
471         //--------------------------------------------------
472         // Alternative methods for overloaded operators
473         //--------------------------------------------------
474
475         // Alternative method for operator +
476         public static SqlMoney Add(SqlMoney x, SqlMoney y) {
477             return x + y;
478         }
479         // Alternative method for operator -
480         public static SqlMoney Subtract(SqlMoney x, SqlMoney y) {
481             return x - y;
482         }
483
484         // Alternative method for operator *
485         public static SqlMoney Multiply(SqlMoney x, SqlMoney y) {
486             return x * y;
487         }
488
489         // Alternative method for operator /
490         public static SqlMoney Divide(SqlMoney x, SqlMoney y) {
491             return x / y;
492         }
493
494         // Alternative method for operator ==
495         public static SqlBoolean Equals(SqlMoney x, SqlMoney y) {
496             return (x == y);
497         }
498
499         // Alternative method for operator !=
500         public static SqlBoolean NotEquals(SqlMoney x, SqlMoney y) {
501             return (x != y);
502         }
503
504         // Alternative method for operator <
505         public static SqlBoolean LessThan(SqlMoney x, SqlMoney y) {
506             return (x < y);
507         }
508
509         // Alternative method for operator >
510         public static SqlBoolean GreaterThan(SqlMoney x, SqlMoney y) {
511             return (x > y);
512         }
513
514         // Alternative method for operator <=
515         public static SqlBoolean LessThanOrEqual(SqlMoney x, SqlMoney y) {
516             return (x <= y);
517         }
518
519         // Alternative method for operator >=
520         public static SqlBoolean GreaterThanOrEqual(SqlMoney x, SqlMoney y) {
521             return (x >= y);
522         }
523
524         // Alternative method for conversions.
525
526         public SqlBoolean ToSqlBoolean() {
527             return (SqlBoolean)this;
528         }
529
530         public SqlByte ToSqlByte() {
531             return (SqlByte)this;
532         }
533
534         public SqlDouble ToSqlDouble() {
535             return (SqlDouble)this;
536         }
537
538         public SqlInt16 ToSqlInt16() {
539             return (SqlInt16)this;
540         }
541
542         public SqlInt32 ToSqlInt32() {
543             return (SqlInt32)this;
544         }
545
546         public SqlInt64 ToSqlInt64() {
547             return (SqlInt64)this;
548         }
549
550         public SqlDecimal ToSqlDecimal() {
551             return (SqlDecimal)this;
552         }
553
554         public SqlSingle ToSqlSingle() {
555             return (SqlSingle)this;
556         }
557
558         public SqlString ToSqlString() {
559             return (SqlString)this;
560         }
561
562
563         // IComparable
564         // Compares this object to another object, returning an integer that
565         // indicates the relationship.
566         // Returns a value less than zero if this < object, zero if this = object,
567         // or a value greater than zero if this > object.
568         // null is considered to be less than any instance.
569         // If object is not of same type, this method throws an ArgumentException.
570         /// <devdoc>
571         ///    <para>[To be supplied.]</para>
572         /// </devdoc>
573         public int CompareTo(Object value) {
574             if (value is SqlMoney) {
575                 SqlMoney i = (SqlMoney)value;
576
577                 return CompareTo(i);
578             }
579             throw ADP.WrongType(value.GetType(), typeof(SqlMoney));
580         }
581
582         public int CompareTo(SqlMoney value) {
583             // If both Null, consider them equal.
584             // Otherwise, Null is less than anything.
585             if (IsNull)
586                 return value.IsNull ? 0  : -1;
587             else if (value.IsNull)
588                 return 1;
589
590             if (this < value) return -1;
591             if (this > value) return 1;
592             return 0;
593         }
594
595         // Compares this instance with a specified object
596         /// <devdoc>
597         ///    <para>[To be supplied.]</para>
598         /// </devdoc>
599         public override bool Equals(Object value) {
600             if (!(value is SqlMoney)) {
601                 return false;
602             }
603
604             SqlMoney i = (SqlMoney)value;
605
606             if (i.IsNull || IsNull)
607                 return (i.IsNull && IsNull);
608             else
609                 return (this == i).Value;
610         }
611
612         // For hashing purpose
613         /// <devdoc>
614         ///    <para>[To be supplied.]</para>
615         /// </devdoc>
616         public override int GetHashCode() {
617             // Don't use Value property, because Value will convert to Decimal, which is not necessary.
618             return IsNull ? 0 : m_value.GetHashCode();
619         }
620
621         /// <devdoc>
622         ///    <para>[To be supplied.]</para>
623         /// </devdoc>
624         XmlSchema IXmlSerializable.GetSchema() { return null; }
625
626         /// <devdoc>
627         ///    <para>[To be supplied.]</para>
628         /// </devdoc>
629         void IXmlSerializable.ReadXml(XmlReader reader) {
630             string isNull = reader.GetAttribute("nil", XmlSchema.InstanceNamespace);
631             if (isNull != null && XmlConvert.ToBoolean(isNull)) {
632                 // VSTFDevDiv# 479603 - SqlTypes read null value infinitely and never read the next value. Fix - Read the next value.
633                 reader.ReadElementString();
634                 this.m_fNotNull = false;
635             }
636             else {
637                 SqlMoney money = new SqlMoney(XmlConvert.ToDecimal(reader.ReadElementString()));
638                 this.m_fNotNull = money.m_fNotNull;
639                 this.m_value = money.m_value;
640             }
641         }
642
643         /// <devdoc>
644         ///    <para>[To be supplied.]</para>
645         /// </devdoc>
646         void IXmlSerializable.WriteXml(XmlWriter writer) {
647             if (IsNull) {
648                 writer.WriteAttributeString("xsi", "nil", XmlSchema.InstanceNamespace, "true");
649             }
650             else {
651                 writer.WriteString( XmlConvert.ToString(ToDecimal()) );
652             }
653         }
654
655         /// <devdoc>
656         ///    <para>[To be supplied.]</para>
657         /// </devdoc>
658         public static XmlQualifiedName GetXsdType(XmlSchemaSet schemaSet) {
659             return new XmlQualifiedName("decimal", XmlSchema.Namespace);
660         }
661
662         /// <devdoc>
663         ///    <para>
664         ///       Represents a null value that can be assigned to
665         ///       the <see cref='System.Data.SqlTypes.SqlMoney.Value'/> property of an instance of
666         ///       the <see cref='System.Data.SqlTypes.SqlMoney'/>class.
667         ///    </para>
668         /// </devdoc>
669         public static readonly SqlMoney Null        = new SqlMoney(true);
670
671         /// <devdoc>
672         ///    <para>
673         ///       Represents the zero value that can be assigned to the <see cref='System.Data.SqlTypes.SqlMoney.Value'/> property of an instance of
674         ///       the <see cref='System.Data.SqlTypes.SqlMoney'/> class.
675         ///    </para>
676         /// </devdoc>
677         public static readonly SqlMoney Zero        = new SqlMoney(0);
678
679         /// <devdoc>
680         ///    <para>
681         ///       Represents the minimum value that can be assigned
682         ///       to <see cref='System.Data.SqlTypes.SqlMoney.Value'/> property of an instance of
683         ///       the <see cref='System.Data.SqlTypes.SqlMoney'/>
684         ///       class.
685         ///    </para>
686         /// </devdoc>
687         public static readonly SqlMoney MinValue    = new SqlMoney(unchecked((long)0x8000000000000000L), 0);
688
689         /// <devdoc>
690         ///    <para>
691         ///       Represents the maximum value that can be assigned to
692         ///       the <see cref='System.Data.SqlTypes.SqlMoney.Value'/> property of an instance of
693         ///       the <see cref='System.Data.SqlTypes.SqlMoney'/>
694         ///       class.
695         ///    </para>
696         /// </devdoc>
697         public static readonly SqlMoney MaxValue    = new SqlMoney(0x7FFFFFFFFFFFFFFFL, 0);
698
699     } // SqlMoney
700
701 } // namespace System.Data.SqlTypes