From efe2014ed1e6eeef10ef33ffaab5b9378676636f Mon Sep 17 00:00:00 2001 From: Claire Ho Date: Fri, 3 Aug 2007 18:43:04 +0000 Subject: [PATCH] ICU-5787 merge from feature branch revision 22024:22252. X-SVN-Rev: 22260 --- icu4c/source/i18n/Makefile.in | 4 +- icu4c/source/i18n/dtptngen.cpp | 1939 ++++++++++++++++++++ icu4c/source/i18n/dtptngen_impl.h | 381 ++++ icu4c/source/i18n/i18n.vcproj | 56 + icu4c/source/i18n/udatpg.cpp | 248 +++ icu4c/source/i18n/unicode/dtptngen.h | 413 +++++ icu4c/source/i18n/unicode/udatpg.h | 472 +++++ icu4c/source/test/cintltst/Makefile.in | 2 +- icu4c/source/test/cintltst/cformtst.c | 4 +- icu4c/source/test/cintltst/cintltst.vcproj | 4 + icu4c/source/test/cintltst/udatpg_test.c | 290 +++ icu4c/source/test/intltest/Makefile.in | 2 +- icu4c/source/test/intltest/dtptngts.cpp | 431 +++++ icu4c/source/test/intltest/dtptngts.h | 31 + icu4c/source/test/intltest/intltest.vcproj | 8 + icu4c/source/test/intltest/itformat.cpp | 4 + 16 files changed, 4284 insertions(+), 5 deletions(-) create mode 100644 icu4c/source/i18n/dtptngen.cpp create mode 100644 icu4c/source/i18n/dtptngen_impl.h create mode 100644 icu4c/source/i18n/udatpg.cpp create mode 100644 icu4c/source/i18n/unicode/dtptngen.h create mode 100644 icu4c/source/i18n/unicode/udatpg.h create mode 100644 icu4c/source/test/cintltst/udatpg_test.c create mode 100644 icu4c/source/test/intltest/dtptngts.cpp create mode 100644 icu4c/source/test/intltest/dtptngts.h diff --git a/icu4c/source/i18n/Makefile.in b/icu4c/source/i18n/Makefile.in index a2b0c19b902..bb92747f514 100644 --- a/icu4c/source/i18n/Makefile.in +++ b/icu4c/source/i18n/Makefile.in @@ -64,7 +64,7 @@ LIBS = $(LIBICUUC) $(DEFAULT_LIBS) OBJECTS = ucln_in.o \ fmtable.o format.o msgfmt.o umsg.o numfmt.o unum.o decimfmt.o dcfmtsym.o \ ucurr.o digitlst.o fmtable_cnv.o \ -choicfmt.o datefmt.o smpdtfmt.o reldtfmt.o dtfmtsym.o udat.o \ +choicfmt.o datefmt.o smpdtfmt.o reldtfmt.o dtfmtsym.o udat.o dtptngen.o \ nfrs.o nfrule.o nfsubs.o rbnf.o ucsdet.o \ ucal.o calendar.o gregocal.o timezone.o simpletz.o olsontz.o \ astro.o taiwncal.o buddhcal.o persncal.o islamcal.o japancal.o gregoimp.o hebrwcal.o indiancal.o \ @@ -75,7 +75,7 @@ translit.o utrans.o esctrn.o unesctrn.o funcrepl.o strrepl.o tridpars.o \ cpdtrans.o rbt.o rbt_data.o rbt_pars.o rbt_rule.o rbt_set.o \ nultrans.o remtrans.o casetrn.o titletrn.o tolowtrn.o toupptrn.o anytrans.o \ name2uni.o uni2name.o nortrans.o quant.o transreg.o \ -regexcmp.o rematch.o repattrn.o regexst.o uregex.o uregexc.o \ +regexcmp.o rematch.o repattrn.o regexst.o udatpg.o uregex.o uregexc.o \ ulocdata.o measfmt.o currfmt.o curramt.o currunit.o measure.o utmscale.o \ csdetect.o csmatch.o csr2022.o csrecog.o csrmbcs.o csrsbcs.o csrucode.o csrutf8.o inputext.o \ windtfmt.o winnmfmt.o basictz.o dtrule.o rbtz.o tzrule.o tztrans.o vtzone.o diff --git a/icu4c/source/i18n/dtptngen.cpp b/icu4c/source/i18n/dtptngen.cpp new file mode 100644 index 00000000000..95dbe266c10 --- /dev/null +++ b/icu4c/source/i18n/dtptngen.cpp @@ -0,0 +1,1939 @@ +/* +******************************************************************************* +* Copyright (C) 2007, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTPTNGEN.CPP +* +******************************************************************************* +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/datefmt.h" +#include "unicode/decimfmt.h" +#include "unicode/dtfmtsym.h" +#include "unicode/dtptngen.h" +#include "unicode/msgfmt.h" +#include "unicode/smpdtfmt.h" +#include "unicode/udat.h" +#include "unicode/udatpg.h" +#include "unicode/uniset.h" +#include "unicode/ures.h" +#include "unicode/rep.h" +#include "cpputils.h" +#include "ucln_in.h" +#include "mutex.h" +#include "cmemory.h" +#include "cstring.h" +#include "locbased.h" +#include "gregoimp.h" +#include "hash.h" +#include "uresimp.h" +#include "dtptngen_impl.h" + +#if defined U_DEBUG_DTPTN +#include +#endif + +#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) + +U_NAMESPACE_BEGIN + +// ***************************************************************************** +// class DateTimePatternGenerator +// ***************************************************************************** +static const UChar Canonical_Items[] = { + // GyQMwWedDFHmsSv + CAP_G, LOW_Y, CAP_Q, CAP_M, LOW_W, CAP_W, LOW_E, LOW_D, CAP_D, CAP_F, + CAP_H, LOW_M, LOW_S, CAP_S, LOW_V, 0 +}; + +static const dtTypeElem dtTypes[] = { + // patternChar, field, type, minLen, weight + {CAP_G, UDATPG_ERA_FIELD, DT_SHORT, 1, 3,}, + {CAP_G, UDATPG_ERA_FIELD, DT_LONG, 4}, + {LOW_Y, UDATPG_YEAR_FIELD, DT_NUMERIC, 1, 20}, + {CAP_Y, UDATPG_YEAR_FIELD, DT_NUMERIC + DT_DELTA, 1, 20}, + {LOW_U, UDATPG_YEAR_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 20}, + {CAP_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC, 1, 2}, + {CAP_Q, UDATPG_QUARTER_FIELD, DT_SHORT, 3}, + {CAP_Q, UDATPG_QUARTER_FIELD, DT_LONG, 4}, + {CAP_M, UDATPG_MONTH_FIELD, DT_NUMERIC, 1, 2}, + {CAP_M, UDATPG_MONTH_FIELD, DT_SHORT, 3}, + {CAP_M, UDATPG_MONTH_FIELD, DT_LONG, 4}, + {CAP_M, UDATPG_MONTH_FIELD, DT_NARROW, 5}, + {CAP_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, + {CAP_L, UDATPG_MONTH_FIELD, DT_SHORT - DT_DELTA, 3}, + {CAP_L, UDATPG_MONTH_FIELD, DT_LONG - DT_DELTA, 4}, + {CAP_L, UDATPG_MONTH_FIELD, DT_NARROW - DT_DELTA, 5}, + {LOW_W, UDATPG_WEEK_OF_YEAR_FIELD, DT_NUMERIC, 1, 2}, + {CAP_W, UDATPG_WEEK_OF_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1}, + {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, + {LOW_E, UDATPG_WEEKDAY_FIELD, DT_SHORT - DT_DELTA, 3}, + {LOW_E, UDATPG_WEEKDAY_FIELD, DT_LONG - DT_DELTA, 4}, + {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NARROW - DT_DELTA, 5}, + {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORT, 1, 3}, + {CAP_E, UDATPG_WEEKDAY_FIELD, DT_LONG, 4}, + {CAP_E, UDATPG_WEEKDAY_FIELD, DT_NARROW, 5}, + {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 2}, + {LOW_C, UDATPG_WEEKDAY_FIELD, DT_SHORT - 2*DT_DELTA, 3}, + {LOW_C, UDATPG_WEEKDAY_FIELD, DT_LONG - 2*DT_DELTA, 4}, + {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NARROW - 2*DT_DELTA, 5}, + {LOW_D, UDATPG_DAY_FIELD, DT_NUMERIC, 1, 2}, + {CAP_D, UDATPG_DAY_OF_YEAR_FIELD, DT_NUMERIC + DT_DELTA, 1, 3}, + {CAP_F, UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, DT_NUMERIC + 2*DT_DELTA, 1}, + {LOW_G, UDATPG_DAY_FIELD, DT_NUMERIC + 3*DT_DELTA, 1, 20}, // really internal use, so we d'ont care + {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_SHORT, 1}, + {CAP_H, UDATPG_HOUR_FIELD, DT_NUMERIC + 10*DT_DELTA, 1, 2}, // 24 hour + {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + 11*DT_DELTA, 1, 2}, + {LOW_H, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12 hour + {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, + {LOW_M, UDATPG_MINUTE_FIELD, DT_NUMERIC, 1, 2}, + {LOW_S, UDATPG_SECOND_FIELD, DT_NUMERIC, 1, 2}, + {CAP_S, UDATPG_FRACTIONAL_SECOND_FIELD, DT_NUMERIC + DT_DELTA, 1, 1000}, + {CAP_A, UDATPG_SECOND_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 1000}, + {LOW_V, UDATPG_ZONE_FIELD, DT_SHORT - 2*DT_DELTA, 1}, + {LOW_V, UDATPG_ZONE_FIELD, DT_LONG - 2*DT_DELTA, 4}, + {LOW_Z, UDATPG_ZONE_FIELD, DT_SHORT, 1, 3}, + {LOW_Z, UDATPG_ZONE_FIELD, DT_LONG, 4}, + {CAP_Z, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 3}, + {CAP_Z, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4}, + {'\0'} , // last row of dtTypes[] + }; + +static const char* CLDR_FIELD_APPEND[] = { + "Era", "Year", "Quarter", "Month", "Week", "*", "Day-Of-Week", "Day", "*", "*", "*", + "Hour", "Minute", "Second", "*", "Timezone" +}; + +static const char* CLDR_FIELD_NAME[] = { + "era", "year", "quarter", "month", "week", "*", "weekday", "day", "*", "*", "dayperiod", + "hour", "minute", "second", "*", "zone" +}; + +static const char* Resource_Fields[] = { + "day", "dayperiod", "era", "hour", "minute", "month", "second", "week", + "weekday", "year", "zone" }; + +static const char* CLDR_AVAILABLE_FORMAT[MAX_AVAILABLE_FORMATS] = { + "Ed", "EMMMd", "H", "HHmm", "HHmmss", "Md", "MMMMd", "MMyy", "Qyy", + "mmss", "yyMMM", "yyyy", +}; // binary ascending order + +static const UnicodeString quotingPattern= UNICODE_STRING_SIMPLE("[[[:script=Latin:][:script=Cyrl:]]&[[:L:][:M:]]]"); + +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimePatternGenerator) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PtnSkeleton) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PtnElem) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimeMatcher) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PatternMap) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(PatternMapIterator) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(FormatParser) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DistanceInfo) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTSkeletonEnumeration) +UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTRedundantEnumeration) + +DateTimePatternGenerator* U_EXPORT2 +DateTimePatternGenerator::createInstance(UErrorCode& status) { + return createInstance(Locale::getDefault(), status); +} + +DateTimePatternGenerator* U_EXPORT2 +DateTimePatternGenerator::createInstance(const Locale& locale, UErrorCode& status) { + return new DateTimePatternGenerator(locale, status); +} + +DateTimePatternGenerator* U_EXPORT2 +DateTimePatternGenerator::createEmptyInstance(UErrorCode& status) { + return new DateTimePatternGenerator(status); +} + +DateTimePatternGenerator::DateTimePatternGenerator(UErrorCode &status) : UObject() { + fStatus = U_ZERO_ERROR; + skipMatcher = NULL; + fAvailableFormatKeyHash=NULL; + fp = new FormatParser(); + dtMatcher = new DateTimeMatcher(); + distanceInfo = new DistanceInfo(); + patternMap = new PatternMap(); +} + +DateTimePatternGenerator::DateTimePatternGenerator(const Locale& locale, UErrorCode &status) : UObject() { + fp = new FormatParser(); + dtMatcher = new DateTimeMatcher(); + distanceInfo = new DistanceInfo(); + patternMap = new PatternMap(); + initData(locale); + status = getStatus(); +} + +DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerator& other) : UObject() { + fStatus = U_ZERO_ERROR; + skipMatcher = NULL; + fAvailableFormatKeyHash=NULL; + fp = new FormatParser(); + dtMatcher = new DateTimeMatcher(); + distanceInfo = new DistanceInfo(); + patternMap = new PatternMap(); + *this=other; +} + +DateTimePatternGenerator& +DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) { + fStatus = U_ZERO_ERROR; + pLocale = other.pLocale; + *fp = *(other.fp); + fp->setFilter(fStatus); + dtMatcher->copyFrom(other.dtMatcher->skeleton); + *distanceInfo = *(other.distanceInfo); + dateTimeFormat = other.dateTimeFormat; + decimal = other.decimal; + // NUL-terminate for the C API. + dateTimeFormat.getTerminatedBuffer(); + decimal.getTerminatedBuffer(); + skipMatcher = other.skipMatcher; + for (int32_t i=0; i< UDATPG_FIELD_COUNT; ++i ) { + appendItemFormats[i] = other.appendItemFormats[i]; + appendItemNames[i] = other.appendItemNames[i]; + // NUL-terminate for the C API. + appendItemFormats[i].getTerminatedBuffer(); + appendItemNames[i].getTerminatedBuffer(); + } + patternMap->copyFrom(*other.patternMap, fStatus); + fAvailableFormatKeyHash=NULL; + copyHashtable(other.fAvailableFormatKeyHash); + return *this; +} + + +UBool +DateTimePatternGenerator::operator==(const DateTimePatternGenerator& other) const { + if (this == &other) { + return TRUE; + } + if ((pLocale==other.pLocale) && (patternMap->equals(*other.patternMap))) { + return TRUE; + } + else { + return FALSE; + } +} + +DateTimePatternGenerator::~DateTimePatternGenerator() { + if (fAvailableFormatKeyHash!=NULL) { + delete fAvailableFormatKeyHash; + fAvailableFormatKeyHash=NULL; + } + + if (fp != NULL) delete fp; + if (dtMatcher != NULL) delete dtMatcher; + if (distanceInfo != NULL) delete distanceInfo; + if (patternMap != NULL) delete patternMap; + +} + +void +DateTimePatternGenerator::initData(const Locale& locale) { + fStatus = U_ZERO_ERROR; + skipMatcher = NULL; + fAvailableFormatKeyHash=NULL; + + addCanonicalItems(); + addICUPatterns(locale); + addCLDRData(locale); + setDateTimeFromCalendar(locale); + setDecimalSymbols(locale, fStatus); +} // DateTimePatternGenerator::initData + +UnicodeString +DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode& status) { + PtnSkeleton *ptrSkeleton; + UnicodeString result; + + dtMatcher->set(pattern, fp); + ptrSkeleton=dtMatcher->getSkeletonPtr(); + result = ptrSkeleton->getSkeleton(); + return result; +} + +UnicodeString +DateTimePatternGenerator::getBaseSkeleton(const UnicodeString& pattern, UErrorCode& status) { + PtnSkeleton *ptrSkeleton; + UnicodeString result; + + dtMatcher->set(pattern, fp); + ptrSkeleton=dtMatcher->getSkeletonPtr(); + for (int32_t i=0; ibaseOriginal[i].length()!=0) { + result += ptrSkeleton->baseOriginal[i]; + } + } + return result; +} + +void +DateTimePatternGenerator::addICUPatterns(const Locale& locale) { + UnicodeString dfPattern, newPattern; + UnicodeString conflictingString; + UDateTimePatternConflict conflictingStatus; + SimpleDateFormat* df; + UErrorCode status = U_ZERO_ERROR; + + // Load with ICU patterns + for (DateFormat::EStyle i=DateFormat::kFull; i<=DateFormat::kShort; i=static_cast(i+1)) { + if ((df = (SimpleDateFormat*)DateFormat::createDateInstance(i, locale))!= NULL) { + newPattern=df->toPattern(dfPattern); + conflictingStatus = addPattern(df->toPattern(dfPattern), FALSE, conflictingString, status); + delete df; + } + + if ((df = (SimpleDateFormat*)DateFormat::createTimeInstance(i, locale)) != NULL) { + conflictingStatus = addPattern(df->toPattern(dfPattern), FALSE, conflictingString, status); + newPattern=df->toPattern(dfPattern); + if (U_FAILURE(status)) { + delete df; + return; + } + // HACK for hh:ss + if ( i==DateFormat::kMedium ) { + hackPattern = df->toPattern(hackPattern); + } + delete df; + } + } + +} + +void +DateTimePatternGenerator::hackTimes(UnicodeString& hackPattern, UErrorCode& status) { + UDateTimePatternConflict conflictingStatus; + UnicodeString conflictingString; + + fp->set(hackPattern); + UnicodeString mmss; + UBool gotMm=FALSE; + for (int32_t i=0; iitemNumber; ++i) { + UnicodeString field = fp->items[i]; + if ( fp->isQuoteLiteral(field) ) { + if ( gotMm ) { + UnicodeString quoteLiteral; + fp->getQuoteLiteral(quoteLiteral, &i); + mmss += quoteLiteral; + } + } + else { + if (fp->isPatternSeparator(field) && gotMm) { + mmss+=field; + } + else { + UChar ch=field.charAt(0); + if (ch==LOW_M) { + gotMm=TRUE; + mmss+=field; + } + else { + if (ch==LOW_S) { + if (!gotMm) { + break; + } + mmss+= field; + conflictingStatus = addPattern(mmss, FALSE, conflictingString, status); + break; + } + else { + if (gotMm || ch==LOW_Z || ch==CAP_Z || ch==LOW_V || ch==CAP_V) { + break; + } + } + } + } + } + } +} + +void +DateTimePatternGenerator::addCLDRData(const Locale& locale) { + UErrorCode err = U_ZERO_ERROR; + UResourceBundle *rb, *gregorianBundle, *calBundle; + UResourceBundle *patBundle, *fieldBundle, *fBundle; + int32_t len=0; + UnicodeString rbPattern, value, field; + UnicodeString conflictingPattern; + UDateTimePatternConflict conflictingStatus; + const char *key=NULL; + + // Initialize appendItems + static const UChar itemFormat[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A, + 0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524 + UnicodeString defaultItemFormat(TRUE, itemFormat, LENGTHOF(itemFormat)-1); // Read-only alias. + + for (int32_t i=0; i=0 ) { + rb = ures_open(NULL, parentLocale, &err); + calBundle = ures_getByKey(rb, "calendar", NULL, &err); + gregorianBundle = ures_getByKey(calBundle, "gregorian", NULL, &err); + patBundle = ures_getByKeyWithFallback(gregorianBundle, "availableFormats", NULL, &err); + if (U_SUCCESS(err)) { + int32_t numberKeys = ures_getSize(patBundle); + int32_t len; + const UChar *retPattern; + key=NULL; + + //printf ("\n available formats from parent locale:%s", parentLocale); + for(int32_t i=0; i0) { + hackTimes(hackPattern, err); + } +} + +void +DateTimePatternGenerator::initHashtable(UErrorCode& err) { + if (fAvailableFormatKeyHash!=NULL) { + return; + } + if ((fAvailableFormatKeyHash = new Hashtable(FALSE, err))!=NULL) { + return; + } +} + + +void +DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field, const UnicodeString& value) { + appendItemFormats[field] = value; + // NUL-terminate for the C API. + appendItemFormats[field].getTerminatedBuffer(); +} + +const UnicodeString& +DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field) const { + return appendItemFormats[field]; +} + +void +DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field, const UnicodeString& value) { + appendItemNames[field] = value; + // NUL-terminate for the C API. + appendItemNames[field].getTerminatedBuffer(); +} + +const UnicodeString& +DateTimePatternGenerator:: getAppendItemName(UDateTimePatternField field) const { + return appendItemNames[field]; +} + +void +DateTimePatternGenerator::getAppendName(UDateTimePatternField field, UnicodeString& value) { + value = SINGLE_QUOTE; + value += appendItemNames[field]; + value += SINGLE_QUOTE; +} + +UnicodeString +DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UErrorCode& status) { + UnicodeString *bestPattern=NULL; + UnicodeString dtFormat; + UErrorCode err = U_ZERO_ERROR; + UnicodeString resultPattern; + + int32_t dateMask=(1<set(patternForm, fp); + bestPattern=getBestRaw(*dtMatcher, -1, distanceInfo); + if ( distanceInfo->missingFieldMask==0 && distanceInfo->extraFieldMask==0 ) { + resultPattern = adjustFieldTypes(*bestPattern, FALSE); + + return resultPattern; + } + int32_t neededFields = dtMatcher->getFieldMask(); + UnicodeString datePattern=getBestAppending(neededFields & dateMask); + UnicodeString timePattern=getBestAppending(neededFields & timeMask); + if (datePattern.length()==0) { + if (timePattern.length()==0) { + resultPattern=""; + return resultPattern; + } + else { + resultPattern=timePattern; + return resultPattern; + } + } + if (timePattern.length()==0) { + resultPattern=datePattern; + return resultPattern; + } + resultPattern=""; + dtFormat=getDateTimeFormat(); + Formattable dateTimeObject[] = { datePattern, timePattern }; + resultPattern = MessageFormat::format(dtFormat, dateTimeObject, 2, resultPattern, err ); + return resultPattern; +} + +UnicodeString +DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern, + const UnicodeString& skeleton, + UErrorCode& status) { + dtMatcher->set(skeleton, fp); + UnicodeString result = adjustFieldTypes(pattern, FALSE); + return result; +} + +void +DateTimePatternGenerator::setDecimal(const UnicodeString& decimal) { + this->decimal = decimal; + // NUL-terminate for the C API. + this->decimal.getTerminatedBuffer(); +} + +const UnicodeString& +DateTimePatternGenerator::getDecimal() const { + return decimal; +} + +void +DateTimePatternGenerator::addCanonicalItems() { + UnicodeString conflictingPattern; + UDateTimePatternConflict conflictingStatus; + UErrorCode status = U_ZERO_ERROR; + + for (int32_t i=0; igetPatternFromBasePattern(basePattern); + if (duplicatePattern != NULL ) { + conflictingStatus = UDATPG_BASE_CONFLICT; + conflictingPattern = *duplicatePattern; + if (!override) { + return conflictingStatus; + } + } + duplicatePattern = patternMap->getPatternFromSkeleton(skeleton); + if (duplicatePattern != NULL ) { + conflictingStatus = UDATPG_CONFLICT; + conflictingPattern = *duplicatePattern; + if (!override) { + return conflictingStatus; + } + } + patternMap->add(basePattern, skeleton, pattern, status); + if(U_FAILURE(status)) { + return conflictingStatus; + } + + return UDATPG_NO_CONFLICT; +} + + +UDateTimePatternField +DateTimePatternGenerator::getAppendFormatNumber(const char* field) { + for (int32_t i=0; igetPatternFromSkeleton(*trial.getSkeletonPtr()); + missingFields->setTo(tempInfo); + if (distance==0) { + break; + } + } + } + + return bestPattern; +} + +UnicodeString +DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, + UBool fixFractionalSeconds) { + UnicodeString newPattern=""; + fp->set(pattern); + for (int32_t i=0; i < fp->itemNumber; i++) { + UnicodeString field = fp->items[i]; + if ( fp->isQuoteLiteral(field) ) { + + UnicodeString quoteLiteral=""; + fp->getQuoteLiteral(quoteLiteral, &i); + newPattern += quoteLiteral; + } + else { + + int32_t canonicalIndex = fp->getCanonicalIndex(field); + if (canonicalIndex < 0) { + if (fp->isPatternSeparator(field)) { + newPattern+=field; + } + continue; //don't adjust + } + const dtTypeElem *row = &dtTypes[canonicalIndex]; + int32_t typeValue = row->field; + if (fixFractionalSeconds && typeValue == UDATPG_SECOND_FIELD) { + UnicodeString newField=dtMatcher->skeleton.original[UDATPG_FRACTIONAL_SECOND_FIELD]; + field = field + decimal + newField; + } + else { + if (dtMatcher->skeleton.type[typeValue]!=0) { + UnicodeString newField=dtMatcher->skeleton.original[typeValue]; + if (typeValue!= UDATPG_HOUR_FIELD) { + field=newField; + } + else { + if (field.length()!=newField.length()) { + UChar c=field.charAt(0); + field=""; + for (int32_t i=newField.length(); i>0; --i) { + field+=c; + } + } + } + } + newPattern+=field; + } + } + } + return newPattern; +} + +UnicodeString +DateTimePatternGenerator::getBestAppending(int32_t missingFields) { + UnicodeString resultPattern, tempPattern, formattedPattern; + UErrorCode err=U_ZERO_ERROR; + int32_t lastMissingFieldMask=0; + if (missingFields!=0) { + resultPattern=UnicodeString(""); + tempPattern = *getBestRaw(*dtMatcher, missingFields, distanceInfo); + resultPattern = adjustFieldTypes(tempPattern, FALSE); + if ( distanceInfo->missingFieldMask==0 ) { + return resultPattern; + } + while (distanceInfo->missingFieldMask!=0) { // precondition: EVERY single field must work! + if ( lastMissingFieldMask == distanceInfo->missingFieldMask ) { + break; // cannot find the proper missing field + } + if (((distanceInfo->missingFieldMask & UDATPG_SECOND_AND_FRACTIONAL_MASK)==UDATPG_FRACTIONAL_MASK) && + ((missingFields & UDATPG_SECOND_AND_FRACTIONAL_MASK) == UDATPG_SECOND_AND_FRACTIONAL_MASK)) { + resultPattern = adjustFieldTypes(resultPattern, FALSE); + resultPattern = tempPattern; + distanceInfo->missingFieldMask &= ~UDATPG_FRACTIONAL_MASK; + continue; + } + int32_t startingMask = distanceInfo->missingFieldMask; + tempPattern = *getBestRaw(*dtMatcher, distanceInfo->missingFieldMask, distanceInfo); + tempPattern = adjustFieldTypes(tempPattern, FALSE); + int32_t foundMask=startingMask& ~distanceInfo->missingFieldMask; + int32_t topField=getTopBitNumber(foundMask); + UnicodeString appendName; + getAppendName((UDateTimePatternField)topField, appendName); + const Formattable formatPattern[] = { + resultPattern, + tempPattern, + appendName + }; + formattedPattern = MessageFormat::format(appendItemFormats[topField], formatPattern, 3, resultPattern, err); + lastMissingFieldMask = distanceInfo->missingFieldMask; + } + } + return formattedPattern; +} + +int32_t +DateTimePatternGenerator::getTopBitNumber(int32_t foundMask) { + if ( foundMask==0 ) { + return 0; + } + int32_t i=0; + while (foundMask!=0) { + foundMask >>=1; + ++i; + } + if (i-1 >UDATPG_ZONE_FIELD) { + return UDATPG_ZONE_FIELD; + } + else + return i-1; +} + +void +DateTimePatternGenerator::setAvailableFormat(const char* key, UErrorCode& err) { + + fAvailableFormatKeyHash->puti(key, 1, err); +} + +UBool +DateTimePatternGenerator::isAvailableFormatSet(const char* key) { + int32_t i=0; + + i=fAvailableFormatKeyHash->geti(key); + if ( i==1 ) { + return TRUE; + } + else{ + return FALSE; + } +} + +void +DateTimePatternGenerator::copyHashtable(Hashtable *other) { + + if (fAvailableFormatKeyHash !=NULL) { + delete fAvailableFormatKeyHash; + } + if (other == NULL) { + fAvailableFormatKeyHash = NULL; + return; + } + initHashtable(fStatus); + if(U_FAILURE(fStatus)){ + return; + } + int32_t pos = -1; + const UHashElement* elem = NULL; + // walk through the hash table and create a deep clone + while((elem = other->nextElement(pos))!= NULL){ + const UHashTok otherKeyTok = elem->key; + UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer; + UnicodeString *key = new UnicodeString(*otherKey); + fAvailableFormatKeyHash->puti(*key, 1, fStatus); + if(U_FAILURE(fStatus)){ + return; + } + } +} + +StringEnumeration* +DateTimePatternGenerator::getSkeletons(UErrorCode& status) const { + StringEnumeration* skeletonEnumerator = new DTSkeletonEnumeration(*patternMap, DT_SKELETON, status); + return skeletonEnumerator; +} + +const UnicodeString& +DateTimePatternGenerator::getPatternForSkeleton(const UnicodeString& skeleton) const { + UChar baseChar; + PtnElem *curElem; + + if (skeleton.length() ==0) { + return emptyString; + } + + baseChar = skeleton.charAt(0); + + // the baseChar must be A-Z or a-z + if ( (baseChar >= CAP_A) && (baseChar <= CAP_Z) ) { + curElem = patternMap->boot[26 + (baseChar-CAP_A)]; + } + else { + if ( (baseChar >=LOW_A) && (baseChar <= LOW_Z) ) { + curElem = patternMap->boot[baseChar-LOW_A]; + } + else { + return emptyString; + } + } + while ( curElem != NULL ) { + if ( curElem->skeleton->getSkeleton()==skeleton ) { + return *(curElem->pattern); + } + curElem=curElem->next; + } + return emptyString; +} + +StringEnumeration* +DateTimePatternGenerator::getBaseSkeletons(UErrorCode& status) const { + StringEnumeration* baseSkeletonEnumerator = new DTSkeletonEnumeration(*patternMap, DT_BASESKELETON, status); + return baseSkeletonEnumerator; +} + +StringEnumeration* +DateTimePatternGenerator::getRedundants(UErrorCode& status) { + StringEnumeration* output = new DTRedundantEnumeration(); + UnicodeString *pattern; + + PatternMapIterator it; + for (it.set(*patternMap); it.hasNext(); ) { + DateTimeMatcher current = it.next(); + pattern = patternMap->getPatternFromSkeleton(*(it.getSkeleton())); + if ( isCanonicalItem(*pattern) ) { + continue; + } + skipMatcher = ¤t; + UnicodeString trial = getBestPattern(current.getPattern(), status); + if (trial == *pattern) { + ((DTRedundantEnumeration *)output)->add(*pattern, status); + } + if (current.equals(skipMatcher)) { + continue; + } + } + return output; +} + +UBool +DateTimePatternGenerator::isCanonicalItem(const UnicodeString& item) { + if ( item.length() != 1 ) { + return FALSE; + } + for (int32_t i=0; iisDupAllowed = other.isDupAllowed; + for (int32_t bootIndex=0; bootIndexboot[bootIndex]= new PtnElem()) == NULL ) { + // out of memory + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + curElem->basePattern = new UnicodeString(*(otherElem->basePattern)); + curElem->pattern = new UnicodeString(*(otherElem->pattern)); + + if ((curElem->skeleton=new PtnSkeleton(*(otherElem->skeleton))) == NULL ) { + // out of memory + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + + if (prevElem!=NULL) { + prevElem->next=curElem; + } + curElem->next=NULL; + prevElem = curElem; + otherElem = otherElem->next; + } + + } +} + +PatternMap::~PatternMap() { + for (int32_t i=0; i < MAX_PATTERN_ENTRIES; ++i ) { + if (boot[i]!=NULL ) { + delete boot[i]; + boot[i]=NULL; + } + } +} // PatternMap destructor + +void +PatternMap::add(const UnicodeString& basePattern, + const PtnSkeleton& skeleton, + const UnicodeString& value,// mapped pattern value + UErrorCode &status) { + UChar baseChar = basePattern.charAt(0); + PtnElem *curElem, *baseElem; + status = U_ZERO_ERROR; + + // the baseChar must be A-Z or a-z + if ((baseChar >= CAP_A) && (baseChar <= CAP_Z)) { + baseElem = boot[baseChar-CAP_A]; + } + else { + if ((baseChar >=LOW_A) && (baseChar <= LOW_Z)) { + baseElem = boot[26+baseChar-LOW_A]; + } + else { + status = U_ILLEGAL_CHARACTER; + return; + } + } + + if (baseElem == NULL) { + if ((curElem = new PtnElem()) == NULL ) { + // out of memory + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + if (baseChar >= LOW_A) { + Mutex mutex; + if ( boot[26 + (baseChar-LOW_A)]==NULL ) { + boot[26 + (baseChar-LOW_A)] = curElem; + } + else { + uprv_free(curElem); + curElem=NULL; + baseElem = boot[26 + (baseChar-LOW_A)]; + } + } + else { + Mutex mutex; + if (boot[baseChar-CAP_A] == NULL ) { + boot[baseChar-CAP_A] = curElem; + } + else { + uprv_free(curElem); + curElem=NULL; + baseElem = boot[baseChar-CAP_A]; + } + } + if ( curElem != NULL ) { + curElem->basePattern = new UnicodeString(basePattern); + curElem->pattern = new UnicodeString(value); + PtnSkeleton* pNewSkeleton = curElem->skeleton = new PtnSkeleton; + + for (int32_t i=0; itype[i] = skeleton.type[i]; + pNewSkeleton->original[i] = skeleton.original[i]; + pNewSkeleton->baseOriginal[i] = skeleton.baseOriginal[i]; + } + curElem->next = NULL; + } + } + if ( baseElem != NULL ) { + curElem = getDuplicateElem(basePattern, skeleton, baseElem); + + if (curElem == NULL) { + // add new element to the list. + curElem = baseElem; + { + Mutex mutex; + while( curElem -> next != NULL ) + { + curElem = curElem->next; + } + if ((curElem->next = new PtnElem()) == NULL ) { + // out of memory + status = U_MEMORY_ALLOCATION_ERROR; + return; + } + curElem=curElem->next; + } + curElem->basePattern = new UnicodeString(basePattern); + curElem->pattern = new UnicodeString(value); + PtnSkeleton* pNewSkeleton = curElem->skeleton = new PtnSkeleton; + for (int32_t i=0; itype[i] = skeleton.type[i]; + pNewSkeleton->original[i] = skeleton.original[i]; + pNewSkeleton->baseOriginal[i] = skeleton.baseOriginal[i]; + } + curElem->next = NULL; + } + else { + // Pattern exists in the list already. + if ( !isDupAllowed ) { + return; + } + // Overwrite the value. + *(curElem->pattern)= value; + } + } +} // PatternMap::add + +// Find the pattern from the given basePattern string. +UnicodeString * +PatternMap::getPatternFromBasePattern(UnicodeString& basePattern) { // key to search for + UChar baseChar = basePattern.charAt(0); + PtnElem *curElem, baseElem; + + // the baseChar must be A-Z or a-z + if ( (baseChar >= CAP_A) && (baseChar <= CAP_Z) ) { + curElem = boot[26 + (baseChar-CAP_A)]; + } + else { + if ( (baseChar >=LOW_A) && (baseChar <= LOW_Z) ) { + curElem = boot[baseChar-LOW_A]; + } + else + return NULL; + } + + if ( curElem == NULL ) { + return NULL; // no match + } + + do { + if ( basePattern.compare(*(curElem->basePattern))==0 ) { + return curElem->pattern; + } + curElem=curElem->next; + }while (curElem != NULL); + + return NULL; +} // PatternMap::getFromBasePattern + + +// Find the pattern from the given skeleton. +UnicodeString * +PatternMap::getPatternFromSkeleton(PtnSkeleton& skeleton) { // key to search for + PtnElem *curElem, baseElem; + + // find boot entry + UChar baseChar='\0'; + for (int32_t i=0; i= CAP_A) && (baseChar <= CAP_Z) ) { + curElem = boot[baseChar-CAP_A]; + } + else { + if ( (baseChar >=LOW_A) && (baseChar <= LOW_Z) ) { + curElem = boot[26+baseChar-LOW_A]; + } + else + return NULL; + } + + if ( curElem == NULL ) { + return NULL; // no match + } + + do { + int32_t i=0; + for (i=0; iskeleton->baseOriginal[i].compare(skeleton.baseOriginal[i]) != 0 ) + { + break; + } + } + if (i == UDATPG_FIELD_COUNT) { + return curElem->pattern; + } + curElem=curElem->next; + }while (curElem != NULL); + + return NULL; +} + +UBool +PatternMap::equals(const PatternMap& other) { + if ( this==&other ) { + return TRUE; + } + for (int32_t bootIndex=0; bootIndexbasePattern) != *(otherElem->basePattern)) || + (*(myElem->pattern) != *(otherElem->pattern) ) ) { + return FALSE; + } + if ((myElem->skeleton!=otherElem->skeleton)&& + !myElem->skeleton->equals(*(otherElem->skeleton))) { + return FALSE; + } + myElem = myElem->next; + otherElem=otherElem->next; + } + } + return TRUE; +} + +// find any key existing in the mapping table already. +// return TRUE if there is an existing key, otherwise return FALSE. +PtnElem* +PatternMap::getDuplicateElem( + const UnicodeString &basePattern, + const PtnSkeleton &skeleton, + PtnElem *baseElem) { + PtnElem *curElem; + + if ( baseElem == (PtnElem *)NULL ) { + return (PtnElem*)NULL; + } + else { + curElem = baseElem; + } + do { + if ( basePattern.compare(*(curElem->basePattern))==0 ) { + UBool isEqual=TRUE; + for (int32_t i=0; iskeleton->type[i] != skeleton.type[i] ) { + isEqual=FALSE; + break; + } + } + if (isEqual) { + return curElem; + } + } + curElem = curElem->next; + } while( curElem != (PtnElem *)NULL ); + + // end of the list + return (PtnElem*)NULL; + +} // PatternMap::getDuplicateElem + +DateTimeMatcher::DateTimeMatcher(void) { +} + + +void +DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp) { + PtnSkeleton skeleton; + return set(pattern, fp, skeleton); +} + +void +DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton& skeleton) { + + const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ" + UnicodeString repeatedPattern=UnicodeString(repeatedPatterns); + + for (int32_t i=0; iset(pattern); + for (int32_t i=0; i < fp->itemNumber; i++) { + UnicodeString field = fp->items[i]; + if ( field.charAt(0) == LOW_A ) { + continue; // skip 'a' + } + + if ( fp->isQuoteLiteral(field) ) { + UnicodeString quoteLiteral=""; + fp->getQuoteLiteral(quoteLiteral, &i); + continue; + } + int32_t canonicalIndex = fp->getCanonicalIndex(field); + if (canonicalIndex < 0 ) { + continue; + } + const dtTypeElem *row = &dtTypes[canonicalIndex]; + int32_t typeValue = row->field; + skeleton.original[typeValue]=field; + UChar repeatChar = row->patternChar; + int32_t repeatCount = row->minLen > 3 ? 3: row->minLen; + while (repeatCount-- > 0) { + skeleton.baseOriginal[typeValue] += repeatChar; + } + int16_t subTypeValue = row->type; + if ( row->type > 0) { + subTypeValue += field.length(); + } + skeleton.type[typeValue] = (char)subTypeValue; + } + { + Mutex mutex; + for (int32_t i=0; iskeleton.type[i] = skeleton.type[i]; + this->skeleton.baseOriginal[i] = skeleton.baseOriginal[i]; + this->skeleton.original[i] = skeleton.original[i]; + } + } + + return; +} + +void +DateTimeMatcher::getBasePattern(UnicodeString &result ) { + result.remove(); // Reset the result first. + for (int32_t i=0; iskeleton.type[i]=skeleton.type[i]; + this->skeleton.original[i]=skeleton.original[i]; + this->skeleton.baseOriginal[i]=skeleton.baseOriginal[i]; + } + return; +} + +void +DateTimeMatcher::copyFrom() { + // same as clear + for (int32_t i=0; iskeleton.type[i]=0; + this->skeleton.original[i]=""; + this->skeleton.baseOriginal[i]=""; + } + return; +} + +UBool +DateTimeMatcher::equals(DateTimeMatcher* other) { + if (other==NULL) { + return FALSE; + } + for (int32_t i=0; iskeleton.original[i]!=other->skeleton.original[i] ) { + return FALSE; + } + } + return TRUE; +} + +int32_t +DateTimeMatcher::getFieldMask() { + int32_t result=0; + + for (int32_t i=0; i= pattern.length()) { + return DONE; + } + // check the current char is between A-Z or a-z + do { + UChar c=pattern.charAt(curLoc); + if ( (c>=CAP_A && c<=CAP_Z) || (c>=LOW_A && c<=LOW_Z) ) { + curLoc++; + } + else { + startPos = curLoc; + *len=1; + return ADD_TOKEN; + } + + if ( pattern.charAt(curLoc)!= pattern.charAt(startPos) ) { + break; // not the same token + } + } while(curLoc <= pattern.length()); + *len = curLoc-startPos; + return ADD_TOKEN; +} + +void +FormatParser::set(const UnicodeString& pattern) { + int32_t startPos=0; + TokenStatus result=START; + int32_t len=0; + itemNumber =0; + + do { + result = setTokens( pattern, startPos, &len ); + if ( result == ADD_TOKEN ) + { + items[itemNumber++] = UnicodeString(pattern, startPos, len ); + startPos += len; + } + else { + break; + } + } while (result==ADD_TOKEN && itemNumber < MAX_DT_TOKEN); +} + +int +FormatParser::getCanonicalIndex(const UnicodeString& s) { + int32_t len = s.length(); + UChar ch = s.charAt(0); + int32_t i=0; + + while (dtTypes[i].patternChar!='\0') { + if ( dtTypes[i].patternChar!=ch ) { + ++i; + continue; + } + if (dtTypes[i].patternChar!=dtTypes[i+1].patternChar) { + return i; + } + if (dtTypes[i+1].minLen <= len) { + ++i; + continue; + } + return i; + } + return -1; +} + +UBool +FormatParser::isQuoteLiteral(UnicodeString s) { + if ((s.charAt(0)==SINGLE_QUOTE)||(s.charAt(0)==FORWARDSLASH)||(s.charAt(0)==BACKSLASH) || + (s.charAt(0)==SPACE) ||(s.charAt(0)==COMMA) ||(s.charAt(0)==HYPHEN) ||(s.charAt(0)==DOT) ) { + return TRUE; + } + else { + return FALSE; + } +} + +// This function aussumes the current itemIndex points to the quote literal. +// Please call isQuoteLiteral prior to this function. +void +FormatParser::getQuoteLiteral(UnicodeString& quote, int32_t *itemIndex) { + UErrorCode status = U_ZERO_ERROR; + int32_t i=*itemIndex; + + if ( quoteFilter == NULL ) { + setFilter(status); + } + if ((items[i].charAt(0)==FORWARDSLASH) || (items[i].charAt(0)==BACKSLASH) || (items[i].charAt(0)==SPACE) || + (items[i].charAt(0)==COMMA) ||(items[i].charAt(0)==HYPHEN) ||(items[i].charAt(0)==DOT)) { + quote += items[i]; + return; + } + if (items[i].charAt(0)==SINGLE_QUOTE) { + quote += items[i]; + ++i; + } + while ( i < itemNumber ) { + if ( items[i].charAt(0)==SINGLE_QUOTE ) { + if ( items[i+1].charAt(0)==SINGLE_QUOTE ) { + // two single quotes e.g. 'o''clock' + quote += items[++i]; + continue; + } + else { + // TODO turn off the filter + //if ( quoteFilter->contains(items[i].charAt(0)) ) { + // TODO add error checking here + quote += items[i]; + //} + break; + } + + } + else { + quote += items[i]; + } + ++i; + } + *itemIndex=i; + return; +} + +UBool +FormatParser::isPatternSeparator(UnicodeString& field) { + for (int32_t i=0; iboot[i]=patternMap->boot[i]; + } + */ + this->patternMap=&patternMap; +} + +PtnSkeleton* +PatternMapIterator::getSkeleton() { + if ( nodePtr == NULL ) { + return NULL; + } + else { + return nodePtr->skeleton; + } +} + +UBool +PatternMapIterator::hasNext() { + int32_t headIndex=bootIndex; + PtnElem *curPtr=nodePtr; + + if (patternMap==NULL) { + return FALSE; + } + while ( headIndex < MAX_PATTERN_ENTRIES ) { + if ( curPtr != NULL ) { + if ( curPtr->next != NULL ) { + return TRUE; + } + else { + headIndex++; + curPtr=NULL; + continue; + } + } + else { + if ( patternMap->boot[headIndex] != NULL ) { + return TRUE; + } + else { + headIndex++; + continue; + } + } + + } + return FALSE; +} + +DateTimeMatcher& +PatternMapIterator::next() { + while ( bootIndex < MAX_PATTERN_ENTRIES ) { + if ( nodePtr != NULL ) { + if ( nodePtr->next != NULL ) { + nodePtr = nodePtr->next; + break; + } + else { + bootIndex++; + nodePtr=NULL; + continue; + } + } + else { + if ( patternMap->boot[bootIndex] != NULL ) { + nodePtr = patternMap->boot[bootIndex]; + break; + } + else { + bootIndex++; + continue; + } + } + } + if (nodePtr!=NULL) { + matcher->copyFrom(*nodePtr->skeleton); + } + else { + matcher->copyFrom(); + } + return *matcher; +} + +PtnSkeleton::PtnSkeleton() : UObject() { +} + + +PtnSkeleton::PtnSkeleton(PtnSkeleton& other) : UObject() { + for (int32_t i=0; itype[i]=other.type[i]; + this->original[i]=other.original[i]; + this->baseOriginal[i]=other.baseOriginal[i]; + } +} + +UBool +PtnSkeleton::equals(PtnSkeleton& other) { + for (int32_t i=0; ibasePattern); + break; + case DT_PATTERN: + s=*(curElem->pattern); + break; + case DT_SKELETON: + curSkeleton=curElem->skeleton; + s=curSkeleton->getSkeleton(); + break; + } + if ( !isCanonicalItem(s) ) { + fSkeletons->addElement(new UnicodeString(s), status); + if (U_FAILURE(status)) { + delete fSkeletons; + fSkeletons = NULL; + return; + } + } + curElem = curElem->next; + } + } + if ((bootIndex==MAX_PATTERN_ENTRIES) && (curElem!=NULL) ) { + status = U_BUFFER_OVERFLOW_ERROR; + } +} + +const UnicodeString* +DTSkeletonEnumeration::snext(UErrorCode& status) { + if (U_SUCCESS(status) && pos < fSkeletons->size()) { + return (const UnicodeString*)fSkeletons->elementAt(pos++); + } + return NULL; +} + +void +DTSkeletonEnumeration::reset(UErrorCode& status) { + pos=0; +} + +int32_t +DTSkeletonEnumeration::count(UErrorCode& status) const { + return (fSkeletons==NULL) ? 0 : fSkeletons->size(); + } +UBool +DTSkeletonEnumeration::isCanonicalItem(const UnicodeString& item) { + if ( item.length() != 1 ) { + return FALSE; + } + for (int32_t i=0; isize(); ++i) { + if ((s=(UnicodeString *)fSkeletons->elementAt(i))!=NULL) { + delete s; + } + } + delete fSkeletons; +} + +DTRedundantEnumeration::DTRedundantEnumeration() { + pos=0; + fPatterns = NULL; +} + +void +DTRedundantEnumeration::add(const UnicodeString& pattern, UErrorCode& status) { + if ( (fPatterns == NULL) && U_SUCCESS(status) ) { + fPatterns = new UVector(status); + if (U_FAILURE(status)) { + delete fPatterns; + fPatterns = NULL; + return; + } + } + fPatterns->addElement(new UnicodeString(pattern), status); + if (U_FAILURE(status)) { + delete fPatterns; + fPatterns = NULL; + return; + } +} + +const UnicodeString* +DTRedundantEnumeration::snext(UErrorCode& status) { + if (U_SUCCESS(status) && pos < fPatterns->size()) { + return (const UnicodeString*)fPatterns->elementAt(pos++); + } + return NULL; +} + +void +DTRedundantEnumeration::reset(UErrorCode& status) { + pos=0; +} + +int32_t +DTRedundantEnumeration::count(UErrorCode& status) const { + return (fPatterns==NULL) ? 0 : fPatterns->size(); +} + +UBool +DTRedundantEnumeration::isCanonicalItem(const UnicodeString& item) { + if ( item.length() != 1 ) { + return FALSE; + } + for (int32_t i=0; isize(); ++i) { + if ((s=(UnicodeString *)fPatterns->elementAt(i))!=NULL) { + delete s; + } + } + delete fPatterns; +} + +U_NAMESPACE_END + + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +//eof diff --git a/icu4c/source/i18n/dtptngen_impl.h b/icu4c/source/i18n/dtptngen_impl.h new file mode 100644 index 00000000000..14675cfbbb9 --- /dev/null +++ b/icu4c/source/i18n/dtptngen_impl.h @@ -0,0 +1,381 @@ +/* +******************************************************************************* +* Copyright (C) 2007, International Business Machines Corporation and +* others. All Rights Reserved. * +******************************************************************************* +* +* File DTPTNGEN.H +* +******************************************************************************* +*/ + +#include "uvector.h" + +#ifndef __DTPTNGEN_IMPL_H__ +#define __DTPTNGEN_IMPL_H__ + +// TODO(claireho): Split off Builder class. +// TODO(claireho): If splitting off Builder class: As subclass or independent? + +#define MAX_PATTERN_ENTRIES 52 +#define MAX_CLDR_FIELD_LEN 60 +#define MAX_DT_TOKEN 50 +#define MAX_RESOURCE_FIELD 11 +#define MAX_AVAILABLE_FORMATS 12 +#define NONE 0 +#define EXTRA_FIELD 0x10000 +#define MISSING_FIELD 0x1000 +#define MAX_STRING_ENUMERATION 200 +#define SINGLE_QUOTE ((UChar)0x0027) +#define FORWARDSLASH ((UChar)0x002F) +#define BACKSLASH ((UChar)0x005C) +#define SPACE ((UChar)0x0020) +#define QUOTATION_MARK ((UChar)0x0022) +#define ASTERISK ((UChar)0x002A) +#define PLUSSITN ((UChar)0x002B) +#define COMMA ((UChar)0x002C) +#define HYPHEN ((UChar)0x002D) +#define DOT ((UChar)0x002E) +#define COLON ((UChar)0x003A) +#define CAP_A ((UChar)0x0041) +#define CAP_C ((UChar)0x0043) +#define CAP_D ((UChar)0x0044) +#define CAP_E ((UChar)0x0045) +#define CAP_F ((UChar)0x0046) +#define CAP_G ((UChar)0x0047) +#define CAP_H ((UChar)0x0048) +#define CAP_L ((UChar)0x004C) +#define CAP_M ((UChar)0x004D) +#define CAP_O ((UChar)0x004F) +#define CAP_Q ((UChar)0x0051) +#define CAP_S ((UChar)0x0053) +#define CAP_T ((UChar)0x0054) +#define CAP_V ((UChar)0x0056) +#define CAP_W ((UChar)0x0057) +#define CAP_Y ((UChar)0x0059) +#define CAP_Z ((UChar)0x005A) +#define LOWLINE ((UChar)0x005F) +#define LOW_A ((UChar)0x0061) +#define LOW_C ((UChar)0x0063) +#define LOW_D ((UChar)0x0064) +#define LOW_E ((UChar)0x0065) +#define LOW_F ((UChar)0x0066) +#define LOW_G ((UChar)0x0067) +#define LOW_H ((UChar)0x0068) +#define LOW_I ((UChar)0x0069) +#define LOW_K ((UChar)0x006B) +#define LOW_L ((UChar)0x006C) +#define LOW_M ((UChar)0x006D) +#define LOW_N ((UChar)0x006E) +#define LOW_O ((UChar)0x006F) +#define LOW_P ((UChar)0x0070) +#define LOW_Q ((UChar)0x0071) +#define LOW_R ((UChar)0x0072) +#define LOW_S ((UChar)0x0073) +#define LOW_T ((UChar)0x0074) +#define LOW_U ((UChar)0x0075) +#define LOW_V ((UChar)0x0076) +#define LOW_W ((UChar)0x0077) +#define LOW_Y ((UChar)0x0079) +#define LOW_Z ((UChar)0x007A) +#define DT_SHORT -0x101 +#define DT_LONG -0x102 +#define DT_NUMERIC 0x100 +#define DT_NARROW -0x100 +#define DT_DELTA 0x10 + +U_NAMESPACE_BEGIN + +const int32_t UDATPG_FRACTIONAL_MASK = 1< + + + + + + + + + + + + @@ -1310,6 +1340,32 @@ /> + + + + + + + + + + diff --git a/icu4c/source/i18n/udatpg.cpp b/icu4c/source/i18n/udatpg.cpp new file mode 100644 index 00000000000..6122912b6be --- /dev/null +++ b/icu4c/source/i18n/udatpg.cpp @@ -0,0 +1,248 @@ +/* +******************************************************************************* +* +* Copyright (C) 2007, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: udatpg.cpp +* encoding: US-ASCII +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2007jul30 +* created by: Markus W. Scherer +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/udatpg.h" +#include "unicode/uenum.h" +#include "unicode/strenum.h" +#include "unicode/dtptngen.h" +#include "ustrenum.h" + +U_NAMESPACE_USE + +U_DRAFT UDateTimePatternGenerator * U_EXPORT2 +udatpg_open(const char *locale, UErrorCode *pErrorCode) { + if(locale==NULL) { + return (UDateTimePatternGenerator *)DateTimePatternGenerator::createInstance(*pErrorCode); + } else { + return (UDateTimePatternGenerator *)DateTimePatternGenerator::createInstance(Locale(locale), *pErrorCode); + } +} + +U_DRAFT UDateTimePatternGenerator * U_EXPORT2 +udatpg_openEmpty(UErrorCode *pErrorCode) { + return (UDateTimePatternGenerator *)DateTimePatternGenerator::createEmptyInstance(*pErrorCode); +} + +U_DRAFT void U_EXPORT2 +udatpg_close(UDateTimePatternGenerator *dtpg) { + delete (DateTimePatternGenerator *)dtpg; +} + +U_DRAFT UDateTimePatternGenerator * U_EXPORT2 +udatpg_clone(const UDateTimePatternGenerator *dtpg, UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return NULL; + } + return (UDateTimePatternGenerator *)(((const DateTimePatternGenerator *)dtpg)->clone()); +} + +U_DRAFT int32_t U_EXPORT2 +udatpg_getBestPattern(UDateTimePatternGenerator *dtpg, + const UChar *skeleton, int32_t length, + UChar *bestPattern, int32_t capacity, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(skeleton==NULL && length!=0) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString skeletonString((UBool)(length<0), skeleton, length); + UnicodeString result=((DateTimePatternGenerator *)dtpg)->getBestPattern(skeletonString, *pErrorCode); + return result.extract(bestPattern, capacity, *pErrorCode); +} + +U_DRAFT int32_t U_EXPORT2 +udatpg_getSkeleton(UDateTimePatternGenerator *dtpg, + const UChar *pattern, int32_t length, + UChar *skeleton, int32_t capacity, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(pattern==NULL && length!=0) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString patternString((UBool)(length<0), pattern, length); + UnicodeString result=((DateTimePatternGenerator *)dtpg)->getSkeleton(patternString, *pErrorCode); + return result.extract(skeleton, capacity, *pErrorCode); +} + +U_DRAFT int32_t U_EXPORT2 +udatpg_getBaseSkeleton(UDateTimePatternGenerator *dtpg, + const UChar *pattern, int32_t length, + UChar *skeleton, int32_t capacity, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if(pattern==NULL && length!=0) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString patternString((UBool)(length<0), pattern, length); + UnicodeString result=((DateTimePatternGenerator *)dtpg)->getBaseSkeleton(patternString, *pErrorCode); + return result.extract(skeleton, capacity, *pErrorCode); +} + +U_DRAFT UDateTimePatternConflict U_EXPORT2 +udatpg_addPattern(UDateTimePatternGenerator *dtpg, + const UChar *pattern, int32_t patternLength, + UBool override, + UChar *conflictingPattern, int32_t capacity, int32_t *pLength, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return UDATPG_NO_CONFLICT; + } + if(pattern==NULL && patternLength!=0) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return UDATPG_NO_CONFLICT; + } + UnicodeString patternString((UBool)(patternLength<0), pattern, patternLength); + UnicodeString conflictingPatternString; + UDateTimePatternConflict result=((DateTimePatternGenerator *)dtpg)-> + addPattern(patternString, override, conflictingPatternString, *pErrorCode); + int32_t length=conflictingPatternString.extract(conflictingPattern, capacity, *pErrorCode); + if(pLength!=NULL) { + *pLength=length; + } + return result; +} + +U_DRAFT void U_EXPORT2 +udatpg_setAppendItemFormat(UDateTimePatternGenerator *dtpg, + UDateTimePatternField field, + const UChar *value, int32_t length) { + UnicodeString valueString((UBool)(length<0), value, length); + ((DateTimePatternGenerator *)dtpg)->setAppendItemFormat(field, valueString); +} + +U_DRAFT const UChar * U_EXPORT2 +udatpg_getAppendItemFormat(const UDateTimePatternGenerator *dtpg, + UDateTimePatternField field, + int32_t *pLength) { + const UnicodeString &result=((const DateTimePatternGenerator *)dtpg)->getAppendItemFormat(field); + if(pLength!=NULL) { + *pLength=result.length(); + } + return result.getBuffer(); +} + +U_DRAFT void U_EXPORT2 +udatpg_setAppendItemName(UDateTimePatternGenerator *dtpg, + UDateTimePatternField field, + const UChar *value, int32_t length) { + UnicodeString valueString((UBool)(length<0), value, length); + ((DateTimePatternGenerator *)dtpg)->setAppendItemName(field, valueString); +} + +U_DRAFT const UChar * U_EXPORT2 +udatpg_getAppendItemName(const UDateTimePatternGenerator *dtpg, + UDateTimePatternField field, + int32_t *pLength) { + const UnicodeString &result=((const DateTimePatternGenerator *)dtpg)->getAppendItemName(field); + if(pLength!=NULL) { + *pLength=result.length(); + } + return result.getBuffer(); +} + +U_DRAFT void U_EXPORT2 +udatpg_setDateTimeFormat(const UDateTimePatternGenerator *dtpg, + const UChar *dtFormat, int32_t length) { + UnicodeString dtFormatString((UBool)(length<0), dtFormat, length); + ((DateTimePatternGenerator *)dtpg)->setDateTimeFormat(dtFormatString); +} + +U_DRAFT const UChar * U_EXPORT2 +udatpg_getDateTimeFormat(const UDateTimePatternGenerator *dtpg, + int32_t *pLength) { + const UnicodeString &result=((const DateTimePatternGenerator *)dtpg)->getDateTimeFormat(); + if(pLength!=NULL) { + *pLength=result.length(); + } + return result.getBuffer(); +} + +U_DRAFT void U_EXPORT2 +udatpg_setDecimal(UDateTimePatternGenerator *dtpg, + const UChar *decimal, int32_t length) { + UnicodeString decimalString((UBool)(length<0), decimal, length); + ((DateTimePatternGenerator *)dtpg)->setDecimal(decimalString); +} + +U_DRAFT const UChar * U_EXPORT2 +udatpg_getDecimal(const UDateTimePatternGenerator *dtpg, + int32_t *pLength) { + const UnicodeString &result=((const DateTimePatternGenerator *)dtpg)->getDecimal(); + if(pLength!=NULL) { + *pLength=result.length(); + } + return result.getBuffer(); +} + +U_DRAFT int32_t U_EXPORT2 +udatpg_replaceFieldTypes(UDateTimePatternGenerator *dtpg, + const UChar *pattern, int32_t patternLength, + const UChar *skeleton, int32_t skeletonLength, + UChar *dest, int32_t destCapacity, + UErrorCode *pErrorCode) { + if(U_FAILURE(*pErrorCode)) { + return 0; + } + if((pattern==NULL && patternLength!=0) || (skeleton==NULL && skeletonLength!=0)) { + *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + UnicodeString patternString((UBool)(patternLength<0), pattern, patternLength); + UnicodeString skeletonString((UBool)(skeletonLength<0), skeleton, skeletonLength); + UnicodeString result=((DateTimePatternGenerator *)dtpg)->replaceFieldTypes(patternString, skeletonString, *pErrorCode); + return result.extract(dest, destCapacity, *pErrorCode); +} + +U_DRAFT UEnumeration * U_EXPORT2 +udatpg_openSkeletons(const UDateTimePatternGenerator *dtpg, UErrorCode *pErrorCode) { + return uenum_openStringEnumeration( + ((DateTimePatternGenerator *)dtpg)->getSkeletons(*pErrorCode), + pErrorCode); +} + +U_DRAFT UEnumeration * U_EXPORT2 +udatpg_openBaseSkeletons(const UDateTimePatternGenerator *dtpg, UErrorCode *pErrorCode) { + return uenum_openStringEnumeration( + ((DateTimePatternGenerator *)dtpg)->getBaseSkeletons(*pErrorCode), + pErrorCode); +} + +U_DRAFT const UChar * U_EXPORT2 +udatpg_getPatternForSkeleton(const UDateTimePatternGenerator *dtpg, + const UChar *skeleton, int32_t skeletonLength, + int32_t *pLength) { + UnicodeString skeletonString((UBool)(skeletonLength<0), skeleton, skeletonLength); + const UnicodeString &result=((const DateTimePatternGenerator *)dtpg)->getPatternForSkeleton(skeletonString); + if(pLength!=NULL) { + *pLength=result.length(); + } + return result.getBuffer(); +} + +#endif diff --git a/icu4c/source/i18n/unicode/dtptngen.h b/icu4c/source/i18n/unicode/dtptngen.h new file mode 100644 index 00000000000..79985459e8b --- /dev/null +++ b/icu4c/source/i18n/unicode/dtptngen.h @@ -0,0 +1,413 @@ +/* +******************************************************************************* +* Copyright (C) 2007, International Business Machines Corporation and +* others. All Rights Reserved. +******************************************************************************* +* +* File DTPTNGEN.H +* +******************************************************************************* +*/ + +#ifndef __DTPTNGEN_H__ +#define __DTPTNGEN_H__ + +#include "unicode/utypes.h" +#include "unicode/format.h" +#include "unicode/datefmt.h" +#include "unicode/calendar.h" +#include "unicode/ustring.h" +#include "unicode/locid.h" +#include "unicode/udat.h" +#include "unicode/udatpg.h" +#include "unicode/uniset.h" + +U_NAMESPACE_BEGIN + +class DateFormatSymbols; +class DateFormat; +class Hashtable; +class FormatParser; +class DateTimeMatcher; +class DistanceInfo; +class PatternMap; + +/** + * This class provides flexible generation of date format patterns, like "yy-MM-dd". + * The user can build up the generator by adding successive patterns. Once that + * is done, a query can be made using a "skeleton", which is a pattern which just + * includes the desired fields and lengths. The generator will return the "best fit" + * pattern corresponding to that skeleton. + *

