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.util.Currency;
22 import java.util.Locale;
23
24 /**
25 * Parsing methods for decimals in a particular locale.
26 *
27 * @author Tony Washer
28 */
29 public class OceanusDecimalParser {
30 /**
31 * Parse Error message.
32 */
33 private static final String ERROR_PARSE = "Non Decimal Numeric Value: ";
34
35 /**
36 * Bounds Error message.
37 */
38 private static final String ERROR_BOUNDS = "Value out of range: ";
39
40 /**
41 * The locale.
42 */
43 private OceanusDecimalLocale theLocale;
44
45 /**
46 * Do we use strict # of decimals?
47 */
48 private boolean useStrictDecimals = true;
49
50 /**
51 * Constructor.
52 */
53 public OceanusDecimalParser() {
54 /* Use default locale */
55 this(OceanusLocale.getDefaultLocale());
56 }
57
58 /**
59 * Constructor.
60 *
61 * @param pLocale the locale
62 */
63 public OceanusDecimalParser(final Locale pLocale) {
64 /* Store locale */
65 setLocale(pLocale);
66 }
67
68 /**
69 * Should we parse to strict decimals.
70 *
71 * @param bStrictDecimals true/false
72 */
73 public void setStrictDecimals(final boolean bStrictDecimals) {
74 /* Set accounting mode on and set the width */
75 useStrictDecimals = bStrictDecimals;
76 }
77
78 /**
79 * Set the locale.
80 *
81 * @param pLocale the locale
82 */
83 public final void setLocale(final Locale pLocale) {
84 /* Store the locale */
85 theLocale = new OceanusDecimalLocale(pLocale);
86 }
87
88 /**
89 * Obtain the default currency.
90 *
91 * @return the default currency
92 */
93 public final Currency getDefaultCurrency() {
94 return theLocale.getDefaultCurrency();
95 }
96
97 /**
98 * Parse a string into a decimal.
99 *
100 * @param pValue The value to parse.
101 * @return the parsed decimal
102 * @throws IllegalArgumentException on invalid decimal
103 */
104 public static OceanusDecimal parseDecimalValue(final String pValue) {
105 final OceanusDecimal myDecimal = new OceanusDecimal();
106 parseDecimalValue(pValue, myDecimal);
107 return myDecimal;
108 }
109
110 /**
111 * Parse a string into a decimal.
112 *
113 * @param pValue The value to parse.
114 * @param pResult the decimal to hold the result in
115 * @throws IllegalArgumentException on invalid decimal
116 */
117 protected static void parseDecimalValue(final String pValue,
118 final OceanusDecimal pResult) {
119 parseDecimalValue(pValue, OceanusDecimalFormatter.LOCALE_DEFAULT, false, pResult);
120 }
121
122 /**
123 * Parse a string into a decimal.
124 *
125 * @param pValue The value to parse.
126 * @param pLocale the Decimal locale
127 * @param useMoneyDecimal use money decimal rather than standard decimal true/false
128 * @param pResult the decimal to hold the result in
129 * @throws IllegalArgumentException on invalid decimal
130 */
131 protected static void parseDecimalValue(final String pValue,
132 final OceanusDecimalLocale pLocale,
133 final boolean useMoneyDecimal,
134 final OceanusDecimal pResult) {
135 /* Handle null value */
136 if (pValue == null) {
137 throw new IllegalArgumentException();
138 }
139
140 /* Create a working copy */
141 final StringBuilder myWork = new StringBuilder(pValue.trim());
142
143 /* If the value is negative, strip the leading minus sign */
144 final boolean isNegative = !myWork.isEmpty()
145 && myWork.charAt(0) == pLocale.getMinusSign();
146 if (isNegative) {
147 myWork.deleteCharAt(0);
148 }
149
150 /* Remove any grouping characters from the value */
151 final String myGrouping = pLocale.getGrouping();
152 int myPos;
153 while (true) {
154 myPos = myWork.indexOf(myGrouping);
155 if (myPos == -1) {
156 break;
157 }
158 myWork.deleteCharAt(myPos);
159 }
160
161 /* Trim leading and trailing blanks again */
162 trimBuffer(myWork);
163
164 /* Locate the exponent if present */
165 int myExponent = 0;
166 myPos = myWork.indexOf("e");
167 if (myPos != -1) {
168 /* Obtain the exponent and remove from decimals */
169 final String myExp = myWork.substring(myPos + 1);
170 myWork.setLength(myPos);
171
172 /* Parse the integral part */
173 try {
174 myExponent = Integer.parseInt(myExp);
175 } catch (NumberFormatException e) {
176 throw new IllegalArgumentException(ERROR_PARSE
177 + pValue, e);
178 }
179 }
180
181 /* Locate the decimal point if present */
182 myPos = myWork.indexOf(useMoneyDecimal
183 ? pLocale.getMoneyDecimal()
184 : pLocale.getDecimal());
185
186 /* Assume no decimals */
187 StringBuilder myDecimals = null;
188 int myScale = 0;
189
190 /* If we have a decimal point */
191 if (myPos != -1) {
192 /* Split into the two parts being careful of a trailing decimal point */
193 if ((myPos + 1) < myWork.length()) {
194 myDecimals = new StringBuilder(myWork.substring(myPos + 1));
195 }
196 myWork.setLength(myPos);
197 }
198
199 /* If we have a positive exponent */
200 if (myExponent > 0) {
201 /* Determine the number of decimals */
202 int myNumDec = myDecimals == null
203 ? 0
204 : myDecimals.length();
205
206 /* Shift decimals across */
207 while (myExponent > 0 && myNumDec > 0) {
208 /* Copy decimal across */
209 final char myChar = myDecimals.charAt(0);
210 myDecimals.deleteCharAt(0);
211 myWork.append(myChar);
212
213 /* Adjust counters */
214 myExponent--;
215 myNumDec--;
216 }
217
218 /* Finish off with zeroes */
219 while (myExponent > 0) {
220 myWork.append(OceanusDecimalConstants.CHAR_ZERO);
221 myExponent--;
222 }
223
224 /* If we now have no decimals remove decimal indication */
225 if (myNumDec == 0) {
226 myDecimals = null;
227 }
228 /* If we have a negative exponent */
229 } else if (myExponent < 0) {
230 /* Determine the number of integer digits */
231 int myNumDigits = myWork.length();
232 final StringBuilder myCopy = new StringBuilder();
233
234 /* Shift decimals across */
235 while (myExponent < 0 && myNumDigits > 0) {
236 /* Copy digit across */
237 final char myChar = myWork.charAt(myNumDigits - 1);
238 myWork.deleteCharAt(myNumDigits - 1);
239 myCopy.insert(0, myChar);
240
241 /* Adjust counters */
242 myExponent++;
243 myNumDigits--;
244 }
245
246 /* Finish off with zeroes */
247 while (myExponent < 0) {
248 myCopy.insert(0, OceanusDecimalConstants.CHAR_ZERO);
249 myExponent++;
250 }
251
252 /* If we have decimals already */
253 if (myDecimals != null) {
254 myDecimals.insert(0, myCopy);
255 } else {
256 myDecimals = myCopy;
257 }
258 }
259
260 /* Handle leading decimal point on value */
261 if (myWork.isEmpty()) {
262 myWork.append(OceanusDecimalConstants.CHAR_ZERO);
263 }
264
265 /* Parse the integral part */
266 long myValue;
267 try {
268 myValue = Long.parseLong(myWork.toString());
269 } catch (NumberFormatException e) {
270 throw new IllegalArgumentException(ERROR_PARSE
271 + pValue, e);
272 }
273
274 /* If we have a decimal part */
275 if (myDecimals != null) {
276 /* If we have too many decimals */
277 char myLastDigit = OceanusDecimalConstants.CHAR_ZERO;
278 myScale = myDecimals.length();
279 if (myScale > OceanusDecimal.MAX_DECIMALS) {
280 /* Extract most significant trailing digit and truncate the value */
281 myLastDigit = myDecimals.charAt(OceanusDecimal.MAX_DECIMALS);
282 myDecimals.setLength(OceanusDecimal.MAX_DECIMALS);
283 myScale = myDecimals.length();
284 }
285
286 /* Adjust the value to make room for the decimals */
287 myValue *= OceanusDecimal.getFactor(myScale);
288
289 /* Parse the decimals */
290 try {
291 myValue += Long.parseLong(myDecimals.toString());
292 } catch (NumberFormatException e) {
293 throw new IllegalArgumentException(ERROR_PARSE
294 + pValue, e);
295 }
296
297 /* Round value according to most significant discarded decimal digit */
298 if (myLastDigit >= Character.forDigit(OceanusDecimal.RADIX_TEN >> 1, OceanusDecimal.RADIX_TEN)) {
299 myValue++;
300 }
301 }
302
303 /* If the value is negative, negate the number */
304 if (isNegative) {
305 myValue = -myValue;
306 }
307
308 /* Store the result into the decimal */
309 pResult.setValue(myValue, myScale);
310 }
311
312 /**
313 * Adjust to desired decimals.
314 *
315 * @param pValue the value to adjust
316 * @param pDecimals the desired decimals
317 */
318 private void adjustDecimals(final OceanusDecimal pValue,
319 final int pDecimals) {
320 /* If we are using strict decimals */
321 if (useStrictDecimals) {
322 /* Correct the scale */
323 pValue.adjustToScale(pDecimals);
324
325 /* else we should honour what we can */
326 } else {
327 /* Calculate the standard correction */
328 final int myAdjust = pDecimals
329 - pValue.scale();
330
331 /* If we have too few decimals */
332 if (myAdjust > 0) {
333 /* Adjust the value appropriately */
334 pValue.movePointLeft(myAdjust);
335
336 /* else if we have too many */
337 } else if (myAdjust < 0) {
338 /* remove redundant decimal places */
339 pValue.reduceScale(pDecimals);
340 }
341 }
342 }
343
344 /**
345 * Parse a string to extract currency information.
346 *
347 * @param pWork the buffer to parse
348 * @param pDeemedCurrency the assumed currency if no currency identifier
349 * @return the parsed currency
350 * @throws IllegalArgumentException on invalid currency
351 */
352 private Currency parseCurrency(final StringBuilder pWork,
353 final Currency pDeemedCurrency) {
354 /* Look for a currency separator */
355 final int iPos = pWork.indexOf(OceanusDecimalConstants.STR_CURRSEP);
356 if (iPos > -1) {
357 /* Extract currency detail and determine currency */
358 final String myCurr = pWork.substring(0, iPos);
359 pWork.delete(0, iPos + 1);
360 return Currency.getInstance(myCurr);
361 }
362
363 /* Set default currency */
364 Currency myCurrency = pDeemedCurrency;
365 final char myMinus = theLocale.getMinusSign();
366
367 /* If we have a leading minus sign */
368 int iNumChars = pWork.length();
369 boolean isNegative = false;
370 if ((iNumChars > 0)
371 && (pWork.charAt(0) == myMinus)) {
372 /* Delete it and note the presence */
373 pWork.deleteCharAt(0);
374 iNumChars--;
375 isNegative = true;
376 }
377
378 /* Look for currency symbol as leading non-digits and non-whitespace */
379 int iNumSymbols = 0;
380 while (iNumSymbols < iNumChars) {
381 final char c = pWork.charAt(iNumSymbols);
382 if (Character.isDigit(c)
383 || (c == OceanusDecimalConstants.CHAR_MINUS)
384 || Character.isWhitespace(c)) {
385 break;
386 }
387 iNumSymbols++;
388 }
389
390 /* If we have a symbol */
391 if (iNumSymbols > 0) {
392 /* Extract Symbol from buffer */
393 final String mySymbol = pWork.substring(0, iNumSymbols);
394 pWork.delete(0, iNumSymbols);
395
396 /* Parse the currency symbol */
397 myCurrency = theLocale.parseCurrencySymbol(mySymbol);
398 }
399
400 /* If we were negative */
401 if (isNegative) {
402 /* Reinsert the minus sign */
403 pWork.insert(0, myMinus);
404 }
405
406 /* Return the currency */
407 return myCurrency;
408 }
409
410 /**
411 * Parse a long value.
412 *
413 * @param pValue The value to parse.
414 * @param pLocale the Decimal locale
415 * @return the long value
416 * @throws IllegalArgumentException on invalid decimal
417 */
418 protected static long parseLongValue(final String pValue,
419 final OceanusDecimalLocale pLocale) {
420 /* Handle null value */
421 if (pValue == null) {
422 throw new IllegalArgumentException();
423 }
424
425 /* Create a working copy */
426 final StringBuilder myWork = new StringBuilder(pValue.trim());
427
428 /* If the value is negative, strip the leading minus sign */
429 final boolean isNegative = !myWork.isEmpty()
430 && myWork.charAt(0) == pLocale.getMinusSign();
431 if (isNegative) {
432 myWork.deleteCharAt(0);
433 }
434
435 /* Remove any grouping characters from the value */
436 final String myGrouping = pLocale.getGrouping();
437 int myPos;
438 while (true) {
439 myPos = myWork.indexOf(myGrouping);
440 if (myPos == -1) {
441 break;
442 }
443 myWork.deleteCharAt(myPos);
444 }
445
446 /* Trim leading and trailing blanks again */
447 trimBuffer(myWork);
448
449 /* Parse the long value */
450 long myValue;
451 try {
452 myValue = Long.parseLong(myWork.toString());
453 } catch (NumberFormatException e) {
454 throw new IllegalArgumentException(ERROR_PARSE
455 + pValue, e);
456 }
457
458 /* If the value is negative, negate the number */
459 if (isNegative) {
460 myValue = -myValue;
461 }
462
463 /* return the result */
464 return myValue;
465 }
466
467 /**
468 * Obtain a new zero money value for the default currency.
469 *
470 * @return the new money
471 */
472 public OceanusMoney zeroMoney() {
473 return new OceanusMoney(theLocale.getDefaultCurrency());
474 }
475
476 /**
477 * Obtain a new zero money value for the currency.
478 *
479 * @param pCurrency the currency
480 * @return the new money
481 */
482 public OceanusMoney zeroMoney(final Currency pCurrency) {
483 return new OceanusMoney(pCurrency);
484 }
485
486 /**
487 * Parse Money value.
488 *
489 * @param pValue the string value to parse.
490 * @return the parsed money
491 * @throws IllegalArgumentException on invalid money value
492 */
493 public OceanusMoney parseMoneyValue(final String pValue) {
494 return parseMoneyValue(pValue, null);
495 }
496
497 /**
498 * Parse Money value.
499 *
500 * @param pValue the string value to parse.
501 * @param pDeemedCurrency the assumed currency if no currency identifier
502 * @return the parsed money
503 * @throws IllegalArgumentException on invalid money value
504 */
505 public OceanusMoney parseMoneyValue(final String pValue,
506 final Currency pDeemedCurrency) {
507 /* Handle null value */
508 if (pValue == null) {
509 return null;
510 }
511
512 /* Create a working trimmed copy */
513 final StringBuilder myWork = new StringBuilder(pValue.trim());
514
515 /* Determine currency */
516 final Currency myCurrency = parseCurrency(myWork, pDeemedCurrency == null
517 ? getDefaultCurrency()
518 : pDeemedCurrency);
519 final char myMinus = theLocale.getMinusSign();
520
521 /* If we have a leading minus sign */
522 if (!myWork.isEmpty()
523 && myWork.charAt(0) == myMinus) {
524 /* Ensure there is no whitespace between minus sign and number */
525 myWork.deleteCharAt(0);
526 trimBuffer(myWork);
527 myWork.insert(0, myMinus);
528 }
529
530 /* Create the new Money object */
531 final OceanusMoney myMoney = new OceanusMoney(myCurrency);
532
533 /* Parse the remaining string */
534 parseDecimalValue(myWork.toString(), theLocale, true, myMoney);
535
536 /* Correct the scale */
537 adjustDecimals(myMoney, myCurrency.getDefaultFractionDigits());
538
539 /* return the parsed money object */
540 return myMoney;
541 }
542
543 /**
544 * Obtain a new zero price value for the default currency.
545 *
546 * @return the new price
547 */
548 public OceanusPrice zeroPrice() {
549 return new OceanusPrice(theLocale.getDefaultCurrency());
550 }
551
552 /**
553 * Obtain a new zero price value for the currency.
554 *
555 * @param pCurrency the currency
556 * @return the new price
557 */
558 public OceanusPrice zeroPrice(final Currency pCurrency) {
559 return new OceanusPrice(pCurrency);
560 }
561
562 /**
563 * Parse Price value.
564 *
565 * @param pValue the string value to parse.
566 * @return the parsed price
567 * @throws IllegalArgumentException on invalid price value
568 */
569 public OceanusPrice parsePriceValue(final String pValue) {
570 return parsePriceValue(pValue, null);
571 }
572
573 /**
574 * Parse Price value.
575 *
576 * @param pValue the string value to parse.
577 * @param pDeemedCurrency the assumed currency if no currency identifier
578 * @return the parsed price
579 * @throws IllegalArgumentException on invalid price value
580 */
581 public OceanusPrice parsePriceValue(final String pValue,
582 final Currency pDeemedCurrency) {
583 /* Handle null value */
584 if (pValue == null) {
585 return null;
586 }
587
588 /* Create a working trimmed copy */
589 final StringBuilder myWork = new StringBuilder(pValue.trim());
590
591 /* Look for explicit currency */
592 final Currency myCurrency = parseCurrency(myWork, pDeemedCurrency == null
593 ? getDefaultCurrency()
594 : pDeemedCurrency);
595 final char myMinus = theLocale.getMinusSign();
596
597 /* If we have a leading minus sign */
598 if (myWork.charAt(0) == myMinus) {
599 /* Ensure there is no whitespace between minus sign and number */
600 myWork.deleteCharAt(0);
601 trimBuffer(myWork);
602 myWork.insert(0, myMinus);
603 }
604
605 /* Create the new Price object */
606 final OceanusPrice myPrice = new OceanusPrice(myCurrency);
607
608 /* Parse the remaining string */
609 parseDecimalValue(myWork.toString(), theLocale, true, myPrice);
610
611 /* Correct the scale */
612 adjustDecimals(myPrice, myCurrency.getDefaultFractionDigits()
613 + OceanusPrice.XTRA_DECIMALS);
614
615 /* return the parsed price object */
616 return myPrice;
617 }
618
619 /**
620 * Parse Rate value.
621 *
622 * @param pValue the string value to parse.
623 * @return the parsed rate
624 * @throws IllegalArgumentException on invalid rate value
625 */
626 public OceanusRate parseRateValue(final String pValue) {
627 /* Handle null value */
628 if (pValue == null) {
629 return null;
630 }
631
632 /* Create a working trimmed copy */
633 final StringBuilder myWork = new StringBuilder(pValue.trim());
634 int myXtraDecimals = 0;
635
636 /* If there is a trailing perCent, remove any percent sign from the end of the string */
637 final int myLast = myWork.length() - 1;
638 if (myWork.charAt(myLast) == theLocale.getPerCent()) {
639 myWork.deleteCharAt(myLast);
640 myXtraDecimals = OceanusDecimalConstants.ADJUST_PERCENT;
641
642 /*
643 * If there is a trailing perMille, remove any percent sign from the end of the string
644 */
645 } else if (myWork.charAt(myLast) == theLocale.getPerMille()) {
646 myWork.deleteCharAt(myLast);
647 myXtraDecimals = OceanusDecimalConstants.ADJUST_PERMILLE;
648 }
649
650 /* Create the new Rate object */
651 final OceanusRate myRate = new OceanusRate();
652
653 /* Parse the remaining string */
654 parseDecimalValue(myWork.toString(), theLocale, false, myRate);
655
656 /* If we have extra Decimals to add */
657 if (myXtraDecimals > 0) {
658 /* Adjust the value appropriately */
659 myRate.recordScale(myXtraDecimals
660 + myRate.scale());
661 }
662
663 /* Correct the scale */
664 adjustDecimals(myRate, OceanusRate.NUM_DECIMALS);
665
666 /* return the parsed rate object */
667 return myRate;
668 }
669
670 /**
671 * Parse Units value.
672 *
673 * @param pValue the string value to parse.
674 * @return the parsed units
675 * @throws IllegalArgumentException on invalid units value
676 */
677 public OceanusUnits parseUnitsValue(final String pValue) {
678 /* Handle null value */
679 if (pValue == null) {
680 return null;
681 }
682
683 /* Create the new Units object */
684 final OceanusUnits myUnits = new OceanusUnits();
685
686 /* Parse the remaining string */
687 parseDecimalValue(pValue.trim(), theLocale, false, myUnits);
688
689 /* Correct the scale */
690 adjustDecimals(myUnits, OceanusUnits.NUM_DECIMALS);
691
692 /* return the parsed units object */
693 return myUnits;
694 }
695
696 /**
697 * Parse Ratio value.
698 *
699 * @param pValue the string value to parse.
700 * @return the parsed ratio
701 * @throws IllegalArgumentException on invalid ratio value
702 */
703 public OceanusRatio parseRatioValue(final String pValue) {
704 /* Handle null value */
705 if (pValue == null) {
706 return null;
707 }
708
709 /* Create the new Ratio object */
710 final OceanusRatio myRatio = new OceanusRatio();
711
712 /* Parse the remaining string */
713 parseDecimalValue(pValue.trim(), theLocale, false, myRatio);
714
715 /* Correct the scale */
716 adjustDecimals(myRatio, OceanusRatio.NUM_DECIMALS);
717
718 /* return the parsed ratio object */
719 return myRatio;
720 }
721
722 /**
723 * Parse Decimal value.
724 *
725 * @param pValue the string value to parse.
726 * @param pScale the scale of the resulting decimal
727 * @return the parsed decimal
728 * @throws IllegalArgumentException on invalid decimal value
729 */
730 public OceanusDecimal parseDecimalValue(final String pValue,
731 final int pScale) {
732 /* Handle null value */
733 if (pValue == null) {
734 return null;
735 }
736
737 /* Create the new Decimal object */
738 final OceanusDecimal myDecimal = new OceanusDecimal();
739
740 /* Parse the remaining string */
741 parseDecimalValue(pValue.trim(), theLocale, false, myDecimal);
742 adjustDecimals(myDecimal, pScale);
743
744 /* return the parsed decimal object */
745 return myDecimal;
746 }
747
748 /**
749 * Parse Long value.
750 *
751 * @param pValue the string value to parse.
752 * @return the parsed value
753 * @throws IllegalArgumentException on invalid value
754 */
755 public Long parseLongValue(final String pValue) {
756 /* Handle null value */
757 if (pValue == null) {
758 return null;
759 }
760
761 /* Parse the value */
762 return parseLongValue(pValue, theLocale);
763 }
764
765 /**
766 * Parse Integer value.
767 *
768 * @param pValue the string value to parse.
769 * @return the parsed value
770 * @throws IllegalArgumentException on invalid value
771 */
772 public Integer parseIntegerValue(final String pValue) {
773 /* Handle null value */
774 if (pValue == null) {
775 return null;
776 }
777
778 /* Parse the value */
779 final long myValue = parseLongValue(pValue, theLocale);
780
781 /* Check bounds */
782 if (myValue > Integer.MAX_VALUE || myValue < Integer.MIN_VALUE) {
783 throw new IllegalArgumentException(ERROR_BOUNDS
784 + pValue);
785 }
786
787 /* Return value */
788 return (int) myValue;
789 }
790
791 /**
792 * Parse Short value.
793 *
794 * @param pValue the string value to parse.
795 * @return the parsed value
796 * @throws IllegalArgumentException on invalid value
797 */
798 public Short parseShortValue(final String pValue) {
799 /* Handle null value */
800 if (pValue == null) {
801 return null;
802 }
803
804 /* Parse the value */
805 final long myValue = parseLongValue(pValue, theLocale);
806
807 /* Check bounds */
808 if ((myValue > Short.MAX_VALUE) || (myValue < Short.MIN_VALUE)) {
809 throw new IllegalArgumentException(ERROR_BOUNDS
810 + pValue);
811 }
812
813 /* Return value */
814 return (short) myValue;
815 }
816
817 /**
818 * Trim parsing buffer.
819 *
820 * @param pBuffer the buffer to trim
821 */
822 private static void trimBuffer(final StringBuilder pBuffer) {
823 /* Remove leading blanks */
824 while (!pBuffer.isEmpty()
825 && Character.isWhitespace(pBuffer.charAt(0))) {
826 pBuffer.deleteCharAt(0);
827 }
828
829 /* Remove trailing blanks */
830 int myLen = pBuffer.length();
831 while (myLen-- > 0) {
832 if (!Character.isWhitespace(pBuffer.charAt(myLen))) {
833 break;
834 }
835 pBuffer.deleteCharAt(myLen);
836 }
837 }
838
839 /**
840 * create Money from double.
841 *
842 * @param pValue the double value.
843 * @return the parsed money
844 * @throws IllegalArgumentException on invalid money value
845 */
846 public OceanusMoney createMoneyFromDouble(final Double pValue) {
847 /* Handle null value */
848 if (pValue == null) {
849 return null;
850 }
851
852 /* Use default currency */
853 final Currency myCurrency = theLocale.getDefaultCurrency();
854 return createMoneyFromDouble(pValue, myCurrency.getCurrencyCode());
855 }
856
857 /**
858 * create Money from double.
859 *
860 * @param pValue the double value.
861 * @param pCurrCode the currency code
862 * @return the parsed money
863 * @throws IllegalArgumentException on invalid money value
864 */
865 public OceanusMoney createMoneyFromDouble(final Double pValue,
866 final String pCurrCode) {
867 /* Handle null value */
868 if (pValue == null) {
869 return null;
870 }
871
872 /* Determine currency */
873 final Currency myCurrency = Currency.getInstance(pCurrCode);
874
875 /* Create the new Money object */
876 final OceanusMoney myMoney = new OceanusMoney(myCurrency);
877
878 /* Parse the remaining string */
879 parseDecimalValue(pValue.toString(), theLocale, true, myMoney);
880
881 /* Correct the scale */
882 adjustDecimals(myMoney, myCurrency.getDefaultFractionDigits());
883
884 /* return the parsed money object */
885 return myMoney;
886 }
887
888 /**
889 * create Price from double.
890 *
891 * @param pValue the double value.
892 * @return the parsed price
893 * @throws IllegalArgumentException on invalid price value
894 */
895 public OceanusPrice createPriceFromDouble(final Double pValue) {
896 /* Handle null value */
897 if (pValue == null) {
898 return null;
899 }
900
901 /* Use default currency */
902 final Currency myCurrency = theLocale.getDefaultCurrency();
903 return createPriceFromDouble(pValue, myCurrency.getCurrencyCode());
904 }
905
906 /**
907 * create Price from double.
908 *
909 * @param pValue the double value.
910 * @param pCurrCode the currency code
911 * @return the parsed price
912 * @throws IllegalArgumentException on invalid price value
913 */
914 public OceanusPrice createPriceFromDouble(final Double pValue,
915 final String pCurrCode) {
916 /* Handle null value */
917 if (pValue == null) {
918 return null;
919 }
920
921 /* Determine currency */
922 final Currency myCurrency = Currency.getInstance(pCurrCode);
923
924 /* Create the new Price object */
925 final OceanusPrice myPrice = new OceanusPrice(myCurrency);
926
927 /* Parse the remaining string */
928 parseDecimalValue(pValue.toString(), theLocale, false, myPrice);
929
930 /* Correct the scale */
931 adjustDecimals(myPrice, myCurrency.getDefaultFractionDigits()
932 + OceanusPrice.XTRA_DECIMALS);
933
934 /* return the parsed price object */
935 return myPrice;
936 }
937
938 /**
939 * create Rate from double.
940 *
941 * @param pValue the double value.
942 * @return the parsed rate
943 * @throws IllegalArgumentException on invalid rate value
944 */
945 public OceanusRate createRateFromDouble(final Double pValue) {
946 /* Handle null value */
947 if (pValue == null) {
948 return null;
949 }
950
951 /* Create the new Rate object */
952 final OceanusRate myRate = new OceanusRate();
953
954 /* Parse the remaining string */
955 parseDecimalValue(pValue.toString(), theLocale, false, myRate);
956
957 /* Correct the scale */
958 adjustDecimals(myRate, OceanusRate.NUM_DECIMALS);
959
960 /* return the parsed rate object */
961 return myRate;
962 }
963
964 /**
965 * create Units from double.
966 *
967 * @param pValue the double value.
968 * @return the parsed units
969 * @throws IllegalArgumentException on invalid units value
970 */
971 public OceanusUnits createUnitsFromDouble(final Double pValue) {
972 /* Handle null value */
973 if (pValue == null) {
974 return null;
975 }
976
977 /* Create the new Units object */
978 final OceanusUnits myUnits = new OceanusUnits();
979
980 /* Parse the remaining string */
981 parseDecimalValue(pValue.toString(), theLocale, false, myUnits);
982
983 /* Correct the scale */
984 adjustDecimals(myUnits, OceanusUnits.NUM_DECIMALS);
985
986 /* return the parsed units object */
987 return myUnits;
988 }
989
990 /**
991 * create Ratio from double.
992 *
993 * @param pValue the double value.
994 * @return the parsed ratio
995 * @throws IllegalArgumentException on invalid ratio value
996 */
997 public OceanusRatio createRatioFromDouble(final Double pValue) {
998 /* Handle null value */
999 if (pValue == null) {
1000 return null;
1001 }
1002
1003 /* Create the new Ratio object */
1004 final OceanusRatio myRatio = new OceanusRatio();
1005
1006 /* Parse the remaining string */
1007 parseDecimalValue(pValue.toString(), theLocale, false, myRatio);
1008
1009 /* Correct the scale */
1010 adjustDecimals(myRatio, OceanusRatio.NUM_DECIMALS);
1011
1012 /* return the parsed ratio object */
1013 return myRatio;
1014 }
1015 }