11package org .unify4j .common ;
22
3+ import java .math .BigDecimal ;
4+ import java .math .BigInteger ;
5+ import java .text .DecimalFormat ;
6+ import java .text .NumberFormat ;
7+ import java .util .Locale ;
8+
39public class Numeric4j {
4- }
10+ public static final BigInteger BIG_INT_LONG_MIN = BigInteger .valueOf (Long .MIN_VALUE );
11+ public static final BigInteger BIG_INT_LONG_MAX = BigInteger .valueOf (Long .MAX_VALUE );
12+ public static final BigDecimal BIG_DEC_DOUBLE_MIN = BigDecimal .valueOf (-Double .MAX_VALUE );
13+ public static final BigDecimal BIG_DEC_DOUBLE_MAX = BigDecimal .valueOf (Double .MAX_VALUE );
14+
15+ /**
16+ * Formats a given amount as currency according to the specified locale.
17+ *
18+ * @param amount The amount to be formatted.
19+ * @param locale The locale to be used for currency formatting.
20+ * @return A string representing the formatted currency.
21+ */
22+ public static String currency (double amount , Locale locale ) {
23+ NumberFormat instance = NumberFormat .getCurrencyInstance (locale );
24+ return instance .format (amount );
25+ }
26+
27+ /**
28+ * Formats a given amount as currency using specified language and country codes.
29+ *
30+ * @param amount The amount to be formatted.
31+ * @param language The language code for the locale.
32+ * @param country The country code for the locale.
33+ * @return A string representing the formatted currency.
34+ */
35+ public static String currency (double amount , String language , String country ) {
36+ Locale locale ;
37+ if (String4j .isAnyEmpty (language , country )) {
38+ locale = Locale .getDefault ();
39+ } else {
40+ locale = new Locale (language , country );
41+ }
42+ return currency (amount , locale );
43+ }
44+
45+ /**
46+ * Formats a given amount as currency according to the specified locale and grouping usage.
47+ *
48+ * @param amount The amount to be formatted.
49+ * @param locale The locale to be used for currency formatting.
50+ * @param isGroup Whether to use grouping separators.
51+ * @return A string representing the formatted currency.
52+ */
53+ public static String currency (Double amount , Locale locale , boolean isGroup ) {
54+ String text = "" ;
55+ if (Object4j .allNotNull (amount , locale )) {
56+ NumberFormat instance = NumberFormat .getCurrencyInstance (locale );
57+ instance .setGroupingUsed (isGroup );
58+ text = instance .format (amount );
59+ }
60+ return text ;
61+ }
62+
63+ /**
64+ * Formats a given amount as currency according to the specified locale and grouping usage.
65+ *
66+ * @param amount The amount to be formatted.
67+ * @param locale The locale to be used for integer formatting.
68+ * @param isGroup Whether to use grouping separators.
69+ * @return A string representing the formatted currency.
70+ */
71+ public static String currency (Long amount , Locale locale , boolean isGroup ) {
72+ String text = "" ;
73+ if (Object4j .allNotNull (amount , locale )) {
74+ NumberFormat instance = NumberFormat .getIntegerInstance (locale );
75+ instance .setGroupingUsed (isGroup );
76+ text = instance .format (amount );
77+ }
78+ return text ;
79+ }
80+
81+ /**
82+ * Formats a given amount as currency according to the specified precision, pattern, and locale.
83+ *
84+ * @param amount The amount to be formatted.
85+ * @param precision The number of decimal places to display.
86+ * @param pattern The pattern to be applied for formatting.
87+ * @param locale The locale to be used for currency formatting.
88+ * @return A string representing the formatted currency.
89+ */
90+ public static String currency (double amount , int precision , String pattern , Locale locale ) {
91+ NumberFormat instance = NumberFormat .getCurrencyInstance (locale );
92+ DecimalFormat format = (DecimalFormat ) instance ;
93+ format .setMinimumFractionDigits (precision );
94+ format .setMaximumFractionDigits (precision );
95+ format .setDecimalSeparatorAlwaysShown (true );
96+ format .applyPattern (pattern );
97+ return format .format (amount );
98+ }
99+
100+ /**
101+ * Formats a given amount as currency according to the specified precision and locale.
102+ *
103+ * @param amount The amount to be formatted.
104+ * @param precision The number of decimal places to display.
105+ * @param locale The locale to be used for currency formatting.
106+ * @return A string representing the formatted currency.
107+ */
108+ public static String currency (double amount , int precision , Locale locale ) {
109+ NumberFormat instance = NumberFormat .getCurrencyInstance (locale );
110+ instance .setMinimumFractionDigits (precision );
111+ instance .setMaximumFractionDigits (precision );
112+ return instance .format (amount );
113+ }
114+
115+ /**
116+ * Calculate the minimum value from an array of values.
117+ *
118+ * @param values Array of values.
119+ * @return minimum value of the provided set.
120+ */
121+ public static long minimum (long ... values ) {
122+ final int len = values .length ;
123+ long current = values [0 ];
124+ for (int i = 1 ; i < len ; i ++) {
125+ current = Math .min (values [i ], current );
126+ }
127+ return current ;
128+ }
129+
130+ /**
131+ * Calculate the minimum value from an array of values.
132+ *
133+ * @param values Array of values.
134+ * @return minimum value of the provided set.
135+ */
136+ public static long maximum (long ... values ) {
137+ final int len = values .length ;
138+ long current = values [0 ];
139+ for (int i = 1 ; i < len ; i ++) {
140+ current = Math .max (values [i ], current );
141+ }
142+ return current ;
143+ }
144+
145+ /**
146+ * Calculate the minimum value from an array of values.
147+ *
148+ * @param values Array of values.
149+ * @return minimum value of the provided set.
150+ */
151+ public static double minimum (double ... values ) {
152+ final int len = values .length ;
153+ double current = values [0 ];
154+ for (int i = 1 ; i < len ; i ++) {
155+ current = Math .min (values [i ], current );
156+ }
157+ return current ;
158+ }
159+
160+ /**
161+ * Calculate the minimum value from an array of values.
162+ *
163+ * @param values Array of values.
164+ * @return minimum value of the provided set.
165+ */
166+ public static double maximum (double ... values ) {
167+ final int len = values .length ;
168+ double current = values [0 ];
169+ for (int i = 1 ; i < len ; i ++) {
170+ current = Math .max (values [i ], current );
171+ }
172+ return current ;
173+ }
174+
175+ /**
176+ * Calculate the minimum value from an array of values.
177+ *
178+ * @param values Array of values.
179+ * @return minimum value of the provided set.
180+ */
181+ public static BigInteger minimum (BigInteger ... values ) {
182+ final int len = values .length ;
183+ if (len == 1 ) {
184+ if (values [0 ] == null ) {
185+ throw new IllegalArgumentException ("Cannot passed null BigInteger entry to minimum()" );
186+ }
187+ return values [0 ];
188+ }
189+ BigInteger current = values [0 ];
190+ for (int i = 1 ; i < len ; i ++) {
191+ if (values [i ] == null ) {
192+ throw new IllegalArgumentException ("Cannot passed null BigInteger entry to minimum()" );
193+ }
194+ current = values [i ].min (current );
195+ }
196+ return current ;
197+ }
198+
199+ /**
200+ * Calculate the minimum value from an array of values.
201+ *
202+ * @param values Array of values.
203+ * @return minimum value of the provided set.
204+ */
205+ public static BigInteger maximum (BigInteger ... values ) {
206+ final int len = values .length ;
207+ if (len == 1 ) {
208+ if (values [0 ] == null ) {
209+ throw new IllegalArgumentException ("Cannot passed null BigInteger entry to maximum()" );
210+ }
211+ return values [0 ];
212+ }
213+ BigInteger current = values [0 ];
214+ for (int i = 1 ; i < len ; i ++) {
215+ if (values [i ] == null ) {
216+ throw new IllegalArgumentException ("Cannot passed null BigInteger entry to maximum()" );
217+ }
218+ current = values [i ].max (current );
219+ }
220+ return current ;
221+ }
222+
223+ /**
224+ * Calculate the minimum value from an array of values.
225+ *
226+ * @param values Array of values.
227+ * @return minimum value of the provided set.
228+ */
229+ public static BigDecimal minimum (BigDecimal ... values ) {
230+ final int len = values .length ;
231+ if (len == 1 ) {
232+ if (values [0 ] == null ) {
233+ throw new IllegalArgumentException ("Cannot passed null BigDecimal entry to minimum()" );
234+ }
235+ return values [0 ];
236+ }
237+ BigDecimal current = values [0 ];
238+ for (int i = 1 ; i < len ; i ++) {
239+ if (values [i ] == null ) {
240+ throw new IllegalArgumentException ("Cannot passed null BigDecimal entry to minimum()" );
241+ }
242+ current = values [i ].min (current );
243+ }
244+ return current ;
245+ }
246+
247+ /**
248+ * Calculate the maximum value from an array of values.
249+ *
250+ * @param values Array of values.
251+ * @return maximum value of the provided set.
252+ */
253+ public static BigDecimal maximum (BigDecimal ... values ) {
254+ final int len = values .length ;
255+ if (len == 1 ) {
256+ if (values [0 ] == null ) {
257+ throw new IllegalArgumentException ("Cannot pass null BigDecimal entry to maximum()" );
258+ }
259+ return values [0 ];
260+ }
261+ BigDecimal current = values [0 ];
262+ for (int i = 1 ; i < len ; i ++) {
263+ if (values [i ] == null ) {
264+ throw new IllegalArgumentException ("Cannot pass null BigDecimal entry to maximum()" );
265+ }
266+ current = values [i ].max (current );
267+ }
268+ return current ;
269+ }
270+
271+ /**
272+ * Parse the passed in String as a numeric value and return the minimal data type between Long, Double,
273+ * BigDecimal, or BigInteger. Useful for processing values from JSON files.
274+ *
275+ * @param numStr String to parse.
276+ * @return Long, BigInteger, Double, or BigDecimal depending on the value. If the value is and integer and
277+ * between the range of Long min/max, a Long is returned. If the value is an integer and outside this range, a
278+ * BigInteger is returned. If the value is a decimal but within the confines of a Double, then a Double is
279+ * returned, otherwise a BigDecimal is returned.
280+ */
281+ public static Number tryNumeric (String numStr ) {
282+ boolean isNegative = numStr .startsWith ("-" ); // Handle and preserve negative signs correctly while removing leading zeros
283+ if (isNegative || numStr .startsWith ("+" )) {
284+ char sign = numStr .charAt (0 );
285+ numStr = sign + numStr .substring (1 ).replaceFirst ("^0+" , "" );
286+ } else {
287+ numStr = numStr .replaceFirst ("^0+" , "" );
288+ }
289+ boolean hasDecimalPoint = false ;
290+ boolean hasExponent = false ;
291+ int mantissaSize = 0 ;
292+ StringBuilder exponentValue = new StringBuilder ();
293+ int len = numStr .length ();
294+ for (int i = 0 ; i < len ; i ++) {
295+ char c = numStr .charAt (i );
296+ if (c == '.' ) {
297+ hasDecimalPoint = true ;
298+ } else if (c == 'e' || c == 'E' ) {
299+ hasExponent = true ;
300+ } else if (c >= '0' && c <= '9' ) {
301+ if (!hasExponent ) {
302+ mantissaSize ++; // Count digits in the mantissa only
303+ } else {
304+ exponentValue .append (c );
305+ }
306+ }
307+ }
308+ if (hasDecimalPoint || hasExponent ) {
309+ if (mantissaSize < 17 && (exponentValue .length () == 0 || Math .abs (Integer .parseInt (exponentValue .toString ())) < 308 )) {
310+ return Double .parseDouble (numStr );
311+ } else {
312+ return new BigDecimal (numStr );
313+ }
314+ } else {
315+ if (numStr .length () < 19 ) {
316+ return Long .parseLong (numStr );
317+ }
318+ BigInteger bigInt = new BigInteger (numStr );
319+ if (bigInt .compareTo (BIG_INT_LONG_MIN ) >= 0 && bigInt .compareTo (BIG_INT_LONG_MAX ) <= 0 ) {
320+ return bigInt .longValue (); // Correctly convert BigInteger back to Long if within range
321+ } else {
322+ return bigInt ;
323+ }
324+ }
325+ }
326+ }
0 commit comments