The main method people will use is getBestPattern(String skeleton), + * since normally this class is pre-built with data from a particular locale. + * However, generators can be built directly from other data as well. + *

Issue: may be useful to also have a function that returns the list of + * fields in a pattern, in order, since we have that internally. + * That would be useful for getting the UI order of field elements. + * @draft ICU 3.8 +**/ +class U_I18N_API DateTimePatternGenerator : public UObject { +public: + /** + * Construct a flexible generator according to default locale. + * @param status Must be a reference to an error code value, + * which must not indicate a failure before the function call. + * @draft ICU 3.8 + */ + static DateTimePatternGenerator* U_EXPORT2 createInstance(UErrorCode& status); + + /** + * Construct a flexible generator according to data for a given locale. + * @param uLocale + * @param status Must be a reference to an error code value, + * which must not indicate a failure before the function call. + * @draft ICU 3.8 + */ + static DateTimePatternGenerator* U_EXPORT2 createInstance(const Locale& uLocale, UErrorCode& status); + + /** + * Create an empty generator, to be constructed with add(...) etc. + * @param status Must be a reference to an error code value, + * which must not indicate a failure before the function call. + * @draft ICU 3.8 + */ + static DateTimePatternGenerator* U_EXPORT2 createEmptyInstance(UErrorCode& status); + + /** + * Destructor. + */ + virtual ~DateTimePatternGenerator(); + + /** + * Clone DateTimePatternGenerator object. Clients are responsible for + * deleting the DateTimePatternGenerator object cloned. + * @draft ICU 3.8 + */ + DateTimePatternGenerator* clone() const; + + /** + * Return true if another object is semantically equal to this one. + * + * @param other the DateTimePatternGenerator object to be compared with. + * @return true if other is semantically equal to this. + * @draft ICU 3.8 + */ + UBool operator==(const DateTimePatternGenerator& other) const; + + /** + * Utility to return a unique skeleton from a given pattern. For example, + * both "MMM-dd" and "dd/MMM" produce the skeleton "MMMdd". + * + * @param pattern Input pattern, such as "dd/MMM" + * @param status Must be a reference to an error code value, + * which must not indicate a failure before the function call. + * @return skeleton such as "MMMdd" + * @draft ICU 3.8 + */ + UnicodeString getSkeleton(const UnicodeString& pattern, UErrorCode& status); + + /** + * Utility to return a unique base skeleton from a given pattern. This is + * the same as the skeleton, except that differences in length are minimized + * so as to only preserve the difference between string and numeric form. So + * for example, both "MMM-dd" and "d/MMM" produce the skeleton "MMMd" + * (notice the single d). + * + * @param pattern Input pattern, such as "dd/MMM" + * @param status Must be a reference to an error code value, + * which must not indicate a failure before the function call. + * @return base skeleton, such as "Md" + * @draft ICU 3.8 + */ + UnicodeString getBaseSkeleton(const UnicodeString& pattern, UErrorCode& status); + + /** + * Adds a pattern to the generator. If the pattern has the same skeleton as + * an existing pattern, and the override parameter is set, then the previous + * value is overriden. Otherwise, the previous value is retained. In either + * case, the conflicting status is set and previous vale is stored in + * conflicting pattern. + *

+ * Note that single-field patterns (like "MMM") are automatically added, and + * don't need to be added explicitly! + * + * @param pattern Input pattern, such as "dd/MMM" + * @param override When existing values are to be overridden use true, + * otherwise use false. + * @param conflicting pattern Previous pattern with the same skeleton. + * @param status Must be a reference to an error code value, + * which must not indicate a failure before the function call. + * @return conflicting status. The value could be UDATPG_NO_CONFLICT, + * UDATPG_BASE_CONFLICT or UDATPG_CONFLICT. + * @draft ICU 3.8 + */ + UDateTimePatternConflict addPattern(const UnicodeString& pattern, + UBool override, + UnicodeString& conflictingPattern, + UErrorCode& status); + + /** + * An AppendItem format is a pattern used to append a field if there is no + * good match. For example, suppose that the input skeleton is "GyyyyMMMd", + * and there is no matching pattern internally, but there is a pattern + * matching "yyyyMMMd", say "d-MM-yyyy". Then that pattern is used, plus the + * G. The way these two are conjoined is by using the AppendItemFormat for G + * (era). So if that value is, say "{0}, {1}" then the final resulting + * pattern is "d-MM-yyyy, G". + *

+ * There are actually three available variables: {0} is the pattern so far, + * {1} is the element we are adding, and {2} is the name of the element. + *

+ * This reflects the way that the CLDR data is organized. + * + * @param field such as UDATPG_ERA_FIELD. + * @param value pattern, such as "{0}, {1}" + * @draft ICU 3.8 + */ + void setAppendItemFormat(UDateTimePatternField field, const UnicodeString& value); + + /** + * Getter corresponding to setAppendItemFormat. Values below 0 or at or + * above UDATPG_FIELD_COUNT are illegal arguments. + * + * @param field such as UDATPG_ERA_FIELD. + * @return append pattern for field + * @draft ICU 3.8 + */ + const UnicodeString& getAppendItemFormat(UDateTimePatternField field) const; + + /** + * Sets the names of field, eg "era" in English for ERA. These are only + * used if the corresponding AppendItemFormat is used, and if it contains a + * {2} variable. + *

+ * This reflects the way that the CLDR data is organized. + * + * @param field such as UDATPG_ERA_FIELD. + * @param value name of the field + * @draft ICU 3.8 + */ + void setAppendItemName(UDateTimePatternField field, const UnicodeString& value); + + /** + * Getter corresponding to setAppendItemNames. Values below 0 or at or above + * UDATPG_FIELD_COUNT are illegal arguments. + * + * @param field such as UDATPG_ERA_FIELD. + * @return name for field + * @draft ICU 3.8 + */ + const UnicodeString& getAppendItemName(UDateTimePatternField field) const; + + /** + * The date time format is a message format pattern used to compose date and + * time patterns. The default value is "{0} {1}", where {0} will be replaced + * by the date pattern and {1} will be replaced by the time pattern. + *

+ * This is used when the input skeleton contains both date and time fields, + * but there is not a close match among the added patterns. For example, + * suppose that this object was created by adding "dd-MMM" and "hh:mm", and + * its datetimeFormat is the default "{0} {1}". Then if the input skeleton + * is "MMMdhmm", there is not an exact match, so the input skeleton is + * broken up into two components "MMMd" and "hmm". There are close matches + * for those two skeletons, so the result is put together with this pattern, + * resulting in "d-MMM h:mm". + * + * @param dateTimeFormat + * message format pattern, here {0} will be replaced by the date + * pattern and {1} will be replaced by the time pattern. + * @draft ICU 3.8 + */ + void setDateTimeFormat(const UnicodeString& dtFormat); + + /** + * Getter corresponding to setDateTimeFormat. + * @return DateTimeFormat. + * @draft ICU 3.8 + */ + const UnicodeString& getDateTimeFormat() const; + + /** + * Return the best pattern matching the input skeleton. It is guaranteed to + * have all of the fields in the skeleton. + * + * @param skeleton + * The skeleton is a pattern containing only the variable fields. + * For example, "MMMdd" and "mmhh" are skeletons. + * @return bestPattern + * The best pattern found from the given skeleton. + * @draft ICU 3.8 + */ + UnicodeString getBestPattern(const UnicodeString& skeleton, UErrorCode& status); + + + /** + * Adjusts the field types (width and subtype) of a pattern to match what is + * in a skeleton. That is, if you supply a pattern like "d-M H:m", and a + * skeleton of "MMMMddhhmm", then the input pattern is adjusted to be + * "dd-MMMM hh:mm". This is used internally to get the best match for the + * input skeleton, but can also be used externally. + * + * @param pattern Input pattern + * @param skeleton + * The skeleton is a pattern containing only the variable fields. + * For example, "MMMdd" and "mmhh" are skeletons. + * @param status Must be a reference to an error code value, + * which must not indicate a failure before the function call. + * @return pattern adjusted to match the skeleton fields widths and subtypes. + * @draft ICU 3.8 + */ + UnicodeString replaceFieldTypes(const UnicodeString& pattern, + const UnicodeString& skeleton, + UErrorCode& status); + + /** + * Return a list of all the skeletons (in canonical form) from this class. + * + * Call getPatternForSkeleton() to get the corresponding pattern. + * + * @param status Must be a reference to an error code value, + * which must not indicate a failure before the function call. + * @return StringEnumeration with the skeletons. + * The caller must delete the object. + * @draft ICU 3.8 + */ + StringEnumeration* getSkeletons(UErrorCode& status) const; + + /** + * Get the pattern corresponding to a given skeleton. + * @param skeleton + * @param status Must be a reference to an error code value, + * which must not indicate a failure before the function call. + * @return pattern corresponding to a given skeleton. + * @draft ICU 3.8 + */ + const UnicodeString& getPatternForSkeleton(const UnicodeString& skeleton) const; + + /** + * Return a list of all the base skeletons (in canonical form) from this class. + * + * @param status Must be a reference to an error code value, + * which must not indicate a failure before the function call. + * @return a StringEnumeration with the base skeletons. + * The caller must delete the object. + * @draft ICU 3.8 + */ + StringEnumeration* getBaseSkeletons(UErrorCode& status) const; + + /** + * Return a list of redundant patterns are those which if removed, make no + * difference in the resulting getBestPattern values. This method returns a + * list of them, to help check the consistency of the patterns used to build + * this generator. + * + * @param status Must be a reference to an error code value, + * which must not indicate a failure before the function call. + * @return a StringEnumeration with the redundant pattern. + * The caller must delete the object. + * @internal ICU 3.8 + */ + StringEnumeration* getRedundants(UErrorCode& status); + + /** + * The decimal value is used in formatting fractions of seconds. If the + * skeleton contains fractional seconds, then this is used with the + * fractional seconds. For example, suppose that the input pattern is + * "hhmmssSSSS", and the best matching pattern internally is "H:mm:ss", and + * the decimal string is ",". Then the resulting pattern is modified to be + * "H:mm:ss,SSSS" + * + * @param decimal + * @draft ICU 3.8 + */ + void setDecimal(const UnicodeString& decimal); + + /** + * Getter corresponding to setDecimal. + * @return UnicodeString corresponding to the decimal point + * @draft ICU 3.8 + */ + const UnicodeString& getDecimal() const; + + /** + * ICU "poor man's RTTI", returns a UClassID for the actual class. + * + * @draft ICU 3.8 + */ + virtual UClassID getDynamicClassID() const; + + /** + * ICU "poor man's RTTI", returns a UClassID for this class. + * + * @draft ICU 3.8 + */ + static UClassID U_EXPORT2 getStaticClassID(void); + +private: + /** + * Constructor. + * @draft ICU 3.8 + */ + DateTimePatternGenerator(UErrorCode & status); + + /** + * Constructor. + * @draft ICU 3.8 + */ + DateTimePatternGenerator(const Locale& locale, UErrorCode & status); + + /** + * Copy constructor. + * @param other DateTimePatternGenerator to copy + * @draft ICU 3.8 + */ + DateTimePatternGenerator(const DateTimePatternGenerator& other); + + /** + * Default assignment operator. + * @param other DateTimePatternGenerator to copy + * @draft ICU 3.8 + */ + DateTimePatternGenerator& operator=(const DateTimePatternGenerator& other); + + Locale pLocale; // pattern locale + FormatParser *fp; + DateTimeMatcher* dtMatcher; + DistanceInfo *distanceInfo; + PatternMap *patternMap; + UnicodeString appendItemFormats[UDATPG_FIELD_COUNT]; + UnicodeString appendItemNames[UDATPG_FIELD_COUNT]; + UnicodeString dateTimeFormat; + UnicodeString decimal; + DateTimeMatcher *skipMatcher; + Hashtable *fAvailableFormatKeyHash; + UnicodeString hackPattern; + UErrorCode fStatus; + UnicodeString emptyString; + + void initData(const Locale &locale); + void addCanonicalItems(); + void addICUPatterns(const Locale& locale); + void hackTimes(UnicodeString& hackPattern, UErrorCode& status); + void addCLDRData(const Locale& locale); + void initHashtable(UErrorCode& status); + void setDateTimeFromCalendar(const Locale& locale); + void setDecimalSymbols(const Locale& locale, UErrorCode& status); + UDateTimePatternField getAppendFormatNumber(const char* field); + UDateTimePatternField getAppendNameNumber(const char* field); + void getAppendName(UDateTimePatternField field, UnicodeString& value); + int32_t getCanonicalIndex(const UnicodeString& field); + UnicodeString* getBestRaw(DateTimeMatcher& source, int32_t includeMask, DistanceInfo* missingFields); + UnicodeString adjustFieldTypes(const UnicodeString& pattern, UBool fixFractionalSeconds); + UnicodeString getBestAppending(const int32_t missingFields); + int32_t getTopBitNumber(int32_t foundMask); + void setAvailableFormat(const char* key, UErrorCode& status); + UBool isAvailableFormatSet(const char* key); + void copyHashtable(Hashtable *other); + UBool isCanonicalItem(const UnicodeString& item); + UErrorCode getStatus() { return fStatus; } ; +} ;// end class DateTimePatternGenerator + +U_NAMESPACE_END + +#endif diff --git a/icu4c/source/i18n/unicode/udatpg.h b/icu4c/source/i18n/unicode/udatpg.h new file mode 100644 index 00000000000..8a973e80125 --- /dev/null +++ b/icu4c/source/i18n/unicode/udatpg.h @@ -0,0 +1,472 @@ +/* +******************************************************************************* +* +* Copyright (C) 2007, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: udatpg.h +* encoding: US-ASCII +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2007jul30 +* created by: Markus W. Scherer +*/ + +#ifndef __UDATPG_H__ +#define __UDATPG_H__ + +#include "unicode/utypes.h" +#include "unicode/uenum.h" + +/** + * \file + * \brief C API: Wrapper for DateTimePatternGenerator (unicode/dtptngen.h). + * + * UDateTimePatternGenerator provides flexible generation of date format patterns, + * like "yy-MM-dd". The user can build up the generator by adding successive + * patterns. Once that is done, a query can be made using a "skeleton", which is + * a pattern which just includes the desired fields and lengths. The generator + * will return the "best fit" pattern corresponding to that skeleton. + *

The main method people will use is udatpg_getBestPattern, since normally + * UDateTimePatternGenerator is pre-built with data from a particular locale. + * However, generators can be built directly from other data as well. + *

Issue: may be useful to also have a function that returns the list of + * fields in a pattern, in order, since we have that internally. + * That would be useful for getting the UI order of field elements. + */ + +/** + * Opaque type for a date/time pattern generator object. + * @draft ICU 3.8 + */ +typedef void *UDateTimePatternGenerator; + +#ifndef U_HIDE_DRAFT_API + +/** + * Field number constants for udatpg_getAppendItemFormats() and similar functions. + * These constants are separate from UDateFormatField despite semantic overlap + * because some fields are merged for the date/time pattern generator. + * @draft ICU 3.8 + */ +typedef enum UDateTimePatternField { + /** @draft ICU 3.8 */ + UDATPG_ERA_FIELD, + /** @draft ICU 3.8 */ + UDATPG_YEAR_FIELD, + /** @draft ICU 3.8 */ + UDATPG_QUARTER_FIELD, + /** @draft ICU 3.8 */ + UDATPG_MONTH_FIELD, + /** @draft ICU 3.8 */ + UDATPG_WEEK_OF_YEAR_FIELD, + /** @draft ICU 3.8 */ + UDATPG_WEEK_OF_MONTH_FIELD, + /** @draft ICU 3.8 */ + UDATPG_WEEKDAY_FIELD, + /** @draft ICU 3.8 */ + UDATPG_DAY_OF_YEAR_FIELD, + /** @draft ICU 3.8 */ + UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, + /** @draft ICU 3.8 */ + UDATPG_DAY_FIELD, + /** @draft ICU 3.8 */ + UDATPG_DAYPERIOD_FIELD, + /** @draft ICU 3.8 */ + UDATPG_HOUR_FIELD, + /** @draft ICU 3.8 */ + UDATPG_MINUTE_FIELD, + /** @draft ICU 3.8 */ + UDATPG_SECOND_FIELD, + /** @draft ICU 3.8 */ + UDATPG_FRACTIONAL_SECOND_FIELD, + /** @draft ICU 3.8 */ + UDATPG_ZONE_FIELD, + /** @draft ICU 3.8 */ + UDATPG_FIELD_COUNT +} UDateTimePatternField; + +/** + * Status return values from udatpg_addPattern(). + * @draft ICU 3.8 + */ +typedef enum UDateTimePatternConflict { + /** @draft ICU 3.8 */ + UDATPG_NO_CONFLICT, + /** @draft ICU 3.8 */ + UDATPG_BASE_CONFLICT, + /** @draft ICU 3.8 */ + UDATPG_CONFLICT, + /** @draft ICU 3.8 */ + UDATPG_CONFLICT_COUNT +} UDateTimePatternConflict; + +#endif + +/** + * Open a generator according to a given locale. + * @param locale + * @param pErrorCode a pointer to the UErrorCode which must not indicate a + * failure before the function call. + * @return a pointer to UDateTimePatternGenerator. + * @draft ICU 3.8 + */ +U_DRAFT UDateTimePatternGenerator * U_EXPORT2 +udatpg_open(const char *locale, UErrorCode *pErrorCode); + +/** + * Open an empty generator, to be constructed with udatpg_addPattern(...) etc. + * @param pErrorCode a pointer to the UErrorCode which must not indicate a + * failure before the function call. + * @return a pointer to UDateTimePatternGenerator. + * @draft ICU 3.8 + */ +U_DRAFT UDateTimePatternGenerator * U_EXPORT2 +udatpg_openEmpty(UErrorCode *pErrorCode); + +/** + * Close a generator. + * @param a pointer to UDateTimePatternGenerator. + * @draft ICU 3.8 + */ +U_DRAFT void U_EXPORT2 +udatpg_close(UDateTimePatternGenerator *dtpg); + +/** + * Create a copy pf a generator. + * @param a pointer to UDateTimePatternGenerator to be copied. + * @param pErrorCode a pointer to the UErrorCode which must not indicate a + * failure before the function call. + * @return a pointer to a new UDateTimePatternGenerator. + * @draft ICU 3.8 + */ +U_DRAFT UDateTimePatternGenerator * U_EXPORT2 +udatpg_clone(const UDateTimePatternGenerator *dtpg, UErrorCode *pErrorCode); + +/** + * Get the best pattern matching the input skeleton. It is guaranteed to + * have all of the fields in the skeleton. + * + * Note that this function uses a non-const UDateTimePatternGenerator: + * It uses a stateful pattern parser which is set up for each generator object, + * rather than creating one for each function call. + * Consecutive calls to this function do not affect each other, + * but this function cannot be used concurrently on a single generator object. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param skeleton + * The skeleton is a pattern containing only the variable fields. + * For example, "MMMdd" and "mmhh" are skeletons. + * @param length the length of skeleton + * @param bestPattern + * The best pattern found from the given skeleton. + * @param capacity the capacity of bestPattern. + * @param pErrorCode a pointer to the UErrorCode which must not indicate a + * failure before the function call. + * @return the length of bestPattern. + * @draft ICU 3.8 + */ +U_DRAFT int32_t U_EXPORT2 +udatpg_getBestPattern(UDateTimePatternGenerator *dtpg, + const UChar *skeleton, int32_t length, + UChar *bestPattern, int32_t capacity, + UErrorCode *pErrorCode); + +/** + * Get a unique skeleton from a given pattern. For example, + * both "MMM-dd" and "dd/MMM" produce the skeleton "MMMdd". + * + * Note that this function uses a non-const UDateTimePatternGenerator: + * It uses a stateful pattern parser which is set up for each generator object, + * rather than creating one for each function call. + * Consecutive calls to this function do not affect each other, + * but this function cannot be used concurrently on a single generator object. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param pattern input pattern, such as "dd/MMM". + * @param length the length of pattern. + * @param skeleton such as "MMMdd" + * @param capacity the capacity of skeleton. + * @param pErrorCode a pointer to the UErrorCode which must not indicate a + * failure before the function call. + * @return the length of skeleton. + * @draft ICU 3.8 + */ +U_DRAFT int32_t U_EXPORT2 +udatpg_getSkeleton(UDateTimePatternGenerator *dtpg, + const UChar *pattern, int32_t length, + UChar *skeleton, int32_t capacity, + UErrorCode *pErrorCode); + +/** + * Get a unique base skeleton from a given pattern. This is the same + * as the skeleton, except that differences in length are minimized so + * as to only preserve the difference between string and numeric form. So + * for example, both "MMM-dd" and "d/MMM" produce the skeleton "MMMd" + * (notice the single d). + * + * Note that this function uses a non-const UDateTimePatternGenerator: + * It uses a stateful pattern parser which is set up for each generator object, + * rather than creating one for each function call. + * Consecutive calls to this function do not affect each other, + * but this function cannot be used concurrently on a single generator object. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param pattern input pattern, such as "dd/MMM". + * @param length the length of pattern. + * @param baseSkeleton such as "Md" + * @param capacity the capacity of base skeleton. + * @param pErrorCode a pointer to the UErrorCode which must not indicate a + * failure before the function call. + * @return the length of baseSkeleton. + * @draft ICU 3.8 + */ +U_DRAFT int32_t U_EXPORT2 +udatpg_getBaseSkeleton(UDateTimePatternGenerator *dtpg, + const UChar *pattern, int32_t length, + UChar *baseSkeleton, int32_t capacity, + UErrorCode *pErrorCode); + +/** + * Adds a pattern to the generator. If the pattern has the same skeleton as + * an existing pattern, and the override parameter is set, then the previous + * value is overriden. Otherwise, the previous value is retained. In either + * case, the conflicting status is set and previous vale is stored in + * conflicting pattern. + *

+ * Note that single-field patterns (like "MMM") are automatically added, and + * don't need to be added explicitly! + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param pattern input pattern, such as "dd/MMM" + * @param patternLength the length of pattern. + * @param override When existing values are to be overridden use true, + * otherwise use false. + * @param conflictingPattern Previous pattern with the same skeleton. + * @param capacity the capacity of conflictingPattern. + * @param pLength a pointer to the length of conflictingPattern. + * @param pErrorCode a pointer to the UErrorCode which must not indicate a + * failure before the function call. + * @return conflicting status. The value could be UDATPG_NO_CONFLICT, + * UDATPG_BASE_CONFLICT or UDATPG_CONFLICT. + * @draft ICU 3.8 + */ +U_DRAFT UDateTimePatternConflict U_EXPORT2 +udatpg_addPattern(UDateTimePatternGenerator *dtpg, + const UChar *pattern, int32_t patternLength, + UBool override, + UChar *conflictingPattern, int32_t capacity, int32_t *pLength, + UErrorCode *pErrorCode); + +/** + * An AppendItem format is a pattern used to append a field if there is no + * good match. For example, suppose that the input skeleton is "GyyyyMMMd", + * and there is no matching pattern internally, but there is a pattern + * matching "yyyyMMMd", say "d-MM-yyyy". Then that pattern is used, plus the + * G. The way these two are conjoined is by using the AppendItemFormat for G + * (era). So if that value is, say "{0}, {1}" then the final resulting + * pattern is "d-MM-yyyy, G". + *

+ * There are actually three available variables: {0} is the pattern so far, + * {1} is the element we are adding, and {2} is the name of the element. + *

+ * This reflects the way that the CLDR data is organized. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param field UDateTimePatternField, such as UDATPG_ERA_FIELD + * @param value pattern, such as "{0}, {1}" + * @param length the length of value. + * @draft ICU 3.8 + */ +U_DRAFT void U_EXPORT2 +udatpg_setAppendItemFormat(UDateTimePatternGenerator *dtpg, + UDateTimePatternField field, + const UChar *value, int32_t length); +/** + * Getter corresponding to setAppendItemFormat. Values below 0 or at or + * above UDATPG_FIELD_COUNT are illegal arguments. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param field UDateTimePatternField, such as UDATPG_ERA_FIELD + * @param length pointer of the length of appendItemFormat. + * @return appendItemFormat for field. + * @draft ICU 3.8 + */ +U_DRAFT const UChar * U_EXPORT2 +udatpg_getAppendItemFormat(const UDateTimePatternGenerator *dtpg, + UDateTimePatternField field, + int32_t *pLength); + +/** + * Set the name of field, eg "era" in English for ERA. These are only + * used if the corresponding AppendItemFormat is used, and if it contains a + * {2} variable. + *

+ * This reflects the way that the CLDR data is organized. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param field UDateTimePatternField + * @param value name for the field. + * @param length the length of value. + * @draft ICU 3.8 + */ +U_DRAFT void U_EXPORT2 +udatpg_setAppendItemName(UDateTimePatternGenerator *dtpg, + UDateTimePatternField field, + const UChar *value, int32_t length); + +/** + * Getter corresponding to setAppendItemNames. Values below 0 or at or above + * UDATPG_FIELD_COUNT are illegal arguments. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param field UDateTimePatternField, such as UDATPG_ERA_FIELD + * @param length pointer of the length of name for field. + * @return name for field + * @draft ICU 3.8 + */ +U_DRAFT const UChar * U_EXPORT2 +udatpg_getAppendItemName(const UDateTimePatternGenerator *dtpg, + UDateTimePatternField field, + int32_t *pLength); +/** + * The date time format is a message format pattern used to compose date and + * time patterns. The default value is "{0} {1}", where {0} will be replaced + * by the date pattern and {1} will be replaced by the time pattern. + *

+ * This is used when the input skeleton contains both date and time fields, + * but there is not a close match among the added patterns. For example, + * suppose that this object was created by adding "dd-MMM" and "hh:mm", and + * its datetimeFormat is the default "{0} {1}". Then if the input skeleton + * is "MMMdhmm", there is not an exact match, so the input skeleton is + * broken up into two components "MMMd" and "hmm". There are close matches + * for those two skeletons, so the result is put together with this pattern, + * resulting in "d-MMM h:mm". + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param dtFormat + * message format pattern, here {0} will be replaced by the date + * pattern and {1} will be replaced by the time pattern. + * @param length the length of dtFormat. + * @draft ICU 3.8 + */ +U_DRAFT void U_EXPORT2 +udatpg_setDateTimeFormat(const UDateTimePatternGenerator *dtpg, + const UChar *dtFormat, int32_t length); + +/** + * Getter corresponding to setDateTimeFormat. + * @param dtpg a pointer to UDateTimePatternGenerator. + * @return dateTimeFormat. + * @draft ICU 3.8 + */ +U_DRAFT const UChar * U_EXPORT2 +udatpg_getDateTimeFormat(const UDateTimePatternGenerator *dtpg, + int32_t *pLength); + +/** + * The decimal value is used in formatting fractions of seconds. If the + * skeleton contains fractional seconds, then this is used with the + * fractional seconds. For example, suppose that the input pattern is + * "hhmmssSSSS", and the best matching pattern internally is "H:mm:ss", and + * the decimal string is ",". Then the resulting pattern is modified to be + * "H:mm:ss,SSSS" + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param decimal + * @param length the length of decimal. + * @draft ICU 3.8 + */ +U_DRAFT void U_EXPORT2 +udatpg_setDecimal(UDateTimePatternGenerator *dtpg, + const UChar *decimal, int32_t length); + +/** + * Getter corresponding to setDecimal. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param length a pointer to the length of decimal. + * @return corresponding to the decimal point. + * @draft ICU 3.8 + */ +U_DRAFT const UChar * U_EXPORT2 +udatpg_getDecimal(const UDateTimePatternGenerator *dtpg, + int32_t *pLength); + +/** + * Adjusts the field types (width and subtype) of a pattern to match what is + * in a skeleton. That is, if you supply a pattern like "d-M H:m", and a + * skeleton of "MMMMddhhmm", then the input pattern is adjusted to be + * "dd-MMMM hh:mm". This is used internally to get the best match for the + * input skeleton, but can also be used externally. + * + * Note that this function uses a non-const UDateTimePatternGenerator: + * It uses a stateful pattern parser which is set up for each generator object, + * rather than creating one for each function call. + * Consecutive calls to this function do not affect each other, + * but this function cannot be used concurrently on a single generator object. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param pattern Input pattern + * @param patternLength the length of input pattern. + * @param skeleton + * @param skeletonLength the length of input skeleton. + * @param dest pattern adjusted to match the skeleton fields widths and subtypes. + * @param capacity the capacity of dest. + * @param pErrorCode a pointer to the UErrorCode which must not indicate a + * failure before the function call. + * @return the length of dest. + * @draft ICU 3.8 + */ +U_DRAFT int32_t U_EXPORT2 +udatpg_replaceFieldTypes(UDateTimePatternGenerator *dtpg, + const UChar *pattern, int32_t patternLength, + const UChar *skeleton, int32_t skeletonLength, + UChar *dest, int32_t destCapacity, + UErrorCode *pErrorCode); + +/** + * Return a UEnumeration list of all the skeletons in canonical form. + * Call udatpg_getPatternForSkeleton() to get the corresponding pattern. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param pErrorCode a pointer to the UErrorCode which must not indicate a + * failure before the function call + * @return a UEnumeration list of all the skeletons + * The caller must close the object. + * @draft ICU 3.8 + */ +U_DRAFT UEnumeration * U_EXPORT2 +udatpg_openSkeletons(const UDateTimePatternGenerator *dtpg, UErrorCode *pErrorCode); + +/** + * Return a UEnumeration list of all the base skeletons in canonical form. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param pErrorCode a pointer to the UErrorCode which must not indicate a + * failure before the function call. + * @return a UEnumeration list of all the base skeletons + * The caller must close the object. + * @draft ICU 3.8 + */ +U_DRAFT UEnumeration * U_EXPORT2 +udatpg_openBaseSkeletons(const UDateTimePatternGenerator *dtpg, UErrorCode *pErrorCode); + + /** + * Get the pattern corresponding to a given skeleton. + * + * @param dtpg a pointer to UDateTimePatternGenerator. + * @param skeleton + * @param skeletonLength pointer to the length of skeleton. + * @param pLength pointer to the length of return pattern. + * @return pattern corresponding to a given skeleton. + * @draft ICU 3.8 + */ +U_DRAFT const UChar * U_EXPORT2 +udatpg_getPatternForSkeleton(const UDateTimePatternGenerator *dtpg, + const UChar *skeleton, int32_t skeletonLength, + int32_t *pLength); + +#endif diff --git a/icu4c/source/test/cintltst/Makefile.in b/icu4c/source/test/cintltst/Makefile.in index a0bb2f02ef0..262b7abf1bc 100644 --- a/icu4c/source/test/cintltst/Makefile.in +++ b/icu4c/source/test/cintltst/Makefile.in @@ -48,7 +48,7 @@ cmsccoll.o cmsgtst.o cposxtst.o cldrtest.o \ cnmdptst.o cnormtst.o cnumtst.o crestst.o creststn.o cturtst.o \ cucdapi.o cucdtst.o custrtst.o cstrcase.o cutiltst.o nucnvtst.o nccbtst.o bocu1tst.o \ cbiditst.o cbididat.o eurocreg.o udatatst.o utf16tst.o utransts.o \ -ncnvfbts.o ncnvtst.o putiltst.o cstrtest.o utf8tst.o \ +ncnvfbts.o ncnvtst.o putiltst.o cstrtest.o udatpg_test.o utf8tst.o \ stdnmtst.o usrchtst.o custrtrn.o sorttest.o trietest.o usettest.o \ uenumtst.o utmstest.o currtest.o \ idnatest.o nfsprep.o spreptst.o sprpdata.o \ diff --git a/icu4c/source/test/cintltst/cformtst.c b/icu4c/source/test/cintltst/cformtst.c index e699ef4e138..cbbcf1ff54a 100644 --- a/icu4c/source/test/cintltst/cformtst.c +++ b/icu4c/source/test/cintltst/cformtst.c @@ -1,6 +1,6 @@ /******************************************************************** * COPYRIGHT: - * Copyright (c) 1997-2005, International Business Machines Corporation + * Copyright (c) 1997-2007, International Business Machines Corporation * and others. All Rights Reserved. ********************************************************************/ /******************************************************************************** @@ -24,6 +24,7 @@ void addCalTest(TestNode**); void addDateForTest(TestNode**); +void addDateTimePatternGeneratorTest(TestNode** root); void addNumForTest(TestNode**); void addMsgForTest(TestNode**); void addDateForRgrTest(TestNode**); @@ -38,6 +39,7 @@ void addFormatTest(TestNode** root) { addCalTest(root); addDateForTest(root); + addDateTimePatternGeneratorTest(root); addNumForTest(root); addNumFrDepTest(root); addMsgForTest(root); diff --git a/icu4c/source/test/cintltst/cintltst.vcproj b/icu4c/source/test/cintltst/cintltst.vcproj index b7e36cf596b..65c4c1b935c 100644 --- a/icu4c/source/test/cintltst/cintltst.vcproj +++ b/icu4c/source/test/cintltst/cintltst.vcproj @@ -534,6 +534,10 @@ RelativePath=".\currtest.c" > + + diff --git a/icu4c/source/test/cintltst/udatpg_test.c b/icu4c/source/test/cintltst/udatpg_test.c new file mode 100644 index 00000000000..429ca6492a4 --- /dev/null +++ b/icu4c/source/test/cintltst/udatpg_test.c @@ -0,0 +1,290 @@ +/* +******************************************************************************* +* +* Copyright (C) 2007, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: udatpg_test.c +* encoding: US-ASCII +* tab size: 8 (not used) +* indentation:4 +* +* created on: 2007aug01 +* created by: Markus W. Scherer +* +* Test of the C wrapper for the DateTimePatternGenerator. +* Calls each C API function and exercises code paths in the wrapper, +* but the full functionality is tested in the C++ intltest. +* +* One item to note: C API functions which return a const UChar * +* should return a NUL-terminated string. +* (The C++ implementation needs to use getTerminatedBuffer() +* on UnicodeString objects which end up being returned this way.) +*/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/udatpg.h" +#include "unicode/ustring.h" +#include "cintltst.h" + +void addDateTimePatternGeneratorTest(TestNode** root); + +#define TESTCASE(x) addTest(root, &x, "tsformat/udatpg_test/" #x) + +static void TestOpenClose(void); +static void TestUsage(void); +static void TestBuilder(void); + +void addDateTimePatternGeneratorTest(TestNode** root) { + TESTCASE(TestOpenClose); + TESTCASE(TestUsage); + TESTCASE(TestBuilder); +} + +/* + * Pipe symbol '|'. We pass only the first UChar without NUL-termination. + * The second UChar is just to verify that the API does not pick that up. + */ +static const UChar pipeString[]={ 0x7c, 0x0a }; + +static const UChar testSkeleton1[]={ 0x48, 0x48, 0x6d, 0x6d, 0 }; /* HHmm */ +static const UChar expectingBestPattern[]={ 0x48, 0x48, 0x2e, 0x6d, 0x6d, 0 }; /* HH.mm */ +static const UChar testPattern[]={ 0x48, 0x48, 0x3a, 0x6d, 0x6d, 0 }; /* HH:mm */ +static const UChar expectingSkeleton[]= { 0x48, 0x48, 0x6d, 0x6d, 0 }; /* HHmm */ +static const UChar expectingBaseSkeleton[]= { 0x48, 0x6d, 0 }; /* HHmm */ +static const UChar redundantPattern[]={ 0x79, 0x79, 0x4d, 0x4d, 0x4d, 0 }; /* yyMMM */ +static const UChar testFormat[]= {0x7B, 0x31, 0x7D, 0x20, 0x7B, 0x30, 0x7D, 0}; /* {1} {0} */ +static const UChar appendItemName[]= {0x68, 0x72, 0}; /* hr */ +static const UChar testPattern2[]={ 0x48, 0x48, 0x3a, 0x6d, 0x6d, 0x20, 0x76, 0 }; /* HH:mm v */ +static const UChar replacedStr[]={ 0x76, 0x76, 0x76, 0x76, 0 }; /* vvvv */ + +static void TestOpenClose() { + UErrorCode errorCode=U_ZERO_ERROR; + UDateTimePatternGenerator *dtpg, *dtpg2; + const UChar *s; + int32_t length; + + /* Open a DateTimePatternGenerator for the default locale. */ + dtpg=udatpg_open(NULL, &errorCode); + if(U_FAILURE(errorCode)) { + log_err("udatpg_open(NULL) failed - %s\n", u_errorName(errorCode)); + return; + } + udatpg_close(dtpg); + + /* Now one for German. */ + dtpg=udatpg_open("de", &errorCode); + if(U_FAILURE(errorCode)) { + log_err("udatpg_open(de) failed - %s\n", u_errorName(errorCode)); + return; + } + + /* Make some modification which we verify gets passed on to the clone. */ + udatpg_setDecimal(dtpg, pipeString, 1); + + /* Clone the generator. */ + dtpg2=udatpg_clone(dtpg, &errorCode); + if(U_FAILURE(errorCode) || dtpg2==NULL) { + log_err("udatpg_clone() failed - %s\n", u_errorName(errorCode)); + return; + } + + /* Verify that the clone has the custom decimal symbol. */ + s=udatpg_getDecimal(dtpg2, &length); + if(s==pipeString || length!=1 || 0!=u_memcmp(s, pipeString, length) || s[length]!=0) { + log_err("udatpg_getDecimal(cloned object) did not return the expected string\n"); + return; + } + + udatpg_close(dtpg); + udatpg_close(dtpg2); +} + +static void TestUsage() { + UErrorCode errorCode=U_ZERO_ERROR; + UDateTimePatternGenerator *dtpg; + UChar bestPattern[20]; + UChar result[20]; + int32_t length; + UChar *s; + const UChar *r; + + dtpg=udatpg_open("fi", &errorCode); + if(U_FAILURE(errorCode)) { + log_err("udatpg_open(fi) failed - %s\n", u_errorName(errorCode)); + return; + } + length = udatpg_getBestPattern(dtpg, testSkeleton1, 4, + bestPattern, 20, &errorCode); + if(U_FAILURE(errorCode)) { + log_err("udatpg_getBestPattern failed - %s\n", u_errorName(errorCode)); + return; + } + if((u_memcmp(bestPattern, expectingBestPattern, length)!=0) || bestPattern[length]!=0) { + log_err("udatpg_getBestPattern did not return the expected string\n"); + return; + } + + + /* Test skeleton == NULL */ + s=NULL; + length = udatpg_getBestPattern(dtpg, s, 0, bestPattern, 20, &errorCode); + if(!U_FAILURE(errorCode)&&(length!=0) ) { + log_err("udatpg_getBestPattern failed in illegal argument - skeleton is NULL.\n"); + return; + } + + /* Test udatpg_getSkeleton */ + length = udatpg_getSkeleton(dtpg, testPattern, 5, result, 20, &errorCode); + if(U_FAILURE(errorCode)) { + log_err("udatpg_getSkeleton failed - %s\n", u_errorName(errorCode)); + return; + } + if((u_memcmp(result, expectingSkeleton, length)!=0) || result[length]!=0) { + log_err("udatpg_getSkeleton did not return the expected string\n"); + return; + } + + /* Test pattern == NULL */ + s=NULL; + length = udatpg_getSkeleton(dtpg, s, 0, result, 20, &errorCode); + if(!U_FAILURE(errorCode)&&(length!=0) ) { + log_err("udatpg_getSkeleton failed in illegal argument - pattern is NULL.\n"); + return; + } + + /* Test udatpg_getBaseSkeleton */ + length = udatpg_getBaseSkeleton(dtpg, testPattern, 5, result, 20, &errorCode); + if(U_FAILURE(errorCode)) { + log_err("udatpg_getBaseSkeleton failed - %s\n", u_errorName(errorCode)); + return; + } + if((u_memcmp(result, expectingBaseSkeleton, length)!=0) || result[length]!=0) { + log_err("udatpg_getBaseSkeleton did not return the expected string\n"); + return; + } + + /* Test pattern == NULL */ + s=NULL; + length = udatpg_getBaseSkeleton(dtpg, s, 0, result, 20, &errorCode); + if(!U_FAILURE(errorCode)&&(length!=0) ) { + log_err("udatpg_getBaseSkeleton failed in illegal argument - pattern is NULL.\n"); + return; + } + + /* set append format to {1}{0} */ + udatpg_setAppendItemFormat( dtpg, UDATPG_MONTH_FIELD, testFormat, 7 ); + r = udatpg_getAppendItemFormat(dtpg, UDATPG_MONTH_FIELD, &length); + + + if(length!=7 || 0!=u_memcmp(r, testFormat, length) || r[length]!=0) { + log_err("udatpg_setAppendItemFormat did not return the expected string\n"); + return; + } + + /* set append name to hr */ + udatpg_setAppendItemName( dtpg, UDATPG_HOUR_FIELD, appendItemName, 7 ); + r = udatpg_getAppendItemName(dtpg, UDATPG_HOUR_FIELD, &length); + + if(length!=7 || 0!=u_memcmp(r, appendItemName, length) || r[length]!=0) { + log_err("udatpg_setAppendItemName did not return the expected string\n"); + return; + } + + /* set date time format to {1}{0} */ + udatpg_setDateTimeFormat( dtpg, testFormat, 7 ); + r = udatpg_getDateTimeFormat(dtpg, &length); + + if(length!=7 || 0!=u_memcmp(r, testFormat, length) || r[length]!=0) { + log_err("udatpg_setDateTimeFormat did not return the expected string\n"); + return; + } + udatpg_close(dtpg); +} + +static void TestBuilder() { + UErrorCode errorCode=U_ZERO_ERROR; + UDateTimePatternGenerator *dtpg; + UDateTimePatternConflict conflict; + UEnumeration *en; + UChar result[20]; + int32_t length, pLength; + UChar *s, *p; + + /* test create an empty DateTimePatternGenerator */ + dtpg=udatpg_openEmpty(&errorCode); + if(U_FAILURE(errorCode)) { + log_err("udatpg_openEmpty() failed - %s\n", u_errorName(errorCode)); + return; + } + + /* Add a pattern */ + conflict = udatpg_addPattern(dtpg, redundantPattern, 5, FALSE, result, 20, + &length, &errorCode); + if(U_FAILURE(errorCode)) { + log_err("udatpg_addPattern() failed - %s\n", u_errorName(errorCode)); + return; + } + /* Add a redundant pattern */ + conflict = udatpg_addPattern(dtpg, redundantPattern, 5, FALSE, result, 20, + &length, &errorCode); + if(conflict == UDATPG_NO_CONFLICT) { + log_err("udatpg_addPattern() failed to find the duplicate pattern.\n"); + return; + } + /* Test pattern == NULL */ + s=NULL; + length = udatpg_addPattern(dtpg, s, 0, FALSE, result, 20, + &length, &errorCode); + if(!U_FAILURE(errorCode)&&(length!=0) ) { + log_err("udatpg_addPattern failed in illegal argument - pattern is NULL.\n"); + return; + } + + /* replace field type */ + errorCode=U_ZERO_ERROR; + conflict = udatpg_addPattern(dtpg, testPattern2, 7, FALSE, result, 20, + &length, &errorCode); + if((conflict != UDATPG_NO_CONFLICT)||U_FAILURE(errorCode)) { + log_err("udatpg_addPattern() failed to add HH:mm v. - %s\n", u_errorName(errorCode)); + return; + } + length = udatpg_replaceFieldTypes(dtpg, testPattern2, 7, replacedStr, 4, + result, 20, &errorCode); + if (U_FAILURE(errorCode) || (length==0) ) { + log_err("udatpg_replaceFieldTypes failed!\n"); + return; + } + + /* Get all skeletons and the crroespong pattern for each skeleton. */ + en = udatpg_openSkeletons(dtpg, &errorCode); + if (U_FAILURE(errorCode) || (length==0) ) { + log_err("udatpg_openSkeletons failed!\n"); + return; + } + while ( (s=uenum_next(en, &length, &errorCode))!= NULL) { + p = udatpg_getPatternForSkeleton(dtpg, s, length, &pLength); + if (U_FAILURE(errorCode) || p==NULL ) { + log_err("udatpg_getPatternForSkeleton failed!\n"); + return; + } + } + uenum_close(en); + + /* Get all baseSkeletons */ + en = udatpg_openBaseSkeletons(dtpg, &errorCode); + if (U_FAILURE(errorCode) || (length==0) ) { + log_err("udatpg_openSkeletons failed!\n"); + return; + } + uenum_close(en); + + udatpg_close(dtpg); +} + + +#endif diff --git a/icu4c/source/test/intltest/Makefile.in b/icu4c/source/test/intltest/Makefile.in index 444fd073bc0..9e66e55162b 100644 --- a/icu4c/source/test/intltest/Makefile.in +++ b/icu4c/source/test/intltest/Makefile.in @@ -42,7 +42,7 @@ LIBS = $(LIBCTESTFW) $(LIBICUI18N) $(LIBICUUC) $(LIBICUTOOLUTIL) $(DEFAULT_LIBS) OBJECTS = aliastst.o allcoll.o apicoll.o astrotst.o callimts.o calregts.o caltest.o \ caltztst.o canittst.o citrtest.o cntabcol.o convtest.o currcoll.o \ dadrcal.o dadrcoll.o dcfmapts.o decoll.o dtfmapts.o dtfmrgts.o dtfmtrtts.o dtfmttst.o \ -encoll.o escoll.o ficoll.o frcoll.o g7coll.o intltest.o \ +dtptngts.o encoll.o escoll.o ficoll.o frcoll.o g7coll.o intltest.o \ itercoll.o itformat.o itmajor.o itutil.o jacoll.o lcukocol.o \ loctest.o miscdtfm.o mnkytst.o msfmrgts.o nmfmapts.o nmfmtrt.o \ numfmtst.o numrgts.o pptest.o regcoll.o restest.o restsnew.o \ diff --git a/icu4c/source/test/intltest/dtptngts.cpp b/icu4c/source/test/intltest/dtptngts.cpp new file mode 100644 index 00000000000..d5113b4562d --- /dev/null +++ b/icu4c/source/test/intltest/dtptngts.cpp @@ -0,0 +1,431 @@ +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2007, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +#if !UCONFIG_NO_FORMATTING + +#include +#include +#include "dtptngts.h" + +#include "unicode/calendar.h" +#include "unicode/smpdtfmt.h" +#include "unicode/dtfmtsym.h" +#include "unicode/dtptngen.h" +#include "unicode/utypes.h" +#include "loctest.h" + +static const UnicodeString patternData[] = { + UnicodeString("yM"), + UnicodeString("yMMM"), + UnicodeString("yMd"), + UnicodeString("yMMMd"), + UnicodeString("Md"), + UnicodeString("MMMd"), + UnicodeString("yQQQ"), + UnicodeString("hhmm"), + UnicodeString("HHmm"), + UnicodeString("mmss"), + UnicodeString(""), + }; + +#define MAX_LOCALE 4 +static const char* testLocale[MAX_LOCALE][3] = { + {"en", "US","\0"}, + {"zh", "Hans", "CN"}, + {"de","DE", "\0"}, + {"fi","\0", "\0"}, + }; + + + +static const UnicodeString patternResults[] = { + UnicodeString("1/1999"), // en_US + UnicodeString("Jan 1999"), + UnicodeString("1/13/1999"), + UnicodeString("Jan/13/1999"), + UnicodeString("1/13"), + UnicodeString("Jan 13"), + UnicodeString("Q1 1999"), + UnicodeString("11:58 PM"), + UnicodeString("23:58"), + UnicodeString("58:59"), + UnicodeString("1999-1"), // zh_Hans_CN + UnicodeString("1999 1"), + UnicodeString("1999113"), + UnicodeString("1999113"), + // TODO: These are diff from CLDR 1.4 to CLDR 1.5. will verify the result soon. + //CharsToUnicodeString("1999\\u5E741\\u670813\\u65E5"), + //CharsToUnicodeString("1999\\u5E741\\u670813\\u65E5"), + UnicodeString("1-13"), + UnicodeString("1 13"), + CharsToUnicodeString("1999 Q1"), + CharsToUnicodeString("\\u4E0B\\u534811:58"), + CharsToUnicodeString("23:58"), + UnicodeString("58:59"), + UnicodeString("1.1999"), // de_DE + UnicodeString("Jan 1999"), + UnicodeString("13.1.1999"), + UnicodeString("13. Jan 1999"), + UnicodeString("13.1."), + UnicodeString("13. Jan"), + UnicodeString("Q1 1999"), + UnicodeString("23:58"), + UnicodeString("23:58"), + UnicodeString("58:59"), + UnicodeString("1/1999"), // fi + UnicodeString("tammi 1999"), + UnicodeString("13.1.1999"), + UnicodeString("13. tammita 1999"), + UnicodeString("13.1."), + UnicodeString("13. tammita"), + UnicodeString("1. nelj./1999"), + UnicodeString("23.58"), + UnicodeString("23.58"), + UnicodeString("58.59"), + UnicodeString(""), + +}; + + + +// This is an API test, not a unit test. It doesn't test very many cases, and doesn't +// try to test the full functionality. It just calls each function in the class and +// verifies that it works on a basic level. + +void IntlTestDateTimePatternGeneratorAPI::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) +{ + if (exec) logln("TestSuite DateTimePatternGeneratorAPI"); + switch (index) { + case 0: name = "DateTimePatternGenerator API test"; + if (exec) { + logln("DateTimePatternGenerator API test---"); logln(""); + UErrorCode status = U_ZERO_ERROR; + Locale saveLocale; + Locale::setDefault(Locale::getEnglish(), status); + if(U_FAILURE(status)) { + errln("ERROR: Could not set default locale, test may not give correct results"); + } + testAPI(/*par*/); + Locale::setDefault(saveLocale, status); + } + break; + + default: name = ""; break; + } +} + +/** + * Test various generic API methods of DateTimePatternGenerator for API coverage. + */ +void IntlTestDateTimePatternGeneratorAPI::testAPI(/*char *par*/) +{ + UErrorCode status = U_ZERO_ERROR; + UnicodeString conflictingPattern; + UDateTimePatternConflict conflictingStatus; + + // ======= Test CreateInstance with default locale + logln("Testing DateTimePatternGenerator createInstance from default locale"); + + DateTimePatternGenerator *instFromDefaultLocale=DateTimePatternGenerator::createInstance(status); + if (U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateTimePatternGenerator (default) - exitting"); + return; + } + else { + delete instFromDefaultLocale; + } + + // ======= Test CreateInstance with given locale + logln("Testing DateTimePatternGenerator createInstance from French locale"); + status = U_ZERO_ERROR; + DateTimePatternGenerator *instFromLocale=DateTimePatternGenerator::createInstance(Locale::getFrench(), status); + if (U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateTimePatternGenerator (Locale::getFrench()) - exitting"); + return; + } + + // ======= Test clone DateTimePatternGenerator + logln("Testing DateTimePatternGenerator::clone()"); + status = U_ZERO_ERROR; + + + UnicodeString decimalSymbol = instFromLocale->getDecimal(); + UnicodeString newDecimalSymbol = UnicodeString("*"); + decimalSymbol = instFromLocale->getDecimal(); + instFromLocale->setDecimal(newDecimalSymbol); + DateTimePatternGenerator *cloneDTPatternGen=instFromLocale->clone(); + decimalSymbol = cloneDTPatternGen->getDecimal(); + if (decimalSymbol != newDecimalSymbol) { + dataerrln("ERROR: inconsistency is found in cloned object- exitting"); + return; + } + if (U_FAILURE(status)) { + delete instFromLocale; + dataerrln("ERROR: Could not create DateTimePatternGenerator (Locale::getFrench()) - exitting"); + return; + } + else { + delete instFromLocale; + delete cloneDTPatternGen; + } + + // ======= Test simple use cases + logln("Testing simple use cases"); + status = U_ZERO_ERROR; + Locale deLocale=Locale::getGermany(); + UDate sampleDate=LocaleTest::date(99, 9, 13, 23, 58, 59); + DateTimePatternGenerator *gen = DateTimePatternGenerator::createInstance(deLocale, status); + UnicodeString findPattern = gen->getBestPattern(UnicodeString("MMMddHmm"), status); + SimpleDateFormat *format = new SimpleDateFormat(findPattern, deLocale, status); + //TimeZone *zone = TimeZone::createTimeZone(UnicodeString("Europe/Paris")); + TimeZone *zone = TimeZone::createTimeZone(UnicodeString("ECT")); + format->setTimeZone(*zone); + UnicodeString dateReturned, expectedResult; + dateReturned=""; + dateReturned = format->format(sampleDate, dateReturned, status); + expectedResult=UnicodeString("8:58 14. Okt"); + if ( dateReturned != expectedResult ) { + if ( format != NULL ) delete format; + if ( zone != NULL ) delete zone; + if ( gen != NULL ) delete gen; + dataerrln("ERROR: Simple test in Locale::getGermany()) - exitting"); + return; + } + // add new pattern + conflictingStatus = gen->addPattern(UnicodeString("d'. von' MMMM"), true, conflictingPattern, status); + status = U_ZERO_ERROR; + UnicodeString testPattern=gen->getBestPattern(UnicodeString("MMMMdd"), status); + testPattern=gen->getBestPattern(UnicodeString("MMMddHmm"), status); + format->applyPattern(gen->getBestPattern(UnicodeString("MMMMddHmm"), status)); + dateReturned=""; + dateReturned = format->format(sampleDate, dateReturned, status); + expectedResult=UnicodeString("8:58 14. von Oktober"); + if ( dateReturned != expectedResult ) { + if ( format != NULL ) delete format; + if ( zone != NULL ) delete zone; + if ( gen != NULL ) delete gen; + dataerrln("ERROR: Simple test add pattern d\'. von\' MMMM - exitting"); + return; + } + if ( format != NULL ) delete format; + + // get a pattern and modify it + format = (SimpleDateFormat *)DateFormat::createDateTimeInstance(DateFormat::kFull, DateFormat::kFull, + deLocale); + format->setTimeZone(*zone); + UnicodeString pattern; + pattern = format->toPattern(pattern); + dateReturned=""; + dateReturned = format->format(sampleDate, dateReturned, status); + //expectedResult=UnicodeString("Donnerstag, 14. Oktober 1999 08:58:59 Frankreich"); + //The mismatch is caused by the setup of Timezone. The output pattern is same as in Java. + expectedResult=UnicodeString("Donnerstag, 14. Oktober 1999 08:58:59 GMT+02:00"); + if ( dateReturned != expectedResult ) { + if ( format != NULL ) delete format; + if ( zone != NULL ) delete zone; + if ( gen != NULL ) delete gen; + dataerrln("ERROR: Simple test uses full date format.- exitting"); + return; + } + + // modify it to change the zone. + UnicodeString newPattern = gen->replaceFieldTypes(pattern, UnicodeString("vvvv"), status); + format->applyPattern(newPattern); + dateReturned=""; + dateReturned = format->format(sampleDate, dateReturned, status); + expectedResult=UnicodeString("Donnerstag, 14. Oktober 1999 08:58:59 GMT+02:00"); + // expectedResult=UnicodeString("Donnerstag, 14. Oktober 1999 08:58:59 Frankreich:); + // The mismatch is caused by the setup of Timezone. The output pattern is same as in Java. + if ( dateReturned != expectedResult ) { + if ( format != NULL ) delete format; + if ( zone != NULL ) delete zone; + if ( gen != NULL ) delete gen; + dataerrln("ERROR: Simple test modify the timezone - exitting"); + return; + } + /* + printf("\n replace pattern:"); + for (int32_t i=0; igetSkeletons(status); + if(U_FAILURE(status)) { + errln("ERROR: Fail to get skeletons !\n"); + } + UnicodeString returnPattern, *ptrSkeleton; + ptrSkeletonEnum->reset(status); + int32_t count=ptrSkeletonEnum->count(status); + for (int32_t i=0; isnext(status); + returnPattern = gen->getPatternForSkeleton(*ptrSkeleton); + } + StringEnumeration* ptrBaseSkeletonEnum = gen->getBaseSkeletons(status); + if(U_FAILURE(status)) { + errln("ERROR: Fail to get base skeletons !\n"); + } + count=ptrBaseSkeletonEnum->count(status); + for (int32_t i=0; isnext(status); + } + + if ( gen != NULL ) delete gen; + + // ======= Test various skeletons. + logln("Testing DateTimePatternGenerator with various skeleton"); + + status = U_ZERO_ERROR; + int32_t localeIndex=0; + int32_t resultIndex=0; + UnicodeString resultDate; + UDate testDate= LocaleTest::date(99, 0, 13, 23, 58, 59); + while (localeIndex < MAX_LOCALE ) + { + int32_t dataIndex=0; + UnicodeString bestPattern; + + Locale loc(testLocale[localeIndex][0], testLocale[localeIndex][1], testLocale[localeIndex][2], ""); + //printf("\n\n Locale: %s_%s_%s", testLocale[localeIndex][0], testLocale[localeIndex][1], testLocale[localeIndex][2]); + //printf("\n Status:%d", status); + DateTimePatternGenerator *patGen=DateTimePatternGenerator::createInstance(loc, status); + if(U_FAILURE(status)) { + errln("ERROR: Could not create DateTimePatternGenerator with locale index:%d .\n", localeIndex); + } + while (patternData[dataIndex].length() > 0) { + bestPattern = patGen->getBestPattern(patternData[dataIndex++], status); + + SimpleDateFormat* sdf = new SimpleDateFormat(bestPattern, loc, status); + resultDate = ""; + resultDate = sdf->format(testDate, resultDate); + if ( resultDate != patternResults[resultIndex] ) { + errln("\nERROR: Test various skeletons[%d] .", dataIndex-1); + // TODO Remove printf once ICU pick up CLDR 1.5 + /* + printf("\nUnmatched result!\n TestPattern:"); + for (int32_t i=0; i < patternData[dataIndex-1].length(); ++i) { + printf("%c", patternData[dataIndex-1].charAt(i)); + } + printf(" BestPattern:"); + for (int32_t i=0; i < bestPattern.length(); ++i) { + printf("%c", bestPattern.charAt(i)); + } + + printf(" expected result:"); + for (int32_t i=0; i < patternResults[resultIndex].length(); ++i) { + printf("%c", patternResults[resultIndex].charAt(i)); + } + printf("\n expected result in hex:"); + for (int32_t i=0; i < patternResults[resultIndex].length(); ++i) { + printf("0x%x ", patternResults[resultIndex].charAt(i)); + } + printf("\n running result:"); + for (int32_t i=0; i < resultDate.length(); ++i) { + printf("%c", resultDate.charAt(i)); + } + printf(" running result in hex:"); + for (int32_t i=0; i < resultDate.length(); ++i) { + printf("0x%x ", resultDate.charAt(i)); + } + */ + } + + resultIndex++; + delete sdf; + } + delete patGen; + localeIndex++; + } + + + + // ======= Test random skeleton + const char randomChars[80] = { + '1','2','3','4','5','6','7','8','9','0','!','@','#','$','%','^','&','*','(',')', + '`',' ','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r', + 's','t','u','v','w','x','y','z','A','B','C','D','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',':',';','<','.','?',';','\\'}; + DateTimePatternGenerator *randDTGen= DateTimePatternGenerator::createInstance(status); + if (U_FAILURE(status)) { + dataerrln("ERROR: Could not create DateTimePatternGenerator (Locale::getFrench()) - exitting"); + return; + } + + for (int32_t i=0; i<10; ++i) { + UnicodeString randomSkeleton=""; + int32_t len = rand() % 20; + for (int32_t j=0; jgetBestPattern(randomSkeleton, status); + } + delete randDTGen; + + // UnicodeString randomString=Unicode + // ======= Test getStaticClassID() + + logln("Testing getStaticClassID()"); + status = U_ZERO_ERROR; + DateTimePatternGenerator *test= DateTimePatternGenerator::createInstance(status); + + if(test->getDynamicClassID() != DateTimePatternGenerator::getStaticClassID()) { + errln("ERROR: getDynamicClassID() didn't return the expected value"); + } + if (test!=NULL) { + delete test; + test=NULL; + } + + + // ====== Test createEmptyInstance() + + logln("Testing createEmptyInstance()"); + status = U_ZERO_ERROR; + + test = DateTimePatternGenerator::createEmptyInstance(status); + if(U_FAILURE(status)) { + errln("ERROR: Fail to create an empty instance !\n"); + } + + conflictingStatus = test->addPattern(UnicodeString("MMMMd"), true, conflictingPattern, status); + status = U_ZERO_ERROR; + testPattern=test->getBestPattern(UnicodeString("MMMMdd"), status); + conflictingStatus = test->addPattern(UnicodeString("HH:mm"), true, conflictingPattern, status); + conflictingStatus = test->addPattern(UnicodeString("MMMMMd"), true, conflictingPattern, status); //duplicate pattern + StringEnumeration *output=NULL; + output = test->getRedundants(status); + expectedResult=UnicodeString("MMMMd"); + if (output != NULL) { + output->reset(status); + const UnicodeString *dupPattern=output->snext(status); + if ( (dupPattern==NULL) || (*dupPattern != expectedResult) ) { + errln("ERROR: Fail in getRedundants !\n"); + } + } + + if (test!=NULL) { + delete test; + } +} + +#endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/dtptngts.h b/icu4c/source/test/intltest/dtptngts.h new file mode 100644 index 00000000000..6834c7d5b4a --- /dev/null +++ b/icu4c/source/test/intltest/dtptngts.h @@ -0,0 +1,31 @@ +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 1997-2001, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +#ifndef _INTLTESTDATETIMEPATTERNGENERATORAPI +#define _INTLTESTDATETIMEPATTERNGENERATORAPI + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "intltest.h" + +/** + * Test basic functionality of various API functions + **/ +class IntlTestDateTimePatternGeneratorAPI : public IntlTest { + void runIndexedTest( int32_t index, UBool exec, const char* &name, char* par = NULL ); + +private: + /** + * Performs tests on many API functions, see detailed comments in source code + **/ + void testAPI(/* char* par */); +}; + +#endif /* #if !UCONFIG_NO_FORMATTING */ + +#endif diff --git a/icu4c/source/test/intltest/intltest.vcproj b/icu4c/source/test/intltest/intltest.vcproj index 1d362b200e7..26d3e48d1a7 100644 --- a/icu4c/source/test/intltest/intltest.vcproj +++ b/icu4c/source/test/intltest/intltest.vcproj @@ -597,6 +597,14 @@ RelativePath=".\dtfmttst.h" > + + + + diff --git a/icu4c/source/test/intltest/itformat.cpp b/icu4c/source/test/intltest/itformat.cpp index 3b652bad9a7..96fe0dc2e77 100644 --- a/icu4c/source/test/intltest/itformat.cpp +++ b/icu4c/source/test/intltest/itformat.cpp @@ -44,6 +44,8 @@ #include "calcasts.h" // CalendarCaseTest #include "tzrulets.h" // TimeZoneRuleTest #include "dadrcal.h" // DataDrivenCalendarTest +#include "dtptngts.h" // IntlTestDateTimePatternGeneratorAPI + #define TESTCLASS(id, TestClass) \ case id: \ @@ -105,6 +107,8 @@ void IntlTestFormat::runIndexedTest( int32_t index, UBool exec, const char* &nam TESTCLASS(28,CalendarCaseTest); TESTCLASS(29,TimeZoneRuleTest); TESTCLASS(30,DataDrivenCalendarTest); + TESTCLASS(31,IntlTestDateTimePatternGeneratorAPI); + default: name = ""; break; //needed to end loop }