ICU-13183 on branch - Handle different field lengths for skeleton metachars j,C

X-SVN-Rev: 40126
This commit is contained in:
Peter Edberg 2017-05-22 22:24:39 +00:00
parent 72c605c1c4
commit 56e884e22e
5 changed files with 181 additions and 89 deletions

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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();