ICU-13183 on branch - Handle different field lengths for skeleton metachars j,C
X-SVN-Rev: 40126
This commit is contained in:
parent
72c605c1c4
commit
56e884e22e
@ -1007,48 +1007,13 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate
|
||||
int32_t timeMask=(1<<UDATPG_FIELD_COUNT) - 1 - dateMask;
|
||||
|
||||
// Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
|
||||
UnicodeString patternFormCopy = UnicodeString(patternForm);
|
||||
int32_t patPos, patLen = patternFormCopy.length();
|
||||
UBool inQuoted = FALSE;
|
||||
for (patPos = 0; patPos < patLen; patPos++) {
|
||||
UChar patChr = patternFormCopy.charAt(patPos);
|
||||
if (patChr == SINGLE_QUOTE) {
|
||||
inQuoted = !inQuoted;
|
||||
} else if (!inQuoted) {
|
||||
if (patChr == LOW_J) {
|
||||
patternFormCopy.setCharAt(patPos, fDefaultHourFormatChar);
|
||||
} else if (patChr == CAP_C) {
|
||||
AllowedHourFormat preferred;
|
||||
if (fAllowedHourFormats[0] != ALLOWED_HOUR_FORMAT_UNKNOWN) {
|
||||
preferred = (AllowedHourFormat)fAllowedHourFormats[0];
|
||||
} else {
|
||||
status = U_INVALID_FORMAT_ERROR;
|
||||
return UnicodeString();
|
||||
}
|
||||
|
||||
if (preferred == ALLOWED_HOUR_FORMAT_H || preferred == ALLOWED_HOUR_FORMAT_HB || preferred == ALLOWED_HOUR_FORMAT_Hb) {
|
||||
patternFormCopy.setCharAt(patPos, CAP_H);
|
||||
} else {
|
||||
patternFormCopy.setCharAt(patPos, LOW_H);
|
||||
}
|
||||
|
||||
// in #13183 just add to skeleton, no longer need to set special flags
|
||||
if (preferred == ALLOWED_HOUR_FORMAT_HB || preferred == ALLOWED_HOUR_FORMAT_hB) {
|
||||
patternFormCopy.append('B');
|
||||
} else if (preferred == ALLOWED_HOUR_FORMAT_Hb || preferred == ALLOWED_HOUR_FORMAT_hb) {
|
||||
patternFormCopy.append('b');
|
||||
}
|
||||
} else if (patChr == CAP_J) {
|
||||
// Get pattern for skeleton with H, then replace H or k
|
||||
// with fDefaultHourFormatChar (if different)
|
||||
patternFormCopy.setCharAt(patPos, CAP_H);
|
||||
flags |= kDTPGSkeletonUsesCapJ;
|
||||
}
|
||||
}
|
||||
UnicodeString patternFormMapped = mapSkeletonMetacharacters(patternForm, &flags, status);
|
||||
if (U_FAILURE(status)) {
|
||||
return UnicodeString();
|
||||
}
|
||||
|
||||
resultPattern.remove();
|
||||
dtMatcher->set(patternFormCopy, fp);
|
||||
dtMatcher->set(patternFormMapped, fp);
|
||||
const PtnSkeleton* specifiedSkeleton=NULL;
|
||||
bestPattern=getBestRaw(*dtMatcher, -1, distanceInfo, &specifiedSkeleton);
|
||||
if ( distanceInfo->missingFieldMask==0 && distanceInfo->extraFieldMask==0 ) {
|
||||
@ -1077,6 +1042,82 @@ DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDate
|
||||
return resultPattern;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map a skeleton that may have metacharacters jJC to one without, by replacing
|
||||
* the metacharacters with locale-appropriate fields of of h/H/k/K and of a/b/B
|
||||
* (depends on fDefaultHourFormatChar and fAllowedHourFormats being set, which in
|
||||
* turn depends on initData having been run). This method also updates the flags
|
||||
* as necessary. Returns the updated skeleton.
|
||||
*/
|
||||
UnicodeString
|
||||
DateTimePatternGenerator::mapSkeletonMetacharacters(const UnicodeString& patternForm, int32_t* flags, UErrorCode& status) {
|
||||
UnicodeString patternFormMapped;
|
||||
patternFormMapped.remove();
|
||||
UBool inQuoted = FALSE;
|
||||
int32_t patPos, patLen = patternForm.length();
|
||||
for (patPos = 0; patPos < patLen; patPos++) {
|
||||
UChar patChr = patternForm.charAt(patPos);
|
||||
if (patChr == SINGLE_QUOTE) {
|
||||
inQuoted = !inQuoted;
|
||||
} else if (!inQuoted) {
|
||||
// Handle special mappings for 'j' and 'C' in which fields lengths
|
||||
// 1,3,5 => hour field length 1
|
||||
// 2,4,6 => hour field length 2
|
||||
// 1,2 => abbreviated dayPeriod (field length 1..3)
|
||||
// 3,4 => long dayPeriod (field length 4)
|
||||
// 5,6 => narrow dayPeriod (field length 5)
|
||||
if (patChr == LOW_J || patChr == CAP_C) {
|
||||
int32_t extraLen = 0; // 1 less than total field length
|
||||
while (patPos+1 < patLen && patternForm.charAt(patPos+1)==patChr) {
|
||||
extraLen++;
|
||||
patPos++;
|
||||
}
|
||||
int32_t hourLen = 1 + (extraLen & 1);
|
||||
int32_t dayPeriodLen = (extraLen < 2)? 1: 3 + (extraLen >> 1);
|
||||
UChar hourChar = LOW_H;
|
||||
UChar dayPeriodChar = LOW_A;
|
||||
if (patChr == LOW_J) {
|
||||
hourChar = fDefaultHourFormatChar;
|
||||
} else {
|
||||
AllowedHourFormat preferred;
|
||||
if (fAllowedHourFormats[0] != ALLOWED_HOUR_FORMAT_UNKNOWN) {
|
||||
preferred = (AllowedHourFormat)fAllowedHourFormats[0];
|
||||
} else {
|
||||
status = U_INVALID_FORMAT_ERROR;
|
||||
return UnicodeString();
|
||||
}
|
||||
if (preferred == ALLOWED_HOUR_FORMAT_H || preferred == ALLOWED_HOUR_FORMAT_HB || preferred == ALLOWED_HOUR_FORMAT_Hb) {
|
||||
hourChar = CAP_H;
|
||||
}
|
||||
// in #13183 just add b/B to skeleton, no longer need to set special flags
|
||||
if (preferred == ALLOWED_HOUR_FORMAT_HB || preferred == ALLOWED_HOUR_FORMAT_hB) {
|
||||
dayPeriodChar = CAP_B;
|
||||
} else if (preferred == ALLOWED_HOUR_FORMAT_Hb || preferred == ALLOWED_HOUR_FORMAT_hb) {
|
||||
dayPeriodChar = LOW_B;
|
||||
}
|
||||
}
|
||||
if (hourChar==CAP_H || hourChar==LOW_K) {
|
||||
dayPeriodLen = 0;
|
||||
}
|
||||
while (dayPeriodLen-- > 0) {
|
||||
patternFormMapped.append(dayPeriodChar);
|
||||
}
|
||||
while (hourLen-- > 0) {
|
||||
patternFormMapped.append(hourChar);
|
||||
}
|
||||
} else if (patChr == CAP_J) {
|
||||
// Get pattern for skeleton with H, then replace H or k
|
||||
// with fDefaultHourFormatChar (if different)
|
||||
patternFormMapped.append(CAP_H);
|
||||
*flags |= kDTPGSkeletonUsesCapJ;
|
||||
} else {
|
||||
patternFormMapped.append(patChr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return patternFormMapped;
|
||||
}
|
||||
|
||||
UnicodeString
|
||||
DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern,
|
||||
const UnicodeString& skeleton,
|
||||
|
@ -545,6 +545,7 @@ private:
|
||||
UDateTimePatternField getAppendNameNumber(const char* field) const;
|
||||
UnicodeString& getMutableAppendItemName(UDateTimePatternField field);
|
||||
void getAppendName(UDateTimePatternField field, UnicodeString& value);
|
||||
UnicodeString mapSkeletonMetacharacters(const UnicodeString& patternForm, int32_t* flags, UErrorCode& status);
|
||||
int32_t getCanonicalIndex(const UnicodeString& field);
|
||||
const UnicodeString* getBestRaw(DateTimeMatcher& source, int32_t includeMask, DistanceInfo* missingFields, const PtnSkeleton** specifiedSkeletonPtr = 0);
|
||||
UnicodeString adjustFieldTypes(const UnicodeString& pattern, const PtnSkeleton* specifiedSkeleton, int32_t flags, UDateTimePatternMatchOptions options = UDATPG_MATCH_NO_OPTIONS);
|
||||
|
@ -1090,13 +1090,18 @@ void IntlTestDateTimePatternGeneratorAPI::testStaticGetSkeleton(/*char *par*/)
|
||||
|
||||
void IntlTestDateTimePatternGeneratorAPI::testC() {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
const int32_t numLocales = 6;
|
||||
const int32_t numLocales = 11;
|
||||
|
||||
const char* tests[numLocales][3] = {
|
||||
// These may change with actual data for Bhmm/bhmm skeletons
|
||||
{"zh", "Cm", "h:mm B"},
|
||||
{"zh", "CCCm", "h:mm BBBB"},
|
||||
{"zh", "CCCCCm", "h:mm BBBBB"},
|
||||
{"zh", "Cm", "h:mm B"},
|
||||
{"de", "Cm", "HH:mm"},
|
||||
{"en", "Cm", "h:mm a"},
|
||||
{"en", "CCCm", "h:mm aaaa"},
|
||||
{"en", "CCCCCm", "h:mm aaaaa"},
|
||||
{"en-BN", "Cm", "h:mm b"},
|
||||
{"gu-IN", "Cm", "h:mm B"},
|
||||
{"und-IN", "Cm", "h:mm a"},
|
||||
@ -1135,17 +1140,18 @@ void IntlTestDateTimePatternGeneratorAPI::testSkeletonsWithDayPeriods() {
|
||||
};
|
||||
const char* testItems[][2] = {
|
||||
// sample requested skeletons and results
|
||||
// skel pattern
|
||||
{ "H", "H"},
|
||||
{ "aH", "H"},
|
||||
{ "BH", "H"},
|
||||
{ "h", "h a"},
|
||||
{ "ah", "h a"},
|
||||
{ "bh", "h b"},
|
||||
{ "Bh", "B h"},
|
||||
{ "a", "a"},
|
||||
{ "b", "b"},
|
||||
{ "B", "B"}
|
||||
// skel pattern
|
||||
{ "H", "H"},
|
||||
{ "aH", "H"},
|
||||
{ "BH", "H"},
|
||||
{ "h", "h a"},
|
||||
{ "ah", "h a"},
|
||||
{ "bh", "h b"},
|
||||
{ "Bh", "B h"},
|
||||
{ "BBBBh", "BBBB h"},
|
||||
{ "a", "a"},
|
||||
{ "b", "b"},
|
||||
{ "B", "B"}
|
||||
};
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
DateTimePatternGenerator *gen = DateTimePatternGenerator::createEmptyInstance(status);
|
||||
|
@ -549,35 +549,10 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
|
||||
private String getBestPattern(String skeleton, DateTimeMatcher skipMatcher, int options) {
|
||||
EnumSet<DTPGflags> flags = EnumSet.noneOf(DTPGflags.class);
|
||||
// Replace hour metacharacters 'j', 'C', and 'J', set flags as necessary
|
||||
StringBuilder skeletonCopy = new StringBuilder(skeleton);
|
||||
boolean inQuoted = false;
|
||||
for (int patPos = 0; patPos < skeleton.length(); patPos++) {
|
||||
char patChr = skeleton.charAt(patPos);
|
||||
if (patChr == '\'') {
|
||||
inQuoted = !inQuoted;
|
||||
} else if (!inQuoted) {
|
||||
if (patChr == 'j') {
|
||||
skeletonCopy.setCharAt(patPos, defaultHourFormatChar);
|
||||
} else if (patChr == 'C') {
|
||||
String preferred = allowedHourFormats[0];
|
||||
skeletonCopy.setCharAt(patPos, preferred.charAt(0));
|
||||
// add to skeleton with #13183, no longer need to set special flags
|
||||
char last = preferred.charAt(preferred.length()-1);
|
||||
if (last=='b' || last=='B') {
|
||||
skeletonCopy.append(last);
|
||||
}
|
||||
} else if (patChr == 'J') {
|
||||
// Get pattern for skeleton with H, then (in adjustFieldTypes)
|
||||
// replace H or k with defaultHourFormatChar
|
||||
skeletonCopy.setCharAt(patPos, 'H');
|
||||
flags.add(DTPGflags.SKELETON_USES_CAP_J);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String skeletonMapped = mapSkeletonMetacharacters(skeleton, flags);
|
||||
String datePattern, timePattern;
|
||||
synchronized(this) {
|
||||
current.set(skeletonCopy.toString(), fp, false);
|
||||
current.set(skeletonMapped, fp, false);
|
||||
PatternWithMatcher bestWithMatcher = getBestRaw(current, -1, _distanceInfo, skipMatcher);
|
||||
if (_distanceInfo.missingFieldMask == 0 && _distanceInfo.extraFieldMask == 0) {
|
||||
// we have a good item. Adjust the field types
|
||||
@ -596,6 +571,70 @@ public class DateTimePatternGenerator implements Freezable<DateTimePatternGenera
|
||||
getDateTimeFormat(), 2, 2, timePattern, datePattern);
|
||||
}
|
||||
|
||||
/*
|
||||
* Map a skeleton that may have metacharacters jJC to one without, by replacing
|
||||
* the metacharacters with locale-appropriate fields of of h/H/k/K and of a/b/B
|
||||
* (depends on defaultHourFormatChar and allowedHourFormats being set, which in
|
||||
* turn depends on initData having been run). This method also updates the flags
|
||||
* as necessary. Returns the updated skeleton.
|
||||
*/
|
||||
private String mapSkeletonMetacharacters(String skeleton, EnumSet<DTPGflags> flags) {
|
||||
StringBuilder skeletonCopy = new StringBuilder();
|
||||
boolean inQuoted = false;
|
||||
for (int patPos = 0; patPos < skeleton.length(); patPos++) {
|
||||
char patChr = skeleton.charAt(patPos);
|
||||
if (patChr == '\'') {
|
||||
inQuoted = !inQuoted;
|
||||
} else if (!inQuoted) {
|
||||
// Handle special mappings for 'j' and 'C' in which fields lengths
|
||||
// 1,3,5 => hour field length 1
|
||||
// 2,4,6 => hour field length 2
|
||||
// 1,2 => abbreviated dayPeriod (field length 1..3)
|
||||
// 3,4 => long dayPeriod (field length 4)
|
||||
// 5,6 => narrow dayPeriod (field length 5)
|
||||
if (patChr == 'j' || patChr == 'C') {
|
||||
int extraLen = 0; // 1 less than total field length
|
||||
while (patPos+1 < skeleton.length() && skeleton.charAt(patPos+1) == patChr) {
|
||||
extraLen++;
|
||||
patPos++;
|
||||
}
|
||||
int hourLen = 1 + (extraLen & 1);
|
||||
int dayPeriodLen = (extraLen < 2)? 1: 3 + (extraLen >> 1);
|
||||
char hourChar = 'h';
|
||||
char dayPeriodChar = 'a';
|
||||
if (patChr == 'j') {
|
||||
hourChar = defaultHourFormatChar;
|
||||
} else { // patChr == 'C'
|
||||
String preferred = allowedHourFormats[0];
|
||||
hourChar = preferred.charAt(0);
|
||||
// in #13183 just add b/B to skeleton, no longer need to set special flags
|
||||
char last = preferred.charAt(preferred.length()-1);
|
||||
if (last=='b' || last=='B') {
|
||||
dayPeriodChar = last;
|
||||
}
|
||||
}
|
||||
if (hourChar=='H' || hourChar=='k') {
|
||||
dayPeriodLen = 0;
|
||||
}
|
||||
while (dayPeriodLen-- > 0) {
|
||||
skeletonCopy.append(dayPeriodChar);
|
||||
}
|
||||
while (hourLen-- > 0) {
|
||||
skeletonCopy.append(hourChar);
|
||||
}
|
||||
} else if (patChr == 'J') {
|
||||
// Get pattern for skeleton with H, then (in adjustFieldTypes)
|
||||
// replace H or k with defaultHourFormatChar
|
||||
skeletonCopy.append('H');
|
||||
flags.add(DTPGflags.SKELETON_USES_CAP_J);
|
||||
} else {
|
||||
skeletonCopy.append(patChr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return skeletonCopy.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* PatternInfo supplies output parameters for addPattern(...). It is used because
|
||||
* Java doesn't have real output parameters. It is treated like a struct (eg
|
||||
|
@ -57,8 +57,12 @@ public class DateTimeGeneratorTest extends TestFmwk {
|
||||
String[][] tests = {
|
||||
// These may change with actual data for Bhmm/bhmm skeletons
|
||||
{"zh", "Cm", "h:mm B"},
|
||||
{"zh", "CCCm", "h:mm BBBB"},
|
||||
{"zh", "CCCCCm", "h:mm BBBBB"},
|
||||
{"de", "Cm", "HH:mm"},
|
||||
{"en", "Cm", "h:mm a"},
|
||||
{"en", "CCCm", "h:mm aaaa"},
|
||||
{"en", "CCCCCm", "h:mm aaaaa"},
|
||||
{"en-BN", "Cm", "h:mm b"},
|
||||
{"gu-IN", "Cm", "h:mm B"},
|
||||
{"und-IN", "Cm", "h:mm a"},
|
||||
@ -82,17 +86,18 @@ public class DateTimeGeneratorTest extends TestFmwk {
|
||||
};
|
||||
String[][] testItems = {
|
||||
// sample requested skeletons and results
|
||||
// skel pattern
|
||||
{ "H", "H"},
|
||||
{ "aH", "H"},
|
||||
{ "BH", "H"},
|
||||
{ "h", "h a"},
|
||||
{ "ah", "h a"},
|
||||
{ "bh", "h b"},
|
||||
{ "Bh", "B h"},
|
||||
{ "a", "a"},
|
||||
{ "b", "b"},
|
||||
{ "B", "B"},
|
||||
// skel pattern
|
||||
{ "H", "H"},
|
||||
{ "aH", "H"},
|
||||
{ "BH", "H"},
|
||||
{ "h", "h a"},
|
||||
{ "ah", "h a"},
|
||||
{ "bh", "h b"},
|
||||
{ "Bh", "B h"},
|
||||
{ "BBBBh", "BBBB h"},
|
||||
{ "a", "a"},
|
||||
{ "b", "b"},
|
||||
{ "B", "B"},
|
||||
};
|
||||
DateTimePatternGenerator gen = DateTimePatternGenerator.getEmptyInstance();
|
||||
DateTimePatternGenerator.PatternInfo returnInfo = new DateTimePatternGenerator.PatternInfo();
|
||||
|
Loading…
Reference in New Issue
Block a user