All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.xfa.ut.LcNum Maven / Gradle / Ivy

There is a newer version: 2024.11.18598.20241113T125352Z-241000
Show newest version
/*
 * ADOBE CONFIDENTIAL
 *
 * Copyright 2005 Adobe Systems Incorporated All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property of
 * Adobe Systems Incorporated and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Adobe Systems
 * Incorporated and its suppliers and may be covered by U.S. and Foreign
 * Patents, patents in process, and are protected by trade secret or copyright
 * law. Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained from
 * Adobe Systems Incorporated.
 */

package com.adobe.xfa.ut;

import java.text.*;
import java.util.*;
import java.math.*;

/**
 * LcNum defines numeric objects
 * in support of XFA numeric picture patterns.
 * 
 * 

Numeric picture patterns are used to parse and format * numeric strings. Here are the metasymbols that form valid * text picture patterns: *

*
9
when output formatting, this is a single digit, or * a zero digit if the input is a space or empty;
* when input parsing, this is a single digit. *
Z
when output formatting, this is a single digit, or * a space if the input is a zero digit, a space, or empty;
* when input parsing, this is a single digit or space. *
z
when output formatting, this is a single digit or * nothing if the input is a zero digit, a space or empty;
* when input parsing, this is a single digit, or nothing. *
S
a minus sign if negative, a plus sign if positive, or * a space otherwise when input parsing; a minus sign if * negative and a space otherwise when output formmatting. *
s
a minus sign if negative, a plus sign if positive, or * nothing otherwise when input parsing; a minus sign if * negative and nothing otherwise when output formmatting. *
CR
a credit symbol (CR) if negative, or (2) spaces otherwise. *
cr
a credit symbol (CR) if negative, or nothing otherwise. *
DB
a debit symbol (DB) if negative, or (2) spaces otherwise. *
db
a debit symbol (db) if negative, or nothing otherwise. *
(
a left parenthesis symbol if negative, or nothing * otherwise. *
)
a right parenthesis symbol if negative, or nothing * otherwise. *
E
an exponent symbol and exponent value. *
$
a currency symbol of the ambient locale. *
$$
an international currency name of the ambient locale. *
.
a decimal radix symbol of the ambient locale. *
V
a decimal radix symbol of the ambient locale, * which may be implied when input parsing. *
v
a decimal radix symbol of the ambient locale, * which may be implied * when input parsing and output formatting. *
,
a grouping separator symbol of the ambient locale. *
* * Here's a snippet of code illustrating the use of * {@link LcNum} to reformat a text string *

 *      import com.adobe.xfa.ut.LcNum; 
 *      ...
 *      LcNum num = new LcNum("007", "en_US");
 *      if (num.isValid())
 *          String s = text.format(".999$");
 * 
* * @author Mike P. Tardif * * @exclude from published api. */ public class LcNum { /** * An inner class to represent this object's locale sensitive * numeric symbols. */ static class Symbols { public Symbols( String radixSymbol, String negativeSymbol, String positiveSymbol, String groupingSymbol, String percentSymbol, String currencySymbol, String currencyName, char zeroDigit) { this.radixSymbol = radixSymbol; this.negativeSymbol = negativeSymbol; this.positiveSymbol = positiveSymbol; this.groupingSymbol = groupingSymbol; this.percentSymbol = percentSymbol; this.currencySymbol = currencySymbol; this.currencyName = currencyName; this.zeroDigit = zeroDigit; } final String radixSymbol; final String negativeSymbol; final String positiveSymbol; final String groupingSymbol; final String percentSymbol; final String currencySymbol; final String currencyName; final char zeroDigit; }; /** * An arbitrarily limited maximal decimal precision: 15. */ public static final int MAX_PRECISION = 15; /** * LcNum pattern symbols: (%$,.)89BCDERSVZbcdrsvzt. */ public static final String NUMERIC_PICTURE_SYMBOLS = "(%$,.)89BCDERSVZbcdrsvzt"; /** * Instantiates an LcNum object from the given text and in the locale given. * @param text * a text string. * @param locale * a locale name. When empty, it will default * to the current locale. */ public LcNum(String text, String locale) { String sLocale = StringUtils.isEmpty(locale) ? LcLocale.getLocale() : locale; LcLocale lcLocale = new LcLocale(sLocale); mLocale = lcLocale.isValid() ? lcLocale : new LcLocale(LcLocale.DEFAULT_LOCALE); LcData oData = new LcData(locale); mSymbols = new Symbols( oData.getRadixSymbol(), oData.getNegativeSymbol(), oData.getPositiveSymbol(), oData.getGroupingSymbol(), oData.getPercentSymbol(), oData.getCurrencySymbol(), oData.getCurrencyName(), oData.getZeroSymbol().charAt(0)); msText = new StringBuilder(); if (text != null) msText.append(text); mdValue = 0.0; mbValid = true; } /** * Instantiates an LcNum object from the given text and pattern pattern * and in the locale given. * @param text * a text string. * @param pic * a numeric pattern pattern. * @param locale * a locale name. When empty, it will default * to the current locale. */ public LcNum(String text, String pic, String locale) { this(text, locale); mbValid = parse(msText.toString(), pic); } /** * Gets the parsed text. * @return * the text associated with this object. */ public String getText() { return msText.toString(); } /** * Gets the parsed value. * @return * the number associated with this object. */ public double getValue() { return mdValue; } /** * Determines if this LcNum object is valid. * @return * boolean true if valid, and false otherwise. */ public boolean isValid() { return mbValid; } /** * Formats this LcNum object given a pattern string. * @param pat * a pattern string. * @param radixPos * the returned radix position within the formatted string * if specified. * @return * the text string formatted according to the given format * string, upon success, and the empty string, upon error. */ public String format(String pat, IntegerHolder radixPos /* = null */) { int nRadixPos = ~0; StringBuilder sRes = new StringBuilder(); try { // // Translate numeric pattern to something than can be formatted // more easily. Set state flags and values from pattern to let // us anticipate what's coming. // String pic = xlate(pat); // // Reset needed state formatting flags. // mbNegative = false; // // Convert to text to a numeric value. Round the value // to the number of insignificant digits in the pattern. // int prec = mnInSignf; if (mbExponSeen) prec += mnSignf; if (mbPercentSeen) prec += 2; double dbl = strToDbl(msText.toString(), prec); // // Do normalize +/- zeroes for Unix. // if (dbl == -0.0) dbl = 0.0; // // Determine the precision of this object's number. // int nPrec = mnInSignf; int nDot = msText.indexOf("."); if (nDot >= 0) nPrec = msText.length() - nDot - 1; else if (mbOnly8Seen) nPrec = -1; mdValue = dbl; if (dbl < 0.0) { dbl = -dbl; mbNegative = true; } if (dbl != 0.0) { mbOnlyZedSeen = false; } // // Normalize the value whenever an exponent was seen in the pattern. // if (mbExponSeen) { // // ... no significant digits. // for (mnExpon = 0; dbl >= 1.0; mnExpon++) dbl /= 10; for (; 0.0 < dbl && dbl < 1.0; mnExpon--) dbl *= 10; for (int i = 1; i < mnSignf; i++, mnExpon--) dbl *= 10; } else if (mbPercentSeen) { dbl *= 100; } // // Convert the numeric value back to text in // the required width, precision, and format. // int fmt = 1; int width = mnSignf; // // Watson fix 1226147 -- don't format in a terminating radix if // no radix needed or seen only fractional picture '8'. // if (mbExponSeen || nPrec > mnInSignf || ! mbOnly8Seen) { width += mnInSignf; if (mbRadixSeen) width++; else fmt = 0; nPrec = mnInSignf; } else /* if (mbOnly8Seen) */ { if (nPrec >= 0) width += nPrec + 1; else fmt = 0; } if (! dblToStr(msText, dbl, width, nPrec, fmt)) throw new ExFull(); // // Trim leading 0 if only fractional digits seen in the pattern. // if (mbFracDigitSeen && dbl < 1.) msText.deleteCharAt(0); // // Reset needed state formatting flags. // mbDigitSeen = false; mbRadixSeen = false; // // Initialize finite state machine that will format the pattern. // char prevChr = 0; int chrCnt = 0; boolean inQuoted = false; boolean inQuoteQuoted = false; boolean bRadixSeen = false; // // Foreach each character of the pattern Do ... // int picLen = pic.length(); int txtLen = msText.length(); int txtIdx = 0; for (int i = 0; i < picLen; ) { char chr = pic.charAt(i++); // // If seen a quote within a quoted string ... // if (inQuoteQuoted) { if (chr == '\'') { // cases like '...'' sRes.append(chr); chrCnt = 0; } else { // cases like '...'X inQuoted = false; chrCnt = 1; prevChr = chr; } inQuoteQuoted = false; } // // Elif within a quoted string ... // else if (inQuoted) { if (chr == '\'') { // cases like '...' inQuoteQuoted = true; } else { // cases like '...X sRes.append(chr); } chrCnt++; } // // Elif start of a quoted string ... // else if (chr == '\'') { if (chrCnt > 0) { // cases like ...X' bRadixSeen = mbRadixSeen; int nPos = sRes.length(); txtIdx = subFormat(prevChr, chrCnt, txtIdx, sRes); if (! bRadixSeen && mbRadixSeen) nRadixPos = nPos; chrCnt = 0; prevChr = 0; } inQuoted = true; } // // Elif start of a metacharacter ... // else if (DateTimeUtil.matchChr(NUMERIC_PICTURE_SYMBOLS, chr) || ('a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z')) { if (chr != prevChr) { if (chrCnt > 0) { // cases like AAX bRadixSeen = mbRadixSeen; int nPos = sRes.length(); txtIdx = subFormat(prevChr, chrCnt, txtIdx, sRes); if (! bRadixSeen && mbRadixSeen) nRadixPos = nPos; chrCnt = 0; } prevChr = chr; } chrCnt++; } // // Elif start of a literal ... // else { if (chrCnt > 0) { // cases like AA- bRadixSeen = mbRadixSeen; int nPos = sRes.length(); txtIdx = subFormat(prevChr, chrCnt, txtIdx, sRes); if (! bRadixSeen && mbRadixSeen) nRadixPos = nPos; chrCnt = 0; } prevChr = 0; if (chr == '?' || chr == '*' || chr == '+') sRes.append(' '); else sRes.append(chr); } } // // Ensure quoted string is terminated. // if (inQuoteQuoted) inQuoted = false; if (inQuoted) throw new ExFull(); // // Format any remaining items in the pattern. // if (prevChr > 0 && chrCnt > 0) { bRadixSeen = mbRadixSeen; int nPos = sRes.length(); txtIdx = subFormat(prevChr, chrCnt, txtIdx, sRes); if (! bRadixSeen && mbRadixSeen) nRadixPos = nPos; } // // Ensure there's no more source to format. Fix for roach #668479. // if (txtIdx > 0 && txtIdx < txtLen) sRes.setLength(0); if (! mbRadixSeen) nRadixPos = ~0; } catch(ExFull e) { sRes.setLength(0); } // // convert radix UTF-8 byte index into a Unicode character index. // if (radixPos != null) { radixPos.value = nRadixPos; } return sRes.toString(); } /** * Parses the given string according to the text pattern given. * @param sStr * the text string to parse. * @param pat * a pattern string. * @return * boolean true if successfully parsed, and false otherwise. */ public boolean parse(String sStr, String pat) { boolean bRes = true; try { // // Translate numeric pattern to something than can be formatted // more easily. Set state flags and values from the pattern to let // us anticipate what's coming. // String pic = xlate(pat); // // Reverse both picture pattern and source data. This way // picture patterns with leading zzz,zzz.99 now become trailing // 99.zzz,zzz and can be handled more easily. // pic = reverse(pic); StringBuilder sBuf = new StringBuilder(sStr).reverse(); String sRadix = mSymbols.radixSymbol; int nRadix = sBuf.indexOf(sRadix); // // If picture pattern contains a radix symbol (but not a vee), // and the value doesn't have a radix, then insert one. // Clearly, this is a hack, but I know no other way to get around // this problem -- accepting integral values in patterns with radix // pictures -- without re-architecting large chunks of this. // if (mbRadixSeen && ! mbVeeSeen && nRadix < 0) { char cZero = mSymbols.zeroDigit; for (int i = 0, n = sBuf.length(); i < n; ) { int j = i; char chr = sBuf.charAt(i++); if (cZero <= chr && chr <= cZero + 9) { sBuf.insert(j, sRadix); break; } } } String str = sBuf.toString(); // // Reset the state of parsing flags. // mbDigitSeen = false; mbRadixSeen = false; mbExponSeen = false; mbLeftParenSeen = false; mbRightParenSeen = false; mbVeeSeen = false; mbCommaSeen = false; mbSignSeen = false; mbPercentSeen = false; mbNegative = false; // // Initialize finite state machine that will parse the pattern. // char prevChr = 0; int chrCnt = 0; boolean inQuoted = false; boolean inQuoteQuoted = false; StringBuilder sRes = new StringBuilder(); // // Foreach each character of the pattern Do ... // int strPos = 0; int picLen = pic.length(); int strLen = str.length(); for (int i = 0; i < picLen; ) { char chr = pic.charAt(i++); boolean fw = (0xFF01 <= chr && chr <= 0xFF5E); // // If seen a quote within a quoted string ... // if (inQuoteQuoted) { if (chr == '\'') { // cases like '...'' if (! DateTimeUtil.matchChr(str, strPos, chr, fw)) throw new ExFull(); strPos += 1; chrCnt = 0; } else { // cases like '...'9 inQuoted = false; chrCnt = 1; prevChr = chr; } inQuoteQuoted = false; } // // Elif within a quoted string ... // else if (inQuoted) { if (chr == '\'') { // cases like '...' inQuoteQuoted = true; } else { // cases like '...9 if (! DateTimeUtil.matchChr(str, strPos, chr, fw)) throw new ExFull(); strPos += 1; } chrCnt++; } // // Elif start of a quoted string ... // else if (chr == '\'') { if (chrCnt > 0) { // cases like 99' if (strPos >= strLen && isNonIgnorable(prevChr)) throw new ExFull(); if (chrCnt >= strLen - strPos) chrCnt = strLen - strPos; if (chrCnt > 0) strPos = subParse(str, strPos, prevChr, chrCnt, sRes); chrCnt = 0; prevChr = 0; } inQuoted = true; } // // Elif start of a metacharacter ... // else if (DateTimeUtil.matchChr(NUMERIC_PICTURE_SYMBOLS, chr) || ('a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z')) { if (chr != prevChr) { if (chrCnt > 0) { // cases like 99Z if (strPos >= strLen && isNonIgnorable(prevChr)) throw new ExFull(); if (chrCnt >= strLen - strPos) chrCnt = strLen - strPos; if (chrCnt > 0) strPos = subParse(str, strPos, prevChr, chrCnt, sRes); chrCnt = 0; } prevChr = chr; } chrCnt++; } // // Elif start of a literal ... // else { if (chrCnt > 0) { // cases like 99- if (strPos >= strLen && isNonIgnorable(prevChr)) throw new ExFull(); if (chrCnt >= strLen - strPos) chrCnt = strLen - strPos; if (chrCnt > 0) strPos = subParse(str, strPos, prevChr, chrCnt, sRes); chrCnt = 0; prevChr = 0; } if (chr == '?') { if (strPos < strLen && Character.isDefined(str.charAt(strPos))) strPos += 1; } else if (chr == '+') { if (strPos >= strLen || ! Character.isWhitespace(str.charAt(strPos))) throw new ExFull(); strPos += 1; while (strPos < strLen && Character.isWhitespace(str.charAt(strPos))) strPos += 1; } else if (chr == '*') { while (strPos < strLen && Character.isWhitespace(str.charAt(strPos))) strPos += 1; } else if (strPos < strLen && str.charAt(strPos) == chr) { strPos += 1; } else { throw new ExFull(); } } } // // Ensure quoted string is terminated. // if (inQuoteQuoted) inQuoted = false; if (inQuoted) throw new ExFull(); // // Parse any remaining items in the pattern. // if (prevChr > 0 && chrCnt > 0) { if (strPos >= strLen && isNonIgnorable(prevChr)) throw new ExFull(); if (chrCnt >= strLen - strPos) chrCnt = strLen - strPos; if (chrCnt > 0) strPos = subParse(str, strPos, prevChr, chrCnt, sRes); prevChr = 0; } // // Parse any remaining signs in the text. // if (! mbSignSeen) { if (strPos >= strLen && isNonIgnorable(prevChr)) throw new ExFull(); strPos = subParse(str, strPos, 's', 1, sRes); } // // Ensure there's no more source to parse. // if (strPos != strLen) throw new ExFull(); // // Re-vert result. // sRes.reverse(); // // Trim leading zeros. // int resLen = sRes.length(); int i = 0; for (; i < resLen; i++) { if (sRes.charAt(i) != '0') break; } // // Watson fix 0670279: avoid prematurely trimming all leading zeros. // if (i < resLen) sRes.delete(0, i); else if (i > 0) sRes.delete(0, i - 1); // // Trim trailing zeros. // if (! mbExponSeen && mbRadixSeen && ! mbOnly8Seen) { i = sRes.length(); while (i > 0 && sRes.charAt(i - 1) == '0') { i--; } sRes.setLength(i); } if (sRes.toString().equals(".")) sRes.append('0'); msText.setLength(0); if (sRes.length() != 0) { if (mbNegative) msText.append('-'); msText.append(sRes); } mdValue = strToDbl(msText.toString(), 11); if (mbPercentSeen) { int nWidth = msText.length(); int nPrec = 2; int nDot = msText.indexOf("."); if (nDot >= 0) nPrec += nWidth - nDot - 1; dblToStr(msText, mdValue / 100.0, nWidth, nPrec, 0); } } catch(ExFull e) { bRes = false; } return bRes; } /** * Parse the picture and return the number of leading and fractional digits * the picture implies. * @param pic - a picture pattern string. * @param lead - the number of lead digits. * @param frac - the number of fractional digits. */ public static void getSymbolCount(String pic, IntegerHolder lead, IntegerHolder frac) { String sLoc = LcLocale.English_US; String sZero = "0.0"; LcNum oNum = new LcNum(sZero, sLoc); oNum.xlate(pic); lead.value = oNum.mnSignf; frac.value = oNum.mnInSignf; } final StringBuilder msText; // The source text. final LcLocale mLocale; // The locale. boolean mbValid; // The validity of this object. double mdValue; // The numeric value of this object. int mnExpon; int mnSignf; int mnInSignf; boolean mbNegative; boolean mbSignSeen; boolean mbLeftParenSeen; boolean mbRightParenSeen; boolean mbDigitSeen; boolean mbRadixSeen; boolean mbExponSeen; boolean mbVeeSeen; boolean mbCommaSeen; boolean mbOnlyZedSeen; boolean mbFracZedSeen; boolean mbOnly8Seen; boolean mbFracDigitSeen; boolean mbPercentSeen; boolean mbFracStartSeen; Symbols mSymbols; /* * DecimalFormat objects are not thread-safe. But * to minimize the overhead of creating a new instance each * time we format a number, keep a partially initialized * instance that can be cloned each time its used. */ static final DecimalFormat gNumberFormat = (DecimalFormat) NumberFormat.getInstance(Locale.US); static final String gsNonIgnorable = "(%$.)BCDERSZ89"; static final String gsCondIgnorable = "(%$.)BCDERS"; static final String gsDB = "DB"; static final String gsCR = "CR"; static final String gsLP = "("; static final String gsRP = ")"; static final String gsE = "E"; static final String gsDSP = " "; static final String gsSSP = " "; /* * Translates a num(eric) pattern into something more easily * handled, making the passing and formatting easier: turn multi-different * character patterns like (DB, and CR) into single character patterns * Ensure the pattern is well formed: *
    *
  • ( ) are paired, *
  • ( ) enclosing all digits, *
  • E is preceeded with digits, *
  • there's but a single radix, and *
  • literals are properly quoted. *
* In the process, flag: *
    *
  • the number of significant digits, *
  • LI> the number of insignificant digits, *
  • if the exponent pattern was seen, *
  • if the VEE pattern was seen, *
  • if any sign patterns were seen. *
      * * @param pic * a numeric pattern. * @return * the translated pattern. * @throws * ExFull if the pattern is not valid. */ private String xlate(String pic) { // // Reset the state formatting flags. // mbFracStartSeen = false; mbFracDigitSeen = false; mbOnly8Seen = false; mbFracZedSeen = false; mbOnlyZedSeen = true; mbDigitSeen = false; mbRadixSeen = false; mbExponSeen = false; mbLeftParenSeen = false; mbRightParenSeen = false; mbVeeSeen = false; mbCommaSeen = false; mbSignSeen = false; mbPercentSeen = false; mnInSignf = 0; mnSignf = 0; // // Initialize finite state scanner. Only operate on previous // character when there's a "state" change triggered by the // current pattern character. // StringBuilder sRes = new StringBuilder(); char prevChr = 0; int chrCnt = 0; boolean inQuoted = false; boolean inQuoteQuoted = false; // // Foreach each character of the pattern Do ... // int picLen = pic.length(); for (int i = 0; i < picLen; ) { char chr = pic.charAt(i++); // // If seen a quote within a quoted string ... // if (inQuoteQuoted) { if (chr == '\'') { // cases like '...'' sRes.append(chr); chrCnt = 0; } else { // cases like '...'9 inQuoted = false; chrCnt = 1; prevChr = chr; } inQuoteQuoted = false; } // // Elif within a quoted string ... // else if (inQuoted) { if (chr == '\'') // cases like '...' inQuoteQuoted = true; sRes.append(chr); chrCnt = 0; prevChr = chr; } // // Elif start of a quoted string ... // else if (chr == '\'') { if (chrCnt > 0) { // cases like ...9' sRes.append(subXlate(prevChr, chrCnt)); chrCnt = 0; prevChr = 0; } sRes.append(chr); inQuoted = true; } // // Elif its a metacharacter ... // else if (DateTimeUtil.matchChr(NUMERIC_PICTURE_SYMBOLS, chr) || ('a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z')) { if (prevChr != chr) { if (chrCnt == 1 && prevChr == 'D' && chr == 'B') { sRes.append('D'); prevChr = 0; chrCnt = 0; mbSignSeen = true; } else if (chrCnt == 1 && prevChr == 'd' && chr == 'b') { sRes.append('d'); prevChr = 0; chrCnt = 0; mbSignSeen = true; } else if (chrCnt == 1 && prevChr == 'C' && chr == 'R') { sRes.append('C'); prevChr = 0; chrCnt = 0; mbSignSeen = true; } else if (chrCnt == 1 && prevChr == 'c' && chr == 'r') { sRes.append('c'); prevChr = 0; chrCnt = 0; mbSignSeen = true; } else if (chrCnt > 0) { // cases like ZZ9 or :9 sRes.append(subXlate(prevChr, chrCnt)); prevChr = chr; chrCnt = 1; } else { prevChr = chr; chrCnt++; } } else { chrCnt++; } } // // Elif start of a literal ... // else { if (chrCnt > 0) { // cases like 99- sRes.append(subXlate(prevChr, chrCnt)); chrCnt = 0; } prevChr = 0; sRes.append(chr); } } // // Ensure quoted string is terminated. // if (inQuoteQuoted) inQuoted = false; if (inQuoted) throw new ExFull(); // // Format any remaining items in the pattern. // if (prevChr > 0 && chrCnt > 0) sRes.append(subXlate(prevChr, chrCnt)); if (mbLeftParenSeen && mbDigitSeen && mbRightParenSeen) mbSignSeen = true; return sRes.toString(); } /* * Translates a sub-element of a numeric pattern given the number of * occurances of a numeric pattern metacharacter. * * @param chr * a numeric pattern metacharacter. * @param chrCnt * the number of consecutive occurances of the metacharacter. * @return * the translated sub-element, or the empty string upon error. * @throws * ExFull if sub-elements aren't translated in the proper * sequence. */ private String subXlate(char chr, int chrCnt) { StringBuilder sRes = new StringBuilder(); for (int i = 0; i < chrCnt; i++) sRes.append(chr); // // Remap any fullwidth metasymbol back to ASCII. // if (0xFF01 <= chr && chr <= 0xFF5E) { chr -= 0xFFE0; chr &= 0x00FF; } if (chr == 'E') { if (chrCnt > 1 || mbExponSeen || ! mbDigitSeen) throw new ExFull(); mbExponSeen = true; } else if (chr == '(') { if (chrCnt > 1 || mbLeftParenSeen || mbDigitSeen || mbRightParenSeen) throw new ExFull(); mbLeftParenSeen = true; } else if (chr == 'S' || chr == 's') { mbSignSeen = true; } else if (chr == '%') { mbPercentSeen = true; } else if (chr == '.' || chr == 'V' || chr == 'v') { if (chr == 'v' || chr == 'V') { if (chrCnt > 1 || mbVeeSeen) throw new ExFull(); mbVeeSeen = true; } if (chrCnt > 1 || mbRadixSeen) throw new ExFull(); mbRadixSeen = true; mbFracStartSeen = true; if (! mbDigitSeen) mbFracDigitSeen = true; } else if (chr == '8' || chr == '9' || chr == 'Z' || chr == 'z') { // // Watson fix 1226147 -- only presume we've seen only fractional // picture 'z' or only fractional picture '8' after we've seen a // radix picture -- the prior implementation presumed prematurely. // if (mbFracStartSeen) { mbFracStartSeen = false; mbFracZedSeen = true; mbOnly8Seen = true; } if (mbRightParenSeen) throw new ExFull(); if (chr != 'z') { mbOnlyZedSeen = false; if (mbRadixSeen) mbFracZedSeen = false; } if (chr != '8') mbOnly8Seen = false; mbDigitSeen = true; if (mbRadixSeen) mnInSignf += chrCnt; else mnSignf += chrCnt; } else if (chr == ')') { if (chrCnt > 1 || ! mbLeftParenSeen || ! mbDigitSeen || mbRightParenSeen) throw new ExFull(); mbRightParenSeen = true; } return sRes.toString(); } /* * Formats a sub-element of a numeric pattern given the number of * occurances of a numeric pattern metacharacter. * * @param chr * a numeric pattern metacharacter. * @param chrCnt * the number of consecutive occurances of the metacharacter. * @param txtIdx * an index into this object's text string. * @param sRes * the formatted sub-element, or the empty upon error. * @return * the index into this object's text string. * @throws * ExFull if the numeric value overflowed the field. */ private int subFormat(char chr, int chrCnt, int txtIdx, StringBuilder sRes) { boolean fw = (0xFF01 <= chr && chr <= 0xFF5E); switch (chr) { case 0xFF19: // Fullwidth '9'. case '9': // Digit or zero if zero. if (mbNegative && ! mbDigitSeen && ! mbSignSeen) { sRes.append(DateTimeUtil.fmtStr(mSymbols.negativeSymbol, fw)); mbSignSeen = true; } while (chrCnt-- > 0 && txtIdx < msText.length()) { char cValue = msText.charAt(txtIdx++); if ('0' > cValue || cValue > '9') throw new ExFull(); int value = cValue - '0'; sRes.append(DateTimeUtil.fmtNum(1, value, fw, mSymbols.zeroDigit)); mbDigitSeen = true; } break; case 0xFF18: // Fullwidth '8'. case '8': // Digit or zero if zero. if (mbNegative && ! mbDigitSeen && ! mbSignSeen) { sRes.append(DateTimeUtil.fmtStr(mSymbols.negativeSymbol, fw)); mbSignSeen = true; } while (chrCnt-- > 0 && txtIdx < msText.length()) { char cValue = msText.charAt(txtIdx++); if ('0' > cValue || cValue > '9') throw new ExFull(); int value = cValue - '0'; sRes.append(DateTimeUtil.fmtNum(1, value, fw, mSymbols.zeroDigit)); mbDigitSeen = true; } break; case 0xFF3A: // Fullwidth 'Z'. case 'Z': // Digit or space if zero. if (mbNegative && ! mbDigitSeen && ! mbSignSeen) { sRes.append(DateTimeUtil.fmtStr(mSymbols.negativeSymbol, fw)); mbSignSeen = true; } while (chrCnt-- > 0 && txtIdx < msText.length()) { char cValue = msText.charAt(txtIdx++); if ('0' > cValue || cValue > '9') throw new ExFull(); int value = cValue - '0'; if (mbDigitSeen || mbRadixSeen || value > 0) { sRes.append(DateTimeUtil.fmtNum(1, value, fw, mSymbols.zeroDigit)); mbDigitSeen = true; } else { sRes.append(DateTimeUtil.matchChr(' ', fw)); } } break; case 0xFF5A: // Fullwidth 'z'. case 'z': // Digit or nothing if zero. if (mbNegative && ! mbDigitSeen && ! mbSignSeen) { sRes.append(DateTimeUtil.fmtStr(mSymbols.negativeSymbol, fw)); mbSignSeen = true; } while (chrCnt-- > 0 && txtIdx < msText.length()) { char cValue = msText.charAt(txtIdx++); if ('0' > cValue || cValue > '9') throw new ExFull(); int value = cValue - '0'; boolean bFormatIt = false; if (mbRadixSeen) { int nSigf = StringUtils.skipUntil(msText.toString(), "123456789", txtIdx); if (nSigf + txtIdx < msText.length()) { bFormatIt = true; } else if (! mbDigitSeen && ! mbFracZedSeen) { bFormatIt = true; } else if (! mbFracZedSeen && value > 0) { bFormatIt = true; } else if (value > 0) { bFormatIt = true; } } else if (mbDigitSeen || value > 0) { if (! mbFracZedSeen || ! mbRadixSeen) bFormatIt = true; } if (bFormatIt) { sRes.append(DateTimeUtil.fmtNum(1, value, fw, mSymbols.zeroDigit)); mbDigitSeen = true; } } break; case 0xFF25: // Fullwidth 'E'. case 'E': // Exponent. if (! mbOnlyZedSeen) { sRes.append(DateTimeUtil.fmtStr(gsE, fw)); int value = mnExpon; if (value < 0) { sRes.append(DateTimeUtil.matchChr('-', fw)); value = - value; } else if (value > 0) { sRes.append(DateTimeUtil.matchChr('+', fw)); } sRes.append(DateTimeUtil.fmtPlainNum(3, value, fw, mSymbols.zeroDigit)); } break; case 'C': // CR symbol if negative and spaces if positive. while (chrCnt-- > 0) { if (! mbOnlyZedSeen) sRes.append((mbNegative) ? gsCR : gsDSP); } break; case 'c': // CR symbol if negative and nothing if positive. while (chrCnt-- > 0) { if (! mbOnlyZedSeen && mbNegative) sRes.append(gsCR); } break; case 'D': // DB symbol if negative and spaces if positive. while (chrCnt-- > 0) { if (! mbOnlyZedSeen) sRes.append((mbNegative) ? gsDB : gsDSP); } break; case 'd': // DB symbol if negative and nothing if positive. while (chrCnt-- > 0) { if (! mbOnlyZedSeen && mbNegative) sRes.append(gsDB); } break; case 0xFF33: // Fullwidth 'S'. case 'S': // Minus sign if negative and a space if positive. while (chrCnt-- > 0) { if (! mbOnlyZedSeen) if (mbNegative) sRes.append(DateTimeUtil.fmtStr( mSymbols.negativeSymbol, fw)); else sRes.append(DateTimeUtil.matchChr(' ', fw)); } break; case 0xFF53: // Fullwidth 's'. case 's': // Minus sign if negative and nothing if positive. while (chrCnt-- > 0) { if (! mbOnlyZedSeen && mbNegative) sRes.append(DateTimeUtil.fmtStr( mSymbols.negativeSymbol, fw)); } break; case 0xFF36: // Fullwidth 'V'. case 'V': // Implied decimal sign if parsing. while (chrCnt-- > 0) { if (msText.charAt(txtIdx++) != '.') throw new ExFull(); if (! mbOnlyZedSeen) sRes.append(DateTimeUtil.fmtStr(mSymbols.radixSymbol, fw)); mbRadixSeen = true; } break; case 0xFF56: // Fullwidth 'v'. case 'v': // Implied decimal sign. while (chrCnt-- > 0) { if (msText.charAt(txtIdx++) != '.') throw new ExFull(); if (! mbOnlyZedSeen && mbExponSeen) sRes.append(DateTimeUtil.fmtStr(mSymbols.radixSymbol, fw)); mbRadixSeen = true; } break; case 0xFF0E: // Fullwidth '.'. case '.': // Decimal radix. while (chrCnt-- > 0) { if (mbOnly8Seen) { char cValue = (txtIdx < msText.length()) ? msText.charAt(txtIdx++) : 0; if (cValue == '.') sRes.append(DateTimeUtil.fmtStr( mSymbols.radixSymbol, fw)); // // Fixed Watson 1252639. Ensure no other digits are // present in the absence of the radix. // else if ('0' <= cValue && cValue <= '9') throw new ExFull(); } else { if (msText.charAt(txtIdx++) != '.') throw new ExFull(); int nSigf = StringUtils.skipUntil(msText.toString(), "123456789", txtIdx); if (! mbFracZedSeen || nSigf + txtIdx < msText.length()) sRes.append(DateTimeUtil.fmtStr( mSymbols.radixSymbol, fw)); } mbRadixSeen = true; } break; case 0xFF0C: // Fullwidth ','. case ',': // Grouping separator. while (chrCnt-- > 0) { if (! mbOnlyZedSeen && mbDigitSeen) sRes.append(DateTimeUtil.fmtStr( mSymbols.groupingSymbol, fw)); mbCommaSeen = true; } break; case 0xFF04: // Fullwidth '$'. case '$': // Currency name or symbol. while (chrCnt-- > 0) { if (! mbOnlyZedSeen) { if (chrCnt > 0) { chrCnt--; sRes.append(DateTimeUtil.fmtStr( mSymbols.currencyName, fw)); } else sRes.append(DateTimeUtil.fmtStr( mSymbols.currencySymbol, fw)); } } break; case 0xFF05: // Fullwidth '%'. case '%': // Percent symbol. while (chrCnt-- > 0) { if (! mbOnlyZedSeen) sRes.append(DateTimeUtil.fmtStr( mSymbols.percentSymbol, fw)); } break; case 0xFF08: // Fullwidth '('. case 0xFF09: // Fullwidth ')'. case '(': // Left parenthesis. case ')': // Right parenthesis. while (chrCnt-- > 0) { if (! mbOnlyZedSeen) sRes.append(DateTimeUtil.matchChr((mbNegative) ? chr : ' ', fw)); } break; case 't': // tab. while (chrCnt-- > 0) { if (! mbOnlyZedSeen) sRes.append('\t'); } break; default: if (! mbOnlyZedSeen) sRes.append(DateTimeUtil.matchChr(chr, fw)); break; } return txtIdx; } /* * Parses sa sub-element at a given position of the given string, * given the number of occurances of a numeric pattern metacharacter. * * @param src * the text string to parse. * @param srcPos * the starting parsing position within the text string. * @param chr * a numeric pattern metacharacter. * @param chrCnt * the number of consecutive occurances of the metacharacter. * @param sRes * a string containing the text that was successfully parsed. * @return * the ending parsing position within the text string * if successfully parsed. * @throws * an ExFull if the text fails to parse. */ private int subParse(String src, int srcPos, char chr, int chrCnt, StringBuilder sRes) { assert(sRes != null); int n; int curPos = srcPos; String sSym; String sPos; String sNeg; boolean fw = (0xFF01 <= chr && chr <= 0xFF5E); switch (chr) { case 0xFF19: // Fullwidth '9'. case '9': // Digit or zero if empty or space. while (chrCnt-- > 0) { n = DateTimeUtil.matchNum(src, srcPos, srcPos + 1, fw, mSymbols.zeroDigit); if (n <= 0) throw new ExFull(); if (mbCommaSeen) throw new ExFull(); curPos = DateTimeUtil.incPos(src, srcPos, n); n = DateTimeUtil.getNum(src, srcPos, curPos, fw, mSymbols.zeroDigit); sRes.append((char) ('0' + n)); srcPos = curPos; mbDigitSeen = true; } break; case 0xFF3A: // Fullwidth 'Z'. case 'Z': // Digit or space if empty, a space or zero. sSym = gsSSP; if (sSym.length() > 1) sSym = reverse(sSym); while (chrCnt-- > 0) { n = DateTimeUtil.matchNum(src, srcPos, srcPos + 1, fw, mSymbols.zeroDigit); if (n == 1) { if (mbCommaSeen) throw new ExFull(); curPos = DateTimeUtil.incPos(src, srcPos, n); n = DateTimeUtil.getNum(src, srcPos, curPos, fw, mSymbols.zeroDigit); sRes.append((char) ('0' + n)); srcPos = curPos; } else if ((n = DateTimeUtil.matchStr(src, srcPos, sSym, fw)) > 0) { if (! mbRadixSeen) sRes.append('0'); srcPos = n; } mbDigitSeen = true; } break; case 0xFF18: // Fullwidth '8'. case 0xFF5A: // Fullwidth 'z'. case '8': case 'z': // Digit or nothing if empty, a space or zero. while (chrCnt-- > 0) { n = DateTimeUtil.matchNum(src, srcPos, srcPos + 1, fw, mSymbols.zeroDigit); if (n == 1) { if (mbCommaSeen) throw new ExFull(); curPos = DateTimeUtil.incPos(src, srcPos, n); n = DateTimeUtil.getNum(src, srcPos, curPos, fw, mSymbols.zeroDigit); sRes.append((char) ('0' + n)); srcPos = curPos; mbDigitSeen = true; } } break; case 0xFF25: // Fullwidth 'E'. case 'E': // Exponent. while (chrCnt-- > 0) { if (mbExponSeen) throw new ExFull(); // // Scan up to 3 decimal digit max for the exponent. // n = DateTimeUtil.matchNum(src, srcPos, srcPos + 3, fw, mSymbols.zeroDigit); if (n < 1 || 3 < n) throw new ExFull(); for (int i = 0; i < n; i++) { curPos = DateTimeUtil.incPos(src, srcPos, 1); int num = DateTimeUtil.getNum(src, srcPos, curPos, fw, mSymbols.zeroDigit); sRes.append((char) ('0' + num)); srcPos = curPos; } if (DateTimeUtil.matchChr(src, srcPos, '+', fw)) { sRes.append('+'); srcPos += 1; } else { if (DateTimeUtil.matchChr(src, srcPos, '-', fw)) { sRes.append('-'); srcPos += 1; } } if (DateTimeUtil.matchChr(src, srcPos, 'E', fw)) { sRes.append('E'); srcPos += 1; } else { if (DateTimeUtil.matchChr(src, srcPos, 'e', fw)) { sRes.append('E'); srcPos += 1; } else throw new ExFull(); } mbExponSeen = true; } break; case 'C': // CR symbol if negative and spaces if positive. sNeg = gsCR; if (sNeg.length() > 1) sNeg = reverse(sNeg); sPos = gsDSP; if (sPos.length() > 1) sPos = reverse(sPos); while (chrCnt-- > 0) { if (mbSignSeen) throw new ExFull(); if ((n = DateTimeUtil.matchStr(src, srcPos, sNeg, fw)) > 0) { srcPos = n; mbNegative = true; } else if ((n = DateTimeUtil.matchStr(src, srcPos, sPos, fw)) > 0) { srcPos = n; } mbSignSeen = true; } break; case 'c': // CR symbol if negative and nothing if positive. sPos = gsCR; if (sPos.length() > 1) sPos = reverse(sPos); while (chrCnt-- > 0) { if (mbSignSeen) throw new ExFull(); if ((n = DateTimeUtil.matchStr(src, srcPos, sPos, fw)) > 0) { srcPos = n; mbNegative = true; } mbSignSeen = true; } break; case 'D': // DB symbol if negative and spaces if positive. sNeg = gsDB; if (sNeg.length() > 1) sNeg = reverse(sNeg); sPos = gsDSP; if (sPos.length() > 1) sPos = reverse(sPos); while (chrCnt-- > 0) { if (mbSignSeen) throw new ExFull(); if ((n = DateTimeUtil.matchStr(src, srcPos, sNeg, fw)) > 0) { srcPos = n; mbNegative = true; } else if ((n = DateTimeUtil.matchStr(src, srcPos, sPos, fw)) > 0) { srcPos = n; } mbSignSeen = true; } break; case 'd': // DB symbol if negative and nothing if positive. sNeg = gsDB; if (sNeg.length() > 1) sNeg = reverse(sNeg); while (chrCnt-- > 0) { if (mbSignSeen) throw new ExFull(); if ((n = DateTimeUtil.matchStr(src, srcPos, sNeg, fw)) > 0) { srcPos = n; mbNegative = true; } mbSignSeen = true; } break; case 0xFF33: // Fullwidth 'S'. case 'S': // Minus sign if negative and a space if positive. sSym = gsSSP; if (sSym.length() > 1) sSym = reverse(sSym); sNeg = mSymbols.negativeSymbol; if (sNeg.length() > 1) sNeg = reverse(sNeg); sPos = mSymbols.positiveSymbol; if (sPos.length() > 1) sPos = reverse(sPos); while (chrCnt-- > 0) { if (mbSignSeen) throw new ExFull(); if ((n = DateTimeUtil.matchStr(src, srcPos, sNeg, fw)) > 0) { srcPos = n; mbNegative = true; } else if ((n = DateTimeUtil.matchStr(src, srcPos, sPos, fw)) > 0) { srcPos = n; } else if ((n = DateTimeUtil.matchStr(src, srcPos, sSym, fw)) > 0) { srcPos = n; } mbSignSeen = true; } break; case 0xFF53: // Fullwidth 's'. case 's': // Minus sign if negative and nothing if positive. sNeg = mSymbols.negativeSymbol; if (sNeg.length() > 1) sNeg = reverse(sNeg); sPos = mSymbols.positiveSymbol; if (sPos.length() > 1) sPos = reverse(sPos); while (chrCnt-- > 0) { if (mbSignSeen) throw new ExFull(); if ((n = DateTimeUtil.matchStr(src, srcPos, sNeg, fw)) > 0) { srcPos = n; mbNegative = true; } else if ((n = DateTimeUtil.matchStr(src, srcPos, sPos, fw)) > 0) { srcPos = n; } mbSignSeen = true; } break; case 0xFF36: // Fullwidth 'V'. case 'V': // Implied decimal sign when parsing. sSym = mSymbols.radixSymbol; if (sSym.length() > 1) sSym = reverse(sSym); while (chrCnt-- > 0) { if (mbVeeSeen || mbRadixSeen) throw new ExFull(); if ((n = DateTimeUtil.matchStr(src, srcPos, sSym, fw)) > 0) srcPos = n; sRes.append('.'); mbVeeSeen = true; mbRadixSeen = true; } break; case 0xFF56: // Fullwidth 'v'. case 'v': // Implied decimal sign when parsing. sSym = mSymbols.radixSymbol; if (sSym.length() > 1) sSym = reverse(sSym); while (chrCnt-- > 0) { if (mbVeeSeen || mbRadixSeen) throw new ExFull(); if ((n = DateTimeUtil.matchStr(src, srcPos, sSym, fw)) > 0) srcPos = n; sRes.append('.'); mbVeeSeen = true; mbRadixSeen = true; } break; case 0xFF0E: // Fullwidth '.'. case '.': // Decimal radix. sSym = mSymbols.radixSymbol; if (sSym.length() > 1) sSym = reverse(sSym); while (chrCnt-- > 0) { if (mbRadixSeen) throw new ExFull(); if ((n = DateTimeUtil.matchStr(src, srcPos, sSym, fw)) <= 0) throw new ExFull(); srcPos = n; sRes.append('.'); mbRadixSeen = true; } break; case 0xFF0C: // Fullwidth ','. case ',': // Grouping separator. sSym = mSymbols.groupingSymbol; if (sSym.length() > 1) sSym = reverse(sSym); sPos = gsSSP; if (sPos.length() > 1) sPos = reverse(sPos); while (chrCnt-- > 0) { if ((n = DateTimeUtil.matchStr(src, srcPos, sSym, fw)) > 0) { srcPos = n; } // // When parsing allow the equivalence of SP with NBSP, // but only when followed by a digit. Watson fix 0670279. // else if (mSymbols.groupingSymbol.equals("\u00a0")) { if ((n = DateTimeUtil.matchStr(src, srcPos, sPos, fw)) > 0 && (n == src.length() || DateTimeUtil.matchNum(src, n, n + 1, fw, mSymbols.zeroDigit) > 0)) srcPos = n; } else mbCommaSeen = true; } break; case 0xFF04: // Fullwidth '$'. case '$': // Currency name or symbol. while (chrCnt-- > 0) { if (chrCnt > 0) { chrCnt--; sSym = mSymbols.currencyName; } else sSym = mSymbols.currencySymbol; if (sSym.length() > 1) sSym = reverse(sSym); if ((n = DateTimeUtil.matchStr(src, srcPos, sSym, fw)) <= 0) throw new ExFull(); srcPos = n; } break; case 0xFF05: // Fullwidth '%'. case '%': // Percent symbol. sSym = mSymbols.percentSymbol; if (sSym.length() > 1) sSym = reverse(sSym); while (chrCnt-- > 0) { if ((n = DateTimeUtil.matchStr(src, srcPos, sSym, fw)) <= 0) throw new ExFull(); srcPos = n; mbPercentSeen = true; } break; case 0xFF08: // Fullwidth '('. case '(': // Left parenthesis. sNeg = gsLP; if (sNeg.length() > 1) sNeg = reverse(sNeg); sPos = gsSSP; if (sPos.length() > 1) sPos = reverse(sPos); while (chrCnt-- > 0) { if (! mbRightParenSeen || ! mbDigitSeen || mbLeftParenSeen) throw new ExFull(); if ((n = DateTimeUtil.matchStr(src, srcPos, sNeg, fw)) > 0) { srcPos = n; mbLeftParenSeen = true; mbNegative = true; } else { if ((n = DateTimeUtil.matchStr(src, srcPos, sPos, fw)) <= 0) throw new ExFull(); srcPos = n; mbLeftParenSeen = true; } } break; case 0xFF09: // Fullwidth ')'. case ')': // Right parenthesis. sNeg = gsRP; if (sNeg.length() > 1) sNeg = reverse(sNeg); sPos = gsSSP; if (sPos.length() > 1) sPos = reverse(sPos); while (chrCnt-- > 0) { if (mbLeftParenSeen || mbDigitSeen || mbRightParenSeen) throw new ExFull(); if ((n = DateTimeUtil.matchStr(src, srcPos, sNeg, fw)) > 0) { srcPos = n; mbRightParenSeen = true; mbNegative = true; } else { if ((n = DateTimeUtil.matchStr(src, srcPos, sPos, fw)) <= 0) throw new ExFull(); srcPos = n; mbRightParenSeen = true; } } break; case 't': // tab while (chrCnt-- != 0) { if (src.charAt(srcPos) != '\t') throw new ExFull(); srcPos += 1; } break; default: if (! DateTimeUtil.matchChr(src, srcPos, chr, fw)) throw new ExFull(); srcPos += 1; break; } return srcPos; } /* * Determines if the given numeric pattern metacharacter * is not ignorable in the current parsing context. * * @param chr * a numeric pattern metacharacter. * @return * boolean true if its not, and false otherwise. * */ private boolean isNonIgnorable(char chr) { if (mbCommaSeen) return DateTimeUtil.matchChr(gsNonIgnorable, chr); else return DateTimeUtil.matchChr(gsCondIgnorable, chr); } /* * Reverses the given string's contents. * * @param str * a string. * @return * the given string with every character reversed. */ private static String reverse(String str) { return new StringBuilder(str).reverse().toString(); } /* * Converts a given double to a numeric string of the width and precision * given. * * @param text * the string equivalent of the given double. * @param dbl * a double. * @param width * the required width. * @param prec * the required precision. * @param fmt * the required format where fmt == 0 requests zero padding, and, * fmt == 1 requests zero padding and radix termination. * @return * Boolean true if the double can be converted to the given width * and precision, and false otherwise. * The numeric string returned in the text parameter. */ private static boolean dblToStr(StringBuilder text, double dbl, int width, int prec, int fmt) { assert(text != null); // // Convert the double to the required width and precision. // StringBuilder s = new StringBuilder(); if (prec > MAX_PRECISION) prec = MAX_PRECISION; // // Emulate the // sprintf(text, fmt == 1 ? "%0#*.*f" : "%0*.*f", width, prec, dbl) // behaviour using DecimalFormat class. // if (prec > 0) { for (int i = 2; i < width - prec; i++) s.append('0'); s.append('0'); s.append('.'); for (int i = 0; i < prec; i++) s.append('0'); } else { for (int i = 1; i < width; i++) s.append('0'); s.append((fmt == 1) ? '.' : '0'); } DecimalFormat oNumberFormat = (DecimalFormat) gNumberFormat.clone(); oNumberFormat.applyPattern(s.toString()); oNumberFormat.setDecimalSeparatorAlwaysShown(fmt == 1); text.setLength(0); text.append(oNumberFormat.format(dbl)); return true; } /* * Converts a given numeric string to a double in the precision given. * * @param text * a (locale-independent) numeric string. * @param dbl * the numeric equivalent of the given numeric string. * @param prec * the required precision. * @return * the double value in the required precision. * @throws * ExFull if the string is not numeric, exclusive of leading * and trailing whitespace. */ private static double strToDbl(String text, int prec) { double dbl = 0.0; if (prec > MAX_PRECISION) prec = MAX_PRECISION; try { // // avoid ROUND_HALF_EVEN mode. // dbl = new BigDecimal(text).setScale(prec, BigDecimal.ROUND_HALF_UP).doubleValue(); } catch (NumberFormatException e) { if (text.length() != 0) throw new ExFull(); } return dbl; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy