Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* |
michael@0 | 2 | ******************************************************************************* |
michael@0 | 3 | * Copyright (C) 2007-2013, International Business Machines Corporation and |
michael@0 | 4 | * others. All Rights Reserved. |
michael@0 | 5 | ******************************************************************************* |
michael@0 | 6 | * |
michael@0 | 7 | * File DTPTNGEN.CPP |
michael@0 | 8 | * |
michael@0 | 9 | ******************************************************************************* |
michael@0 | 10 | */ |
michael@0 | 11 | |
michael@0 | 12 | #include "unicode/utypes.h" |
michael@0 | 13 | #if !UCONFIG_NO_FORMATTING |
michael@0 | 14 | |
michael@0 | 15 | #include "unicode/datefmt.h" |
michael@0 | 16 | #include "unicode/decimfmt.h" |
michael@0 | 17 | #include "unicode/dtfmtsym.h" |
michael@0 | 18 | #include "unicode/dtptngen.h" |
michael@0 | 19 | #include "unicode/msgfmt.h" |
michael@0 | 20 | #include "unicode/smpdtfmt.h" |
michael@0 | 21 | #include "unicode/udat.h" |
michael@0 | 22 | #include "unicode/udatpg.h" |
michael@0 | 23 | #include "unicode/uniset.h" |
michael@0 | 24 | #include "unicode/uloc.h" |
michael@0 | 25 | #include "unicode/ures.h" |
michael@0 | 26 | #include "unicode/ustring.h" |
michael@0 | 27 | #include "unicode/rep.h" |
michael@0 | 28 | #include "cpputils.h" |
michael@0 | 29 | #include "ucln_in.h" |
michael@0 | 30 | #include "mutex.h" |
michael@0 | 31 | #include "cmemory.h" |
michael@0 | 32 | #include "cstring.h" |
michael@0 | 33 | #include "locbased.h" |
michael@0 | 34 | #include "gregoimp.h" |
michael@0 | 35 | #include "hash.h" |
michael@0 | 36 | #include "uresimp.h" |
michael@0 | 37 | #include "dtptngen_impl.h" |
michael@0 | 38 | |
michael@0 | 39 | #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) |
michael@0 | 40 | |
michael@0 | 41 | #if U_CHARSET_FAMILY==U_EBCDIC_FAMILY |
michael@0 | 42 | /** |
michael@0 | 43 | * If we are on EBCDIC, use an iterator which will |
michael@0 | 44 | * traverse the bundles in ASCII order. |
michael@0 | 45 | */ |
michael@0 | 46 | #define U_USE_ASCII_BUNDLE_ITERATOR |
michael@0 | 47 | #define U_SORT_ASCII_BUNDLE_ITERATOR |
michael@0 | 48 | #endif |
michael@0 | 49 | |
michael@0 | 50 | #if defined(U_USE_ASCII_BUNDLE_ITERATOR) |
michael@0 | 51 | |
michael@0 | 52 | #include "unicode/ustring.h" |
michael@0 | 53 | #include "uarrsort.h" |
michael@0 | 54 | |
michael@0 | 55 | struct UResAEntry { |
michael@0 | 56 | UChar *key; |
michael@0 | 57 | UResourceBundle *item; |
michael@0 | 58 | }; |
michael@0 | 59 | |
michael@0 | 60 | struct UResourceBundleAIterator { |
michael@0 | 61 | UResourceBundle *bund; |
michael@0 | 62 | UResAEntry *entries; |
michael@0 | 63 | int32_t num; |
michael@0 | 64 | int32_t cursor; |
michael@0 | 65 | }; |
michael@0 | 66 | |
michael@0 | 67 | /* Must be C linkage to pass function pointer to the sort function */ |
michael@0 | 68 | |
michael@0 | 69 | U_CDECL_BEGIN |
michael@0 | 70 | |
michael@0 | 71 | static int32_t U_CALLCONV |
michael@0 | 72 | ures_a_codepointSort(const void *context, const void *left, const void *right) { |
michael@0 | 73 | //CompareContext *cmp=(CompareContext *)context; |
michael@0 | 74 | return u_strcmp(((const UResAEntry *)left)->key, |
michael@0 | 75 | ((const UResAEntry *)right)->key); |
michael@0 | 76 | } |
michael@0 | 77 | |
michael@0 | 78 | U_CDECL_END |
michael@0 | 79 | |
michael@0 | 80 | static void ures_a_open(UResourceBundleAIterator *aiter, UResourceBundle *bund, UErrorCode *status) { |
michael@0 | 81 | if(U_FAILURE(*status)) { |
michael@0 | 82 | return; |
michael@0 | 83 | } |
michael@0 | 84 | aiter->bund = bund; |
michael@0 | 85 | aiter->num = ures_getSize(aiter->bund); |
michael@0 | 86 | aiter->cursor = 0; |
michael@0 | 87 | #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR) |
michael@0 | 88 | aiter->entries = NULL; |
michael@0 | 89 | #else |
michael@0 | 90 | aiter->entries = (UResAEntry*)uprv_malloc(sizeof(UResAEntry)*aiter->num); |
michael@0 | 91 | for(int i=0;i<aiter->num;i++) { |
michael@0 | 92 | aiter->entries[i].item = ures_getByIndex(aiter->bund, i, NULL, status); |
michael@0 | 93 | const char *akey = ures_getKey(aiter->entries[i].item); |
michael@0 | 94 | int32_t len = uprv_strlen(akey)+1; |
michael@0 | 95 | aiter->entries[i].key = (UChar*)uprv_malloc(len*sizeof(UChar)); |
michael@0 | 96 | u_charsToUChars(akey, aiter->entries[i].key, len); |
michael@0 | 97 | } |
michael@0 | 98 | uprv_sortArray(aiter->entries, aiter->num, sizeof(UResAEntry), ures_a_codepointSort, NULL, TRUE, status); |
michael@0 | 99 | #endif |
michael@0 | 100 | } |
michael@0 | 101 | |
michael@0 | 102 | static void ures_a_close(UResourceBundleAIterator *aiter) { |
michael@0 | 103 | #if defined(U_SORT_ASCII_BUNDLE_ITERATOR) |
michael@0 | 104 | for(int i=0;i<aiter->num;i++) { |
michael@0 | 105 | uprv_free(aiter->entries[i].key); |
michael@0 | 106 | ures_close(aiter->entries[i].item); |
michael@0 | 107 | } |
michael@0 | 108 | #endif |
michael@0 | 109 | } |
michael@0 | 110 | |
michael@0 | 111 | static const UChar *ures_a_getNextString(UResourceBundleAIterator *aiter, int32_t *len, const char **key, UErrorCode *err) { |
michael@0 | 112 | #if !defined(U_SORT_ASCII_BUNDLE_ITERATOR) |
michael@0 | 113 | return ures_getNextString(aiter->bund, len, key, err); |
michael@0 | 114 | #else |
michael@0 | 115 | if(U_FAILURE(*err)) return NULL; |
michael@0 | 116 | UResourceBundle *item = aiter->entries[aiter->cursor].item; |
michael@0 | 117 | const UChar* ret = ures_getString(item, len, err); |
michael@0 | 118 | *key = ures_getKey(item); |
michael@0 | 119 | aiter->cursor++; |
michael@0 | 120 | return ret; |
michael@0 | 121 | #endif |
michael@0 | 122 | } |
michael@0 | 123 | |
michael@0 | 124 | |
michael@0 | 125 | #endif |
michael@0 | 126 | |
michael@0 | 127 | |
michael@0 | 128 | U_NAMESPACE_BEGIN |
michael@0 | 129 | |
michael@0 | 130 | |
michael@0 | 131 | // ***************************************************************************** |
michael@0 | 132 | // class DateTimePatternGenerator |
michael@0 | 133 | // ***************************************************************************** |
michael@0 | 134 | static const UChar Canonical_Items[] = { |
michael@0 | 135 | // GyQMwWEdDFHmsSv |
michael@0 | 136 | CAP_G, LOW_Y, CAP_Q, CAP_M, LOW_W, CAP_W, CAP_E, LOW_D, CAP_D, CAP_F, |
michael@0 | 137 | CAP_H, LOW_M, LOW_S, CAP_S, LOW_V, 0 |
michael@0 | 138 | }; |
michael@0 | 139 | |
michael@0 | 140 | static const dtTypeElem dtTypes[] = { |
michael@0 | 141 | // patternChar, field, type, minLen, weight |
michael@0 | 142 | {CAP_G, UDATPG_ERA_FIELD, DT_SHORT, 1, 3,}, |
michael@0 | 143 | {CAP_G, UDATPG_ERA_FIELD, DT_LONG, 4, 0}, |
michael@0 | 144 | {LOW_Y, UDATPG_YEAR_FIELD, DT_NUMERIC, 1, 20}, |
michael@0 | 145 | {CAP_Y, UDATPG_YEAR_FIELD, DT_NUMERIC + DT_DELTA, 1, 20}, |
michael@0 | 146 | {LOW_U, UDATPG_YEAR_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 20}, |
michael@0 | 147 | {CAP_U, UDATPG_YEAR_FIELD, DT_SHORT, 1, 3}, |
michael@0 | 148 | {CAP_U, UDATPG_YEAR_FIELD, DT_LONG, 4, 0}, |
michael@0 | 149 | {CAP_U, UDATPG_YEAR_FIELD, DT_NARROW, 5, 0}, |
michael@0 | 150 | {CAP_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC, 1, 2}, |
michael@0 | 151 | {CAP_Q, UDATPG_QUARTER_FIELD, DT_SHORT, 3, 0}, |
michael@0 | 152 | {CAP_Q, UDATPG_QUARTER_FIELD, DT_LONG, 4, 0}, |
michael@0 | 153 | {LOW_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, |
michael@0 | 154 | {LOW_Q, UDATPG_QUARTER_FIELD, DT_SHORT + DT_DELTA, 3, 0}, |
michael@0 | 155 | {LOW_Q, UDATPG_QUARTER_FIELD, DT_LONG + DT_DELTA, 4, 0}, |
michael@0 | 156 | {CAP_M, UDATPG_MONTH_FIELD, DT_NUMERIC, 1, 2}, |
michael@0 | 157 | {CAP_M, UDATPG_MONTH_FIELD, DT_SHORT, 3, 0}, |
michael@0 | 158 | {CAP_M, UDATPG_MONTH_FIELD, DT_LONG, 4, 0}, |
michael@0 | 159 | {CAP_M, UDATPG_MONTH_FIELD, DT_NARROW, 5, 0}, |
michael@0 | 160 | {CAP_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, |
michael@0 | 161 | {CAP_L, UDATPG_MONTH_FIELD, DT_SHORT - DT_DELTA, 3, 0}, |
michael@0 | 162 | {CAP_L, UDATPG_MONTH_FIELD, DT_LONG - DT_DELTA, 4, 0}, |
michael@0 | 163 | {CAP_L, UDATPG_MONTH_FIELD, DT_NARROW - DT_DELTA, 5, 0}, |
michael@0 | 164 | {LOW_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 1}, |
michael@0 | 165 | {LOW_W, UDATPG_WEEK_OF_YEAR_FIELD, DT_NUMERIC, 1, 2}, |
michael@0 | 166 | {CAP_W, UDATPG_WEEK_OF_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 0}, |
michael@0 | 167 | {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORT, 1, 3}, |
michael@0 | 168 | {CAP_E, UDATPG_WEEKDAY_FIELD, DT_LONG, 4, 0}, |
michael@0 | 169 | {CAP_E, UDATPG_WEEKDAY_FIELD, DT_NARROW, 5, 0}, |
michael@0 | 170 | {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 2}, |
michael@0 | 171 | {LOW_C, UDATPG_WEEKDAY_FIELD, DT_SHORT - 2*DT_DELTA, 3, 0}, |
michael@0 | 172 | {LOW_C, UDATPG_WEEKDAY_FIELD, DT_LONG - 2*DT_DELTA, 4, 0}, |
michael@0 | 173 | {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NARROW - 2*DT_DELTA, 5, 0}, |
michael@0 | 174 | {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // LOW_E is currently not used in CLDR data, should not be canonical |
michael@0 | 175 | {LOW_E, UDATPG_WEEKDAY_FIELD, DT_SHORT - DT_DELTA, 3, 0}, |
michael@0 | 176 | {LOW_E, UDATPG_WEEKDAY_FIELD, DT_LONG - DT_DELTA, 4, 0}, |
michael@0 | 177 | {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NARROW - DT_DELTA, 5, 0}, |
michael@0 | 178 | {LOW_D, UDATPG_DAY_FIELD, DT_NUMERIC, 1, 2}, |
michael@0 | 179 | {CAP_D, UDATPG_DAY_OF_YEAR_FIELD, DT_NUMERIC + DT_DELTA, 1, 3}, |
michael@0 | 180 | {CAP_F, UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 0}, |
michael@0 | 181 | {LOW_G, UDATPG_DAY_FIELD, DT_NUMERIC + 3*DT_DELTA, 1, 20}, // really internal use, so we don't care |
michael@0 | 182 | {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_SHORT, 1, 0}, |
michael@0 | 183 | {CAP_H, UDATPG_HOUR_FIELD, DT_NUMERIC + 10*DT_DELTA, 1, 2}, // 24 hour |
michael@0 | 184 | {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + 11*DT_DELTA, 1, 2}, // 24 hour |
michael@0 | 185 | {LOW_H, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12 hour |
michael@0 | 186 | {CAP_K, UDATPG_HOUR_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // 12 hour |
michael@0 | 187 | {LOW_M, UDATPG_MINUTE_FIELD, DT_NUMERIC, 1, 2}, |
michael@0 | 188 | {LOW_S, UDATPG_SECOND_FIELD, DT_NUMERIC, 1, 2}, |
michael@0 | 189 | {CAP_S, UDATPG_FRACTIONAL_SECOND_FIELD, DT_NUMERIC + DT_DELTA, 1, 1000}, |
michael@0 | 190 | {CAP_A, UDATPG_SECOND_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 1000}, |
michael@0 | 191 | {LOW_V, UDATPG_ZONE_FIELD, DT_SHORT - 2*DT_DELTA, 1, 0}, |
michael@0 | 192 | {LOW_V, UDATPG_ZONE_FIELD, DT_LONG - 2*DT_DELTA, 4, 0}, |
michael@0 | 193 | {LOW_Z, UDATPG_ZONE_FIELD, DT_SHORT, 1, 3}, |
michael@0 | 194 | {LOW_Z, UDATPG_ZONE_FIELD, DT_LONG, 4, 0}, |
michael@0 | 195 | {CAP_Z, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 3}, |
michael@0 | 196 | {CAP_Z, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, |
michael@0 | 197 | {CAP_Z, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 5, 0}, |
michael@0 | 198 | {CAP_O, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0}, |
michael@0 | 199 | {CAP_O, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, |
michael@0 | 200 | {CAP_V, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0}, |
michael@0 | 201 | {CAP_V, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 2, 0}, |
michael@0 | 202 | {CAP_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0}, |
michael@0 | 203 | {CAP_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0}, |
michael@0 | 204 | {CAP_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, |
michael@0 | 205 | {LOW_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0}, |
michael@0 | 206 | {LOW_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0}, |
michael@0 | 207 | {LOW_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0}, |
michael@0 | 208 | {0, UDATPG_FIELD_COUNT, 0, 0, 0} , // last row of dtTypes[] |
michael@0 | 209 | }; |
michael@0 | 210 | |
michael@0 | 211 | static const char* const CLDR_FIELD_APPEND[] = { |
michael@0 | 212 | "Era", "Year", "Quarter", "Month", "Week", "*", "Day-Of-Week", "Day", "*", "*", "*", |
michael@0 | 213 | "Hour", "Minute", "Second", "*", "Timezone" |
michael@0 | 214 | }; |
michael@0 | 215 | |
michael@0 | 216 | static const char* const CLDR_FIELD_NAME[] = { |
michael@0 | 217 | "era", "year", "quarter", "month", "week", "*", "weekday", "*", "*", "day", "dayperiod", |
michael@0 | 218 | "hour", "minute", "second", "*", "zone" |
michael@0 | 219 | }; |
michael@0 | 220 | |
michael@0 | 221 | static const char* const Resource_Fields[] = { |
michael@0 | 222 | "day", "dayperiod", "era", "hour", "minute", "month", "second", "week", |
michael@0 | 223 | "weekday", "year", "zone", "quarter" }; |
michael@0 | 224 | |
michael@0 | 225 | // For appendItems |
michael@0 | 226 | static const UChar UDATPG_ItemFormat[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A, |
michael@0 | 227 | 0x20, 0x7B, 0x31, 0x7D, 0x2524, 0}; // {0} \u251C{2}: {1}\u2524 |
michael@0 | 228 | |
michael@0 | 229 | //static const UChar repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ" |
michael@0 | 230 | |
michael@0 | 231 | static const char DT_DateTimePatternsTag[]="DateTimePatterns"; |
michael@0 | 232 | static const char DT_DateTimeCalendarTag[]="calendar"; |
michael@0 | 233 | static const char DT_DateTimeGregorianTag[]="gregorian"; |
michael@0 | 234 | static const char DT_DateTimeAppendItemsTag[]="appendItems"; |
michael@0 | 235 | static const char DT_DateTimeFieldsTag[]="fields"; |
michael@0 | 236 | static const char DT_DateTimeAvailableFormatsTag[]="availableFormats"; |
michael@0 | 237 | //static const UnicodeString repeatedPattern=UnicodeString(repeatedPatterns); |
michael@0 | 238 | |
michael@0 | 239 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimePatternGenerator) |
michael@0 | 240 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTSkeletonEnumeration) |
michael@0 | 241 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTRedundantEnumeration) |
michael@0 | 242 | |
michael@0 | 243 | DateTimePatternGenerator* U_EXPORT2 |
michael@0 | 244 | DateTimePatternGenerator::createInstance(UErrorCode& status) { |
michael@0 | 245 | return createInstance(Locale::getDefault(), status); |
michael@0 | 246 | } |
michael@0 | 247 | |
michael@0 | 248 | DateTimePatternGenerator* U_EXPORT2 |
michael@0 | 249 | DateTimePatternGenerator::createInstance(const Locale& locale, UErrorCode& status) { |
michael@0 | 250 | DateTimePatternGenerator *result = new DateTimePatternGenerator(locale, status); |
michael@0 | 251 | if (result == NULL) { |
michael@0 | 252 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 253 | } |
michael@0 | 254 | if (U_FAILURE(status)) { |
michael@0 | 255 | delete result; |
michael@0 | 256 | result = NULL; |
michael@0 | 257 | } |
michael@0 | 258 | return result; |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | DateTimePatternGenerator* U_EXPORT2 |
michael@0 | 262 | DateTimePatternGenerator::createEmptyInstance(UErrorCode& status) { |
michael@0 | 263 | DateTimePatternGenerator *result = new DateTimePatternGenerator(status); |
michael@0 | 264 | if (result == NULL) { |
michael@0 | 265 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 266 | } |
michael@0 | 267 | if (U_FAILURE(status)) { |
michael@0 | 268 | delete result; |
michael@0 | 269 | result = NULL; |
michael@0 | 270 | } |
michael@0 | 271 | return result; |
michael@0 | 272 | } |
michael@0 | 273 | |
michael@0 | 274 | DateTimePatternGenerator::DateTimePatternGenerator(UErrorCode &status) : |
michael@0 | 275 | skipMatcher(NULL), |
michael@0 | 276 | fAvailableFormatKeyHash(NULL) |
michael@0 | 277 | { |
michael@0 | 278 | fp = new FormatParser(); |
michael@0 | 279 | dtMatcher = new DateTimeMatcher(); |
michael@0 | 280 | distanceInfo = new DistanceInfo(); |
michael@0 | 281 | patternMap = new PatternMap(); |
michael@0 | 282 | if (fp == NULL || dtMatcher == NULL || distanceInfo == NULL || patternMap == NULL) { |
michael@0 | 283 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 284 | } |
michael@0 | 285 | } |
michael@0 | 286 | |
michael@0 | 287 | DateTimePatternGenerator::DateTimePatternGenerator(const Locale& locale, UErrorCode &status) : |
michael@0 | 288 | skipMatcher(NULL), |
michael@0 | 289 | fAvailableFormatKeyHash(NULL) |
michael@0 | 290 | { |
michael@0 | 291 | fp = new FormatParser(); |
michael@0 | 292 | dtMatcher = new DateTimeMatcher(); |
michael@0 | 293 | distanceInfo = new DistanceInfo(); |
michael@0 | 294 | patternMap = new PatternMap(); |
michael@0 | 295 | if (fp == NULL || dtMatcher == NULL || distanceInfo == NULL || patternMap == NULL) { |
michael@0 | 296 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 297 | } |
michael@0 | 298 | else { |
michael@0 | 299 | initData(locale, status); |
michael@0 | 300 | } |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerator& other) : |
michael@0 | 304 | UObject(), |
michael@0 | 305 | skipMatcher(NULL), |
michael@0 | 306 | fAvailableFormatKeyHash(NULL) |
michael@0 | 307 | { |
michael@0 | 308 | fp = new FormatParser(); |
michael@0 | 309 | dtMatcher = new DateTimeMatcher(); |
michael@0 | 310 | distanceInfo = new DistanceInfo(); |
michael@0 | 311 | patternMap = new PatternMap(); |
michael@0 | 312 | *this=other; |
michael@0 | 313 | } |
michael@0 | 314 | |
michael@0 | 315 | DateTimePatternGenerator& |
michael@0 | 316 | DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) { |
michael@0 | 317 | pLocale = other.pLocale; |
michael@0 | 318 | fDefaultHourFormatChar = other.fDefaultHourFormatChar; |
michael@0 | 319 | *fp = *(other.fp); |
michael@0 | 320 | dtMatcher->copyFrom(other.dtMatcher->skeleton); |
michael@0 | 321 | *distanceInfo = *(other.distanceInfo); |
michael@0 | 322 | dateTimeFormat = other.dateTimeFormat; |
michael@0 | 323 | decimal = other.decimal; |
michael@0 | 324 | // NUL-terminate for the C API. |
michael@0 | 325 | dateTimeFormat.getTerminatedBuffer(); |
michael@0 | 326 | decimal.getTerminatedBuffer(); |
michael@0 | 327 | delete skipMatcher; |
michael@0 | 328 | if ( other.skipMatcher == NULL ) { |
michael@0 | 329 | skipMatcher = NULL; |
michael@0 | 330 | } |
michael@0 | 331 | else { |
michael@0 | 332 | skipMatcher = new DateTimeMatcher(*other.skipMatcher); |
michael@0 | 333 | } |
michael@0 | 334 | for (int32_t i=0; i< UDATPG_FIELD_COUNT; ++i ) { |
michael@0 | 335 | appendItemFormats[i] = other.appendItemFormats[i]; |
michael@0 | 336 | appendItemNames[i] = other.appendItemNames[i]; |
michael@0 | 337 | // NUL-terminate for the C API. |
michael@0 | 338 | appendItemFormats[i].getTerminatedBuffer(); |
michael@0 | 339 | appendItemNames[i].getTerminatedBuffer(); |
michael@0 | 340 | } |
michael@0 | 341 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 342 | patternMap->copyFrom(*other.patternMap, status); |
michael@0 | 343 | copyHashtable(other.fAvailableFormatKeyHash, status); |
michael@0 | 344 | return *this; |
michael@0 | 345 | } |
michael@0 | 346 | |
michael@0 | 347 | |
michael@0 | 348 | UBool |
michael@0 | 349 | DateTimePatternGenerator::operator==(const DateTimePatternGenerator& other) const { |
michael@0 | 350 | if (this == &other) { |
michael@0 | 351 | return TRUE; |
michael@0 | 352 | } |
michael@0 | 353 | if ((pLocale==other.pLocale) && (patternMap->equals(*other.patternMap)) && |
michael@0 | 354 | (dateTimeFormat==other.dateTimeFormat) && (decimal==other.decimal)) { |
michael@0 | 355 | for ( int32_t i=0 ; i<UDATPG_FIELD_COUNT; ++i ) { |
michael@0 | 356 | if ((appendItemFormats[i] != other.appendItemFormats[i]) || |
michael@0 | 357 | (appendItemNames[i] != other.appendItemNames[i]) ) { |
michael@0 | 358 | return FALSE; |
michael@0 | 359 | } |
michael@0 | 360 | } |
michael@0 | 361 | return TRUE; |
michael@0 | 362 | } |
michael@0 | 363 | else { |
michael@0 | 364 | return FALSE; |
michael@0 | 365 | } |
michael@0 | 366 | } |
michael@0 | 367 | |
michael@0 | 368 | UBool |
michael@0 | 369 | DateTimePatternGenerator::operator!=(const DateTimePatternGenerator& other) const { |
michael@0 | 370 | return !operator==(other); |
michael@0 | 371 | } |
michael@0 | 372 | |
michael@0 | 373 | DateTimePatternGenerator::~DateTimePatternGenerator() { |
michael@0 | 374 | if (fAvailableFormatKeyHash!=NULL) { |
michael@0 | 375 | delete fAvailableFormatKeyHash; |
michael@0 | 376 | } |
michael@0 | 377 | |
michael@0 | 378 | if (fp != NULL) delete fp; |
michael@0 | 379 | if (dtMatcher != NULL) delete dtMatcher; |
michael@0 | 380 | if (distanceInfo != NULL) delete distanceInfo; |
michael@0 | 381 | if (patternMap != NULL) delete patternMap; |
michael@0 | 382 | if (skipMatcher != NULL) delete skipMatcher; |
michael@0 | 383 | } |
michael@0 | 384 | |
michael@0 | 385 | void |
michael@0 | 386 | DateTimePatternGenerator::initData(const Locale& locale, UErrorCode &status) { |
michael@0 | 387 | //const char *baseLangName = locale.getBaseName(); // unused |
michael@0 | 388 | |
michael@0 | 389 | skipMatcher = NULL; |
michael@0 | 390 | fAvailableFormatKeyHash=NULL; |
michael@0 | 391 | addCanonicalItems(); |
michael@0 | 392 | addICUPatterns(locale, status); |
michael@0 | 393 | if (U_FAILURE(status)) { |
michael@0 | 394 | return; |
michael@0 | 395 | } |
michael@0 | 396 | addCLDRData(locale, status); |
michael@0 | 397 | setDateTimeFromCalendar(locale, status); |
michael@0 | 398 | setDecimalSymbols(locale, status); |
michael@0 | 399 | } // DateTimePatternGenerator::initData |
michael@0 | 400 | |
michael@0 | 401 | UnicodeString |
michael@0 | 402 | DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode& |
michael@0 | 403 | /*status*/) { |
michael@0 | 404 | dtMatcher->set(pattern, fp); |
michael@0 | 405 | return dtMatcher->getSkeletonPtr()->getSkeleton(); |
michael@0 | 406 | } |
michael@0 | 407 | |
michael@0 | 408 | UnicodeString |
michael@0 | 409 | DateTimePatternGenerator::getBaseSkeleton(const UnicodeString& pattern, UErrorCode& /*status*/) { |
michael@0 | 410 | dtMatcher->set(pattern, fp); |
michael@0 | 411 | return dtMatcher->getSkeletonPtr()->getBaseSkeleton(); |
michael@0 | 412 | } |
michael@0 | 413 | |
michael@0 | 414 | void |
michael@0 | 415 | DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& status) { |
michael@0 | 416 | UnicodeString dfPattern; |
michael@0 | 417 | UnicodeString conflictingString; |
michael@0 | 418 | DateFormat* df; |
michael@0 | 419 | |
michael@0 | 420 | if (U_FAILURE(status)) { |
michael@0 | 421 | return; |
michael@0 | 422 | } |
michael@0 | 423 | |
michael@0 | 424 | // Load with ICU patterns |
michael@0 | 425 | for (int32_t i=DateFormat::kFull; i<=DateFormat::kShort; i++) { |
michael@0 | 426 | DateFormat::EStyle style = (DateFormat::EStyle)i; |
michael@0 | 427 | df = DateFormat::createDateInstance(style, locale); |
michael@0 | 428 | SimpleDateFormat* sdf; |
michael@0 | 429 | if (df != NULL && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != NULL) { |
michael@0 | 430 | addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status); |
michael@0 | 431 | } |
michael@0 | 432 | // TODO Maybe we should return an error when the date format isn't simple. |
michael@0 | 433 | delete df; |
michael@0 | 434 | if (U_FAILURE(status)) { |
michael@0 | 435 | return; |
michael@0 | 436 | } |
michael@0 | 437 | |
michael@0 | 438 | df = DateFormat::createTimeInstance(style, locale); |
michael@0 | 439 | if (df != NULL && (sdf = dynamic_cast<SimpleDateFormat*>(df)) != NULL) { |
michael@0 | 440 | addPattern(sdf->toPattern(dfPattern), FALSE, conflictingString, status); |
michael@0 | 441 | // HACK for hh:ss |
michael@0 | 442 | if ( i==DateFormat::kMedium ) { |
michael@0 | 443 | hackPattern = dfPattern; |
michael@0 | 444 | } |
michael@0 | 445 | } |
michael@0 | 446 | // TODO Maybe we should return an error when the date format isn't simple. |
michael@0 | 447 | delete df; |
michael@0 | 448 | if (U_FAILURE(status)) { |
michael@0 | 449 | return; |
michael@0 | 450 | } |
michael@0 | 451 | } |
michael@0 | 452 | } |
michael@0 | 453 | |
michael@0 | 454 | void |
michael@0 | 455 | DateTimePatternGenerator::hackTimes(const UnicodeString& hackPattern, UErrorCode& status) { |
michael@0 | 456 | UnicodeString conflictingString; |
michael@0 | 457 | |
michael@0 | 458 | fp->set(hackPattern); |
michael@0 | 459 | UnicodeString mmss; |
michael@0 | 460 | UBool gotMm=FALSE; |
michael@0 | 461 | for (int32_t i=0; i<fp->itemNumber; ++i) { |
michael@0 | 462 | UnicodeString field = fp->items[i]; |
michael@0 | 463 | if ( fp->isQuoteLiteral(field) ) { |
michael@0 | 464 | if ( gotMm ) { |
michael@0 | 465 | UnicodeString quoteLiteral; |
michael@0 | 466 | fp->getQuoteLiteral(quoteLiteral, &i); |
michael@0 | 467 | mmss += quoteLiteral; |
michael@0 | 468 | } |
michael@0 | 469 | } |
michael@0 | 470 | else { |
michael@0 | 471 | if (fp->isPatternSeparator(field) && gotMm) { |
michael@0 | 472 | mmss+=field; |
michael@0 | 473 | } |
michael@0 | 474 | else { |
michael@0 | 475 | UChar ch=field.charAt(0); |
michael@0 | 476 | if (ch==LOW_M) { |
michael@0 | 477 | gotMm=TRUE; |
michael@0 | 478 | mmss+=field; |
michael@0 | 479 | } |
michael@0 | 480 | else { |
michael@0 | 481 | if (ch==LOW_S) { |
michael@0 | 482 | if (!gotMm) { |
michael@0 | 483 | break; |
michael@0 | 484 | } |
michael@0 | 485 | mmss+= field; |
michael@0 | 486 | addPattern(mmss, FALSE, conflictingString, status); |
michael@0 | 487 | break; |
michael@0 | 488 | } |
michael@0 | 489 | else { |
michael@0 | 490 | if (gotMm || ch==LOW_Z || ch==CAP_Z || ch==LOW_V || ch==CAP_V) { |
michael@0 | 491 | break; |
michael@0 | 492 | } |
michael@0 | 493 | } |
michael@0 | 494 | } |
michael@0 | 495 | } |
michael@0 | 496 | } |
michael@0 | 497 | } |
michael@0 | 498 | } |
michael@0 | 499 | |
michael@0 | 500 | #define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY) |
michael@0 | 501 | |
michael@0 | 502 | static const UChar hourFormatChars[] = { CAP_H, LOW_H, CAP_K, LOW_K, 0 }; // HhKk, the hour format characters |
michael@0 | 503 | |
michael@0 | 504 | void |
michael@0 | 505 | DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& err) { |
michael@0 | 506 | UResourceBundle *rb, *calTypeBundle, *calBundle; |
michael@0 | 507 | UResourceBundle *patBundle, *fieldBundle, *fBundle; |
michael@0 | 508 | UnicodeString rbPattern, value, field; |
michael@0 | 509 | UnicodeString conflictingPattern; |
michael@0 | 510 | const char *key=NULL; |
michael@0 | 511 | int32_t i; |
michael@0 | 512 | |
michael@0 | 513 | UnicodeString defaultItemFormat(TRUE, UDATPG_ItemFormat, LENGTHOF(UDATPG_ItemFormat)-1); // Read-only alias. |
michael@0 | 514 | |
michael@0 | 515 | err = U_ZERO_ERROR; |
michael@0 | 516 | |
michael@0 | 517 | fDefaultHourFormatChar = 0; |
michael@0 | 518 | for (i=0; i<UDATPG_FIELD_COUNT; ++i ) { |
michael@0 | 519 | appendItemNames[i]=CAP_F; |
michael@0 | 520 | if (i<10) { |
michael@0 | 521 | appendItemNames[i]+=(UChar)(i+0x30); |
michael@0 | 522 | } |
michael@0 | 523 | else { |
michael@0 | 524 | appendItemNames[i]+=(UChar)0x31; |
michael@0 | 525 | appendItemNames[i]+=(UChar)(i-10 + 0x30); |
michael@0 | 526 | } |
michael@0 | 527 | // NUL-terminate for the C API. |
michael@0 | 528 | appendItemNames[i].getTerminatedBuffer(); |
michael@0 | 529 | } |
michael@0 | 530 | |
michael@0 | 531 | rb = ures_open(NULL, locale.getName(), &err); |
michael@0 | 532 | if (rb == NULL || U_FAILURE(err)) { |
michael@0 | 533 | return; |
michael@0 | 534 | } |
michael@0 | 535 | const char *curLocaleName=ures_getLocaleByType(rb, ULOC_ACTUAL_LOCALE, &err); |
michael@0 | 536 | const char * calendarTypeToUse = DT_DateTimeGregorianTag; // initial default |
michael@0 | 537 | char calendarType[ULOC_KEYWORDS_CAPACITY]; // to be filled in with the type to use, if all goes well |
michael@0 | 538 | if ( U_SUCCESS(err) ) { |
michael@0 | 539 | char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY]; |
michael@0 | 540 | // obtain a locale that always has the calendar key value that should be used |
michael@0 | 541 | (void)ures_getFunctionalEquivalent(localeWithCalendarKey, ULOC_LOCALE_IDENTIFIER_CAPACITY, NULL, |
michael@0 | 542 | "calendar", "calendar", locale.getName(), NULL, FALSE, &err); |
michael@0 | 543 | localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination |
michael@0 | 544 | // now get the calendar key value from that locale |
michael@0 | 545 | int32_t calendarTypeLen = uloc_getKeywordValue(localeWithCalendarKey, "calendar", calendarType, ULOC_KEYWORDS_CAPACITY, &err); |
michael@0 | 546 | if (U_SUCCESS(err) && calendarTypeLen < ULOC_KEYWORDS_CAPACITY) { |
michael@0 | 547 | calendarTypeToUse = calendarType; |
michael@0 | 548 | } |
michael@0 | 549 | err = U_ZERO_ERROR; |
michael@0 | 550 | } |
michael@0 | 551 | calBundle = ures_getByKeyWithFallback(rb, DT_DateTimeCalendarTag, NULL, &err); |
michael@0 | 552 | calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &err); |
michael@0 | 553 | |
michael@0 | 554 | key=NULL; |
michael@0 | 555 | int32_t dtCount=0; |
michael@0 | 556 | patBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimePatternsTag, NULL, &err); |
michael@0 | 557 | while (U_SUCCESS(err)) { |
michael@0 | 558 | rbPattern = ures_getNextUnicodeString(patBundle, &key, &err); |
michael@0 | 559 | dtCount++; |
michael@0 | 560 | if (rbPattern.length()==0 ) { |
michael@0 | 561 | break; // no more pattern |
michael@0 | 562 | } |
michael@0 | 563 | else { |
michael@0 | 564 | if (dtCount==9) { |
michael@0 | 565 | setDateTimeFormat(rbPattern); |
michael@0 | 566 | } else if (dtCount==4) { // short time format |
michael@0 | 567 | // set fDefaultHourFormatChar to the hour format character from this pattern |
michael@0 | 568 | int32_t tfIdx, tfLen = rbPattern.length(); |
michael@0 | 569 | UBool ignoreChars = FALSE; |
michael@0 | 570 | for (tfIdx = 0; tfIdx < tfLen; tfIdx++) { |
michael@0 | 571 | UChar tfChar = rbPattern.charAt(tfIdx); |
michael@0 | 572 | if ( tfChar == SINGLE_QUOTE ) { |
michael@0 | 573 | ignoreChars = !ignoreChars; // toggle (handle quoted literals & '' for single quote) |
michael@0 | 574 | } else if ( !ignoreChars && u_strchr(hourFormatChars, tfChar) != NULL ) { |
michael@0 | 575 | fDefaultHourFormatChar = tfChar; |
michael@0 | 576 | break; |
michael@0 | 577 | } |
michael@0 | 578 | } |
michael@0 | 579 | } |
michael@0 | 580 | } |
michael@0 | 581 | } |
michael@0 | 582 | ures_close(patBundle); |
michael@0 | 583 | |
michael@0 | 584 | err = U_ZERO_ERROR; |
michael@0 | 585 | patBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimeAppendItemsTag, NULL, &err); |
michael@0 | 586 | key=NULL; |
michael@0 | 587 | UnicodeString itemKey; |
michael@0 | 588 | while (U_SUCCESS(err)) { |
michael@0 | 589 | rbPattern = ures_getNextUnicodeString(patBundle, &key, &err); |
michael@0 | 590 | if (rbPattern.length()==0 ) { |
michael@0 | 591 | break; // no more pattern |
michael@0 | 592 | } |
michael@0 | 593 | else { |
michael@0 | 594 | setAppendItemFormat(getAppendFormatNumber(key), rbPattern); |
michael@0 | 595 | } |
michael@0 | 596 | } |
michael@0 | 597 | ures_close(patBundle); |
michael@0 | 598 | |
michael@0 | 599 | key=NULL; |
michael@0 | 600 | err = U_ZERO_ERROR; |
michael@0 | 601 | fBundle = ures_getByKeyWithFallback(rb, DT_DateTimeFieldsTag, NULL, &err); |
michael@0 | 602 | for (i=0; i<MAX_RESOURCE_FIELD; ++i) { |
michael@0 | 603 | err = U_ZERO_ERROR; |
michael@0 | 604 | patBundle = ures_getByKeyWithFallback(fBundle, Resource_Fields[i], NULL, &err); |
michael@0 | 605 | fieldBundle = ures_getByKeyWithFallback(patBundle, "dn", NULL, &err); |
michael@0 | 606 | rbPattern = ures_getNextUnicodeString(fieldBundle, &key, &err); |
michael@0 | 607 | ures_close(fieldBundle); |
michael@0 | 608 | ures_close(patBundle); |
michael@0 | 609 | if (rbPattern.length()==0 ) { |
michael@0 | 610 | continue; |
michael@0 | 611 | } |
michael@0 | 612 | else { |
michael@0 | 613 | setAppendItemName(getAppendNameNumber(Resource_Fields[i]), rbPattern); |
michael@0 | 614 | } |
michael@0 | 615 | } |
michael@0 | 616 | ures_close(fBundle); |
michael@0 | 617 | |
michael@0 | 618 | // add available formats |
michael@0 | 619 | UBool firstTimeThrough = TRUE; |
michael@0 | 620 | err = U_ZERO_ERROR; |
michael@0 | 621 | initHashtable(err); |
michael@0 | 622 | UBool override = TRUE; |
michael@0 | 623 | while (TRUE) { |
michael@0 | 624 | // At the start of the loop: |
michael@0 | 625 | // - rb is the open resource bundle for the current locale being processed, |
michael@0 | 626 | // whose actual name is in curLocaleName. |
michael@0 | 627 | // - if U_SUCCESS(err), then calBundle and calTypeBundle are open; |
michael@0 | 628 | // process contents of calTypeBundle, then close calBundle and calTypeBundle. |
michael@0 | 629 | if (U_SUCCESS(err)) { |
michael@0 | 630 | // process contents of calTypeBundle |
michael@0 | 631 | patBundle = ures_getByKeyWithFallback(calTypeBundle, DT_DateTimeAvailableFormatsTag, NULL, &err); |
michael@0 | 632 | if (U_SUCCESS(err)) { |
michael@0 | 633 | int32_t numberKeys = ures_getSize(patBundle); |
michael@0 | 634 | int32_t len; |
michael@0 | 635 | const UChar *retPattern; |
michael@0 | 636 | key=NULL; |
michael@0 | 637 | #if defined(U_USE_ASCII_BUNDLE_ITERATOR) |
michael@0 | 638 | UResourceBundleAIterator aiter; |
michael@0 | 639 | ures_a_open(&aiter, patBundle, &err); |
michael@0 | 640 | #endif |
michael@0 | 641 | for(i=0; i<numberKeys; ++i) { |
michael@0 | 642 | #if defined(U_USE_ASCII_BUNDLE_ITERATOR) |
michael@0 | 643 | retPattern=ures_a_getNextString(&aiter, &len, &key, &err); |
michael@0 | 644 | #else |
michael@0 | 645 | retPattern=ures_getNextString(patBundle, &len, &key, &err); |
michael@0 | 646 | #endif |
michael@0 | 647 | UnicodeString format=UnicodeString(retPattern); |
michael@0 | 648 | UnicodeString retKey=UnicodeString(key, -1, US_INV); |
michael@0 | 649 | if ( firstTimeThrough || !isAvailableFormatSet(retKey) ) { |
michael@0 | 650 | setAvailableFormat(retKey, err); |
michael@0 | 651 | // Add pattern with its associated skeleton. Override any duplicate derived from std patterns, |
michael@0 | 652 | // but not a previous availableFormats entry: |
michael@0 | 653 | addPatternWithSkeleton(format, &retKey, override, conflictingPattern, err); |
michael@0 | 654 | } |
michael@0 | 655 | } |
michael@0 | 656 | #if defined(U_USE_ASCII_BUNDLE_ITERATOR) |
michael@0 | 657 | ures_a_close(&aiter); |
michael@0 | 658 | #endif |
michael@0 | 659 | ures_close(patBundle); |
michael@0 | 660 | } |
michael@0 | 661 | firstTimeThrough = FALSE; |
michael@0 | 662 | // close calBundle and calTypeBundle |
michael@0 | 663 | ures_close(calTypeBundle); |
michael@0 | 664 | ures_close(calBundle); |
michael@0 | 665 | } |
michael@0 | 666 | if (uprv_strcmp(curLocaleName,"root")==0 || uprv_strlen(curLocaleName)==0) { |
michael@0 | 667 | // we just finished handling root, nothing more to check |
michael@0 | 668 | ures_close(rb); |
michael@0 | 669 | break; |
michael@0 | 670 | } |
michael@0 | 671 | // Find the name of the appropriate parent locale (from %%Parent if present, else |
michael@0 | 672 | // uloc_getParent on the actual locale name) |
michael@0 | 673 | // (It would be nice to have a ures function that did this...) |
michael@0 | 674 | err = U_ZERO_ERROR; |
michael@0 | 675 | char parentLocale[ULOC_FULLNAME_CAPACITY]; |
michael@0 | 676 | int32_t locNameLen; |
michael@0 | 677 | const UChar * parentUName = ures_getStringByKey(rb, "%%Parent", &locNameLen, &err); |
michael@0 | 678 | if (U_SUCCESS(err) && err != U_USING_FALLBACK_WARNING && locNameLen < ULOC_FULLNAME_CAPACITY) { |
michael@0 | 679 | u_UCharsToChars(parentUName, parentLocale, locNameLen + 1); |
michael@0 | 680 | } else { |
michael@0 | 681 | err = U_ZERO_ERROR; |
michael@0 | 682 | uloc_getParent(curLocaleName, parentLocale, ULOC_FULLNAME_CAPACITY, &err); |
michael@0 | 683 | if (U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) { |
michael@0 | 684 | // just fallback to root, since we are not already there |
michael@0 | 685 | parentLocale[0] = 0; |
michael@0 | 686 | err = U_ZERO_ERROR; |
michael@0 | 687 | } |
michael@0 | 688 | } |
michael@0 | 689 | // Close current locale bundle |
michael@0 | 690 | ures_close(rb); |
michael@0 | 691 | // And open its parent, which becomes the new current locale being processed |
michael@0 | 692 | rb = ures_open(NULL, parentLocale, &err); |
michael@0 | 693 | if ( U_FAILURE(err) ) { |
michael@0 | 694 | err = U_ZERO_ERROR; |
michael@0 | 695 | break; |
michael@0 | 696 | } |
michael@0 | 697 | // Get the name of the parent / new current locale |
michael@0 | 698 | curLocaleName=ures_getLocaleByType(rb, ULOC_ACTUAL_LOCALE, &err); |
michael@0 | 699 | if ( U_FAILURE(err) ) { |
michael@0 | 700 | curLocaleName = parentLocale; |
michael@0 | 701 | err = U_ZERO_ERROR; |
michael@0 | 702 | } |
michael@0 | 703 | if (uprv_strcmp(curLocaleName,"root")==0 || uprv_strlen(curLocaleName)==0) { |
michael@0 | 704 | override = FALSE; |
michael@0 | 705 | } |
michael@0 | 706 | // Open calBundle and calTypeBundle |
michael@0 | 707 | calBundle = ures_getByKeyWithFallback(rb, DT_DateTimeCalendarTag, NULL, &err); |
michael@0 | 708 | if (U_SUCCESS(err)) { |
michael@0 | 709 | calTypeBundle = ures_getByKeyWithFallback(calBundle, calendarTypeToUse, NULL, &err); |
michael@0 | 710 | if ( U_FAILURE(err) ) { |
michael@0 | 711 | ures_close(calBundle); |
michael@0 | 712 | } |
michael@0 | 713 | } |
michael@0 | 714 | // Go to the top of the loop to process contents of calTypeBundle |
michael@0 | 715 | } |
michael@0 | 716 | |
michael@0 | 717 | if (hackPattern.length()>0) { |
michael@0 | 718 | hackTimes(hackPattern, err); |
michael@0 | 719 | } |
michael@0 | 720 | } |
michael@0 | 721 | |
michael@0 | 722 | void |
michael@0 | 723 | DateTimePatternGenerator::initHashtable(UErrorCode& err) { |
michael@0 | 724 | if (fAvailableFormatKeyHash!=NULL) { |
michael@0 | 725 | return; |
michael@0 | 726 | } |
michael@0 | 727 | if ((fAvailableFormatKeyHash = new Hashtable(FALSE, err))==NULL) { |
michael@0 | 728 | err=U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 729 | return; |
michael@0 | 730 | } |
michael@0 | 731 | } |
michael@0 | 732 | |
michael@0 | 733 | |
michael@0 | 734 | void |
michael@0 | 735 | DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field, const UnicodeString& value) { |
michael@0 | 736 | appendItemFormats[field] = value; |
michael@0 | 737 | // NUL-terminate for the C API. |
michael@0 | 738 | appendItemFormats[field].getTerminatedBuffer(); |
michael@0 | 739 | } |
michael@0 | 740 | |
michael@0 | 741 | const UnicodeString& |
michael@0 | 742 | DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field) const { |
michael@0 | 743 | return appendItemFormats[field]; |
michael@0 | 744 | } |
michael@0 | 745 | |
michael@0 | 746 | void |
michael@0 | 747 | DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field, const UnicodeString& value) { |
michael@0 | 748 | appendItemNames[field] = value; |
michael@0 | 749 | // NUL-terminate for the C API. |
michael@0 | 750 | appendItemNames[field].getTerminatedBuffer(); |
michael@0 | 751 | } |
michael@0 | 752 | |
michael@0 | 753 | const UnicodeString& |
michael@0 | 754 | DateTimePatternGenerator:: getAppendItemName(UDateTimePatternField field) const { |
michael@0 | 755 | return appendItemNames[field]; |
michael@0 | 756 | } |
michael@0 | 757 | |
michael@0 | 758 | void |
michael@0 | 759 | DateTimePatternGenerator::getAppendName(UDateTimePatternField field, UnicodeString& value) { |
michael@0 | 760 | value = SINGLE_QUOTE; |
michael@0 | 761 | value += appendItemNames[field]; |
michael@0 | 762 | value += SINGLE_QUOTE; |
michael@0 | 763 | } |
michael@0 | 764 | |
michael@0 | 765 | UnicodeString |
michael@0 | 766 | DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UErrorCode& status) { |
michael@0 | 767 | return getBestPattern(patternForm, UDATPG_MATCH_NO_OPTIONS, status); |
michael@0 | 768 | } |
michael@0 | 769 | |
michael@0 | 770 | UnicodeString |
michael@0 | 771 | DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDateTimePatternMatchOptions options, UErrorCode& status) { |
michael@0 | 772 | const UnicodeString *bestPattern=NULL; |
michael@0 | 773 | UnicodeString dtFormat; |
michael@0 | 774 | UnicodeString resultPattern; |
michael@0 | 775 | int32_t flags = kDTPGNoFlags; |
michael@0 | 776 | |
michael@0 | 777 | int32_t dateMask=(1<<UDATPG_DAYPERIOD_FIELD) - 1; |
michael@0 | 778 | int32_t timeMask=(1<<UDATPG_FIELD_COUNT) - 1 - dateMask; |
michael@0 | 779 | |
michael@0 | 780 | // Replace hour metacharacters 'j' and 'J', set flags as necessary |
michael@0 | 781 | UnicodeString patternFormCopy = UnicodeString(patternForm); |
michael@0 | 782 | int32_t patPos, patLen = patternFormCopy.length(); |
michael@0 | 783 | UBool inQuoted = FALSE; |
michael@0 | 784 | for (patPos = 0; patPos < patLen; patPos++) { |
michael@0 | 785 | UChar patChr = patternFormCopy.charAt(patPos); |
michael@0 | 786 | if (patChr == SINGLE_QUOTE) { |
michael@0 | 787 | inQuoted = !inQuoted; |
michael@0 | 788 | } else if (!inQuoted) { |
michael@0 | 789 | if (patChr == LOW_J) { |
michael@0 | 790 | patternFormCopy.setCharAt(patPos, fDefaultHourFormatChar); |
michael@0 | 791 | } else if (patChr == CAP_J) { |
michael@0 | 792 | // Get pattern for skeleton with H, then replace H or k |
michael@0 | 793 | // with fDefaultHourFormatChar (if different) |
michael@0 | 794 | patternFormCopy.setCharAt(patPos, CAP_H); |
michael@0 | 795 | flags |= kDTPGSkeletonUsesCapJ; |
michael@0 | 796 | } |
michael@0 | 797 | } |
michael@0 | 798 | } |
michael@0 | 799 | |
michael@0 | 800 | resultPattern.remove(); |
michael@0 | 801 | dtMatcher->set(patternFormCopy, fp); |
michael@0 | 802 | const PtnSkeleton* specifiedSkeleton=NULL; |
michael@0 | 803 | bestPattern=getBestRaw(*dtMatcher, -1, distanceInfo, &specifiedSkeleton); |
michael@0 | 804 | if ( distanceInfo->missingFieldMask==0 && distanceInfo->extraFieldMask==0 ) { |
michael@0 | 805 | resultPattern = adjustFieldTypes(*bestPattern, specifiedSkeleton, flags, options); |
michael@0 | 806 | |
michael@0 | 807 | return resultPattern; |
michael@0 | 808 | } |
michael@0 | 809 | int32_t neededFields = dtMatcher->getFieldMask(); |
michael@0 | 810 | UnicodeString datePattern=getBestAppending(neededFields & dateMask, flags, options); |
michael@0 | 811 | UnicodeString timePattern=getBestAppending(neededFields & timeMask, flags, options); |
michael@0 | 812 | if (datePattern.length()==0) { |
michael@0 | 813 | if (timePattern.length()==0) { |
michael@0 | 814 | resultPattern.remove(); |
michael@0 | 815 | } |
michael@0 | 816 | else { |
michael@0 | 817 | return timePattern; |
michael@0 | 818 | } |
michael@0 | 819 | } |
michael@0 | 820 | if (timePattern.length()==0) { |
michael@0 | 821 | return datePattern; |
michael@0 | 822 | } |
michael@0 | 823 | resultPattern.remove(); |
michael@0 | 824 | status = U_ZERO_ERROR; |
michael@0 | 825 | dtFormat=getDateTimeFormat(); |
michael@0 | 826 | Formattable dateTimeObject[] = { timePattern, datePattern }; |
michael@0 | 827 | resultPattern = MessageFormat::format(dtFormat, dateTimeObject, 2, resultPattern, status ); |
michael@0 | 828 | return resultPattern; |
michael@0 | 829 | } |
michael@0 | 830 | |
michael@0 | 831 | UnicodeString |
michael@0 | 832 | DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern, |
michael@0 | 833 | const UnicodeString& skeleton, |
michael@0 | 834 | UErrorCode& status) { |
michael@0 | 835 | return replaceFieldTypes(pattern, skeleton, UDATPG_MATCH_NO_OPTIONS, status); |
michael@0 | 836 | } |
michael@0 | 837 | |
michael@0 | 838 | UnicodeString |
michael@0 | 839 | DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern, |
michael@0 | 840 | const UnicodeString& skeleton, |
michael@0 | 841 | UDateTimePatternMatchOptions options, |
michael@0 | 842 | UErrorCode& /*status*/) { |
michael@0 | 843 | dtMatcher->set(skeleton, fp); |
michael@0 | 844 | UnicodeString result = adjustFieldTypes(pattern, NULL, kDTPGNoFlags, options); |
michael@0 | 845 | return result; |
michael@0 | 846 | } |
michael@0 | 847 | |
michael@0 | 848 | void |
michael@0 | 849 | DateTimePatternGenerator::setDecimal(const UnicodeString& newDecimal) { |
michael@0 | 850 | this->decimal = newDecimal; |
michael@0 | 851 | // NUL-terminate for the C API. |
michael@0 | 852 | this->decimal.getTerminatedBuffer(); |
michael@0 | 853 | } |
michael@0 | 854 | |
michael@0 | 855 | const UnicodeString& |
michael@0 | 856 | DateTimePatternGenerator::getDecimal() const { |
michael@0 | 857 | return decimal; |
michael@0 | 858 | } |
michael@0 | 859 | |
michael@0 | 860 | void |
michael@0 | 861 | DateTimePatternGenerator::addCanonicalItems() { |
michael@0 | 862 | UnicodeString conflictingPattern; |
michael@0 | 863 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 864 | |
michael@0 | 865 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; i++) { |
michael@0 | 866 | addPattern(UnicodeString(Canonical_Items[i]), FALSE, conflictingPattern, status); |
michael@0 | 867 | } |
michael@0 | 868 | } |
michael@0 | 869 | |
michael@0 | 870 | void |
michael@0 | 871 | DateTimePatternGenerator::setDateTimeFormat(const UnicodeString& dtFormat) { |
michael@0 | 872 | dateTimeFormat = dtFormat; |
michael@0 | 873 | // NUL-terminate for the C API. |
michael@0 | 874 | dateTimeFormat.getTerminatedBuffer(); |
michael@0 | 875 | } |
michael@0 | 876 | |
michael@0 | 877 | const UnicodeString& |
michael@0 | 878 | DateTimePatternGenerator::getDateTimeFormat() const { |
michael@0 | 879 | return dateTimeFormat; |
michael@0 | 880 | } |
michael@0 | 881 | |
michael@0 | 882 | void |
michael@0 | 883 | DateTimePatternGenerator::setDateTimeFromCalendar(const Locale& locale, UErrorCode& status) { |
michael@0 | 884 | const UChar *resStr; |
michael@0 | 885 | int32_t resStrLen = 0; |
michael@0 | 886 | |
michael@0 | 887 | Calendar* fCalendar = Calendar::createInstance(locale, status); |
michael@0 | 888 | CalendarData calData(locale, fCalendar?fCalendar->getType():NULL, status); |
michael@0 | 889 | UResourceBundle *dateTimePatterns = calData.getByKey(DT_DateTimePatternsTag, status); |
michael@0 | 890 | if (U_FAILURE(status)) return; |
michael@0 | 891 | |
michael@0 | 892 | if (ures_getSize(dateTimePatterns) <= DateFormat::kDateTime) |
michael@0 | 893 | { |
michael@0 | 894 | status = U_INVALID_FORMAT_ERROR; |
michael@0 | 895 | return; |
michael@0 | 896 | } |
michael@0 | 897 | resStr = ures_getStringByIndex(dateTimePatterns, (int32_t)DateFormat::kDateTime, &resStrLen, &status); |
michael@0 | 898 | setDateTimeFormat(UnicodeString(TRUE, resStr, resStrLen)); |
michael@0 | 899 | |
michael@0 | 900 | delete fCalendar; |
michael@0 | 901 | } |
michael@0 | 902 | |
michael@0 | 903 | void |
michael@0 | 904 | DateTimePatternGenerator::setDecimalSymbols(const Locale& locale, UErrorCode& status) { |
michael@0 | 905 | DecimalFormatSymbols dfs = DecimalFormatSymbols(locale, status); |
michael@0 | 906 | if(U_SUCCESS(status)) { |
michael@0 | 907 | decimal = dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); |
michael@0 | 908 | // NUL-terminate for the C API. |
michael@0 | 909 | decimal.getTerminatedBuffer(); |
michael@0 | 910 | } |
michael@0 | 911 | } |
michael@0 | 912 | |
michael@0 | 913 | UDateTimePatternConflict |
michael@0 | 914 | DateTimePatternGenerator::addPattern( |
michael@0 | 915 | const UnicodeString& pattern, |
michael@0 | 916 | UBool override, |
michael@0 | 917 | UnicodeString &conflictingPattern, |
michael@0 | 918 | UErrorCode& status) |
michael@0 | 919 | { |
michael@0 | 920 | return addPatternWithSkeleton(pattern, NULL, override, conflictingPattern, status); |
michael@0 | 921 | } |
michael@0 | 922 | |
michael@0 | 923 | // For DateTimePatternGenerator::addPatternWithSkeleton - |
michael@0 | 924 | // If skeletonToUse is specified, then an availableFormats entry is being added. In this case: |
michael@0 | 925 | // 1. We pass that skeleton to matcher.set instead of having it derive a skeleton from the pattern. |
michael@0 | 926 | // 2. If the new entry's skeleton or basePattern does match an existing entry but that entry also had a skeleton specified |
michael@0 | 927 | // (i.e. it was also from availableFormats), then the new entry does not override it regardless of the value of the override |
michael@0 | 928 | // parameter. This prevents later availableFormats entries from a parent locale overriding earlier ones from the actual |
michael@0 | 929 | // specified locale. However, availableFormats entries *should* override entries with matching skeleton whose skeleton was |
michael@0 | 930 | // derived (i.e. entries derived from the standard date/time patters for the specified locale). |
michael@0 | 931 | // 3. When adding the pattern (patternMap->add), we set a new boolean to indicate that the added entry had a |
michael@0 | 932 | // specified skeleton (which sets a new field in the PtnElem in the PatternMap). |
michael@0 | 933 | UDateTimePatternConflict |
michael@0 | 934 | DateTimePatternGenerator::addPatternWithSkeleton( |
michael@0 | 935 | const UnicodeString& pattern, |
michael@0 | 936 | const UnicodeString* skeletonToUse, |
michael@0 | 937 | UBool override, |
michael@0 | 938 | UnicodeString& conflictingPattern, |
michael@0 | 939 | UErrorCode& status) |
michael@0 | 940 | { |
michael@0 | 941 | |
michael@0 | 942 | UnicodeString basePattern; |
michael@0 | 943 | PtnSkeleton skeleton; |
michael@0 | 944 | UDateTimePatternConflict conflictingStatus = UDATPG_NO_CONFLICT; |
michael@0 | 945 | |
michael@0 | 946 | DateTimeMatcher matcher; |
michael@0 | 947 | if ( skeletonToUse == NULL ) { |
michael@0 | 948 | matcher.set(pattern, fp, skeleton); |
michael@0 | 949 | matcher.getBasePattern(basePattern); |
michael@0 | 950 | } else { |
michael@0 | 951 | matcher.set(*skeletonToUse, fp, skeleton); // no longer trims skeleton fields to max len 3, per #7930 |
michael@0 | 952 | matcher.getBasePattern(basePattern); // or perhaps instead: basePattern = *skeletonToUse; |
michael@0 | 953 | } |
michael@0 | 954 | // We only care about base conflicts - and replacing the pattern associated with a base - if: |
michael@0 | 955 | // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous |
michael@0 | 956 | // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or |
michael@0 | 957 | // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen |
michael@0 | 958 | // if we are getting here from a subsequent call to addPattern). |
michael@0 | 959 | // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking |
michael@0 | 960 | // availableFormats items from root, which should not override any previous entry with the same base. |
michael@0 | 961 | UBool entryHadSpecifiedSkeleton; |
michael@0 | 962 | const UnicodeString *duplicatePattern = patternMap->getPatternFromBasePattern(basePattern, entryHadSpecifiedSkeleton); |
michael@0 | 963 | if (duplicatePattern != NULL && (!entryHadSpecifiedSkeleton || (skeletonToUse != NULL && !override))) { |
michael@0 | 964 | conflictingStatus = UDATPG_BASE_CONFLICT; |
michael@0 | 965 | conflictingPattern = *duplicatePattern; |
michael@0 | 966 | if (!override) { |
michael@0 | 967 | return conflictingStatus; |
michael@0 | 968 | } |
michael@0 | 969 | } |
michael@0 | 970 | // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats |
michael@0 | 971 | // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with |
michael@0 | 972 | // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for |
michael@0 | 973 | // the previously-specified conflicting item. |
michael@0 | 974 | const PtnSkeleton* entrySpecifiedSkeleton = NULL; |
michael@0 | 975 | duplicatePattern = patternMap->getPatternFromSkeleton(skeleton, &entrySpecifiedSkeleton); |
michael@0 | 976 | if (duplicatePattern != NULL ) { |
michael@0 | 977 | conflictingStatus = UDATPG_CONFLICT; |
michael@0 | 978 | conflictingPattern = *duplicatePattern; |
michael@0 | 979 | if (!override || (skeletonToUse != NULL && entrySpecifiedSkeleton != NULL)) { |
michael@0 | 980 | return conflictingStatus; |
michael@0 | 981 | } |
michael@0 | 982 | } |
michael@0 | 983 | patternMap->add(basePattern, skeleton, pattern, skeletonToUse != NULL, status); |
michael@0 | 984 | if(U_FAILURE(status)) { |
michael@0 | 985 | return conflictingStatus; |
michael@0 | 986 | } |
michael@0 | 987 | |
michael@0 | 988 | return UDATPG_NO_CONFLICT; |
michael@0 | 989 | } |
michael@0 | 990 | |
michael@0 | 991 | |
michael@0 | 992 | UDateTimePatternField |
michael@0 | 993 | DateTimePatternGenerator::getAppendFormatNumber(const char* field) const { |
michael@0 | 994 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) { |
michael@0 | 995 | if (uprv_strcmp(CLDR_FIELD_APPEND[i], field)==0) { |
michael@0 | 996 | return (UDateTimePatternField)i; |
michael@0 | 997 | } |
michael@0 | 998 | } |
michael@0 | 999 | return UDATPG_FIELD_COUNT; |
michael@0 | 1000 | } |
michael@0 | 1001 | |
michael@0 | 1002 | UDateTimePatternField |
michael@0 | 1003 | DateTimePatternGenerator::getAppendNameNumber(const char* field) const { |
michael@0 | 1004 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) { |
michael@0 | 1005 | if (uprv_strcmp(CLDR_FIELD_NAME[i],field)==0) { |
michael@0 | 1006 | return (UDateTimePatternField)i; |
michael@0 | 1007 | } |
michael@0 | 1008 | } |
michael@0 | 1009 | return UDATPG_FIELD_COUNT; |
michael@0 | 1010 | } |
michael@0 | 1011 | |
michael@0 | 1012 | const UnicodeString* |
michael@0 | 1013 | DateTimePatternGenerator::getBestRaw(DateTimeMatcher& source, |
michael@0 | 1014 | int32_t includeMask, |
michael@0 | 1015 | DistanceInfo* missingFields, |
michael@0 | 1016 | const PtnSkeleton** specifiedSkeletonPtr) { |
michael@0 | 1017 | int32_t bestDistance = 0x7fffffff; |
michael@0 | 1018 | DistanceInfo tempInfo; |
michael@0 | 1019 | const UnicodeString *bestPattern=NULL; |
michael@0 | 1020 | const PtnSkeleton* specifiedSkeleton=NULL; |
michael@0 | 1021 | |
michael@0 | 1022 | PatternMapIterator it; |
michael@0 | 1023 | for (it.set(*patternMap); it.hasNext(); ) { |
michael@0 | 1024 | DateTimeMatcher trial = it.next(); |
michael@0 | 1025 | if (trial.equals(skipMatcher)) { |
michael@0 | 1026 | continue; |
michael@0 | 1027 | } |
michael@0 | 1028 | int32_t distance=source.getDistance(trial, includeMask, tempInfo); |
michael@0 | 1029 | if (distance<bestDistance) { |
michael@0 | 1030 | bestDistance=distance; |
michael@0 | 1031 | bestPattern=patternMap->getPatternFromSkeleton(*trial.getSkeletonPtr(), &specifiedSkeleton); |
michael@0 | 1032 | missingFields->setTo(tempInfo); |
michael@0 | 1033 | if (distance==0) { |
michael@0 | 1034 | break; |
michael@0 | 1035 | } |
michael@0 | 1036 | } |
michael@0 | 1037 | } |
michael@0 | 1038 | |
michael@0 | 1039 | // If the best raw match had a specified skeleton and that skeleton was requested by the caller, |
michael@0 | 1040 | // then return it too. This generally happens when the caller needs to pass that skeleton |
michael@0 | 1041 | // through to adjustFieldTypes so the latter can do a better job. |
michael@0 | 1042 | if (bestPattern && specifiedSkeletonPtr) { |
michael@0 | 1043 | *specifiedSkeletonPtr = specifiedSkeleton; |
michael@0 | 1044 | } |
michael@0 | 1045 | return bestPattern; |
michael@0 | 1046 | } |
michael@0 | 1047 | |
michael@0 | 1048 | UnicodeString |
michael@0 | 1049 | DateTimePatternGenerator::adjustFieldTypes(const UnicodeString& pattern, |
michael@0 | 1050 | const PtnSkeleton* specifiedSkeleton, |
michael@0 | 1051 | int32_t flags, |
michael@0 | 1052 | UDateTimePatternMatchOptions options) { |
michael@0 | 1053 | UnicodeString newPattern; |
michael@0 | 1054 | fp->set(pattern); |
michael@0 | 1055 | for (int32_t i=0; i < fp->itemNumber; i++) { |
michael@0 | 1056 | UnicodeString field = fp->items[i]; |
michael@0 | 1057 | if ( fp->isQuoteLiteral(field) ) { |
michael@0 | 1058 | |
michael@0 | 1059 | UnicodeString quoteLiteral; |
michael@0 | 1060 | fp->getQuoteLiteral(quoteLiteral, &i); |
michael@0 | 1061 | newPattern += quoteLiteral; |
michael@0 | 1062 | } |
michael@0 | 1063 | else { |
michael@0 | 1064 | if (fp->isPatternSeparator(field)) { |
michael@0 | 1065 | newPattern+=field; |
michael@0 | 1066 | continue; |
michael@0 | 1067 | } |
michael@0 | 1068 | int32_t canonicalIndex = fp->getCanonicalIndex(field); |
michael@0 | 1069 | if (canonicalIndex < 0) { |
michael@0 | 1070 | newPattern+=field; |
michael@0 | 1071 | continue; // don't adjust |
michael@0 | 1072 | } |
michael@0 | 1073 | const dtTypeElem *row = &dtTypes[canonicalIndex]; |
michael@0 | 1074 | int32_t typeValue = row->field; |
michael@0 | 1075 | if ((flags & kDTPGFixFractionalSeconds) != 0 && typeValue == UDATPG_SECOND_FIELD) { |
michael@0 | 1076 | UnicodeString newField=dtMatcher->skeleton.original[UDATPG_FRACTIONAL_SECOND_FIELD]; |
michael@0 | 1077 | field = field + decimal + newField; |
michael@0 | 1078 | } else if (dtMatcher->skeleton.type[typeValue]!=0) { |
michael@0 | 1079 | // Here: |
michael@0 | 1080 | // - "reqField" is the field from the originally requested skeleton, with length |
michael@0 | 1081 | // "reqFieldLen". |
michael@0 | 1082 | // - "field" is the field from the found pattern. |
michael@0 | 1083 | // |
michael@0 | 1084 | // The adjusted field should consist of characters from the originally requested |
michael@0 | 1085 | // skeleton, except in the case of UDATPG_HOUR_FIELD or UDATPG_MONTH_FIELD or |
michael@0 | 1086 | // UDATPG_WEEKDAY_FIELD or UDATPG_YEAR_FIELD, in which case it should consist |
michael@0 | 1087 | // of characters from the found pattern. |
michael@0 | 1088 | // |
michael@0 | 1089 | // The length of the adjusted field (adjFieldLen) should match that in the originally |
michael@0 | 1090 | // requested skeleton, except that in the following cases the length of the adjusted field |
michael@0 | 1091 | // should match that in the found pattern (i.e. the length of this pattern field should |
michael@0 | 1092 | // not be adjusted): |
michael@0 | 1093 | // 1. typeValue is UDATPG_HOUR_FIELD/MINUTE/SECOND and the corresponding bit in options is |
michael@0 | 1094 | // not set (ticket #7180). Note, we may want to implement a similar change for other |
michael@0 | 1095 | // numeric fields (MM, dd, etc.) so the default behavior is to get locale preference for |
michael@0 | 1096 | // field length, but options bits can be used to override this. |
michael@0 | 1097 | // 2. There is a specified skeleton for the found pattern and one of the following is true: |
michael@0 | 1098 | // a) The length of the field in the skeleton (skelFieldLen) is equal to reqFieldLen. |
michael@0 | 1099 | // b) The pattern field is numeric and the skeleton field is not, or vice versa. |
michael@0 | 1100 | |
michael@0 | 1101 | UnicodeString reqField = dtMatcher->skeleton.original[typeValue]; |
michael@0 | 1102 | int32_t reqFieldLen = reqField.length(); |
michael@0 | 1103 | if (reqField.charAt(0) == CAP_E && reqFieldLen < 3) |
michael@0 | 1104 | reqFieldLen = 3; // 1-3 for E are equivalent to 3 for c,e |
michael@0 | 1105 | int32_t adjFieldLen = reqFieldLen; |
michael@0 | 1106 | if ( (typeValue==UDATPG_HOUR_FIELD && (options & UDATPG_MATCH_HOUR_FIELD_LENGTH)==0) || |
michael@0 | 1107 | (typeValue==UDATPG_MINUTE_FIELD && (options & UDATPG_MATCH_MINUTE_FIELD_LENGTH)==0) || |
michael@0 | 1108 | (typeValue==UDATPG_SECOND_FIELD && (options & UDATPG_MATCH_SECOND_FIELD_LENGTH)==0) ) { |
michael@0 | 1109 | adjFieldLen = field.length(); |
michael@0 | 1110 | } else if (specifiedSkeleton) { |
michael@0 | 1111 | UnicodeString skelField = specifiedSkeleton->original[typeValue]; |
michael@0 | 1112 | int32_t skelFieldLen = skelField.length(); |
michael@0 | 1113 | UBool patFieldIsNumeric = (row->type > 0); |
michael@0 | 1114 | UBool skelFieldIsNumeric = (specifiedSkeleton->type[typeValue] > 0); |
michael@0 | 1115 | if (skelFieldLen == reqFieldLen || (patFieldIsNumeric && !skelFieldIsNumeric) || (skelFieldIsNumeric && !patFieldIsNumeric)) { |
michael@0 | 1116 | // don't adjust the field length in the found pattern |
michael@0 | 1117 | adjFieldLen = field.length(); |
michael@0 | 1118 | } |
michael@0 | 1119 | } |
michael@0 | 1120 | UChar c = (typeValue!= UDATPG_HOUR_FIELD && typeValue!= UDATPG_MONTH_FIELD && |
michael@0 | 1121 | typeValue!= UDATPG_WEEKDAY_FIELD && (typeValue!= UDATPG_YEAR_FIELD || reqField.charAt(0)==CAP_Y))? |
michael@0 | 1122 | reqField.charAt(0): field.charAt(0); |
michael@0 | 1123 | if (typeValue == UDATPG_HOUR_FIELD && (flags & kDTPGSkeletonUsesCapJ) != 0) { |
michael@0 | 1124 | c = fDefaultHourFormatChar; |
michael@0 | 1125 | } |
michael@0 | 1126 | field.remove(); |
michael@0 | 1127 | for (int32_t i=adjFieldLen; i>0; --i) { |
michael@0 | 1128 | field+=c; |
michael@0 | 1129 | } |
michael@0 | 1130 | } |
michael@0 | 1131 | newPattern+=field; |
michael@0 | 1132 | } |
michael@0 | 1133 | } |
michael@0 | 1134 | return newPattern; |
michael@0 | 1135 | } |
michael@0 | 1136 | |
michael@0 | 1137 | UnicodeString |
michael@0 | 1138 | DateTimePatternGenerator::getBestAppending(int32_t missingFields, int32_t flags, UDateTimePatternMatchOptions options) { |
michael@0 | 1139 | UnicodeString resultPattern, tempPattern; |
michael@0 | 1140 | UErrorCode err=U_ZERO_ERROR; |
michael@0 | 1141 | int32_t lastMissingFieldMask=0; |
michael@0 | 1142 | if (missingFields!=0) { |
michael@0 | 1143 | resultPattern=UnicodeString(); |
michael@0 | 1144 | const PtnSkeleton* specifiedSkeleton=NULL; |
michael@0 | 1145 | tempPattern = *getBestRaw(*dtMatcher, missingFields, distanceInfo, &specifiedSkeleton); |
michael@0 | 1146 | resultPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options); |
michael@0 | 1147 | if ( distanceInfo->missingFieldMask==0 ) { |
michael@0 | 1148 | return resultPattern; |
michael@0 | 1149 | } |
michael@0 | 1150 | while (distanceInfo->missingFieldMask!=0) { // precondition: EVERY single field must work! |
michael@0 | 1151 | if ( lastMissingFieldMask == distanceInfo->missingFieldMask ) { |
michael@0 | 1152 | break; // cannot find the proper missing field |
michael@0 | 1153 | } |
michael@0 | 1154 | if (((distanceInfo->missingFieldMask & UDATPG_SECOND_AND_FRACTIONAL_MASK)==UDATPG_FRACTIONAL_MASK) && |
michael@0 | 1155 | ((missingFields & UDATPG_SECOND_AND_FRACTIONAL_MASK) == UDATPG_SECOND_AND_FRACTIONAL_MASK)) { |
michael@0 | 1156 | resultPattern = adjustFieldTypes(resultPattern, specifiedSkeleton, flags | kDTPGFixFractionalSeconds, options); |
michael@0 | 1157 | distanceInfo->missingFieldMask &= ~UDATPG_FRACTIONAL_MASK; |
michael@0 | 1158 | continue; |
michael@0 | 1159 | } |
michael@0 | 1160 | int32_t startingMask = distanceInfo->missingFieldMask; |
michael@0 | 1161 | tempPattern = *getBestRaw(*dtMatcher, distanceInfo->missingFieldMask, distanceInfo, &specifiedSkeleton); |
michael@0 | 1162 | tempPattern = adjustFieldTypes(tempPattern, specifiedSkeleton, flags, options); |
michael@0 | 1163 | int32_t foundMask=startingMask& ~distanceInfo->missingFieldMask; |
michael@0 | 1164 | int32_t topField=getTopBitNumber(foundMask); |
michael@0 | 1165 | UnicodeString appendName; |
michael@0 | 1166 | getAppendName((UDateTimePatternField)topField, appendName); |
michael@0 | 1167 | const Formattable formatPattern[] = { |
michael@0 | 1168 | resultPattern, |
michael@0 | 1169 | tempPattern, |
michael@0 | 1170 | appendName |
michael@0 | 1171 | }; |
michael@0 | 1172 | UnicodeString emptyStr; |
michael@0 | 1173 | resultPattern = MessageFormat::format(appendItemFormats[topField], formatPattern, 3, emptyStr, err); |
michael@0 | 1174 | lastMissingFieldMask = distanceInfo->missingFieldMask; |
michael@0 | 1175 | } |
michael@0 | 1176 | } |
michael@0 | 1177 | return resultPattern; |
michael@0 | 1178 | } |
michael@0 | 1179 | |
michael@0 | 1180 | int32_t |
michael@0 | 1181 | DateTimePatternGenerator::getTopBitNumber(int32_t foundMask) { |
michael@0 | 1182 | if ( foundMask==0 ) { |
michael@0 | 1183 | return 0; |
michael@0 | 1184 | } |
michael@0 | 1185 | int32_t i=0; |
michael@0 | 1186 | while (foundMask!=0) { |
michael@0 | 1187 | foundMask >>=1; |
michael@0 | 1188 | ++i; |
michael@0 | 1189 | } |
michael@0 | 1190 | if (i-1 >UDATPG_ZONE_FIELD) { |
michael@0 | 1191 | return UDATPG_ZONE_FIELD; |
michael@0 | 1192 | } |
michael@0 | 1193 | else |
michael@0 | 1194 | return i-1; |
michael@0 | 1195 | } |
michael@0 | 1196 | |
michael@0 | 1197 | void |
michael@0 | 1198 | DateTimePatternGenerator::setAvailableFormat(const UnicodeString &key, UErrorCode& err) |
michael@0 | 1199 | { |
michael@0 | 1200 | fAvailableFormatKeyHash->puti(key, 1, err); |
michael@0 | 1201 | } |
michael@0 | 1202 | |
michael@0 | 1203 | UBool |
michael@0 | 1204 | DateTimePatternGenerator::isAvailableFormatSet(const UnicodeString &key) const { |
michael@0 | 1205 | return (UBool)(fAvailableFormatKeyHash->geti(key) == 1); |
michael@0 | 1206 | } |
michael@0 | 1207 | |
michael@0 | 1208 | void |
michael@0 | 1209 | DateTimePatternGenerator::copyHashtable(Hashtable *other, UErrorCode &status) { |
michael@0 | 1210 | |
michael@0 | 1211 | if (other == NULL) { |
michael@0 | 1212 | return; |
michael@0 | 1213 | } |
michael@0 | 1214 | if (fAvailableFormatKeyHash != NULL) { |
michael@0 | 1215 | delete fAvailableFormatKeyHash; |
michael@0 | 1216 | fAvailableFormatKeyHash = NULL; |
michael@0 | 1217 | } |
michael@0 | 1218 | initHashtable(status); |
michael@0 | 1219 | if(U_FAILURE(status)){ |
michael@0 | 1220 | return; |
michael@0 | 1221 | } |
michael@0 | 1222 | int32_t pos = -1; |
michael@0 | 1223 | const UHashElement* elem = NULL; |
michael@0 | 1224 | // walk through the hash table and create a deep clone |
michael@0 | 1225 | while((elem = other->nextElement(pos))!= NULL){ |
michael@0 | 1226 | const UHashTok otherKeyTok = elem->key; |
michael@0 | 1227 | UnicodeString* otherKey = (UnicodeString*)otherKeyTok.pointer; |
michael@0 | 1228 | fAvailableFormatKeyHash->puti(*otherKey, 1, status); |
michael@0 | 1229 | if(U_FAILURE(status)){ |
michael@0 | 1230 | return; |
michael@0 | 1231 | } |
michael@0 | 1232 | } |
michael@0 | 1233 | } |
michael@0 | 1234 | |
michael@0 | 1235 | StringEnumeration* |
michael@0 | 1236 | DateTimePatternGenerator::getSkeletons(UErrorCode& status) const { |
michael@0 | 1237 | StringEnumeration* skeletonEnumerator = new DTSkeletonEnumeration(*patternMap, DT_SKELETON, status); |
michael@0 | 1238 | return skeletonEnumerator; |
michael@0 | 1239 | } |
michael@0 | 1240 | |
michael@0 | 1241 | const UnicodeString& |
michael@0 | 1242 | DateTimePatternGenerator::getPatternForSkeleton(const UnicodeString& skeleton) const { |
michael@0 | 1243 | PtnElem *curElem; |
michael@0 | 1244 | |
michael@0 | 1245 | if (skeleton.length() ==0) { |
michael@0 | 1246 | return emptyString; |
michael@0 | 1247 | } |
michael@0 | 1248 | curElem = patternMap->getHeader(skeleton.charAt(0)); |
michael@0 | 1249 | while ( curElem != NULL ) { |
michael@0 | 1250 | if ( curElem->skeleton->getSkeleton()==skeleton ) { |
michael@0 | 1251 | return curElem->pattern; |
michael@0 | 1252 | } |
michael@0 | 1253 | curElem=curElem->next; |
michael@0 | 1254 | } |
michael@0 | 1255 | return emptyString; |
michael@0 | 1256 | } |
michael@0 | 1257 | |
michael@0 | 1258 | StringEnumeration* |
michael@0 | 1259 | DateTimePatternGenerator::getBaseSkeletons(UErrorCode& status) const { |
michael@0 | 1260 | StringEnumeration* baseSkeletonEnumerator = new DTSkeletonEnumeration(*patternMap, DT_BASESKELETON, status); |
michael@0 | 1261 | return baseSkeletonEnumerator; |
michael@0 | 1262 | } |
michael@0 | 1263 | |
michael@0 | 1264 | StringEnumeration* |
michael@0 | 1265 | DateTimePatternGenerator::getRedundants(UErrorCode& status) { |
michael@0 | 1266 | StringEnumeration* output = new DTRedundantEnumeration(); |
michael@0 | 1267 | const UnicodeString *pattern; |
michael@0 | 1268 | PatternMapIterator it; |
michael@0 | 1269 | for (it.set(*patternMap); it.hasNext(); ) { |
michael@0 | 1270 | DateTimeMatcher current = it.next(); |
michael@0 | 1271 | pattern = patternMap->getPatternFromSkeleton(*(it.getSkeleton())); |
michael@0 | 1272 | if ( isCanonicalItem(*pattern) ) { |
michael@0 | 1273 | continue; |
michael@0 | 1274 | } |
michael@0 | 1275 | if ( skipMatcher == NULL ) { |
michael@0 | 1276 | skipMatcher = new DateTimeMatcher(current); |
michael@0 | 1277 | } |
michael@0 | 1278 | else { |
michael@0 | 1279 | *skipMatcher = current; |
michael@0 | 1280 | } |
michael@0 | 1281 | UnicodeString trial = getBestPattern(current.getPattern(), status); |
michael@0 | 1282 | if (trial == *pattern) { |
michael@0 | 1283 | ((DTRedundantEnumeration *)output)->add(*pattern, status); |
michael@0 | 1284 | } |
michael@0 | 1285 | if (current.equals(skipMatcher)) { |
michael@0 | 1286 | continue; |
michael@0 | 1287 | } |
michael@0 | 1288 | } |
michael@0 | 1289 | return output; |
michael@0 | 1290 | } |
michael@0 | 1291 | |
michael@0 | 1292 | UBool |
michael@0 | 1293 | DateTimePatternGenerator::isCanonicalItem(const UnicodeString& item) const { |
michael@0 | 1294 | if ( item.length() != 1 ) { |
michael@0 | 1295 | return FALSE; |
michael@0 | 1296 | } |
michael@0 | 1297 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 1298 | if (item.charAt(0)==Canonical_Items[i]) { |
michael@0 | 1299 | return TRUE; |
michael@0 | 1300 | } |
michael@0 | 1301 | } |
michael@0 | 1302 | return FALSE; |
michael@0 | 1303 | } |
michael@0 | 1304 | |
michael@0 | 1305 | |
michael@0 | 1306 | DateTimePatternGenerator* |
michael@0 | 1307 | DateTimePatternGenerator::clone() const { |
michael@0 | 1308 | return new DateTimePatternGenerator(*this); |
michael@0 | 1309 | } |
michael@0 | 1310 | |
michael@0 | 1311 | PatternMap::PatternMap() { |
michael@0 | 1312 | for (int32_t i=0; i < MAX_PATTERN_ENTRIES; ++i ) { |
michael@0 | 1313 | boot[i]=NULL; |
michael@0 | 1314 | } |
michael@0 | 1315 | isDupAllowed = TRUE; |
michael@0 | 1316 | } |
michael@0 | 1317 | |
michael@0 | 1318 | void |
michael@0 | 1319 | PatternMap::copyFrom(const PatternMap& other, UErrorCode& status) { |
michael@0 | 1320 | this->isDupAllowed = other.isDupAllowed; |
michael@0 | 1321 | for (int32_t bootIndex=0; bootIndex<MAX_PATTERN_ENTRIES; ++bootIndex ) { |
michael@0 | 1322 | PtnElem *curElem, *otherElem, *prevElem=NULL; |
michael@0 | 1323 | otherElem = other.boot[bootIndex]; |
michael@0 | 1324 | while (otherElem!=NULL) { |
michael@0 | 1325 | if ((curElem = new PtnElem(otherElem->basePattern, otherElem->pattern))==NULL) { |
michael@0 | 1326 | // out of memory |
michael@0 | 1327 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1328 | return; |
michael@0 | 1329 | } |
michael@0 | 1330 | if ( this->boot[bootIndex]== NULL ) { |
michael@0 | 1331 | this->boot[bootIndex] = curElem; |
michael@0 | 1332 | } |
michael@0 | 1333 | if ((curElem->skeleton=new PtnSkeleton(*(otherElem->skeleton))) == NULL ) { |
michael@0 | 1334 | // out of memory |
michael@0 | 1335 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1336 | return; |
michael@0 | 1337 | } |
michael@0 | 1338 | |
michael@0 | 1339 | if (prevElem!=NULL) { |
michael@0 | 1340 | prevElem->next=curElem; |
michael@0 | 1341 | } |
michael@0 | 1342 | curElem->next=NULL; |
michael@0 | 1343 | prevElem = curElem; |
michael@0 | 1344 | otherElem = otherElem->next; |
michael@0 | 1345 | } |
michael@0 | 1346 | |
michael@0 | 1347 | } |
michael@0 | 1348 | } |
michael@0 | 1349 | |
michael@0 | 1350 | PtnElem* |
michael@0 | 1351 | PatternMap::getHeader(UChar baseChar) { |
michael@0 | 1352 | PtnElem* curElem; |
michael@0 | 1353 | |
michael@0 | 1354 | if ( (baseChar >= CAP_A) && (baseChar <= CAP_Z) ) { |
michael@0 | 1355 | curElem = boot[baseChar-CAP_A]; |
michael@0 | 1356 | } |
michael@0 | 1357 | else { |
michael@0 | 1358 | if ( (baseChar >=LOW_A) && (baseChar <= LOW_Z) ) { |
michael@0 | 1359 | curElem = boot[26+baseChar-LOW_A]; |
michael@0 | 1360 | } |
michael@0 | 1361 | else { |
michael@0 | 1362 | return NULL; |
michael@0 | 1363 | } |
michael@0 | 1364 | } |
michael@0 | 1365 | return curElem; |
michael@0 | 1366 | } |
michael@0 | 1367 | |
michael@0 | 1368 | PatternMap::~PatternMap() { |
michael@0 | 1369 | for (int32_t i=0; i < MAX_PATTERN_ENTRIES; ++i ) { |
michael@0 | 1370 | if (boot[i]!=NULL ) { |
michael@0 | 1371 | delete boot[i]; |
michael@0 | 1372 | boot[i]=NULL; |
michael@0 | 1373 | } |
michael@0 | 1374 | } |
michael@0 | 1375 | } // PatternMap destructor |
michael@0 | 1376 | |
michael@0 | 1377 | void |
michael@0 | 1378 | PatternMap::add(const UnicodeString& basePattern, |
michael@0 | 1379 | const PtnSkeleton& skeleton, |
michael@0 | 1380 | const UnicodeString& value,// mapped pattern value |
michael@0 | 1381 | UBool skeletonWasSpecified, |
michael@0 | 1382 | UErrorCode &status) { |
michael@0 | 1383 | UChar baseChar = basePattern.charAt(0); |
michael@0 | 1384 | PtnElem *curElem, *baseElem; |
michael@0 | 1385 | status = U_ZERO_ERROR; |
michael@0 | 1386 | |
michael@0 | 1387 | // the baseChar must be A-Z or a-z |
michael@0 | 1388 | if ((baseChar >= CAP_A) && (baseChar <= CAP_Z)) { |
michael@0 | 1389 | baseElem = boot[baseChar-CAP_A]; |
michael@0 | 1390 | } |
michael@0 | 1391 | else { |
michael@0 | 1392 | if ((baseChar >=LOW_A) && (baseChar <= LOW_Z)) { |
michael@0 | 1393 | baseElem = boot[26+baseChar-LOW_A]; |
michael@0 | 1394 | } |
michael@0 | 1395 | else { |
michael@0 | 1396 | status = U_ILLEGAL_CHARACTER; |
michael@0 | 1397 | return; |
michael@0 | 1398 | } |
michael@0 | 1399 | } |
michael@0 | 1400 | |
michael@0 | 1401 | if (baseElem == NULL) { |
michael@0 | 1402 | if ((curElem = new PtnElem(basePattern, value)) == NULL ) { |
michael@0 | 1403 | // out of memory |
michael@0 | 1404 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1405 | return; |
michael@0 | 1406 | } |
michael@0 | 1407 | if (baseChar >= LOW_A) { |
michael@0 | 1408 | boot[26 + (baseChar-LOW_A)] = curElem; |
michael@0 | 1409 | } |
michael@0 | 1410 | else { |
michael@0 | 1411 | boot[baseChar-CAP_A] = curElem; |
michael@0 | 1412 | } |
michael@0 | 1413 | curElem->skeleton = new PtnSkeleton(skeleton); |
michael@0 | 1414 | curElem->skeletonWasSpecified = skeletonWasSpecified; |
michael@0 | 1415 | } |
michael@0 | 1416 | if ( baseElem != NULL ) { |
michael@0 | 1417 | curElem = getDuplicateElem(basePattern, skeleton, baseElem); |
michael@0 | 1418 | |
michael@0 | 1419 | if (curElem == NULL) { |
michael@0 | 1420 | // add new element to the list. |
michael@0 | 1421 | curElem = baseElem; |
michael@0 | 1422 | while( curElem -> next != NULL ) |
michael@0 | 1423 | { |
michael@0 | 1424 | curElem = curElem->next; |
michael@0 | 1425 | } |
michael@0 | 1426 | if ((curElem->next = new PtnElem(basePattern, value)) == NULL ) { |
michael@0 | 1427 | // out of memory |
michael@0 | 1428 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1429 | return; |
michael@0 | 1430 | } |
michael@0 | 1431 | curElem=curElem->next; |
michael@0 | 1432 | curElem->skeleton = new PtnSkeleton(skeleton); |
michael@0 | 1433 | curElem->skeletonWasSpecified = skeletonWasSpecified; |
michael@0 | 1434 | } |
michael@0 | 1435 | else { |
michael@0 | 1436 | // Pattern exists in the list already. |
michael@0 | 1437 | if ( !isDupAllowed ) { |
michael@0 | 1438 | return; |
michael@0 | 1439 | } |
michael@0 | 1440 | // Overwrite the value. |
michael@0 | 1441 | curElem->pattern = value; |
michael@0 | 1442 | // It was a bug that we were not doing the following previously, |
michael@0 | 1443 | // though that bug hid other problems by making things partly work. |
michael@0 | 1444 | curElem->skeletonWasSpecified = skeletonWasSpecified; |
michael@0 | 1445 | } |
michael@0 | 1446 | } |
michael@0 | 1447 | } // PatternMap::add |
michael@0 | 1448 | |
michael@0 | 1449 | // Find the pattern from the given basePattern string. |
michael@0 | 1450 | const UnicodeString * |
michael@0 | 1451 | PatternMap::getPatternFromBasePattern(UnicodeString& basePattern, UBool& skeletonWasSpecified) { // key to search for |
michael@0 | 1452 | PtnElem *curElem; |
michael@0 | 1453 | |
michael@0 | 1454 | if ((curElem=getHeader(basePattern.charAt(0)))==NULL) { |
michael@0 | 1455 | return NULL; // no match |
michael@0 | 1456 | } |
michael@0 | 1457 | |
michael@0 | 1458 | do { |
michael@0 | 1459 | if ( basePattern.compare(curElem->basePattern)==0 ) { |
michael@0 | 1460 | skeletonWasSpecified = curElem->skeletonWasSpecified; |
michael@0 | 1461 | return &(curElem->pattern); |
michael@0 | 1462 | } |
michael@0 | 1463 | curElem=curElem->next; |
michael@0 | 1464 | }while (curElem != NULL); |
michael@0 | 1465 | |
michael@0 | 1466 | return NULL; |
michael@0 | 1467 | } // PatternMap::getFromBasePattern |
michael@0 | 1468 | |
michael@0 | 1469 | |
michael@0 | 1470 | // Find the pattern from the given skeleton. |
michael@0 | 1471 | // At least when this is called from getBestRaw & addPattern (in which case specifiedSkeletonPtr is non-NULL), |
michael@0 | 1472 | // the comparison should be based on skeleton.original (which is unique and tied to the distance measurement in bestRaw) |
michael@0 | 1473 | // and not skeleton.baseOriginal (which is not unique); otherwise we may pick a different skeleton than the one with the |
michael@0 | 1474 | // optimum distance value in getBestRaw. When this is called from public getRedundants (specifiedSkeletonPtr is NULL), |
michael@0 | 1475 | // for now it will continue to compare based on baseOriginal so as not to change the behavior unnecessarily. |
michael@0 | 1476 | const UnicodeString * |
michael@0 | 1477 | PatternMap::getPatternFromSkeleton(PtnSkeleton& skeleton, const PtnSkeleton** specifiedSkeletonPtr) { // key to search for |
michael@0 | 1478 | PtnElem *curElem; |
michael@0 | 1479 | |
michael@0 | 1480 | if (specifiedSkeletonPtr) { |
michael@0 | 1481 | *specifiedSkeletonPtr = NULL; |
michael@0 | 1482 | } |
michael@0 | 1483 | |
michael@0 | 1484 | // find boot entry |
michael@0 | 1485 | UChar baseChar='\0'; |
michael@0 | 1486 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 1487 | if (skeleton.baseOriginal[i].length() !=0 ) { |
michael@0 | 1488 | baseChar = skeleton.baseOriginal[i].charAt(0); |
michael@0 | 1489 | break; |
michael@0 | 1490 | } |
michael@0 | 1491 | } |
michael@0 | 1492 | |
michael@0 | 1493 | if ((curElem=getHeader(baseChar))==NULL) { |
michael@0 | 1494 | return NULL; // no match |
michael@0 | 1495 | } |
michael@0 | 1496 | |
michael@0 | 1497 | do { |
michael@0 | 1498 | int32_t i=0; |
michael@0 | 1499 | if (specifiedSkeletonPtr != NULL) { // called from DateTimePatternGenerator::getBestRaw or addPattern, use original |
michael@0 | 1500 | for (i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 1501 | if (curElem->skeleton->original[i].compare(skeleton.original[i]) != 0 ) |
michael@0 | 1502 | { |
michael@0 | 1503 | break; |
michael@0 | 1504 | } |
michael@0 | 1505 | } |
michael@0 | 1506 | } else { // called from DateTimePatternGenerator::getRedundants, use baseOriginal |
michael@0 | 1507 | for (i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 1508 | if (curElem->skeleton->baseOriginal[i].compare(skeleton.baseOriginal[i]) != 0 ) |
michael@0 | 1509 | { |
michael@0 | 1510 | break; |
michael@0 | 1511 | } |
michael@0 | 1512 | } |
michael@0 | 1513 | } |
michael@0 | 1514 | if (i == UDATPG_FIELD_COUNT) { |
michael@0 | 1515 | if (specifiedSkeletonPtr && curElem->skeletonWasSpecified) { |
michael@0 | 1516 | *specifiedSkeletonPtr = curElem->skeleton; |
michael@0 | 1517 | } |
michael@0 | 1518 | return &(curElem->pattern); |
michael@0 | 1519 | } |
michael@0 | 1520 | curElem=curElem->next; |
michael@0 | 1521 | }while (curElem != NULL); |
michael@0 | 1522 | |
michael@0 | 1523 | return NULL; |
michael@0 | 1524 | } |
michael@0 | 1525 | |
michael@0 | 1526 | UBool |
michael@0 | 1527 | PatternMap::equals(const PatternMap& other) { |
michael@0 | 1528 | if ( this==&other ) { |
michael@0 | 1529 | return TRUE; |
michael@0 | 1530 | } |
michael@0 | 1531 | for (int32_t bootIndex=0; bootIndex<MAX_PATTERN_ENTRIES; ++bootIndex ) { |
michael@0 | 1532 | if ( boot[bootIndex]==other.boot[bootIndex] ) { |
michael@0 | 1533 | continue; |
michael@0 | 1534 | } |
michael@0 | 1535 | if ( (boot[bootIndex]==NULL)||(other.boot[bootIndex]==NULL) ) { |
michael@0 | 1536 | return FALSE; |
michael@0 | 1537 | } |
michael@0 | 1538 | PtnElem *otherElem = other.boot[bootIndex]; |
michael@0 | 1539 | PtnElem *myElem = boot[bootIndex]; |
michael@0 | 1540 | while ((otherElem!=NULL) || (myElem!=NULL)) { |
michael@0 | 1541 | if ( myElem == otherElem ) { |
michael@0 | 1542 | break; |
michael@0 | 1543 | } |
michael@0 | 1544 | if ((otherElem==NULL) || (myElem==NULL)) { |
michael@0 | 1545 | return FALSE; |
michael@0 | 1546 | } |
michael@0 | 1547 | if ( (myElem->basePattern != otherElem->basePattern) || |
michael@0 | 1548 | (myElem->pattern != otherElem->pattern) ) { |
michael@0 | 1549 | return FALSE; |
michael@0 | 1550 | } |
michael@0 | 1551 | if ((myElem->skeleton!=otherElem->skeleton)&& |
michael@0 | 1552 | !myElem->skeleton->equals(*(otherElem->skeleton))) { |
michael@0 | 1553 | return FALSE; |
michael@0 | 1554 | } |
michael@0 | 1555 | myElem = myElem->next; |
michael@0 | 1556 | otherElem=otherElem->next; |
michael@0 | 1557 | } |
michael@0 | 1558 | } |
michael@0 | 1559 | return TRUE; |
michael@0 | 1560 | } |
michael@0 | 1561 | |
michael@0 | 1562 | // find any key existing in the mapping table already. |
michael@0 | 1563 | // return TRUE if there is an existing key, otherwise return FALSE. |
michael@0 | 1564 | PtnElem* |
michael@0 | 1565 | PatternMap::getDuplicateElem( |
michael@0 | 1566 | const UnicodeString &basePattern, |
michael@0 | 1567 | const PtnSkeleton &skeleton, |
michael@0 | 1568 | PtnElem *baseElem) { |
michael@0 | 1569 | PtnElem *curElem; |
michael@0 | 1570 | |
michael@0 | 1571 | if ( baseElem == (PtnElem *)NULL ) { |
michael@0 | 1572 | return (PtnElem*)NULL; |
michael@0 | 1573 | } |
michael@0 | 1574 | else { |
michael@0 | 1575 | curElem = baseElem; |
michael@0 | 1576 | } |
michael@0 | 1577 | do { |
michael@0 | 1578 | if ( basePattern.compare(curElem->basePattern)==0 ) { |
michael@0 | 1579 | UBool isEqual=TRUE; |
michael@0 | 1580 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 1581 | if (curElem->skeleton->type[i] != skeleton.type[i] ) { |
michael@0 | 1582 | isEqual=FALSE; |
michael@0 | 1583 | break; |
michael@0 | 1584 | } |
michael@0 | 1585 | } |
michael@0 | 1586 | if (isEqual) { |
michael@0 | 1587 | return curElem; |
michael@0 | 1588 | } |
michael@0 | 1589 | } |
michael@0 | 1590 | curElem = curElem->next; |
michael@0 | 1591 | } while( curElem != (PtnElem *)NULL ); |
michael@0 | 1592 | |
michael@0 | 1593 | // end of the list |
michael@0 | 1594 | return (PtnElem*)NULL; |
michael@0 | 1595 | |
michael@0 | 1596 | } // PatternMap::getDuplicateElem |
michael@0 | 1597 | |
michael@0 | 1598 | DateTimeMatcher::DateTimeMatcher(void) { |
michael@0 | 1599 | } |
michael@0 | 1600 | |
michael@0 | 1601 | DateTimeMatcher::~DateTimeMatcher() {} |
michael@0 | 1602 | |
michael@0 | 1603 | DateTimeMatcher::DateTimeMatcher(const DateTimeMatcher& other) { |
michael@0 | 1604 | copyFrom(other.skeleton); |
michael@0 | 1605 | } |
michael@0 | 1606 | |
michael@0 | 1607 | |
michael@0 | 1608 | void |
michael@0 | 1609 | DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp) { |
michael@0 | 1610 | PtnSkeleton localSkeleton; |
michael@0 | 1611 | return set(pattern, fp, localSkeleton); |
michael@0 | 1612 | } |
michael@0 | 1613 | |
michael@0 | 1614 | void |
michael@0 | 1615 | DateTimeMatcher::set(const UnicodeString& pattern, FormatParser* fp, PtnSkeleton& skeletonResult) { |
michael@0 | 1616 | int32_t i; |
michael@0 | 1617 | for (i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 1618 | skeletonResult.type[i]=NONE; |
michael@0 | 1619 | } |
michael@0 | 1620 | fp->set(pattern); |
michael@0 | 1621 | for (i=0; i < fp->itemNumber; i++) { |
michael@0 | 1622 | UnicodeString field = fp->items[i]; |
michael@0 | 1623 | if ( field.charAt(0) == LOW_A ) { |
michael@0 | 1624 | continue; // skip 'a' |
michael@0 | 1625 | } |
michael@0 | 1626 | |
michael@0 | 1627 | if ( fp->isQuoteLiteral(field) ) { |
michael@0 | 1628 | UnicodeString quoteLiteral; |
michael@0 | 1629 | fp->getQuoteLiteral(quoteLiteral, &i); |
michael@0 | 1630 | continue; |
michael@0 | 1631 | } |
michael@0 | 1632 | int32_t canonicalIndex = fp->getCanonicalIndex(field); |
michael@0 | 1633 | if (canonicalIndex < 0 ) { |
michael@0 | 1634 | continue; |
michael@0 | 1635 | } |
michael@0 | 1636 | const dtTypeElem *row = &dtTypes[canonicalIndex]; |
michael@0 | 1637 | int32_t typeValue = row->field; |
michael@0 | 1638 | skeletonResult.original[typeValue]=field; |
michael@0 | 1639 | UChar repeatChar = row->patternChar; |
michael@0 | 1640 | int32_t repeatCount = row->minLen; // #7930 removes cap at 3 |
michael@0 | 1641 | while (repeatCount-- > 0) { |
michael@0 | 1642 | skeletonResult.baseOriginal[typeValue] += repeatChar; |
michael@0 | 1643 | } |
michael@0 | 1644 | int16_t subTypeValue = row->type; |
michael@0 | 1645 | if ( row->type > 0) { |
michael@0 | 1646 | subTypeValue += field.length(); |
michael@0 | 1647 | } |
michael@0 | 1648 | skeletonResult.type[typeValue] = subTypeValue; |
michael@0 | 1649 | } |
michael@0 | 1650 | copyFrom(skeletonResult); |
michael@0 | 1651 | } |
michael@0 | 1652 | |
michael@0 | 1653 | void |
michael@0 | 1654 | DateTimeMatcher::getBasePattern(UnicodeString &result ) { |
michael@0 | 1655 | result.remove(); // Reset the result first. |
michael@0 | 1656 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) { |
michael@0 | 1657 | if (skeleton.baseOriginal[i].length()!=0) { |
michael@0 | 1658 | result += skeleton.baseOriginal[i]; |
michael@0 | 1659 | } |
michael@0 | 1660 | } |
michael@0 | 1661 | } |
michael@0 | 1662 | |
michael@0 | 1663 | UnicodeString |
michael@0 | 1664 | DateTimeMatcher::getPattern() { |
michael@0 | 1665 | UnicodeString result; |
michael@0 | 1666 | |
michael@0 | 1667 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) { |
michael@0 | 1668 | if (skeleton.original[i].length()!=0) { |
michael@0 | 1669 | result += skeleton.original[i]; |
michael@0 | 1670 | } |
michael@0 | 1671 | } |
michael@0 | 1672 | return result; |
michael@0 | 1673 | } |
michael@0 | 1674 | |
michael@0 | 1675 | int32_t |
michael@0 | 1676 | DateTimeMatcher::getDistance(const DateTimeMatcher& other, int32_t includeMask, DistanceInfo& distanceInfo) { |
michael@0 | 1677 | int32_t result=0; |
michael@0 | 1678 | distanceInfo.clear(); |
michael@0 | 1679 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) { |
michael@0 | 1680 | int32_t myType = (includeMask&(1<<i))==0 ? 0 : skeleton.type[i]; |
michael@0 | 1681 | int32_t otherType = other.skeleton.type[i]; |
michael@0 | 1682 | if (myType==otherType) { |
michael@0 | 1683 | continue; |
michael@0 | 1684 | } |
michael@0 | 1685 | if (myType==0) {// and other is not |
michael@0 | 1686 | result += EXTRA_FIELD; |
michael@0 | 1687 | distanceInfo.addExtra(i); |
michael@0 | 1688 | } |
michael@0 | 1689 | else { |
michael@0 | 1690 | if (otherType==0) { |
michael@0 | 1691 | result += MISSING_FIELD; |
michael@0 | 1692 | distanceInfo.addMissing(i); |
michael@0 | 1693 | } |
michael@0 | 1694 | else { |
michael@0 | 1695 | result += abs(myType - otherType); |
michael@0 | 1696 | } |
michael@0 | 1697 | } |
michael@0 | 1698 | |
michael@0 | 1699 | } |
michael@0 | 1700 | return result; |
michael@0 | 1701 | } |
michael@0 | 1702 | |
michael@0 | 1703 | void |
michael@0 | 1704 | DateTimeMatcher::copyFrom(const PtnSkeleton& newSkeleton) { |
michael@0 | 1705 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 1706 | this->skeleton.type[i]=newSkeleton.type[i]; |
michael@0 | 1707 | this->skeleton.original[i]=newSkeleton.original[i]; |
michael@0 | 1708 | this->skeleton.baseOriginal[i]=newSkeleton.baseOriginal[i]; |
michael@0 | 1709 | } |
michael@0 | 1710 | } |
michael@0 | 1711 | |
michael@0 | 1712 | void |
michael@0 | 1713 | DateTimeMatcher::copyFrom() { |
michael@0 | 1714 | // same as clear |
michael@0 | 1715 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 1716 | this->skeleton.type[i]=0; |
michael@0 | 1717 | this->skeleton.original[i].remove(); |
michael@0 | 1718 | this->skeleton.baseOriginal[i].remove(); |
michael@0 | 1719 | } |
michael@0 | 1720 | } |
michael@0 | 1721 | |
michael@0 | 1722 | UBool |
michael@0 | 1723 | DateTimeMatcher::equals(const DateTimeMatcher* other) const { |
michael@0 | 1724 | if (other==NULL) { |
michael@0 | 1725 | return FALSE; |
michael@0 | 1726 | } |
michael@0 | 1727 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 1728 | if (this->skeleton.original[i]!=other->skeleton.original[i] ) { |
michael@0 | 1729 | return FALSE; |
michael@0 | 1730 | } |
michael@0 | 1731 | } |
michael@0 | 1732 | return TRUE; |
michael@0 | 1733 | } |
michael@0 | 1734 | |
michael@0 | 1735 | int32_t |
michael@0 | 1736 | DateTimeMatcher::getFieldMask() { |
michael@0 | 1737 | int32_t result=0; |
michael@0 | 1738 | |
michael@0 | 1739 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 1740 | if (skeleton.type[i]!=0) { |
michael@0 | 1741 | result |= (1<<i); |
michael@0 | 1742 | } |
michael@0 | 1743 | } |
michael@0 | 1744 | return result; |
michael@0 | 1745 | } |
michael@0 | 1746 | |
michael@0 | 1747 | PtnSkeleton* |
michael@0 | 1748 | DateTimeMatcher::getSkeletonPtr() { |
michael@0 | 1749 | return &skeleton; |
michael@0 | 1750 | } |
michael@0 | 1751 | |
michael@0 | 1752 | FormatParser::FormatParser () { |
michael@0 | 1753 | status = START; |
michael@0 | 1754 | itemNumber=0; |
michael@0 | 1755 | } |
michael@0 | 1756 | |
michael@0 | 1757 | |
michael@0 | 1758 | FormatParser::~FormatParser () { |
michael@0 | 1759 | } |
michael@0 | 1760 | |
michael@0 | 1761 | |
michael@0 | 1762 | // Find the next token with the starting position and length |
michael@0 | 1763 | // Note: the startPos may |
michael@0 | 1764 | FormatParser::TokenStatus |
michael@0 | 1765 | FormatParser::setTokens(const UnicodeString& pattern, int32_t startPos, int32_t *len) { |
michael@0 | 1766 | int32_t curLoc = startPos; |
michael@0 | 1767 | if ( curLoc >= pattern.length()) { |
michael@0 | 1768 | return DONE; |
michael@0 | 1769 | } |
michael@0 | 1770 | // check the current char is between A-Z or a-z |
michael@0 | 1771 | do { |
michael@0 | 1772 | UChar c=pattern.charAt(curLoc); |
michael@0 | 1773 | if ( (c>=CAP_A && c<=CAP_Z) || (c>=LOW_A && c<=LOW_Z) ) { |
michael@0 | 1774 | curLoc++; |
michael@0 | 1775 | } |
michael@0 | 1776 | else { |
michael@0 | 1777 | startPos = curLoc; |
michael@0 | 1778 | *len=1; |
michael@0 | 1779 | return ADD_TOKEN; |
michael@0 | 1780 | } |
michael@0 | 1781 | |
michael@0 | 1782 | if ( pattern.charAt(curLoc)!= pattern.charAt(startPos) ) { |
michael@0 | 1783 | break; // not the same token |
michael@0 | 1784 | } |
michael@0 | 1785 | } while(curLoc <= pattern.length()); |
michael@0 | 1786 | *len = curLoc-startPos; |
michael@0 | 1787 | return ADD_TOKEN; |
michael@0 | 1788 | } |
michael@0 | 1789 | |
michael@0 | 1790 | void |
michael@0 | 1791 | FormatParser::set(const UnicodeString& pattern) { |
michael@0 | 1792 | int32_t startPos=0; |
michael@0 | 1793 | TokenStatus result=START; |
michael@0 | 1794 | int32_t len=0; |
michael@0 | 1795 | itemNumber =0; |
michael@0 | 1796 | |
michael@0 | 1797 | do { |
michael@0 | 1798 | result = setTokens( pattern, startPos, &len ); |
michael@0 | 1799 | if ( result == ADD_TOKEN ) |
michael@0 | 1800 | { |
michael@0 | 1801 | items[itemNumber++] = UnicodeString(pattern, startPos, len ); |
michael@0 | 1802 | startPos += len; |
michael@0 | 1803 | } |
michael@0 | 1804 | else { |
michael@0 | 1805 | break; |
michael@0 | 1806 | } |
michael@0 | 1807 | } while (result==ADD_TOKEN && itemNumber < MAX_DT_TOKEN); |
michael@0 | 1808 | } |
michael@0 | 1809 | |
michael@0 | 1810 | int32_t |
michael@0 | 1811 | FormatParser::getCanonicalIndex(const UnicodeString& s, UBool strict) { |
michael@0 | 1812 | int32_t len = s.length(); |
michael@0 | 1813 | if (len == 0) { |
michael@0 | 1814 | return -1; |
michael@0 | 1815 | } |
michael@0 | 1816 | UChar ch = s.charAt(0); |
michael@0 | 1817 | |
michael@0 | 1818 | // Verify that all are the same character. |
michael@0 | 1819 | for (int32_t l = 1; l < len; l++) { |
michael@0 | 1820 | if (ch != s.charAt(l)) { |
michael@0 | 1821 | return -1; |
michael@0 | 1822 | } |
michael@0 | 1823 | } |
michael@0 | 1824 | int32_t i = 0; |
michael@0 | 1825 | int32_t bestRow = -1; |
michael@0 | 1826 | while (dtTypes[i].patternChar != '\0') { |
michael@0 | 1827 | if ( dtTypes[i].patternChar != ch ) { |
michael@0 | 1828 | ++i; |
michael@0 | 1829 | continue; |
michael@0 | 1830 | } |
michael@0 | 1831 | bestRow = i; |
michael@0 | 1832 | if (dtTypes[i].patternChar != dtTypes[i+1].patternChar) { |
michael@0 | 1833 | return i; |
michael@0 | 1834 | } |
michael@0 | 1835 | if (dtTypes[i+1].minLen <= len) { |
michael@0 | 1836 | ++i; |
michael@0 | 1837 | continue; |
michael@0 | 1838 | } |
michael@0 | 1839 | return i; |
michael@0 | 1840 | } |
michael@0 | 1841 | return strict ? -1 : bestRow; |
michael@0 | 1842 | } |
michael@0 | 1843 | |
michael@0 | 1844 | UBool |
michael@0 | 1845 | FormatParser::isQuoteLiteral(const UnicodeString& s) const { |
michael@0 | 1846 | return (UBool)(s.charAt(0)==SINGLE_QUOTE); |
michael@0 | 1847 | } |
michael@0 | 1848 | |
michael@0 | 1849 | // This function aussumes the current itemIndex points to the quote literal. |
michael@0 | 1850 | // Please call isQuoteLiteral prior to this function. |
michael@0 | 1851 | void |
michael@0 | 1852 | FormatParser::getQuoteLiteral(UnicodeString& quote, int32_t *itemIndex) { |
michael@0 | 1853 | int32_t i=*itemIndex; |
michael@0 | 1854 | |
michael@0 | 1855 | quote.remove(); |
michael@0 | 1856 | if (items[i].charAt(0)==SINGLE_QUOTE) { |
michael@0 | 1857 | quote += items[i]; |
michael@0 | 1858 | ++i; |
michael@0 | 1859 | } |
michael@0 | 1860 | while ( i < itemNumber ) { |
michael@0 | 1861 | if ( items[i].charAt(0)==SINGLE_QUOTE ) { |
michael@0 | 1862 | if ( (i+1<itemNumber) && (items[i+1].charAt(0)==SINGLE_QUOTE)) { |
michael@0 | 1863 | // two single quotes e.g. 'o''clock' |
michael@0 | 1864 | quote += items[i++]; |
michael@0 | 1865 | quote += items[i++]; |
michael@0 | 1866 | continue; |
michael@0 | 1867 | } |
michael@0 | 1868 | else { |
michael@0 | 1869 | quote += items[i]; |
michael@0 | 1870 | break; |
michael@0 | 1871 | } |
michael@0 | 1872 | } |
michael@0 | 1873 | else { |
michael@0 | 1874 | quote += items[i]; |
michael@0 | 1875 | } |
michael@0 | 1876 | ++i; |
michael@0 | 1877 | } |
michael@0 | 1878 | *itemIndex=i; |
michael@0 | 1879 | } |
michael@0 | 1880 | |
michael@0 | 1881 | UBool |
michael@0 | 1882 | FormatParser::isPatternSeparator(UnicodeString& field) { |
michael@0 | 1883 | for (int32_t i=0; i<field.length(); ++i ) { |
michael@0 | 1884 | UChar c= field.charAt(i); |
michael@0 | 1885 | if ( (c==SINGLE_QUOTE) || (c==BACKSLASH) || (c==SPACE) || (c==COLON) || |
michael@0 | 1886 | (c==QUOTATION_MARK) || (c==COMMA) || (c==HYPHEN) ||(items[i].charAt(0)==DOT) ) { |
michael@0 | 1887 | continue; |
michael@0 | 1888 | } |
michael@0 | 1889 | else { |
michael@0 | 1890 | return FALSE; |
michael@0 | 1891 | } |
michael@0 | 1892 | } |
michael@0 | 1893 | return TRUE; |
michael@0 | 1894 | } |
michael@0 | 1895 | |
michael@0 | 1896 | DistanceInfo::~DistanceInfo() {} |
michael@0 | 1897 | |
michael@0 | 1898 | void |
michael@0 | 1899 | DistanceInfo::setTo(DistanceInfo &other) { |
michael@0 | 1900 | missingFieldMask = other.missingFieldMask; |
michael@0 | 1901 | extraFieldMask= other.extraFieldMask; |
michael@0 | 1902 | } |
michael@0 | 1903 | |
michael@0 | 1904 | PatternMapIterator::PatternMapIterator() { |
michael@0 | 1905 | bootIndex = 0; |
michael@0 | 1906 | nodePtr = NULL; |
michael@0 | 1907 | patternMap=NULL; |
michael@0 | 1908 | matcher= new DateTimeMatcher(); |
michael@0 | 1909 | } |
michael@0 | 1910 | |
michael@0 | 1911 | |
michael@0 | 1912 | PatternMapIterator::~PatternMapIterator() { |
michael@0 | 1913 | delete matcher; |
michael@0 | 1914 | } |
michael@0 | 1915 | |
michael@0 | 1916 | void |
michael@0 | 1917 | PatternMapIterator::set(PatternMap& newPatternMap) { |
michael@0 | 1918 | this->patternMap=&newPatternMap; |
michael@0 | 1919 | } |
michael@0 | 1920 | |
michael@0 | 1921 | PtnSkeleton* |
michael@0 | 1922 | PatternMapIterator::getSkeleton() { |
michael@0 | 1923 | if ( nodePtr == NULL ) { |
michael@0 | 1924 | return NULL; |
michael@0 | 1925 | } |
michael@0 | 1926 | else { |
michael@0 | 1927 | return nodePtr->skeleton; |
michael@0 | 1928 | } |
michael@0 | 1929 | } |
michael@0 | 1930 | |
michael@0 | 1931 | UBool |
michael@0 | 1932 | PatternMapIterator::hasNext() { |
michael@0 | 1933 | int32_t headIndex=bootIndex; |
michael@0 | 1934 | PtnElem *curPtr=nodePtr; |
michael@0 | 1935 | |
michael@0 | 1936 | if (patternMap==NULL) { |
michael@0 | 1937 | return FALSE; |
michael@0 | 1938 | } |
michael@0 | 1939 | while ( headIndex < MAX_PATTERN_ENTRIES ) { |
michael@0 | 1940 | if ( curPtr != NULL ) { |
michael@0 | 1941 | if ( curPtr->next != NULL ) { |
michael@0 | 1942 | return TRUE; |
michael@0 | 1943 | } |
michael@0 | 1944 | else { |
michael@0 | 1945 | headIndex++; |
michael@0 | 1946 | curPtr=NULL; |
michael@0 | 1947 | continue; |
michael@0 | 1948 | } |
michael@0 | 1949 | } |
michael@0 | 1950 | else { |
michael@0 | 1951 | if ( patternMap->boot[headIndex] != NULL ) { |
michael@0 | 1952 | return TRUE; |
michael@0 | 1953 | } |
michael@0 | 1954 | else { |
michael@0 | 1955 | headIndex++; |
michael@0 | 1956 | continue; |
michael@0 | 1957 | } |
michael@0 | 1958 | } |
michael@0 | 1959 | |
michael@0 | 1960 | } |
michael@0 | 1961 | return FALSE; |
michael@0 | 1962 | } |
michael@0 | 1963 | |
michael@0 | 1964 | DateTimeMatcher& |
michael@0 | 1965 | PatternMapIterator::next() { |
michael@0 | 1966 | while ( bootIndex < MAX_PATTERN_ENTRIES ) { |
michael@0 | 1967 | if ( nodePtr != NULL ) { |
michael@0 | 1968 | if ( nodePtr->next != NULL ) { |
michael@0 | 1969 | nodePtr = nodePtr->next; |
michael@0 | 1970 | break; |
michael@0 | 1971 | } |
michael@0 | 1972 | else { |
michael@0 | 1973 | bootIndex++; |
michael@0 | 1974 | nodePtr=NULL; |
michael@0 | 1975 | continue; |
michael@0 | 1976 | } |
michael@0 | 1977 | } |
michael@0 | 1978 | else { |
michael@0 | 1979 | if ( patternMap->boot[bootIndex] != NULL ) { |
michael@0 | 1980 | nodePtr = patternMap->boot[bootIndex]; |
michael@0 | 1981 | break; |
michael@0 | 1982 | } |
michael@0 | 1983 | else { |
michael@0 | 1984 | bootIndex++; |
michael@0 | 1985 | continue; |
michael@0 | 1986 | } |
michael@0 | 1987 | } |
michael@0 | 1988 | } |
michael@0 | 1989 | if (nodePtr!=NULL) { |
michael@0 | 1990 | matcher->copyFrom(*nodePtr->skeleton); |
michael@0 | 1991 | } |
michael@0 | 1992 | else { |
michael@0 | 1993 | matcher->copyFrom(); |
michael@0 | 1994 | } |
michael@0 | 1995 | return *matcher; |
michael@0 | 1996 | } |
michael@0 | 1997 | |
michael@0 | 1998 | PtnSkeleton::PtnSkeleton() { |
michael@0 | 1999 | } |
michael@0 | 2000 | |
michael@0 | 2001 | |
michael@0 | 2002 | PtnSkeleton::PtnSkeleton(const PtnSkeleton& other) { |
michael@0 | 2003 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 2004 | this->type[i]=other.type[i]; |
michael@0 | 2005 | this->original[i]=other.original[i]; |
michael@0 | 2006 | this->baseOriginal[i]=other.baseOriginal[i]; |
michael@0 | 2007 | } |
michael@0 | 2008 | } |
michael@0 | 2009 | |
michael@0 | 2010 | UBool |
michael@0 | 2011 | PtnSkeleton::equals(const PtnSkeleton& other) { |
michael@0 | 2012 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 2013 | if ( (type[i]!= other.type[i]) || |
michael@0 | 2014 | (original[i]!=other.original[i]) || |
michael@0 | 2015 | (baseOriginal[i]!=other.baseOriginal[i]) ) { |
michael@0 | 2016 | return FALSE; |
michael@0 | 2017 | } |
michael@0 | 2018 | } |
michael@0 | 2019 | return TRUE; |
michael@0 | 2020 | } |
michael@0 | 2021 | |
michael@0 | 2022 | UnicodeString |
michael@0 | 2023 | PtnSkeleton::getSkeleton() { |
michael@0 | 2024 | UnicodeString result; |
michael@0 | 2025 | |
michael@0 | 2026 | for(int32_t i=0; i< UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 2027 | if (original[i].length()!=0) { |
michael@0 | 2028 | result += original[i]; |
michael@0 | 2029 | } |
michael@0 | 2030 | } |
michael@0 | 2031 | return result; |
michael@0 | 2032 | } |
michael@0 | 2033 | |
michael@0 | 2034 | UnicodeString |
michael@0 | 2035 | PtnSkeleton::getBaseSkeleton() { |
michael@0 | 2036 | UnicodeString result; |
michael@0 | 2037 | |
michael@0 | 2038 | for(int32_t i=0; i< UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 2039 | if (baseOriginal[i].length()!=0) { |
michael@0 | 2040 | result += baseOriginal[i]; |
michael@0 | 2041 | } |
michael@0 | 2042 | } |
michael@0 | 2043 | return result; |
michael@0 | 2044 | } |
michael@0 | 2045 | |
michael@0 | 2046 | PtnSkeleton::~PtnSkeleton() { |
michael@0 | 2047 | } |
michael@0 | 2048 | |
michael@0 | 2049 | PtnElem::PtnElem(const UnicodeString &basePat, const UnicodeString &pat) : |
michael@0 | 2050 | basePattern(basePat), |
michael@0 | 2051 | skeleton(NULL), |
michael@0 | 2052 | pattern(pat), |
michael@0 | 2053 | next(NULL) |
michael@0 | 2054 | { |
michael@0 | 2055 | } |
michael@0 | 2056 | |
michael@0 | 2057 | PtnElem::~PtnElem() { |
michael@0 | 2058 | |
michael@0 | 2059 | if (next!=NULL) { |
michael@0 | 2060 | delete next; |
michael@0 | 2061 | } |
michael@0 | 2062 | delete skeleton; |
michael@0 | 2063 | } |
michael@0 | 2064 | |
michael@0 | 2065 | DTSkeletonEnumeration::DTSkeletonEnumeration(PatternMap &patternMap, dtStrEnum type, UErrorCode& status) { |
michael@0 | 2066 | PtnElem *curElem; |
michael@0 | 2067 | PtnSkeleton *curSkeleton; |
michael@0 | 2068 | UnicodeString s; |
michael@0 | 2069 | int32_t bootIndex; |
michael@0 | 2070 | |
michael@0 | 2071 | pos=0; |
michael@0 | 2072 | fSkeletons = new UVector(status); |
michael@0 | 2073 | if (U_FAILURE(status)) { |
michael@0 | 2074 | delete fSkeletons; |
michael@0 | 2075 | return; |
michael@0 | 2076 | } |
michael@0 | 2077 | for (bootIndex=0; bootIndex<MAX_PATTERN_ENTRIES; ++bootIndex ) { |
michael@0 | 2078 | curElem = patternMap.boot[bootIndex]; |
michael@0 | 2079 | while (curElem!=NULL) { |
michael@0 | 2080 | switch(type) { |
michael@0 | 2081 | case DT_BASESKELETON: |
michael@0 | 2082 | s=curElem->basePattern; |
michael@0 | 2083 | break; |
michael@0 | 2084 | case DT_PATTERN: |
michael@0 | 2085 | s=curElem->pattern; |
michael@0 | 2086 | break; |
michael@0 | 2087 | case DT_SKELETON: |
michael@0 | 2088 | curSkeleton=curElem->skeleton; |
michael@0 | 2089 | s=curSkeleton->getSkeleton(); |
michael@0 | 2090 | break; |
michael@0 | 2091 | } |
michael@0 | 2092 | if ( !isCanonicalItem(s) ) { |
michael@0 | 2093 | fSkeletons->addElement(new UnicodeString(s), status); |
michael@0 | 2094 | if (U_FAILURE(status)) { |
michael@0 | 2095 | delete fSkeletons; |
michael@0 | 2096 | fSkeletons = NULL; |
michael@0 | 2097 | return; |
michael@0 | 2098 | } |
michael@0 | 2099 | } |
michael@0 | 2100 | curElem = curElem->next; |
michael@0 | 2101 | } |
michael@0 | 2102 | } |
michael@0 | 2103 | if ((bootIndex==MAX_PATTERN_ENTRIES) && (curElem!=NULL) ) { |
michael@0 | 2104 | status = U_BUFFER_OVERFLOW_ERROR; |
michael@0 | 2105 | } |
michael@0 | 2106 | } |
michael@0 | 2107 | |
michael@0 | 2108 | const UnicodeString* |
michael@0 | 2109 | DTSkeletonEnumeration::snext(UErrorCode& status) { |
michael@0 | 2110 | if (U_SUCCESS(status) && pos < fSkeletons->size()) { |
michael@0 | 2111 | return (const UnicodeString*)fSkeletons->elementAt(pos++); |
michael@0 | 2112 | } |
michael@0 | 2113 | return NULL; |
michael@0 | 2114 | } |
michael@0 | 2115 | |
michael@0 | 2116 | void |
michael@0 | 2117 | DTSkeletonEnumeration::reset(UErrorCode& /*status*/) { |
michael@0 | 2118 | pos=0; |
michael@0 | 2119 | } |
michael@0 | 2120 | |
michael@0 | 2121 | int32_t |
michael@0 | 2122 | DTSkeletonEnumeration::count(UErrorCode& /*status*/) const { |
michael@0 | 2123 | return (fSkeletons==NULL) ? 0 : fSkeletons->size(); |
michael@0 | 2124 | } |
michael@0 | 2125 | |
michael@0 | 2126 | UBool |
michael@0 | 2127 | DTSkeletonEnumeration::isCanonicalItem(const UnicodeString& item) { |
michael@0 | 2128 | if ( item.length() != 1 ) { |
michael@0 | 2129 | return FALSE; |
michael@0 | 2130 | } |
michael@0 | 2131 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 2132 | if (item.charAt(0)==Canonical_Items[i]) { |
michael@0 | 2133 | return TRUE; |
michael@0 | 2134 | } |
michael@0 | 2135 | } |
michael@0 | 2136 | return FALSE; |
michael@0 | 2137 | } |
michael@0 | 2138 | |
michael@0 | 2139 | DTSkeletonEnumeration::~DTSkeletonEnumeration() { |
michael@0 | 2140 | UnicodeString *s; |
michael@0 | 2141 | for (int32_t i=0; i<fSkeletons->size(); ++i) { |
michael@0 | 2142 | if ((s=(UnicodeString *)fSkeletons->elementAt(i))!=NULL) { |
michael@0 | 2143 | delete s; |
michael@0 | 2144 | } |
michael@0 | 2145 | } |
michael@0 | 2146 | delete fSkeletons; |
michael@0 | 2147 | } |
michael@0 | 2148 | |
michael@0 | 2149 | DTRedundantEnumeration::DTRedundantEnumeration() { |
michael@0 | 2150 | pos=0; |
michael@0 | 2151 | fPatterns = NULL; |
michael@0 | 2152 | } |
michael@0 | 2153 | |
michael@0 | 2154 | void |
michael@0 | 2155 | DTRedundantEnumeration::add(const UnicodeString& pattern, UErrorCode& status) { |
michael@0 | 2156 | if (U_FAILURE(status)) return; |
michael@0 | 2157 | if (fPatterns == NULL) { |
michael@0 | 2158 | fPatterns = new UVector(status); |
michael@0 | 2159 | if (U_FAILURE(status)) { |
michael@0 | 2160 | delete fPatterns; |
michael@0 | 2161 | fPatterns = NULL; |
michael@0 | 2162 | return; |
michael@0 | 2163 | } |
michael@0 | 2164 | } |
michael@0 | 2165 | fPatterns->addElement(new UnicodeString(pattern), status); |
michael@0 | 2166 | if (U_FAILURE(status)) { |
michael@0 | 2167 | delete fPatterns; |
michael@0 | 2168 | fPatterns = NULL; |
michael@0 | 2169 | return; |
michael@0 | 2170 | } |
michael@0 | 2171 | } |
michael@0 | 2172 | |
michael@0 | 2173 | const UnicodeString* |
michael@0 | 2174 | DTRedundantEnumeration::snext(UErrorCode& status) { |
michael@0 | 2175 | if (U_SUCCESS(status) && pos < fPatterns->size()) { |
michael@0 | 2176 | return (const UnicodeString*)fPatterns->elementAt(pos++); |
michael@0 | 2177 | } |
michael@0 | 2178 | return NULL; |
michael@0 | 2179 | } |
michael@0 | 2180 | |
michael@0 | 2181 | void |
michael@0 | 2182 | DTRedundantEnumeration::reset(UErrorCode& /*status*/) { |
michael@0 | 2183 | pos=0; |
michael@0 | 2184 | } |
michael@0 | 2185 | |
michael@0 | 2186 | int32_t |
michael@0 | 2187 | DTRedundantEnumeration::count(UErrorCode& /*status*/) const { |
michael@0 | 2188 | return (fPatterns==NULL) ? 0 : fPatterns->size(); |
michael@0 | 2189 | } |
michael@0 | 2190 | |
michael@0 | 2191 | UBool |
michael@0 | 2192 | DTRedundantEnumeration::isCanonicalItem(const UnicodeString& item) { |
michael@0 | 2193 | if ( item.length() != 1 ) { |
michael@0 | 2194 | return FALSE; |
michael@0 | 2195 | } |
michael@0 | 2196 | for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i) { |
michael@0 | 2197 | if (item.charAt(0)==Canonical_Items[i]) { |
michael@0 | 2198 | return TRUE; |
michael@0 | 2199 | } |
michael@0 | 2200 | } |
michael@0 | 2201 | return FALSE; |
michael@0 | 2202 | } |
michael@0 | 2203 | |
michael@0 | 2204 | DTRedundantEnumeration::~DTRedundantEnumeration() { |
michael@0 | 2205 | UnicodeString *s; |
michael@0 | 2206 | for (int32_t i=0; i<fPatterns->size(); ++i) { |
michael@0 | 2207 | if ((s=(UnicodeString *)fPatterns->elementAt(i))!=NULL) { |
michael@0 | 2208 | delete s; |
michael@0 | 2209 | } |
michael@0 | 2210 | } |
michael@0 | 2211 | delete fPatterns; |
michael@0 | 2212 | } |
michael@0 | 2213 | |
michael@0 | 2214 | U_NAMESPACE_END |
michael@0 | 2215 | |
michael@0 | 2216 | |
michael@0 | 2217 | #endif /* #if !UCONFIG_NO_FORMATTING */ |
michael@0 | 2218 | |
michael@0 | 2219 | //eof |