1 /* 2 * hunt-time: A time library for D programming language. 3 * 4 * Copyright (C) 2015-2018 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.time.format.DecimalStyle; 13 14 import hunt.time.util.Common; 15 16 // import hunt.text.DecimalFormatSymbols; 17 import hunt.collection.Collections; 18 import hunt.collection.HashSet; 19 import hunt.collection.Set; 20 import hunt.collection.HashMap; 21 import hunt.collection.Map; 22 import hunt.Exceptions; 23 import hunt.util.Locale; 24 // import hunt.concurrent.ConcurrentMap; 25 26 /** 27 * Localized decimal style used _in date and time formatting. 28 * !(p) 29 * A significant part of dealing with dates and times is the localization. 30 * This class acts as a central point for accessing the information. 31 * 32 * @implSpec 33 * This class is immutable and thread-safe. 34 * 35 * @since 1.8 36 */ 37 public final class DecimalStyle { 38 39 /** 40 * The standard set of non-localized decimal style symbols. 41 * !(p) 42 * This uses standard ASCII characters for zero, positive, negative and a dot for the decimal point. 43 */ 44 // public __gshared DecimalStyle STANDARD; 45 /** 46 * The cache of DecimalStyle instances. 47 */ 48 // __gshared Map!(Locale, DecimalStyle) CACHE; 49 50 /** 51 * The zero digit. 52 */ 53 private char zeroDigit; 54 /** 55 * The positive sign. 56 */ 57 private char positiveSign; 58 /** 59 * The negative sign. 60 */ 61 private char negativeSign; 62 /** 63 * The decimal separator. 64 */ 65 private char decimalSeparator; 66 67 // shared static this() 68 // { 69 // STANDARD = new DecimalStyle('0', '+', '-', '.'); 70 mixin(MakeGlobalVar!(DecimalStyle)("STANDARD",`new DecimalStyle('0', '+', '-', '.')`)); 71 // CACHE = new HashMap!(Locale, DecimalStyle)(16, 0.75f/* , 2 */); 72 mixin(MakeGlobalVar!(Map!(Locale, DecimalStyle))("CACHE",`new HashMap!(Locale, DecimalStyle)(16, 0.75f/* , 2 */)`)); 73 // } 74 //----------------------------------------------------------------------- 75 /** 76 * Lists all the locales that are supported. 77 * !(p) 78 * The locale 'en_US' will always be present. 79 * 80 * @return a Set of Locales for which localization is supported 81 */ 82 ///@gxc 83 // public static Set!(Locale) getAvailableLocales() { 84 // Locale[] l = DecimalFormatSymbols.getAvailableLocales(); 85 // Set!(Locale) locales = new HashSet!(Locale)(l.length); 86 // foreach(d ; l) { 87 // locales.add(d); 88 // } 89 // // Collections.addAll(locales, l); 90 // return locales; 91 // } 92 93 /** 94 * Obtains the DecimalStyle for the default 95 * {@link java.util.Locale.Category#FORMAT FORMAT} locale. 96 * !(p) 97 * This method provides access to locale sensitive decimal style symbols. 98 * !(p) 99 * This is equivalent to calling 100 * {@link #of(Locale) 101 * of(Locale.getDefault(Locale.Category.FORMAT))}. 102 * 103 * @see java.util.Locale.Category#FORMAT 104 * @return the decimal style, not null 105 */ 106 ///@gxc 107 // public static DecimalStyle ofDefaultLocale() { 108 // return of(Locale.getDefault(Locale.Category.FORMAT)); 109 // } 110 111 /** 112 * Obtains the DecimalStyle for the specified locale. 113 * !(p) 114 * This method provides access to locale sensitive decimal style symbols. 115 * If the locale contains "nu" (Numbering System) and/or "rg" 116 * (Region Override) <a href="../../util/Locale.html#def_locale_extension"> 117 * Unicode extensions</a>, returned instance will reflect the values specified with 118 * those extensions. If both "nu" and "rg" are specified, the value from 119 * the "nu" extension supersedes the implicit one from the "rg" extension. 120 * 121 * @param locale the locale, not null 122 * @return the decimal style, not null 123 */ 124 public static DecimalStyle of(Locale locale) { 125 assert(locale, "locale"); 126 DecimalStyle info = CACHE.get(locale); 127 if (info is null) { 128 info = create(locale); 129 CACHE.putIfAbsent(locale, info); 130 info = CACHE.get(locale); 131 } 132 return info; 133 } 134 135 private static DecimalStyle create(Locale locale) { 136 // DecimalFormatSymbols oldSymbols = DecimalFormatSymbols.getInstance(locale); 137 // char zeroDigit = oldSymbols.getZeroDigit(); 138 // char positiveSign = '+'; 139 // char negativeSign = oldSymbols.getMinusSign(); 140 // char decimalSeparator = oldSymbols.getDecimalSeparator(); 141 // if (zeroDigit == '0' && negativeSign == '-' && decimalSeparator == '.') { 142 // return STANDARD; 143 // } 144 // return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator); 145 implementationMissing(); 146 return null; 147 } 148 149 //----------------------------------------------------------------------- 150 /** 151 * Restricted constructor. 152 * 153 * @param zeroChar the character to use for the digit of zero 154 * @param positiveSignChar the character to use for the positive sign 155 * @param negativeSignChar the character to use for the negative sign 156 * @param decimalPointChar the character to use for the decimal point 157 */ 158 this(char zeroChar, char positiveSignChar, char negativeSignChar, char decimalPointChar) { 159 this.zeroDigit = zeroChar; 160 this.positiveSign = positiveSignChar; 161 this.negativeSign = negativeSignChar; 162 this.decimalSeparator = decimalPointChar; 163 } 164 165 //----------------------------------------------------------------------- 166 /** 167 * Gets the character that represents zero. 168 * !(p) 169 * The character used to represent digits may vary by culture. 170 * This method specifies the zero character to use, which implies the characters for one to nine. 171 * 172 * @return the character for zero 173 */ 174 public char getZeroDigit() { 175 return zeroDigit; 176 } 177 178 /** 179 * Returns a copy of the info with a new character that represents zero. 180 * !(p) 181 * The character used to represent digits may vary by culture. 182 * This method specifies the zero character to use, which implies the characters for one to nine. 183 * 184 * @param zeroDigit the character for zero 185 * @return a copy with a new character that represents zero, not null 186 */ 187 public DecimalStyle withZeroDigit(char zeroDigit) { 188 if (zeroDigit == this.zeroDigit) { 189 return this; 190 } 191 return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator); 192 } 193 194 //----------------------------------------------------------------------- 195 /** 196 * Gets the character that represents the positive sign. 197 * !(p) 198 * The character used to represent a positive number may vary by culture. 199 * This method specifies the character to use. 200 * 201 * @return the character for the positive sign 202 */ 203 public char getPositiveSign() { 204 return positiveSign; 205 } 206 207 /** 208 * Returns a copy of the info with a new character that represents the positive sign. 209 * !(p) 210 * The character used to represent a positive number may vary by culture. 211 * This method specifies the character to use. 212 * 213 * @param positiveSign the character for the positive sign 214 * @return a copy with a new character that represents the positive sign, not null 215 */ 216 public DecimalStyle withPositiveSign(char positiveSign) { 217 if (positiveSign == this.positiveSign) { 218 return this; 219 } 220 return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator); 221 } 222 223 //----------------------------------------------------------------------- 224 /** 225 * Gets the character that represents the negative sign. 226 * !(p) 227 * The character used to represent a negative number may vary by culture. 228 * This method specifies the character to use. 229 * 230 * @return the character for the negative sign 231 */ 232 public char getNegativeSign() { 233 return negativeSign; 234 } 235 236 /** 237 * Returns a copy of the info with a new character that represents the negative sign. 238 * !(p) 239 * The character used to represent a negative number may vary by culture. 240 * This method specifies the character to use. 241 * 242 * @param negativeSign the character for the negative sign 243 * @return a copy with a new character that represents the negative sign, not null 244 */ 245 public DecimalStyle withNegativeSign(char negativeSign) { 246 if (negativeSign == this.negativeSign) { 247 return this; 248 } 249 return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator); 250 } 251 252 //----------------------------------------------------------------------- 253 /** 254 * Gets the character that represents the decimal point. 255 * !(p) 256 * The character used to represent a decimal point may vary by culture. 257 * This method specifies the character to use. 258 * 259 * @return the character for the decimal point 260 */ 261 public char getDecimalSeparator() { 262 return decimalSeparator; 263 } 264 265 /** 266 * Returns a copy of the info with a new character that represents the decimal point. 267 * !(p) 268 * The character used to represent a decimal point may vary by culture. 269 * This method specifies the character to use. 270 * 271 * @param decimalSeparator the character for the decimal point 272 * @return a copy with a new character that represents the decimal point, not null 273 */ 274 public DecimalStyle withDecimalSeparator(char decimalSeparator) { 275 if (decimalSeparator == this.decimalSeparator) { 276 return this; 277 } 278 return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator); 279 } 280 281 //----------------------------------------------------------------------- 282 /** 283 * Checks whether the character is a digit, based on the currently set zero character. 284 * 285 * @param ch the character to check 286 * @return the value, 0 to 9, of the character, or -1 if not a digit 287 */ 288 int convertToDigit(char ch) { 289 int val = ch - zeroDigit; 290 return (val >= 0 && val <= 9) ? val : -1; 291 } 292 293 /** 294 * Converts the input numeric text to the internationalized form using the zero character. 295 * 296 * @param numericText the text, consisting of digits 0 to 9, to convert, not null 297 * @return the internationalized text, not null 298 */ 299 string convertNumberToI18N(string numericText) { 300 if (zeroDigit == '0') { 301 return numericText; 302 } 303 int diff = zeroDigit - '0'; 304 char[] array = cast(char[])numericText/* .toCharArray() */; 305 for (int i = 0; i < array.length; i++) { 306 array[i] = cast(char) (array[i] + diff); 307 } 308 return cast(string)(array); 309 } 310 311 //----------------------------------------------------------------------- 312 /** 313 * Checks if this DecimalStyle is equal to another DecimalStyle. 314 * 315 * @param obj the object to check, null returns false 316 * @return true if this is equal to the other date 317 */ 318 override 319 public bool opEquals(Object obj) { 320 if (this is obj) { 321 return true; 322 } 323 if (cast(DecimalStyle)(obj) !is null) { 324 DecimalStyle other = cast(DecimalStyle) obj; 325 return (zeroDigit == other.zeroDigit && positiveSign == other.positiveSign && 326 negativeSign == other.negativeSign && decimalSeparator == other.decimalSeparator); 327 } 328 return false; 329 } 330 331 /** 332 * A hash code for this DecimalStyle. 333 * 334 * @return a suitable hash code 335 */ 336 override 337 public size_t toHash() @trusted nothrow { 338 return zeroDigit + positiveSign + negativeSign + decimalSeparator; 339 } 340 341 //----------------------------------------------------------------------- 342 /** 343 * Returns a string describing this DecimalStyle. 344 * 345 * @return a string description, not null 346 */ 347 override 348 public string toString() { 349 return "DecimalStyle[" ~ zeroDigit ~ positiveSign ~ negativeSign ~ decimalSeparator ~ "]"; 350 } 351 352 }