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