com.adobe.xfa.ut.LcNum Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/*
* 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;
}
}