1 /* 2 * Copyright (C) 2018 Julien Viet 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 module hunt.database.base.Numeric; 18 19 import hunt.Double; 20 import hunt.Exceptions; 21 import hunt.Float; 22 import hunt.Long; 23 import hunt.Integer; 24 import hunt.math.BigDecimal; 25 import hunt.math.BigInteger; 26 import hunt.Number; 27 28 import std.concurrency : initOnce; 29 import std.conv; 30 31 32 /** 33 * The Postgres <i>NUMERIC</i> type. 34 */ 35 final class Numeric : Number { 36 37 /** 38 * Constant for the {@code NaN} value. 39 */ 40 static Numeric NaN() { 41 __gshared Numeric inst; 42 return initOnce!inst(new Numeric(new Double(Double.NaN))); 43 } 44 45 private Number value; 46 47 /** 48 * Return a {@code Numeric} instance for the given {@code number}. 49 * <p/> 50 * Null values or infinite {@code Double} or {@code Float} are rejected. 51 * 52 * @param number the number 53 * @return the {@code Numeric} value 54 * @throws NumberFormatException when the number is infinite 55 */ 56 static Numeric create(Number number) { 57 if (number is null) { 58 throw new NullPointerException(); 59 } 60 61 Double d = cast(Double)number; 62 Float f = cast(Float)number; 63 64 if (d !is null && d.isInfinite() || f !is null && f.isInfinite()) { 65 throw new NumberFormatException("Infinite numbers are not valid numerics"); 66 } 67 return new Numeric(number); 68 } 69 70 /** 71 * Parse and return a {@code Numeric} instance for the given {@code s}. 72 * <p/> 73 * The string {@code "Nan"} will return the {@link #NaN} instance. 74 * 75 * @param s the string 76 * @return the {@code Numeric} value 77 */ 78 static Numeric parse(string s) { 79 switch (s) { 80 case "NaN": 81 return NaN; 82 default: 83 return new Numeric(new BigDecimal(s)); 84 } 85 } 86 87 private this(Number value) { 88 this.value = value; 89 } 90 91 byte byteValue() { 92 return value.byteValue(); 93 } 94 95 override 96 short shortValue() { 97 return value.shortValue(); 98 } 99 100 override 101 int intValue() { 102 return value.intValue(); 103 } 104 105 override 106 long longValue() { 107 return value.longValue(); 108 } 109 110 override 111 float floatValue() { 112 return value.floatValue(); 113 } 114 115 override 116 double doubleValue() { 117 return value.doubleValue(); 118 } 119 120 /** 121 * @return {@code true} when this number represents {@code NaN} 122 */ 123 bool isNaN() { 124 Double d = cast(Double)value; 125 Float f = cast(Float)value; 126 return d !is null && d.isNaN() || f !is null && f.isNaN(); 127 } 128 129 /** 130 * @return the numeric value represented by this object after conversion 131 * to type {@code BigDecimal}. It can be {@code null} when this instance 132 * represents the {@code NaN} value. 133 */ 134 BigDecimal bigDecimalValue() { 135 BigDecimal bd = cast(BigDecimal) value; 136 if (bd !is null) { 137 return bd; 138 } 139 140 BigInteger bi = cast(BigInteger)value; 141 if (bi !is null) { 142 return new BigDecimal(bi); 143 } else if (isNaN()) { 144 return null; 145 } else { 146 return new BigDecimal(value.toString()); 147 } 148 } 149 150 /** 151 * @return the numeric value represented by this object after conversion 152 * to type {@code BigInteger}. It can be {@code null} when this instance 153 * represents the {@code NaN} value. 154 */ 155 BigInteger bigIntegerValue() { 156 BigInteger bi = cast(BigInteger)value; 157 if (bi !is null) { 158 return bi; 159 } 160 161 BigDecimal bd = cast(BigDecimal)value; 162 if (bd !is null) { 163 return bd.toBigInteger(); 164 } else if (isNaN()) { 165 return null; 166 } else { 167 return new BigInteger(to!string(value.longValue())); 168 } 169 } 170 171 override 172 bool opEquals(Object obj) { 173 Numeric that = cast(Numeric) obj; 174 if (that !is null) { 175 if (typeid(value) == typeid(that)) { 176 return value == that.value; 177 } else { 178 BigDecimal l = bigDecimalValue(); 179 BigDecimal r = that.bigDecimalValue(); 180 if (l is null) { 181 return r is null; 182 } else if (r is null) { 183 return false; 184 } 185 186 // TODO: Tasks pending completion -@zxp at 8/12/2019, 3:15:15 PM 187 // 188 // return l == r; 189 // return l.compareTo(r) == 0; 190 implementationMissing(false); 191 return false; 192 } 193 } 194 return false; 195 } 196 197 override 198 size_t toHash() @trusted nothrow { 199 try { 200 return cast(size_t)intValue(); 201 } catch(Exception ex) { 202 version(HUNT_DEBUG) { 203 import hunt.logging; 204 trace(ex); 205 } 206 return 0; 207 } 208 } 209 210 override 211 string toString() { 212 return value.toString(); 213 } 214 }