View Javadoc
1   /*
2    * Oceanus: Java Utilities
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.oceanus.decimal;
18  
19  import io.github.tonywasher.joceanus.oceanus.base.OceanusLocale;
20  
21  import java.text.DecimalFormat;
22  import java.text.DecimalFormatSymbols;
23  import java.text.NumberFormat;
24  import java.util.Currency;
25  import java.util.HashMap;
26  import java.util.Locale;
27  import java.util.Map;
28  
29  /**
30   * Locale constants.
31   */
32  public class OceanusDecimalLocale {
33      /**
34       * The dollar.
35       */
36      private static final String DOLLAR = "$";
37  
38      /**
39       * The pound.
40       */
41      private static final String POUND = "£";
42  
43      /**
44       * The locale.
45       */
46      private final Locale theLocale;
47  
48      /**
49       * The currencies map.
50       */
51      private final Map<String, Currency> theCurrencyMap;
52  
53      /**
54       * The currency symbols map.
55       */
56      private final Map<String, String> theSymbolMap;
57  
58      /**
59       * The grouping size.
60       */
61      private final int theGroupingSize;
62  
63      /**
64       * The grouping separator.
65       */
66      private final String theGrouping;
67  
68      /**
69       * The minus sign.
70       */
71      private final char theMinusSign;
72  
73      /**
74       * The perCent symbol.
75       */
76      private final char thePerCent;
77  
78      /**
79       * The perMille symbol.
80       */
81      private final char thePerMille;
82  
83      /**
84       * The decimal separator.
85       */
86      private final String theDecimal;
87  
88      /**
89       * The money decimal separator.
90       */
91      private final String theMoneyDecimal;
92  
93      /**
94       * The default currency.
95       */
96      private final Currency theCurrency;
97  
98      /**
99       * Constructor.
100      */
101     protected OceanusDecimalLocale() {
102         /* Use default locale */
103         this(OceanusLocale.getDefaultLocale());
104     }
105 
106     /**
107      * Constructor.
108      *
109      * @param pLocale the locale
110      */
111     protected OceanusDecimalLocale(final Locale pLocale) {
112         /* Default to UK locale if the locale only has a pseudo-currency */
113         DecimalFormatSymbols mySymbols = DecimalFormatSymbols.getInstance(pLocale);
114         final Currency myCurrency = mySymbols.getCurrency();
115         theLocale = myCurrency.getDefaultFractionDigits() == -1
116                 ? Locale.UK
117                 : pLocale;
118 
119         /* Create currency maps */
120         theCurrencyMap = new HashMap<>();
121         theSymbolMap = new HashMap<>();
122 
123         /* Access decimal formats */
124         mySymbols = DecimalFormatSymbols.getInstance(theLocale);
125         final DecimalFormat myFormat = (DecimalFormat) NumberFormat.getInstance(theLocale);
126         theGroupingSize = myFormat.getGroupingSize();
127 
128         /* Access various interesting formats */
129         theMinusSign = mySymbols.getMinusSign();
130         thePerCent = mySymbols.getPercent();
131         thePerMille = mySymbols.getPerMill();
132         theGrouping = Character.toString(mySymbols.getGroupingSeparator());
133         theDecimal = Character.toString(mySymbols.getDecimalSeparator());
134         theMoneyDecimal = Character.toString(mySymbols.getMonetaryDecimalSeparator());
135 
136         /* Access the default currency */
137         theCurrency = mySymbols.getCurrency();
138         final String myCurrSymbol = theCurrency.getSymbol(theLocale);
139         declareSymbol(myCurrSymbol, theCurrency);
140 
141         /* Declare simplified USD/GBP if possible */
142         if (!DOLLAR.equals(myCurrSymbol)) {
143             declareSymbol(DOLLAR, Currency.getInstance(Locale.US));
144         }
145         if (!POUND.equals(myCurrSymbol)) {
146             declareSymbol(POUND, Currency.getInstance(Locale.UK));
147         }
148     }
149 
150     /**
151      * Obtain the grouping size.
152      *
153      * @return the size
154      */
155     protected int getGroupingSize() {
156         return theGroupingSize;
157     }
158 
159     /**
160      * Obtain the grouping string.
161      *
162      * @return the string
163      */
164     protected String getGrouping() {
165         return theGrouping;
166     }
167 
168     /**
169      * Obtain the minus sign.
170      *
171      * @return the sign
172      */
173     protected char getMinusSign() {
174         return theMinusSign;
175     }
176 
177     /**
178      * Obtain the perCent sign.
179      *
180      * @return the sign
181      */
182     protected char getPerCent() {
183         return thePerCent;
184     }
185 
186     /**
187      * Obtain the perMille sign.
188      *
189      * @return the sign
190      */
191     protected char getPerMille() {
192         return thePerMille;
193     }
194 
195     /**
196      * Obtain the decimal string.
197      *
198      * @return the string
199      */
200     protected String getDecimal() {
201         return theDecimal;
202     }
203 
204     /**
205      * Obtain the grouping string.
206      *
207      * @return the string
208      */
209     protected String getMoneyDecimal() {
210         return theMoneyDecimal;
211     }
212 
213     /**
214      * Obtain the default currency.
215      *
216      * @return the currency
217      */
218     protected Currency getDefaultCurrency() {
219         return theCurrency;
220     }
221 
222     /**
223      * Parse currency symbol.
224      *
225      * @param pSymbol the symbol
226      * @return the currency
227      * @throws IllegalArgumentException on invalid currency
228      */
229     protected Currency parseCurrencySymbol(final String pSymbol) {
230         /* Look for the currency in the map */
231         Currency myCurrency = theCurrencyMap.get(pSymbol);
232 
233         /* If this is a new currency */
234         if (myCurrency == null) {
235             /* Loop through all the currencies */
236             for (Currency myCurr : Currency.getAvailableCurrencies()) {
237                 /* If the symbol matches */
238                 if (pSymbol.equals(myCurr.getSymbol(theLocale))) {
239                     /* Record currency and break the loop */
240                     myCurrency = myCurr;
241                     declareSymbol(pSymbol, myCurrency);
242                     break;
243                 }
244             }
245 
246             /* If we did not find a currency */
247             if (myCurrency == null) {
248                 /* Reject the currency */
249                 throw new IllegalArgumentException("Invalid currency: "
250                         + pSymbol);
251             }
252         }
253 
254         /* Return the currency */
255         return myCurrency;
256     }
257 
258     /**
259      * Declare symbol.
260      *
261      * @param pSymbol   the symbol
262      * @param pCurrency the currency
263      */
264     private void declareSymbol(final String pSymbol,
265                                final Currency pCurrency) {
266         /* Store in currency map */
267         theCurrencyMap.put(pSymbol, pCurrency);
268 
269         /* Store symbol if not already declared */
270         final String myCode = pCurrency.getCurrencyCode();
271         theSymbolMap.computeIfAbsent(myCode, c -> pSymbol);
272     }
273 
274     /**
275      * Get currency symbol.
276      *
277      * @param pCurrency the currency
278      * @return the symbol
279      */
280     protected String getSymbol(final Currency pCurrency) {
281         /* Look for the currency in the map */
282         final String mySymbol = theSymbolMap.get(pCurrency.getCurrencyCode());
283         return mySymbol == null
284                 ? pCurrency.getSymbol(theLocale)
285                 : mySymbol;
286     }
287 }