com.adobe.xfa.ut.PictureFmt Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*
* 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.awt.Point;
import java.util.ArrayList;
import java.util.List;
/**
* The PictureFmt class defines methods to parse and format data
* according to XFA picture patterns.
*
* Here's a snippet of code illustrating its use to format a date string:
*
*
* import com.adobe.xfa.ut.PictureFmt;
* ...
* StringBufer sResult = new StringBuilder();
* PictureFmt oFmt = new PictureFmt("en");
* if (oFmt.parse("28/2/2000", "D/M/YYYY", sResult))
* oFmt.format(sResult.toString(), "EEEE', the 'D' of 'MMMM', 'YYYY", sResult)
* ...
*
*
* PictureFmt also defines methods to validate picture patterns.
* Specifically, the methods
*
* -
* {@link PictureFmt#isDatePictureValid(String)},
*
-
* {@link PictureFmt#isTimePictureValid(String)},
*
-
* {@link PictureFmt#isNumericPictureValid(String)},
*
-
* {@link PictureFmt#isTextPictureValid(String)},
*
* {@link PictureFmt#isZeroPictureValid(String)}, and,
*
* {@link PictureFmt#isNullPictureValid(String)}.
*
* are used for validating the syntax of the given source for each of the
* six categories of pictures. This includes validating the well-formedness of
* patterns like
* category{subpicture}
and
* category(locale){subpicture}
,
* validating the well-formedness of literals, and ensuring the given source
* symbols all belong to one category of picture. As an example,
* the time picture pattern:
*
* h:MM:SS 'o'clock 'A X
*
* is syntactically invalid - a quote is missing and X is not a valid time
* picture symbol.
*
* PictureFmt also defines methods to semantically validate picture
* patterns. The methods
*
* - {@link PictureFmt#isDatePicture(String)},
*
- {@link PictureFmt#isTimePicture(String)},
*
- {@link PictureFmt#isNumericPicture(String)}, and,
*
- {@link PictureFmt#isTextPicture(String)}
*
* are used for semantially validating the given picture. This means that the
* combination of XFA symbols constitutes a valid XFA picture pattern. A time
* picture pattern like:
*
* h:MM:SS 'o''clock 'Z
*
* is semantically invalid when used to parse time input -- requesting a time
* from a 12-hour clock without the meridiem could never yield a correct time
* value. A date picture pattern like: EEEE, MMMM YYYY is semantically invalid
* when used to parse date input -- requesting a date without the day of the
* month could never yield a correct date value.
*
* Do note however, that semantically invalid pictures patterns are used in
* output formatting in acceptable circumstances. So apply these method with
* discretion.
*
* @exclude from published api.
*/
public final class PictureFmt {
/*
* This function emulates ResolveRange method in C++ side of XTG. That method takes a String and rationalizes the start and
* end indices as follows:
* If startIndex>s.length(), make startIndex=s.length()
* If endIndex>s.length(), make endIndex=s.length()
* Since, unlike C++, these values can't be returned, envelope them as a Point
* with Point.x = startIndex (maybe modified)
* and Point.y = endIndex (maybe modified)
*/
/** @exclude from published api. */
public static Point resolveRange(String s, int startIndex, int endIndex){
if (startIndex>s.length()) startIndex=s.length();
if (endIndex>s.length()) endIndex=s.length();
return new Point(startIndex, endIndex);
}
/**
* Instantiates an PictureFmt object.
* @param sLocale
* the locale name.
*/
public PictureFmt(String sLocale /* = LcLocale.getLocale() */) {
msLocale = sLocale;
}
/**
* Formats a given data source according to the given picture.
*
* @param sSource
* the source data in canonical format.
* @param sPicture
* the formatting picture.
* @param sResult
* the resulting string, which may be empty upon error.
* @return
* boolean true if successful and false otherwise.
*/
@FindBugsSuppress(pattern="NP_LOAD_OF_KNOWN_NULL_VALUE") // false positive
public boolean format(String sSource, String sPicture,
StringBuilder sResult) {
sResult.setLength(0);
//
// If no formatting picture found Then return source.
//
if (StringUtils.isEmpty(sPicture)) {
sResult.append(sSource);
return true;
}
//
// Segregate alternate pictures into a list.
//
List oAlt = new ArrayList();
if (! getAlternates(sPicture, oAlt)) {
return true;
}
//
// For each alternate picture Do ...
//
boolean bSuccess = false;
for (int i = 0; i < oAlt.size(); i++) {
String sMask = interpret(oAlt.get(i));
StringBuilder sDateMask = new StringBuilder();
StringBuilder sTimeMask = new StringBuilder();
//
// Try formatting each alternate (in picture category order, except
// for null data, which can ONLY be formatted with a null picture).
//
if (sSource == null) {
if (isNullPicture(sMask))
bSuccess = formatNull(sSource, sMask, msLocale, sResult);
}
// Watson bug 1660883, allows text{} to return an empty string and success.
else if (sSource.length() == 0) {
if (isEmptyPicture(sMask)) {
bSuccess = true;
sResult.setLength(0);
}
// don't need to check the null picture because the input
// to formatNull must be a null string, instead assume failure.
}
else if (isNumericPicture(sMask))
bSuccess = formatNumeric(sSource, sMask, msLocale, sResult);
else if (isNullPicture(sMask))
bSuccess = formatNull(sSource, sMask, msLocale, sResult);
else if (isZeroPicture(sMask))
bSuccess = formatZero(sSource, sMask, msLocale, sResult);
else if (isDateTimePicture(sMask, sDateMask, sTimeMask))
bSuccess = formatDateTime(sSource, sMask,
sDateMask.toString(),
sTimeMask.toString(),
msLocale, sResult, true);
else if (isDatePicture(sMask))
bSuccess = formatDate(sSource, sMask, msLocale, sResult, true);
else if (isTimePicture(sMask))
bSuccess = formatTime(sSource, sMask, msLocale, sResult, true);
else if (isTextPicture(sMask))
bSuccess = formatText(sSource, sMask, msLocale, sResult);
else if (isCompoundPicture(sMask))
bSuccess = formatCompound(sSource, sMask, msLocale, sResult);
else
bSuccess = false;
//
// Were done one first successful format.
//
if (bSuccess)
break;
}
return bSuccess;
}
/**
* Parses a given data source according to the given picture.
*
* @param sSource
* the source data.
* @param sPicture
* the parsing picture.
* @param pbSuccess
* the canonical result.
* @return
* the string result.
*/
public String parse(String sSource, String sPicture, BooleanHolder pbSuccess) {
//
// If no formatting picture found Then return source.
//
if (StringUtils.isEmpty(sPicture)) {
return sSource;
}
//
// If source is null string Then return false.
//
if (sSource == null) {
if (pbSuccess != null)
pbSuccess.value = true;
return null;
}
//
// Segregate alternate pictures into a list.
//
List oAlt = new ArrayList();
if (! getAlternates(sPicture, oAlt))
return "";
//
// For each alternate picture Do ...
//
boolean bSuccess = false;
StringHolder sResult = new StringHolder();
StringBuilder sDateMask = new StringBuilder();
StringBuilder sTimeMask = new StringBuilder();
for (int i = 0; i < oAlt.size(); i++) {
String sMask = interpret(oAlt.get(i));
sDateMask.setLength(0);
sTimeMask.setLength(0);
// JavaPort: The following line of C++ implementation has the
// unintended side-effect of changing whether the result is null
// or empty, depending on the length of the input.
// This code emulates that behaviour, but it is probably a bug.
//sResult.ReAlloc(sSource.Length(), FALSE, sSource.GetThreadId());
sResult.value = sSource.length() > 0 ? "" : null;
//
// Try parsing each alternate in picture category order.
//
if (isNumericPicture(sMask))
bSuccess = parseNumeric(sSource, sMask, msLocale, sResult);
else if (isNullPicture(sMask))
bSuccess = parseNull(sSource, sMask, msLocale, sResult);
else if (isZeroPicture(sMask))
bSuccess = parseZero(sSource, sMask, msLocale, sResult);
else if (isDateTimePicture(sMask, sDateMask, sTimeMask))
bSuccess = parseDateTime(sSource, sMask, sDateMask.toString(), sTimeMask.toString(), msLocale, sResult, true);
else if (isDatePicture(sMask))
bSuccess = parseDate(sSource, sMask, msLocale, sResult, true);
else if (isTimePicture(sMask))
bSuccess = parseTime(sSource, sMask, msLocale, sResult, true);
else if (isTextPicture(sMask))
bSuccess = parseText(sSource, sMask, msLocale, sResult);
else if (isCompoundPicture(sMask))
bSuccess = parseCompound(sSource, sMask, msLocale, sResult);
else
bSuccess = false;
//
// Were done one first successful parse.
//
if (bSuccess)
break;
}
if (pbSuccess != null)
pbSuccess.value = bSuccess;
return sResult.value;
}
/**
* Converts a F99 picture to an equivalent XFA picture.
* F99 pictures contain either double quote enclosed literals,
* or single quote enclosed literals. Change all to single
* quote enclosed literals, suitably escaping any embedded
* double quotes.
*
* @param sPicture
* the source picture.
* @return
* the equivalent XFA picture.
*/
public static String FF99ToXFA(String sPicture) {
int picLen = sPicture.length();
int needs_escapes = 0;
for (int i = 0; i < picLen; i++) {
char chr = sPicture.charAt(i);
if (chr == '\"' || chr == '\'')
needs_escapes++;
}
StringBuilder sRes = new StringBuilder(sPicture.length() + needs_escapes);
final int START = 0;
final int SGLQUOTE = 1;
final int DBLQUOTE = 2;
//
// A finite state machine to translate
// '..."...' to '..."...' and
// "...'..." to '...''...'.
//
int eState = START;
for (int i = 0; i < picLen; ) {
char chr = sPicture.charAt(i++);
if (eState == START) {
if (chr == '\'') {
eState = SGLQUOTE;
}
else if (chr == '\"') {
eState = DBLQUOTE;
chr = '\'';
}
}
else if (eState == SGLQUOTE) {
if (chr == '\'') {
eState = START;
}
else if (chr == '\"') {
sRes.append(chr);
}
}
else if (eState == DBLQUOTE) {
if (chr == '\"') {
eState = START;
chr = '\'';
}
else if (chr == '\'') {
sRes.append(chr);
}
}
sRes.append(chr);
}
return sRes.toString();
}
/**
* Formats a given data source according to the given compound picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the compound picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting string, which may be empty upon error.
* @return
* This method is not operational!
*/
public static boolean formatCompound(String sSource, String sPicture,
String sLocale, StringBuilder sResult) {
MsgFormatPos oMessage = new MsgFormatPos(ResId.UNSUPPORTED_OPERATION, "PictureFmt#formatCompound");
oMessage.format("formatCompound");
throw new ExFull(oMessage);
}
/**
* Formats a given data source according to the given date picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the date picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting string, which may be empty upon error.
* @param bAsLocal
* interpret the source data as a local date when true,
* and a GMT date when false.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean formatDate(String sSource,
String sPicture,
String sLocale,
StringBuilder sResult,
boolean bAsLocal) {
assert(sResult != null);
sResult.setLength(0);
if (sSource == null)
return false;
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sDateMask = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sDateMask)
&& ! sCategory.toString().equals(gsDate)) {
sDateMask.replace(0, sDateMask.length(), sPicture);
}
ISODate oDate = new ISODate(sSource, sLoc.toString());
if (oDate.isValid()) {
if (bAsLocal)
oDate.setLocalDate();
sResult.append(oDate.format(sDateMask.toString()));
}
return sResult.length() != 0;
}
/**
* Formats a given data source according to the given datetime picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the datetime picture.
* @param sDateMask
* the date sub-picture.
* @param sTimeMask
* the time sub-picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting string, which may be empty upon error.
* @param bAsLocal
* interpret the data source as locale datetime when true,
* and GMT datetime when false.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean formatDateTime(String sSource, String sPicture,
String sDateMask, String sTimeMask, String sLocale,
StringBuilder sResult, boolean bAsLocal) {
assert(sResult != null);
sResult.setLength(0);
if (sSource == null)
return false;
boolean bValid = true;
String sFormattedDate = "";
String sFormattedTime = "";
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sDateSubMask = new StringBuilder();
StringBuilder sTimeSubMask = new StringBuilder();
if (! isSubPicture(sDateMask, 0, sCategory, sLoc, sDateSubMask)
&& ! sCategory.toString().startsWith(gsDate)) {
sDateSubMask.replace(0, sDateSubMask.length(), sDateMask);
}
String sDateLocale = sLoc.toString();
sLoc.replace(0, sLoc.length(), sLocale);
if (! isSubPicture(sTimeMask, 0, sCategory, sLoc, sTimeSubMask)
&& ! sCategory.toString().startsWith(gsTime)) {
sTimeSubMask.replace(0, sTimeSubMask.length(), sTimeMask);
}
String sTimeLocale = sLoc.toString();
int nFound = sSource.indexOf('T');
if (nFound >= 0) {
//
// Extract the date and time data.
//
String sSourceDate = sSource.substring(0, nFound);
String sSourceTime = sSource.substring(nFound + 1);
//
// Vantive 571490. Pass locale into ISODateTime constructor
//
ISODate oISODate = new ISODate(sSourceDate, sDateLocale);
ISOTime oISOTime = new ISOTime(sSourceTime, sTimeLocale);
bValid = oISODate.isValid() && oISOTime.isValid();
if (bValid) {
if (bAsLocal) {
oISODate.setLocalDate();
oISOTime.setLocalTime();
}
sFormattedDate = oISODate.format(sDateSubMask.toString());
sFormattedTime = oISOTime.format(sTimeSubMask.toString());
}
}
else if ((nFound = sSource.indexOf(' ')) >= 0) {
//
// Extract the date and time data.
//
String sSourceDate = sSource.substring(0, nFound);
String sSourceTime = sSource.substring(nFound + 1);
//
// Ensure source is in source locale's short date time format.
//
String sSourceDateFmt = new LcData(sLocale).getDateFormat(1);
String sSourceTimeFmt = new LcData(sLocale).getTimeFormat(1);
LcDate oDate = new LcDate(sSourceDate, sSourceDateFmt, sDateLocale,
LcDate.DEFAULT_CENTURY_SPLIT);
LcTime oTime = new LcTime(sSourceTime, sSourceTimeFmt, sTimeLocale);
bValid = oDate.isValid() && oTime.isValid();
if (bValid) {
if (bAsLocal) {
oDate.setLocalDate();
oTime.setLocalTime();
}
sFormattedDate = oDate.format(sDateSubMask.toString());
sFormattedTime = oTime.format(sTimeSubMask.toString());
}
}
else
bValid = false;
if (bValid) {
if (sPicture.startsWith(gsDateTime)) {
sResult.append(sDateMask);
sResult.append(' ');
sResult.append(sTimeMask);
}
else {
sResult.replace(0, sResult.length(), sPicture);
}
//
// Check for possible literal strings in the mask
//
int nEnd;
int nCharPos = 0;
while (nCharPos < sResult.length()) {
int nPrevPos = nCharPos;
char cChar = sResult.charAt(nCharPos++);
if (cChar == '\'') {
StringBuilder sLiteral = new StringBuilder();
nEnd = nCharPos;
int n = getLiteralSubstr(sResult.toString(), cChar,
nEnd, sLiteral);
if (n > nEnd) {
nEnd = n;
sResult.replace(nPrevPos, nEnd, sLiteral.toString());
//
// Watson #1278748. Subtract 2 since 2 quote chars
// were removed.
//
nCharPos = nEnd - 2;
}
}
}
//
// Vantive 571490. Search for 'date' rather than 'date{'
// as we might have a locale in the format
//
int nDateTag = sResult.indexOf(gsDate);
nEnd = sResult.substring(nDateTag, sResult.length()).indexOf('}');
sResult.replace(nDateTag, nDateTag + nEnd + 1, sFormattedDate);
int nTimeTag = sResult.indexOf(gsTime);
nEnd = sResult.substring(nTimeTag, sResult.length()).indexOf('}');
sResult.replace(nTimeTag, nTimeTag + nEnd + 1, sFormattedTime);
}
return bValid;
}
/**
* Formats a given data source according to the given null picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the null picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting string, which may be empty upon error.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean formatNull(String sSource, String sPicture,
String sLocale, StringBuilder sResult) {
assert(sResult != null);
sResult.setLength(0);
if (sSource != null)
return false;
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sNullMask = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sNullMask)
&& ! sCategory.toString().equals(gsNull)) {
sNullMask.replace(0, sNullMask.length(), sPicture);
}
if (sNullMask.length() == 0)
return true;
LcNum oNum = new LcNum("0", sLoc.toString());
if (oNum.isValid())
sResult.append(oNum.format(sNullMask.toString(), null));
return sResult.length() != 0;
}
/**
* Formats a given data source according to the given numeric picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the numeric picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting string, which may be empty upon error.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean formatNumeric(String sSource, String sPicture,
String sLocale, StringBuilder sResult) {
assert(sResult != null);
sResult.setLength(0);
if (sSource == null)
return false;
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sNumMask = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sNumMask)
&& ! sCategory.toString().equals(gsNum)) {
sNumMask.replace(0, sNumMask.length(), sPicture);
}
LcNum oNum = new LcNum(sSource, sLoc.toString());
if (oNum.isValid())
sResult.append(oNum.format(sNumMask.toString(), null));
return sResult.length() != 0;
}
/**
* Formats a given data source according to the given text picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the text picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting string, which may be empty upon error.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean formatText(String sSource, String sPicture,
String sLocale, StringBuilder sResult) {
assert(sResult != null);
sResult.setLength(0);
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sTextMask = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sTextMask)
&& ! sCategory.toString().equals(gsText)) {
sTextMask.replace(0, sTextMask.length(), sPicture);
}
LcText oText = new LcText(sSource, sLoc.toString());
if (oText.isValid())
sResult.append(oText.format(sTextMask.toString()));
return sResult.length() != 0;
}
/**
* Formats a given data source according to the given time picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the time picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting string, which may be empty upon error.
* @param bAsLocal
* interpret the data source as local time when true,
* and a GMT time when false.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean formatTime(String sSource, String sPicture,
String sLocale, StringBuilder sResult, boolean bAsLocal) {
assert(sResult != null);
sResult.setLength(0);
if (sSource == null)
return false;
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sTimeMask = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sTimeMask)
&& ! sCategory.toString().equals(gsTime)) {
sTimeMask.replace(0, sTimeMask.length(), sPicture);
}
ISOTime oTime = new ISOTime(sSource, sLoc.toString());
if (oTime.isValid()) {
if (bAsLocal)
oTime.setLocalTime();
sResult.append(oTime.format(sTimeMask.toString()));
}
return sResult.length() != 0;
}
/**
* Formats a given data source according to the given zero picture
* under the given locale.
*
* @param sSource
* the source data. It must be empty or zero!
* @param sPicture
* the zero picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting string, which may be empty upon error.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean formatZero(String sSource, String sPicture,
String sLocale, StringBuilder sResult) {
assert(sResult != null);
sResult.setLength(0);
if (sSource == null)
return false;
if (! sSource.equals("0"))
return false;
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sZeroMask = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sZeroMask)
&& ! sCategory.toString().equals(gsZero)) {
sZeroMask.replace(0, sZeroMask.length(), sPicture);
}
if (sZeroMask.length() == 0)
return true;
LcNum oNum = new LcNum(sSource, sLoc.toString());
if (oNum.isValid())
sResult.append(oNum.format(sZeroMask.toString(), null));
return sResult.length() != 0;
}
/**
* Gets all the picture alternatives.
*
* @param sSource
* the source picture. Alternate pictures are each
* separated by the vertical bar '|' character.
* @param oAlternates
* the object to be populated with all the alternate
* pictures found in the source.
* @return
* boolean true upon success and false if the source picture is
* invalid.
*/
@FindBugsSuppress(code="DB")
public static boolean getAlternates(String sSource, List oAlternates) {
char prevChr = 0;
int chrCnt = 0;
boolean inQuoted = false;
boolean inQuoteQuoted = false;
//
// Foreach each character of the picture Do ...
//
int picLen = sSource.length();
int i = 0;
int k = i;
while (i < picLen) {
int j = i;
char chr = sSource.charAt(i++);
//
// If seen a quote within a quoted string ...
//
if (inQuoteQuoted) {
if (chr == '\'') { // cases like '...''
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;
}
chrCnt++;
prevChr = chr;
}
//
// Elif start of a quoted string ...
//
else if (chr == '\'') {
if (chrCnt > 0) { // cases like ...X'
if (prevChr == '|') {
oAlternates.add(sSource.substring(k, j - 1));
k = j;
}
chrCnt = 0;
prevChr = 0;
}
inQuoted = true;
}
//
// Elif start of a metacharacter ...
//
else if (DateTimeUtil.matchChr(LcNum.NUMERIC_PICTURE_SYMBOLS, chr)
|| DateTimeUtil.matchChr(LcText.TEXT_PICTURE_SYMBOLS, chr)
|| DateTimeUtil.matchChr(LcDate.DATE_PICTURE_SYMBOLS, chr)
|| DateTimeUtil.matchChr(LcTime.TIME_PICTURE_SYMBOLS, chr)
|| ('a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z')) {
if (chr != prevChr) {
if (chrCnt > 0) {
if (prevChr == '|') {
oAlternates.add(sSource.substring(k, j - 1));
k = j;
}
chrCnt = 0;
}
prevChr = chr;
}
chrCnt++;
}
//
// Elif start of a literal ...
//
else if (chrCnt > 0) { // cases like AA-
if (prevChr == '|') {
oAlternates.add(sSource.substring(k, j - 1));
k = j;
}
chrCnt = 1;
prevChr = chr;
}
//
// Else yet another literal ...
//
else {
if (prevChr == '|') {
oAlternates.add(sSource.substring(k, j - 1));
k = j;
}
chrCnt = 1;
prevChr = chr;
}
}
//
// Ensure quoted string is terminated.
//
if (inQuoteQuoted)
inQuoted = false;
if (inQuoted) {
int n = oAlternates.size();
while (n-- > 0)
oAlternates.remove(n);
return false;
}
//
// Handle any remaining items in the picture.
//
if (prevChr > 0 && chrCnt > 0) {
assert(i == picLen);
oAlternates.add(sSource.substring(k, i));
}
return true;
}
/**
* Gets the locale given a valid picture and category.
*
* @param sPicture
* the valid picture.
* @param sCategory
* the picture category: one of "date", "time", "num", "text",
* "null" or "zero".
* @param sLocale
* the returned locale.
* @return
* boolean true if the source picture is semantically valid,
* and false otherwise.
*/
public static boolean getLocaleFromPicture(String sPicture,
String sCategory,
StringBuilder sLocale) {
int nPictIndex = 0;
int nPrevIndex = 0;
int nCategoryBeg, nCategoryEnd;
int nLocaleBeg, nLocaleEnd;
sLocale.setLength(0);
while (nPictIndex < sPicture.length()) {
nPrevIndex = nPictIndex;
char cChar = sPicture.charAt(nPictIndex++);
if (cChar == '\'') {
int nIndex = getLiteralSubstr(sPicture, cChar,
nPictIndex, null);
if (nIndex == nPictIndex) {
return false;
}
nPictIndex = nIndex;
continue;
}
else if (isCharAlphabetic(cChar)) {
nCategoryBeg = nPrevIndex;
if (nPictIndex == sPicture.length())
return false;
nPrevIndex = nPictIndex;
while (isCharAlphabetic(sPicture.charAt(nPictIndex++))) {
nPrevIndex = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
}
nCategoryEnd = nPrevIndex;
nLocaleBeg = nLocaleEnd = nPictIndex;
nPictIndex = nPrevIndex;
cChar = sPicture.charAt(nPictIndex++);
if (cChar == '(') {
nLocaleBeg = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
nPrevIndex = nPictIndex;
while (sPicture.charAt(nPictIndex++) != ')') {
nPrevIndex = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
}
nLocaleEnd = nPrevIndex;
cChar = sPicture.charAt(nPictIndex++);
}
if (cChar == '{') {
while (true) {
if (nPictIndex == sPicture.length())
return false;
cChar = sPicture.charAt(nPictIndex++);
if (cChar == '\'') {
int nIndex = getLiteralSubstr(sPicture, cChar,
nPictIndex, null);
if (nIndex == nPictIndex)
return false;
nPictIndex = nIndex;
}
else if (cChar == '}') {
break;
}
}
String sCandidate = sPicture.substring(nCategoryBeg,
nCategoryEnd);
if (sCandidate.equals(sCategory)) {
if (sLocale.length() != 0)
return false;
sLocale.replace(0, sLocale.length(),
sPicture.substring(nLocaleBeg, nLocaleEnd));
continue;
}
}
return false;
}
else if (isCharNumeric(cChar)) {
continue;
}
else if (isCharLiteral(cChar)) {
continue;
}
else {
return false;
}
}
return true;
}
/**
* Determines if the given picture has a sub-picture. If so, the
* sub-picture's components are returned. Subpictures are of the form:
* category{subpicture}
or
* category(locale){subpicture}
.
*
* @param sPicture
* the picture.
* @param nPictIndex
* the starting picture index.
* @param sCategory
* the picture's category.
* @param sLocale
* the picture's locale.
* @param sSubMask
* the picture's sub-picture.
* @return
* boolean true if there's a sub-picture, and false otherwise.
*/
public static boolean hasSubPicture(String sPicture, int nPictIndex,
StringBuilder sCategory,
StringBuilder sLocale,
StringBuilder sSubMask) {
int nLiteralBeg, nLiteralEnd;
int nCategoryBeg, nCategoryEnd;
int nLocaleBeg, nLocaleEnd;
int nSubMaskBeg, nSubMaskEnd;
String sSubCategory = "";
while (nPictIndex < sPicture.length()) {
int nPrevIndex = nPictIndex;
char cChar = sPicture.charAt(nPictIndex++);
if (cChar == '\'') {
nLiteralBeg = nPrevIndex;
int nIndex = getLiteralSubstr(sPicture, cChar,
nPictIndex, null);
if (nIndex == nPictIndex)
return false;
nPictIndex = nIndex;
nLiteralEnd = nPictIndex - 1;
sSubMask.append(sPicture, nLiteralBeg, nLiteralEnd + 1);
continue;
}
else if (isCharAlphabetic(cChar)) {
nCategoryBeg = nPrevIndex;
if (nPictIndex == sPicture.length())
return false;
nPrevIndex = nPictIndex;
while (isCharAlphabetic(sPicture.charAt(nPictIndex++))) {
nPrevIndex = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
}
nCategoryEnd = nPrevIndex;
nLocaleBeg = nLocaleEnd = nPictIndex;
nPictIndex = nPrevIndex;
cChar = sPicture.charAt(nPictIndex++);
if (cChar == '.') {
int nSubCategoryBeg = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
nPrevIndex = nPictIndex;
while (true) {
cChar = sPicture.charAt(nPictIndex++);
if (cChar == '(' || cChar == '{')
break;
nPrevIndex = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
}
int nSubCategoryEnd = nPrevIndex;
sSubCategory = sPicture.substring(nSubCategoryBeg,
nSubCategoryEnd);
}
if (cChar == '(') {
nLocaleBeg = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
nPrevIndex = nPictIndex;
while (sPicture.charAt(nPictIndex++) != ')') {
nPrevIndex = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
}
nLocaleEnd = nPrevIndex;
if (nLocaleEnd > nLocaleBeg)
sLocale.replace(0, sLocale.length(),
sPicture.substring(nLocaleBeg, nLocaleEnd));
cChar = sPicture.charAt(nPictIndex++);
}
if (cChar == '{') {
nSubMaskBeg = nPictIndex;
while (true) {
if (nPictIndex >= sPicture.length())
return false;
nPrevIndex = nPictIndex;
cChar = sPicture.charAt(nPictIndex++);
if (cChar == '\'') {
int nIndex = getLiteralSubstr(sPicture, cChar,
nPictIndex, null);
if (nIndex == nPictIndex)
return false;
nPictIndex = nIndex;
}
else if (cChar == '}') {
nSubMaskEnd = nPrevIndex;
break;
}
}
String sCandidate = sPicture.substring(nCategoryBeg,
nCategoryEnd);
if (sCandidate.equals(gsNull)
|| sCandidate.equals(gsZero)
|| sCandidate.equals(gsText)) {
sCategory.append(sCandidate);
if (sLocale.length() == 0)
sLocale.append(LcLocale.DEFAULT_LOCALE);
sSubMask.append(sPicture, nSubMaskBeg, nSubMaskEnd);
continue;
}
else if (sCandidate.equals(gsDate)
|| sCandidate.equals(gsTime)) {
sCategory.append(sCandidate);
if (sLocale.length() == 0)
sLocale.append(LcLocale.DEFAULT_LOCALE);
String sFmt = null;
if (sSubCategory.length() == 0) {
sFmt = sPicture.substring(nSubMaskBeg, nSubMaskEnd);
}
else if (sSubCategory.equals(gsShort)) {
LcData oData = new LcData(sLocale.toString());
if (sCandidate.equals(gsDate))
sFmt = oData.getDateFormat(LcData.SHORT_FMT);
else
sFmt = oData.getTimeFormat(LcData.SHORT_FMT);
}
else if (sSubCategory.equals(gsMedium)) {
LcData oData = new LcData(sLocale.toString());
if (sCandidate.equals(gsDate))
sFmt = oData.getDateFormat(LcData.MED_FMT);
else
sFmt = oData.getTimeFormat(LcData.MED_FMT);
}
else if (sSubCategory.equals(gsLong)) {
LcData oData = new LcData(sLocale.toString());
if (sCandidate.equals(gsDate))
sFmt = oData.getDateFormat(LcData.LONG_FMT);
else
sFmt = oData.getTimeFormat(LcData.LONG_FMT);
}
else if (sSubCategory.equals(gsFull)) {
LcData oData = new LcData(sLocale.toString());
if (sCandidate.equals(gsDate))
sFmt = oData.getDateFormat(LcData.FULL_FMT);
else
sFmt = oData.getTimeFormat(LcData.FULL_FMT);
}
else if (sSubCategory.equals(gsDefault)) {
LcData oData = new LcData(sLocale.toString());
if (sCandidate.equals(gsDate))
sFmt = oData.getDateFormat(LcData.DEFLT_FMT);
else
sFmt = oData.getTimeFormat(LcData.DEFLT_FMT);
}
else {
return false;
}
sSubMask.append(sFmt);
continue;
}
else if (sCandidate.equals(gsNum)) {
sCategory.append(sCandidate);
if (sLocale.length() == 0)
sLocale.append(LcLocale.DEFAULT_LOCALE);
LcData oData = new LcData(sLocale.toString());
String sFmt = null;
if (sSubCategory.length() == 0) {
sFmt = sPicture.substring(nSubMaskBeg, nSubMaskEnd);
}
else if (sSubCategory.equals(gsInteger)) {
int nOptn = LcData.WITH_GROUPINGS;
sFmt = oData.getNumberFormat(LcData.INTEGRAL_FMT,
nOptn);
}
else if (sSubCategory.equals(gsDecimal)) {
int nOptn = LcData.withPrecision(~0)
| LcData.WITH_GROUPINGS | LcData.KEEP_NINES;
sFmt = oData.getNumberFormat(LcData.DECIMAL_FMT,
nOptn);
}
else if (sSubCategory.equals(gsCurrency)) {
int nOptn = LcData.withPrecision(~0)
| LcData.WITH_GROUPINGS | LcData.KEEP_NINES;
sFmt = oData.getNumberFormat(LcData.CURRENCY_FMT,
nOptn);
}
else if (sSubCategory.equals(gsPercent)) {
int nOptn = LcData.WITH_GROUPINGS;
sFmt = oData.getNumberFormat(LcData.PERCENT_FMT,
nOptn);
}
else {
return false;
}
sSubMask.append(sFmt);
continue;
}
else if (sCandidate.equals(gsDateTime)) {
sCategory.append(sCandidate);
if (sLocale.length() == 0)
sLocale.append(LcLocale.DEFAULT_LOCALE);
LcData oData = new LcData(sLocale.toString());
int nOptn = LcData.WITH_CATEGORIES;
String sFmt = null;
if (sSubCategory.equals(gsShort)) {
sFmt = oData.getDateTimeFormat(LcData.SHORT_FMT,
nOptn);
}
else if (sSubCategory.equals(gsMedium)) {
sFmt = oData.getDateTimeFormat(LcData.MED_FMT,
nOptn);
}
else if (sSubCategory.equals(gsLong)) {
sFmt = oData.getDateTimeFormat(LcData.LONG_FMT,
nOptn);
}
else if (sSubCategory.equals(gsFull)) {
sFmt = oData.getDateTimeFormat(LcData.FULL_FMT,
nOptn);
}
else if (sSubCategory.equals(gsDefault)) {
sFmt = oData.getDateTimeFormat(LcData.DEFLT_FMT,
nOptn);
}
else {
return false;
}
sSubMask.append(sFmt);
continue;
}
}
return false;
}
else if (isCharNumeric(cChar)) {
sSubMask.append(cChar);
continue;
}
else if (isCharLiteral(cChar)) {
sSubMask.append(cChar);
continue;
}
else {
return false;
}
}
return true;
}
/*
* Interprets any Unicode escape sequences (c;uXXXX) in a given string.
* @param
* the source string.
* @return
* the interpreted string.
*/
private static String interpret(String sSrc) {
int sSrcLen = sSrc.length();
StringBuilder sDst = new StringBuilder();
for (int i = 0; i < sSrcLen; ) {
char ch = sSrc.charAt(i++);
if (ch == '\\' && sSrc.charAt(i + 1) == 'u' && i + 5 < sSrcLen
&& isCharHexDigit(sSrc.charAt(i + 2))
&& isCharHexDigit(sSrc.charAt(i + 3))
&& isCharHexDigit(sSrc.charAt(i + 4))
&& isCharHexDigit(sSrc.charAt(i + 5))) {
String sHex = sSrc.substring(i + 2, i + 6);
try {
char hHex = (char) Integer.parseInt(sHex, 16);
sDst.append(hHex);
i += 5;
} catch (NumberFormatException e) {
assert(e != null);
}
}
else {
sDst.append(ch);
}
}
return sDst.toString();
}
private static boolean isCharAlphabetic(char cChar) {
return Character.isLetter(cChar);
}
private static boolean isCharHexDigit(char cChar) {
return Character.isDigit(cChar)
|| ('a' <= Character.toLowerCase(cChar)
&& Character.toLowerCase(cChar) <= 'f');
}
private static boolean isCharLiteral(char cChar) {
return DateTimeUtil.matchChr(gsPictureLiterals, cChar);
}
private static boolean isCharNumeric(char cChar) {
return (cChar == '0' || cChar == '9');
}
private static boolean isCompoundPicture(String sPicture) {
boolean bValid = true;
int nCharIndex = 0;
StringBuilder sCategory = new StringBuilder();
StringBuilder sLoc = new StringBuilder();
StringBuilder sMask = new StringBuilder();
while (nCharIndex < sPicture.length()) {
int nPrevIndex = nCharIndex;
char cChar = sPicture.charAt(nCharIndex++);
// If this is an open quote, then skip the substring.
if (cChar == '\'') {
// Skip by the literal string including trailing quote.
int nIndex = getLiteralSubstr(sPicture, cChar,
nCharIndex, null);
if (nIndex > nCharIndex) {
nCharIndex = nIndex;
continue;
}
bValid = false;
}
else if (isSubPicture(sPicture, nPrevIndex,
sCategory, sLoc, sMask)) {
if (sCategory.toString().startsWith(gsDate))
bValid = isDatePicture(sMask.toString());
else if (sCategory.toString().startsWith(gsTime))
bValid = isTimePicture(sMask.toString());
else if (sCategory.toString().equals(gsText))
bValid = isTextPicture(sMask.toString());
else if (sCategory.toString().equals(gsNull))
bValid = isNullPicture(sMask.toString());
else if (sCategory.toString().equals(gsZero))
bValid = isZeroPicture(sMask.toString());
else if (sCategory.toString().equals(gsNum))
bValid = isNumericPicture(sMask.toString());
if (! bValid)
break;
nPrevIndex += sCategory.length();
if (sLoc.length() != 0)
nPrevIndex += sLoc.length() + 2;
nPrevIndex += sMask.length() + 2;
nCharIndex = nPrevIndex;
}
else if (isCharNumeric(cChar)) {
continue;
}
else if (isCharLiteral(cChar)) {
continue;
}
else {
bValid = false;
break;
}
}
return bValid;
}
/**
* Determines if the given picture is a date picture.
*
* @param sPicture
* the source picture.
* @return
* boolean true if the source picture is syntactically valid,
* and false otherwise.
*/
public static boolean isDatePicture(String sPicture) {
return isPicture(sPicture, gsDate, LcDate.DATE_PICTURE_SYMBOLS);
}
/**
* Determines if the given date picture is (semantically) valid.
*
* @param sPicture
* the date picture.
* @return
* boolean true if the source picture is semantically valid,
* and false otherwise.
*/
public static boolean isDatePictureValid(String sPicture) {
StringBuilder sLoc = new StringBuilder(LcLocale.DEFAULT_LOCALE);
StringBuilder sCat = new StringBuilder();
StringBuilder sPict = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCat, sLoc, sPict)
|| ! sCat.toString().equals(gsDate)) {
sPict.replace(0, sPict.length(), sPicture);
}
//
// Pick any date -- current date will do.
//
LcDate today = new LcDate(1, sLoc.toString(),
LcDate.DEFAULT_CENTURY_SPLIT);
//
// Try parsing today's date as formatted.
//
return today.parse(today.format(sPict.toString()),sPict.toString());
}
/**
* Determines if the given picture is a date time picture.
*
* @param sPicture
* the source picture.
* @param sDateMask
* the returned date picture found in the given source picture.
* @param sTimeMask
* the returned time picture found in the given source picture.
* @return
* boolean true if the source picture is syntactically valid,
* and false otherwise.
*/
public static boolean isDateTimePicture(String sPicture,
StringBuilder sDateMask, StringBuilder sTimeMask) {
assert(sDateMask != null);
assert(sTimeMask != null);
sDateMask.setLength(0);
sTimeMask.setLength(0);
for (int i = 0; i < sPicture.length(); ) {
StringBuilder sCategory = new StringBuilder();
StringBuilder sLoc = new StringBuilder();
StringBuilder sMask = new StringBuilder();
int j = i;
char cChar = sPicture.charAt(i++);
if (cChar == '\'') {
int k = getLiteralSubstr(sPicture, cChar, i, null);
if (k > i) {
i = k;
continue;
}
}
else if (isCharLiteral(cChar) || cChar == 'T') {
continue;
}
else if (isSubPicture(sPicture, j, sCategory, sLoc, sMask)) {
if (sCategory.toString().startsWith(gsDate)
&& isDatePicture(sMask.toString())) {
j += sCategory.length();
if (sLoc.length() != 0)
j += sLoc.length() + 2;
if (sCategory.indexOf(".") < 0)
j += sMask.length();
j += 2;
sDateMask.append(sCategory);
if (sLoc.length() != 0) {
sDateMask.append('(');
sDateMask.append(sLoc);
sDateMask.append(')');
}
sDateMask.append('{');
if (sCategory.indexOf(".") < 0)
sDateMask.append(sMask);
sDateMask.append('}');
i = j;
continue;
}
if (sCategory.toString().startsWith(gsTime)
&& isTimePicture(sMask.toString())) {
j += sCategory.length();
if (sLoc.length() != 0)
j += sLoc.length() + 2;
if (sCategory.indexOf(".") < 0)
j += sMask.length();
j += 2;
sTimeMask.append(sCategory);
if (sLoc.length() != 0) {
sTimeMask.append('(');
sTimeMask.append(sLoc);
sTimeMask.append(')');
}
sTimeMask.append('{');
if (sCategory.indexOf(".") < 0)
sTimeMask.append(sMask);
sTimeMask.append('}');
i = j;
continue;
}
if (sCategory.toString().startsWith(gsDateTime)) {
if (! isDateTimePicture(sMask.toString(),
sDateMask, sTimeMask))
return false;
j += sPicture.length();
i = j;
continue;
}
}
return false;
}
return (sDateMask.length() != 0 && sTimeMask.length() != 0);
}
/**
* Determimes if the given datetime picture is (semantically) valid.
*
* @param sPicture
* the datetime picture.
* @return
* boolean true if the source picture is semantically valid,
* and false otherwise.
*/
public static boolean isDateTimePictureValid(String sPicture) {
StringBuilder sDatePict = new StringBuilder();
StringBuilder sTimePict = new StringBuilder();
if (! isDateTimePicture(sPicture, sDatePict, sTimePict))
return false;
StringBuilder sLoc = new StringBuilder(LcLocale.DEFAULT_LOCALE);
StringBuilder sCat = new StringBuilder();
StringBuilder sPict = new StringBuilder();
if (hasSubPicture(sDatePict.toString(), 0, sCat, sLoc, sPict))
sDatePict.replace(0, sDatePict.length(), sPict.toString());
//
// Pick any date and try parsing today's date as formatted.
//
LcDate today = new LcDate(1, sLoc.toString(),
LcDate.DEFAULT_CENTURY_SPLIT);
if (! today.parse(today.format(sDatePict.toString()), sDatePict.toString()))
return false;
//
// Repeat the above on the time component.
//
sLoc = new StringBuilder(LcLocale.DEFAULT_LOCALE);
sPict.setLength(0);
sCat.setLength(0);
if (hasSubPicture(sTimePict.toString(), 0, sCat, sLoc, sPict))
sTimePict.replace(0, sTimePict.length(), sPict.toString());
//
// Pick any time and try parsing current time as formatted.
//
LcTime now = new LcTime(1, sLoc.toString());
if (! now.parse(now.format(sTimePict.toString()), sTimePict.toString()))
return false;
return true;
}
/**
* Determines if the given picture is a null picture.
*
* @param sPicture
* the source picture.
* @return
* boolean true if the source picture is syntactically valid,
* and false otherwise.
*/
public static boolean isNullPicture(String sPicture) {
StringBuilder sCat = new StringBuilder();
StringBuilder sLoc = new StringBuilder();
StringBuilder sMask = new StringBuilder();
return isSubPicture(sPicture, 0, sCat, sLoc, sMask)
&& sCat.toString().equals(gsNull);
}
/**
* Determines if the given picture is an empty picture.
*
* @param sPicture
* the source picture.
* @return
* boolean true if the source picture is syntactically valid,
* and false otherwise.
*/
public static boolean isEmptyPicture(String sPicture) {
// A null picture must be named; we never infer it's presence
StringBuilder sCat = new StringBuilder();
StringBuilder sLoc = new StringBuilder();
StringBuilder sMask = new StringBuilder();
return (isSubPicture(sPicture, 0, sCat, sLoc, sMask)
&& (sMask.toString().length() == 0)
&& !sCat.toString().equals(gsNull)
&& !sCat.toString().equals(gsZero));
}
/**
* Determimes if the given null picture is (semantically) valid.
*
* @param sPicture
* the null picture.
* @return
* boolean true if the source picture is semantically valid,
* and false otherwise.
*/
public static boolean isNullPictureValid(String sPicture) {
StringBuilder sLoc = new StringBuilder(LcLocale.DEFAULT_LOCALE);
StringBuilder sCat = new StringBuilder();
StringBuilder sPict = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCat, sLoc, sPict)
|| ! sCat.toString().equals(gsNull)) {
sPict.replace(0, sPict.length(), sPicture);
}
//
// Pick a number -- one will do (zero will not).
//
LcNum one = new LcNum("1", sLoc.toString());
//
// Try parsing number as formatted.
//
return one.parse(one.format(sPict.toString(), null), sPict.toString());
}
/**
* Determimes if the given picture is a numeric picture.
*
* @param sPicture
* the source picture.
* @return
* boolean true if the source picture is syntactically valid,
* and false otherwise.
*/
public static boolean isNumericPicture(String sPicture) {
return isPicture(sPicture, gsNum, LcNum.NUMERIC_PICTURE_SYMBOLS);
}
/**
* Determimes if the given numeric picture is (semantically) valid.
*
* @param sPicture
* the numeric picture.
* @return
* boolean true if the source picture is semantically valid,
* and false otherwise.
*/
public static boolean isNumericPictureValid(String sPicture) {
StringBuilder sLoc = new StringBuilder(LcLocale.DEFAULT_LOCALE);
StringBuilder sCat = new StringBuilder();
StringBuilder sPict = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCat, sLoc, sPict)
|| ! sCat.toString().equals(gsNum)) {
sPict.replace(0, sPict.length(), sPicture);
}
//
// Pick a number -- one will do (zero will not).
//
StringBuilder sOne = new StringBuilder("1");
//
// Fix for Watson 115121. Determining a number that will parse
// as formatted is getting increasing difficult what with
// fractional 8, z and Z pictures. This is obviously a hack that
// needs migration into the LcNum class -- Mike Tardif, Feb 16 2005.
//
int nDot = sPict.indexOf(".");
if (nDot >= 0) {
sOne.append('.');
if (sPict.indexOf("8", nDot + 1) >= 0)
sOne.append('1');
else if (sPict.indexOf("z", nDot + 1) >= 0)
sOne.append('1');
else if (sPict.indexOf("Z", nDot + 1) >= 0)
sOne.append('1');
if (sPict.indexOf("%") >= 0)
sOne.replace(0, sOne.length(), ".001");
}
else if (sPict.indexOf("%") >= 0)
sOne.replace(0, 1, ".00");
LcNum one = new LcNum(sOne.toString(), sLoc.toString());
//
// Try parsing number as formatted.
//
LcNum num = new LcNum(one.format(sPict.toString(), null), sPict.toString());
return num.isValid();
}
private static boolean isPicture(String sPicture,
String sCategory, String sSymbols) {
StringBuilder sCat = new StringBuilder();
StringBuilder sLoc = new StringBuilder();
StringBuilder sMask = new StringBuilder();
for (int i = 0; i < sPicture.length(); ) {
int j = i;
char cChar = sPicture.charAt(i++);
if (cChar == '\'') {
int k = getLiteralSubstr(sPicture, cChar, i, null);
if (k > i) {
i = k;
continue;
}
}
else if (DateTimeUtil.matchChr(gsPictureLiterals, cChar)) {
continue;
}
else if (isSubPicture(sPicture, j, sCat, sLoc, sMask)
&& sCat.toString().startsWith(sCategory)) {
String sCatStr = sCat.toString();
String sMaskStr = sMask.toString();
if ((sCatStr.startsWith(gsNum) && isNumericPicture(sMaskStr))
|| (sCatStr.equals(gsNull) && isNullPicture(sMaskStr))
|| (sCatStr.equals(gsZero) && isZeroPicture(sMaskStr))
|| (sCatStr.equals(gsText) && isTextPicture(sMaskStr))
|| (sCatStr.startsWith(gsDate) && isDatePicture(sMaskStr))
|| (sCatStr.startsWith(gsTime) && isTimePicture(sMaskStr))) {
j += sCat.length();
if (sLoc.length() != 0)
j += sLoc.length() + 2;
j += sMask.length() + 2;
i = j;
continue;
}
}
else if (DateTimeUtil.matchChr(sSymbols, cChar)) {
if (sSymbols == LcNum.NUMERIC_PICTURE_SYMBOLS) {
// Further check for some numeric picture pattern symbol:
// like "CR","cr","db", "DB"
// They need to show up as one unit
if (cChar =='C' || cChar =='c'
|| cChar =='d' || cChar =='D') {
// Get next character
if (i < sPicture.length()) {
char cNextChar = sPicture.charAt(i++);
if ((cChar =='C' && cNextChar != 'R')
|| (cChar =='c' && cNextChar != 'r')
|| (cChar =='d' && cNextChar != 'b')
|| (cChar =='D' && cNextChar != 'B') )
return false;
}
else
return false; // to the end of the picture
}
else if (cChar=='r' || cChar=='R'
|| cChar=='b' || cChar=='B')
return false; // They can't be lead symbol
}
continue;
}
return false;
}
return true;
}
/**
* Determimes if the given picture has a sub-picture,
* and if so, returns the picture's category, locale, and sub-picture.
*
* @param sPicture
* the source picture.
* @param nPictIndex
* the starting character index within the given source picture.
* @param sCategory
* the picture's category.
* @param sLocale
* the picture's locale.
* @param sSubMask
* the picture's sub-picture.
* @return
* boolean true if the picture is a locale sensitive picture
* and false otherwise.
*/
public static boolean isSubPicture(String sPicture, int nPictIndex,
StringBuilder sCategory,
StringBuilder sLocale,
StringBuilder sSubMask) {
int nCategoryBeg, nCategoryEnd;
int nLocaleBeg, nLocaleEnd;
int nSubMaskBeg, nSubMaskEnd;
String sSubCategory = "";
sCategory.setLength(0);
sSubMask.setLength(0);
int nPrevIndex = nPictIndex;
char cChar = sPicture.charAt(nPictIndex++);
if (isCharAlphabetic(cChar)) {
nCategoryBeg = nPrevIndex;
if (nPictIndex == sPicture.length())
return false;
nPrevIndex = nPictIndex;
while (isCharAlphabetic(sPicture.charAt(nPictIndex++))) {
nPrevIndex = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
}
nCategoryEnd = nPrevIndex;
nLocaleBeg = nLocaleEnd = nPictIndex;
nPictIndex = nPrevIndex;
cChar = sPicture.charAt(nPictIndex++);
if (cChar == '.') {
int nSubCategoryBeg = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
nPrevIndex = nPictIndex;
while (true) {
cChar = sPicture.charAt(nPictIndex++);
if (cChar == '(' || cChar == '{')
break;
nPrevIndex = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
}
int nSubCategoryEnd = nPrevIndex;
sSubCategory = sPicture.substring(nSubCategoryBeg,
nSubCategoryEnd);
}
if (cChar == '(') {
nLocaleBeg = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
nPrevIndex = nPictIndex;
while (sPicture.charAt(nPictIndex++) != ')') {
nPrevIndex = nPictIndex;
if (nPictIndex == sPicture.length())
return false;
}
nLocaleEnd = nPrevIndex;
cChar = sPicture.charAt(nPictIndex++);
}
if (cChar == '{') {
nSubMaskBeg = nPictIndex;
while (true) {
if (nPictIndex == sPicture.length())
return false;
nPrevIndex = nPictIndex;
cChar = sPicture.charAt(nPictIndex++);
if (cChar == '\'') {
int nIndex = getLiteralSubstr(sPicture, cChar,
nPictIndex, null);
if (nIndex == nPictIndex)
return false;
nPictIndex = nIndex;
}
else if (cChar == '}') {
nSubMaskEnd = nPrevIndex;
break;
}
}
String sCandidate = sPicture.substring(nCategoryBeg,
nCategoryEnd);
if (sCandidate.equals(gsNull)
|| sCandidate.equals(gsZero)
|| sCandidate.equals(gsText)) {
sCategory.replace(0, sCategory.length(), sCandidate);
if (nLocaleEnd > nLocaleBeg)
sLocale.replace(0, sLocale.length(),
sPicture.substring(nLocaleBeg, nLocaleEnd));
sSubMask.replace(0, sSubMask.length(),
sPicture.substring(nSubMaskBeg, nSubMaskEnd));
return true;
}
else if (sCandidate.equals(gsDate)
|| sCandidate.equals(gsTime)) {
sCategory.replace(0, sCategory.length(), sCandidate);
if (nLocaleEnd > nLocaleBeg)
sLocale.replace(0, sLocale.length(),
sPicture.substring(nLocaleBeg, nLocaleEnd));
String sLoc = LcLocale.DEFAULT_LOCALE;
if (sLocale.length() != 0)
sLoc = sLocale.toString();
String sFmt = null;
if (sSubCategory.length() == 0) {
sFmt = sPicture.substring(nSubMaskBeg, nSubMaskEnd);
}
else if (sSubCategory.equals(gsShort)) {
LcData oData = new LcData(sLoc);
if (sCandidate.equals(gsDate))
sFmt = oData.getDateFormat(LcData.SHORT_FMT);
else
sFmt = oData.getTimeFormat(LcData.SHORT_FMT);
}
else if (sSubCategory.equals(gsMedium)) {
LcData oData = new LcData(sLoc);
if (sCandidate.equals(gsDate))
sFmt = oData.getDateFormat(LcData.MED_FMT);
else
sFmt = oData.getTimeFormat(LcData.MED_FMT);
}
else if (sSubCategory.equals(gsLong)) {
LcData oData = new LcData(sLoc);
if (sCandidate.equals(gsDate))
sFmt = oData.getDateFormat(LcData.LONG_FMT);
else
sFmt = oData.getTimeFormat(LcData.LONG_FMT);
}
else if (sSubCategory.equals(gsFull)) {
LcData oData = new LcData(sLoc);
if (sCandidate.equals(gsDate))
sFmt = oData.getDateFormat(LcData.FULL_FMT);
else
sFmt = oData.getTimeFormat(LcData.FULL_FMT);
}
else if (sSubCategory.equals(gsDefault)) {
LcData oData = new LcData(sLoc);
if (sCandidate.equals(gsDate))
sFmt = oData.getDateFormat(LcData.DEFLT_FMT);
else
sFmt = oData.getTimeFormat(LcData.DEFLT_FMT);
}
else {
return false;
}
sSubMask.replace(0, sSubMask.length(), sFmt);
if (sSubCategory.length() != 0) {
sCategory.append('.');
sCategory.append(sSubCategory);
}
return true;
}
else if (sCandidate.equals(gsNum)) {
sCategory.replace(0, sCategory.length(), sCandidate);
if (nLocaleEnd > nLocaleBeg)
sLocale.replace(0, sLocale.length(),
sPicture.substring(nLocaleBeg, nLocaleEnd));
String sLoc = LcLocale.DEFAULT_LOCALE;
if (sLocale.length() != 0)
sLoc = sLocale.toString();
String sFmt = null;
if (sSubCategory.length() == 0) {
sFmt = sPicture.substring(nSubMaskBeg, nSubMaskEnd);
}
else if (sSubCategory.equals(gsInteger)) {
LcData oData = new LcData(sLoc);
int nOptn = LcData.WITH_GROUPINGS;
sFmt = oData.getNumberFormat(LcData.INTEGRAL_FMT,
nOptn);
}
else if (sSubCategory.equals(gsDecimal)) {
LcData oData = new LcData(sLoc);
int nOptn = LcData.withPrecision(~0)
| LcData.WITH_GROUPINGS;
sFmt = oData.getNumberFormat(LcData.DECIMAL_FMT,
nOptn);
}
else if (sSubCategory.equals(gsCurrency)) {
LcData oData = new LcData(sLoc);
int nOptn = LcData.withPrecision(~0)
| LcData.WITH_GROUPINGS;
sFmt = oData.getNumberFormat(LcData.CURRENCY_FMT,
nOptn);
}
else if (sSubCategory.equals(gsPercent)) {
LcData oData = new LcData(sLoc);
int nOptn = LcData.WITH_GROUPINGS;
sFmt = oData.getNumberFormat(LcData.PERCENT_FMT,
nOptn);
}
else {
return false;
}
sSubMask.replace(0, sSubMask.length(), sFmt);
if (sSubCategory.length() != 0) {
sCategory.append('.');
sCategory.append(sSubCategory);
}
return true;
}
else if (sCandidate.equals(gsDateTime)) {
sCategory.replace(0, sCategory.length(), sCandidate);
if (nLocaleEnd > nLocaleBeg)
sLocale.replace(0, sLocale.length(),
sPicture.substring(nLocaleBeg, nLocaleEnd));
String sLoc = LcLocale.DEFAULT_LOCALE;
if (sLocale.length() != 0)
sLoc = sLocale.toString();
String sFmt = null;
LcData oData = new LcData(sLoc);
int nOptn = LcData.WITH_CATEGORIES;
if (sSubCategory.equals(gsShort)) {
sFmt = oData.getDateTimeFormat(LcData.SHORT_FMT, nOptn);
}
else if (sSubCategory.equals(gsMedium)) {
sFmt = oData.getDateTimeFormat(LcData.MED_FMT, nOptn);
}
else if (sSubCategory.equals(gsLong)) {
sFmt = oData.getDateTimeFormat(LcData.LONG_FMT, nOptn);
}
else if (sSubCategory.equals(gsFull)) {
sFmt = oData.getDateTimeFormat(LcData.FULL_FMT, nOptn);
}
else if (sSubCategory.equals(gsDefault)) {
sFmt = oData.getDateTimeFormat(LcData.DEFLT_FMT, nOptn);
}
else {
return false;
}
sSubMask.replace(0, sSubMask.length(), sFmt);
if (sSubCategory.length() != 0) {
sCategory.append('.');
sCategory.append(sSubCategory);
}
int nCurly = sSubMask.indexOf("date{");
if (nCurly >= 0) {
nCurly += 4;
sSubMask.insert(nCurly, ')');
sSubMask.insert(nCurly, sLoc);
sSubMask.insert(nCurly, '(');
}
nCurly = sSubMask.indexOf("time{");
if (nCurly >= 0) {
nCurly += 4;
sSubMask.insert(nCurly, ')');
sSubMask.insert(nCurly, sLoc);
sSubMask.insert(nCurly, '(');
}
return true;
}
}
}
return false;
}
/**
* Determines if the given picture is a text picture.
*
* @param sPicture
* the source picture.
* @return
* boolean true if the source picture is syntactically valid,
* and false otherwise.
*/
public static boolean isTextPicture(String sPicture) {
return isPicture(sPicture, gsText, LcText.TEXT_PICTURE_SYMBOLS);
}
/**
* Determines if the given text picture is (semantically) valid.
*
* @param sPicture
* the text picture.
* @return
* boolean true if the source picture is semantically valid,
* and false otherwise.
*/
public static boolean isTextPictureValid(String sPicture) {
StringBuilder sLoc = new StringBuilder(LcLocale.DEFAULT_LOCALE);
StringBuilder sCat = new StringBuilder();
StringBuilder sPict = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCat, sLoc, sPict)
|| ! sCat.toString().equals(gsText)) {
sPict.replace(0, sPict.length(), sPicture);
}
if (false) { // NOPMD
//
// Pick any text -- nothing will do.
//
LcText nothing = new LcText("", sLoc.toString());
//
// Try parsing nothing as formatted.
//
LcText text = new LcText(nothing.format(sPict.toString()),
sPict.toString(), sLoc.toString());
return text.isValid();
}
else {
return true;
}
}
/**
* Determines if the given picture is a time picture.
*
* @param sPicture
* the source picture.
* @return
* boolean true if the source picture is syntactically valid,
* and false otherwise.
*/
public static boolean isTimePicture(String sPicture) {
return isPicture(sPicture, gsTime, LcTime.TIME_PICTURE_SYMBOLS);
}
/**
* Determines if the given time picture is (semantically) valid.
*
* @param sPicture
* the time picture.
* @return
* boolean true if the source picture is semantically valid,
* and false otherwise.
*/
public static boolean isTimePictureValid(String sPicture) {
StringBuilder sLoc = new StringBuilder(LcLocale.DEFAULT_LOCALE);
StringBuilder sCat = new StringBuilder();
StringBuilder sPict = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCat, sLoc, sPict)
|| ! sCat.toString().equals(gsTime)) {
sPict.replace(0, sPict.length(), sPicture);
}
//
// Pick any time -- current time will do.
//
LcTime now = new LcTime(1, sLoc.toString());
//
// Try parsing current time as formatted.
//
return now.parse(now.format(sPict.toString()), sPict.toString());
}
/**
* Determines if the given picture is a zero picture.
*
* @param sPicture
* the source picture.
* @return
* boolean true if the source picture is syntactically valid,
* and false otherwise.
*/
public static boolean isZeroPicture(String sPicture) {
StringBuilder sCat = new StringBuilder();
StringBuilder sLoc = new StringBuilder();
StringBuilder sMask = new StringBuilder();
return (isSubPicture(sPicture, 0, sCat, sLoc, sMask)
&& sCat.toString().equals(gsZero));
}
/**
* Determines if the given zero picture is (semantically) valid.
*
* @param sPicture
* the zero picture.
* @return
* boolean true if the source picture is semantically valid,
* and false otherwise.
*/
public static boolean isZeroPictureValid(String sPicture) {
StringBuilder sLoc = new StringBuilder(LcLocale.DEFAULT_LOCALE);
StringBuilder sCat = new StringBuilder();
StringBuilder sPict = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCat, sLoc, sPict)
|| ! sCat.toString().equals(gsZero)) {
sPict.replace(0, sPict.length(), sPicture);
}
//
// Pick a number -- one will do (zero will not).
//
LcNum one = new LcNum("1", sLoc.toString());
//
// Try parsing number as formatted.
//
return one.parse(one.format(sPict.toString(), null), sPict.toString());
}
/**
* Parses a given data source according to the given compound picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the compound picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting canonical string, which may be empty upon error.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean parseCompound(String sSource, String sPicture,
String sLocale, StringHolder sResult) {
MsgFormatPos oMessage = new MsgFormatPos(ResId.UNSUPPORTED_OPERATION, "PictureFmt#parseCompound");
oMessage.format("parseCompound");
throw new ExFull(oMessage);
}
/**
* Parses a given data source according to the given date picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the date picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting canonical string, which may be empty upon error.
* @param bAsLocal
* return the canonical result as a local date when true,
* and as a GMT date when false.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean parseDate(String sSource, String sPicture,
String sLocale, StringHolder sResult, boolean bAsLocal) {
//sResult.value = "";
if (!StringUtils.isEmpty(sResult.value)) sResult.value = "";
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sDateMask = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sDateMask)
&& ! sCategory.toString().equals(gsDate)) {
sDateMask.replace(0, sDateMask.length(), sPicture);
}
LcDate oDate = new LcDate(sSource,
sDateMask.toString(),
sLoc.toString(),
LcDate.DEFAULT_CENTURY_SPLIT);
if (oDate.isValid()) {
ISODate oISODate = new ISODate(oDate.getDays());
if (bAsLocal)
oISODate.setLocalDate();
sResult.value = oISODate.format(LcDate.DATE_FMT2);
}
return !StringUtils.isEmpty(sResult.value);
}
/**
* Parses a given data source according to the given datetime picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the datetime picture.
* @param sDateMask
* the date sub-picture.
* @param sTimeMask
* the time sub-picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting canonical string, which may be empty upon error.
* @param bAsLocal
* return the canonical result as a local datetime when true,
* and as a GMT datetime value when false.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean parseDateTime(String sSource, String sPicture,
String sDateMask, String sTimeMask, String sLocale,
StringHolder sResult, boolean bAsLocal) {
boolean bDateFirst = false;
//
// Determine date/time order. Why did we allow this in the first place?
//
int nDateFound = sPicture.indexOf(sDateMask);
assert(nDateFound >= 0);
int nTimeFound = sPicture.indexOf(sTimeMask);
assert(nTimeFound >= 0);
bDateFirst = (nDateFound < nTimeFound);
//
// Extract the prefix, infix and postfix mask literals from the picture.
//
String sPrefixMask, sInfixMask, sPostfixMask;
if (bDateFirst) {
{
Point p=resolveRange(sPicture, 0, nDateFound);
sPrefixMask = sPicture.substring(p.x,p.y);
}
{
Point p=resolveRange(sPicture, nDateFound + sDateMask.length(), nTimeFound);
sInfixMask = sPicture.substring(p.x,p.y);
}
{
Point p=resolveRange(sPicture, nTimeFound + sTimeMask.length(), sPicture.length());
sPostfixMask = sPicture.substring(p.x,p.y);
}
}
else {
{
Point p=resolveRange(sPicture, 0, nTimeFound);
sPrefixMask = sPicture.substring(p.x,p.y);
}
{
Point p=resolveRange(sPicture, nTimeFound + sTimeMask.length(), nDateFound);
sInfixMask = sPicture.substring(p.x,p.y);
}
{
Point p=resolveRange(sPicture, nDateFound + sDateMask.length(), sPicture.length());
sPostfixMask = sPicture.substring(p.x,p.y);
}
}
//
// Construct prefix, infix and postfix literals from mask literals.
//
LcDate oPrefixData = new LcDate(sLocale, LcDate.DEFAULT_CENTURY_SPLIT);
String sPrefixMaskData = oPrefixData.format(sPrefixMask);
LcDate oInfixData = new LcDate(sLocale, LcDate.DEFAULT_CENTURY_SPLIT);
String sInfixMaskData = oInfixData.format(sInfixMask);
LcDate oPostfixData = new LcDate(sLocale, LcDate.DEFAULT_CENTURY_SPLIT);
String sPostfixMaskData = oPostfixData.format(sPostfixMask);
//
// Count number of occurences of infix literal.
//
int nInfixFound = 0;
int nInfixSourceStart;
if (sInfixMaskData.length() != 0) {
int nAt, nStart = 0;
while ((nAt = sSource.indexOf(sInfixMaskData, nStart)) >= 0) {
nInfixSourceStart = nAt;
nStart = nAt + sInfixMaskData.length();
nInfixFound++;
}
}
String sPrefixSource;
String sDateSource;
String sInfixSource;
String sTimeSource;
String sPostfixSource;
//
// If there are no occurence of the infix literal, use
// use a fixed date and time to generate data based on picture
// masks and use their lengths to separate the date data from the time
// data. This approach will invariably fail for pictures that specify
// variable length symbols.
//
if (nInfixFound == 0) {
//
// Construct date and time data from date and time masks.
//
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sDateSubMask = new StringBuilder();
StringBuilder sTimeSubMask = new StringBuilder();
isSubPicture(sDateMask, 0, sCategory, sLoc, sDateSubMask);
LcDate oToday = new LcDate(1, sLoc.toString(),
LcDate.DEFAULT_CENTURY_SPLIT);
String sDateMaskData = oToday.format(sDateSubMask.toString());
sLoc.replace(0, sLoc.length(), sLocale);
isSubPicture(sTimeMask, 0, sCategory, sLoc, sTimeSubMask);
LcTime oNow = new LcTime(LcTime.MILLISPERHOUR
+ LcTime.MILLISPERMINUTE
+ LcTime.MILLISPERSECOND + 1,
sLoc.toString());
String sTimeMaskData = oNow.format(sTimeSubMask.toString());
//
// Apply mask data lengths to the source data to extract
// any prefix, infix and postfix literals, the
// date source data, and the time source data.
//
int nDateSourceStart, nTimeSourceStart, nPostfixSourceStart;
if (bDateFirst) {
nDateSourceStart = sPrefixMaskData.length();
nInfixSourceStart = nDateSourceStart + sDateMaskData.length();
nTimeSourceStart = nInfixSourceStart + sInfixMaskData.length();
nPostfixSourceStart = nTimeSourceStart + sTimeMaskData.length();
}
else {
nTimeSourceStart = sPrefixMaskData.length();
nInfixSourceStart = nTimeSourceStart + sTimeMaskData.length();
nDateSourceStart = nInfixSourceStart + sInfixMaskData.length();
nPostfixSourceStart = nDateSourceStart + sDateMaskData.length();
}
{
Point p=resolveRange(sSource, 0, sPrefixMaskData.length());
sPrefixSource = sSource.substring(p.x,p.y);
}
{
Point p=resolveRange(sSource, nDateSourceStart, nDateSourceStart + sDateMaskData.length());
sDateSource = sSource.substring(p.x,p.y);
}
{
Point p=resolveRange(sSource, nInfixSourceStart, nInfixSourceStart + sInfixMaskData.length());
sInfixSource = sSource.substring(p.x,p.y);
}
{
Point p=resolveRange(sSource, nTimeSourceStart, nTimeSourceStart + sTimeMaskData.length());
sTimeSource = sSource.substring(p.x,p.y);
}
{
Point p=resolveRange(sSource, nPostfixSourceStart, sSource.length());
sPostfixSource = sSource.substring(p.x,p.y);
}
//
// Having separated the 5 possible components of a datetime value,
// do check each individually.
//
if (! sPrefixSource.equals(sPrefixMaskData))
return false;
if (! sPostfixSource.equals(sPostfixMaskData))
return false;
if (! sInfixSource.equals(sInfixMaskData))
return false;
StringHolder sParsedDate = new StringHolder();
if (! parseDate(sDateSource, sDateMask, sLocale,
sParsedDate, bAsLocal))
return false;
StringHolder sParsedTime = new StringHolder();
if (! parseTime(sTimeSource, sTimeMask, sLocale,
sParsedTime, bAsLocal))
return false;
sResult.value = sParsedDate.value + 'T' + sParsedTime.value;
return true;
}
//
// Otherwise For each occurence of the infix literal Do use it
// to separate the date data from the time data.
//
else {
int nAt;
for (int nStart = 0;
(nAt = sSource.indexOf(sInfixMaskData, nStart)) >= 0;
nStart = nAt + sInfixMaskData.length()) {
nInfixSourceStart = nAt;
int nDateSourceStart, nTimeSourceStart, nPostfixSourceStart;
nPostfixSourceStart = sSource.length()
- sPostfixMaskData.length();
if (bDateFirst) {
nDateSourceStart = sPrefixMaskData.length();
{
Point p=resolveRange(sSource, nDateSourceStart, nInfixSourceStart);
sDateSource = sSource.substring(p.x,p.y);
}
nTimeSourceStart = nInfixSourceStart
+ sInfixMaskData.length();
{
Point p=resolveRange(sSource, nTimeSourceStart, nPostfixSourceStart);
sTimeSource = sSource.substring(p.x,p.y);
}
}
else {
nTimeSourceStart = sPrefixMaskData.length();
{
Point p=resolveRange(sSource, nTimeSourceStart, nInfixSourceStart);
sTimeSource = sSource.substring(p.x,p.y);
}
nDateSourceStart = nInfixSourceStart
+ sInfixMaskData.length();
{
Point p=resolveRange(sSource, nDateSourceStart, nPostfixSourceStart);
sDateSource = sSource.substring(p.x,p.y);
}
}
{
Point p=resolveRange(sSource, 0, sPrefixMaskData.length());
sPrefixSource = sSource.substring(p.x,p.y);
}
{
Point p=resolveRange(sSource, nInfixSourceStart, nInfixSourceStart + sInfixMaskData.length());
sInfixSource = sSource.substring(p.x,p.y);
}
{
Point p=resolveRange(sSource, nPostfixSourceStart, sSource.length());
sPostfixSource = sSource.substring(p.x,p.y);
}
//
// Now having separated the 5 possible components
// of a datetime value, do check each individually.
//
if (! sPrefixSource.equals(sPrefixMaskData))
continue;
if (! sPostfixSource.equals(sPostfixMaskData))
continue;
if (! sInfixSource.equals(sInfixMaskData))
continue;
StringHolder sParsedDate = new StringHolder();
if (! parseDate(sDateSource, sDateMask, sLocale,
sParsedDate, bAsLocal))
continue;
StringHolder sParsedTime = new StringHolder();
if (! parseTime(sTimeSource, sTimeMask, sLocale,
sParsedTime, bAsLocal))
continue;
sResult.value = sParsedDate.value + 'T' + sParsedTime.value;
return true;
}
}
return false;
}
/**
* Parses a given data source according to the given null picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the null picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting canonical string, which may be empty upon error.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean parseNull(String sSource, String sPicture,
String sLocale, StringHolder sResult) {
sResult.value = "";
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sNullMask= new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sNullMask)
&& ! sCategory.toString().equals(gsNull)) {
sNullMask.replace(0, sNullMask.length(), sPicture);
}
LcNum oNum = new LcNum(sSource, sNullMask.toString(), sLoc.toString());
if (oNum.isValid())
sResult.value = null;
return sResult.value == null;
}
/**
* Parses a given data source according to the given numeric picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the numeric picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting canonical string, which may be empty upon error.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean parseNumeric(String sSource, String sPicture,
String sLocale, StringHolder sResult) {
if (!StringUtils.isEmpty(sResult.value)) sResult.value = "";
//sResult.value = "";
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sNumMask= new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sNumMask)
&& ! sCategory.toString().equals(gsNum)) {
sNumMask.replace(0, sNumMask.length(), sPicture);
}
LcNum oNum = new LcNum(sSource, sNumMask.toString(), sLoc.toString());
if (oNum.isValid()) {
sResult.value = oNum.getText();
}
return !StringUtils.isEmpty(sResult.value);
}
/**
* Parses a given data source according to the given text picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the text picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting canonical string, which may be empty upon error.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean parseText(String sSource, String sPicture,
String sLocale, StringHolder sResult) {
//sResult.value = "";
if (!StringUtils.isEmpty(sResult.value)) sResult.value = "";
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sTextMask = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sTextMask)
&& ! sCategory.toString().equals(gsText)) {
sTextMask.replace(0, sTextMask.length(), sPicture);
}
LcText oText = new LcText(sSource, sTextMask.toString(),
sLoc.toString());
if (oText.isValid())
sResult.value = oText.getText();
return !StringUtils.isEmpty(sResult.value);
}
/**
* Parses a given data source according to the given time picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the time picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting canonical string, which may be empty upon error.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean parseTime(String sSource, String sPicture,
String sLocale, StringHolder sResult, boolean bAsLocal) {
//sResult.value = "";
if (!StringUtils.isEmpty(sResult.value)) sResult.value = "";
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sTimeMask = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sTimeMask)
&& ! sCategory.toString().equals(gsTime)) {
sTimeMask.replace(0, sTimeMask.length(), sPicture);
}
LcTime oTime = new LcTime(sSource, sTimeMask.toString(),
sLoc.toString());
if (oTime.isValid()) {
ISOTime oISOTime = new ISOTime(oTime.getMillis());
if (bAsLocal)
oISOTime.setLocalTime();
//
// Pick format string depending on how specified the time is
//
if (oISOTime.getMilliSecond() > 0)
sResult.value = oISOTime.format("HH:MM:SS.FFF");
else if (oISOTime.getSecond() > 0)
sResult.value = oISOTime.format("HH:MM:SS");
else if (oISOTime.getMinute() > 0)
sResult.value = oISOTime.format("HH:MM");
else
sResult.value = oISOTime.format("HH");
}
return !StringUtils.isEmpty(sResult.value);
}
/**
* Parses a given data source according to the given zero picture
* under the given locale.
*
* @param sSource
* the source data.
* @param sPicture
* the zero picture.
* @param sLocale
* the locale name.
* @param sResult
* the resulting canonical string, which may be empty upon error.
* @return
* boolean true upon success and false otherwise.
*/
public static boolean parseZero(String sSource, String sPicture,
String sLocale, StringHolder sResult) {
//sResult.value = "";
if (!StringUtils.isEmpty(sResult.value)) sResult.value = "";
StringBuilder sLoc = new StringBuilder(sLocale);
StringBuilder sCategory = new StringBuilder();
StringBuilder sZeroMask = new StringBuilder();
if (! hasSubPicture(sPicture, 0, sCategory, sLoc, sZeroMask)
&& ! sCategory.toString().equals(gsZero)) {
sZeroMask.replace(0, sZeroMask.length(), sPicture);
}
LcNum oNum = new LcNum(sSource, sZeroMask.toString(), sLoc.toString());
if (oNum.isValid() && oNum.getValue() == 0)
sResult.value = "0";
return !StringUtils.isEmpty(sResult.value);
}
private final String msLocale;
static final String gsPictureLiterals = "\"' ,-./:?+*$()";
static final String gsNull = "null";
static final String gsZero = "zero";
static final String gsNum = "num";
static final String gsText = "text";
static final String gsDate = "date";
static final String gsTime = "time";
static final String gsDateTime = "datetime";
static final String gsShort = "short";
static final String gsMedium = "medium";
static final String gsLong = "long";
static final String gsFull = "full";
static final String gsDefault = "default";
static final String gsInteger = "integer";
static final String gsDecimal = "decimal";
static final String gsCurrency = "currency";
static final String gsPercent = "percent";
private static int getLiteralSubstr(String sPicture,
char cChar, int nCharPos, StringBuilder sLiteral) {
assert(cChar == '\'');
int nFoundPos = nCharPos;
int nPos = 0;
int nQuoteBeg = nCharPos;
int nQuoteEnd = sPicture.length() - 1;
int nStart = nPos = nCharPos;
while ((nPos = sPicture.indexOf(cChar, nStart)) >= 0) {
if (nPos + 1 == sPicture.length())
break;
if (sPicture.charAt(nPos + 1) != cChar)
break;
nStart = nPos + 2;
}
if (nPos > nCharPos) {
nQuoteEnd = nPos;
nFoundPos = nPos + 1; // skip trailing quote.
if (sLiteral != null)
sLiteral.append(sPicture, nQuoteBeg, nQuoteEnd);
}
return nFoundPos;
}
}