+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
*
-* Copyright (C) 2013-2014, International Business Machines
+* Copyright (C) 2013-2016, International Business Machines
* Corporation and others. All Rights Reserved.
*
*******************************************************************************
* file name: listformatter.cpp
-* encoding: US-ASCII
+* encoding: UTF-8
* tab size: 8 (not used)
* indentation:4
*
*/
#include "unicode/listformatter.h"
-#include "simplepatternformatter.h"
+#include "unicode/simpleformatter.h"
#include "mutex.h"
#include "hash.h"
#include "cstring.h"
#include "charstr.h"
#include "ucln_cmn.h"
#include "uresimp.h"
-
-#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
+#include "resource.h"
U_NAMESPACE_BEGIN
struct ListFormatInternal : public UMemory {
- SimplePatternFormatter twoPattern;
- SimplePatternFormatter startPattern;
- SimplePatternFormatter middlePattern;
- SimplePatternFormatter endPattern;
+ SimpleFormatter twoPattern;
+ SimpleFormatter startPattern;
+ SimpleFormatter middlePattern;
+ SimpleFormatter endPattern;
ListFormatInternal(
const UnicodeString& two,
const UnicodeString& start,
const UnicodeString& middle,
- const UnicodeString& end) :
- twoPattern(two),
- startPattern(start),
- middlePattern(middle),
- endPattern(end) {}
-
-ListFormatInternal(const ListFormatData &data) :
- twoPattern(data.twoPattern),
- startPattern(data.startPattern),
- middlePattern(data.middlePattern),
- endPattern(data.endPattern) { }
+ const UnicodeString& end,
+ UErrorCode &errorCode) :
+ twoPattern(two, 2, 2, errorCode),
+ startPattern(start, 2, 2, errorCode),
+ middlePattern(middle, 2, 2, errorCode),
+ endPattern(end, 2, 2, errorCode) {}
+
+ListFormatInternal(const ListFormatData &data, UErrorCode &errorCode) :
+ twoPattern(data.twoPattern, errorCode),
+ startPattern(data.startPattern, errorCode),
+ middlePattern(data.middlePattern, errorCode),
+ endPattern(data.endPattern, errorCode) { }
ListFormatInternal(const ListFormatInternal &other) :
twoPattern(other.twoPattern),
U_CDECL_END
-static ListFormatInternal* loadListFormatInternal(
- const Locale& locale,
- const char* style,
- UErrorCode& errorCode);
-
-static void getStringByKey(
- const UResourceBundle* rb,
- const char* key,
- UnicodeString& result,
- UErrorCode& errorCode);
-
ListFormatter::ListFormatter(const ListFormatter& other) :
owned(other.owned), data(other.data) {
if (other.owned != NULL) {
return result;
}
-static ListFormatInternal* loadListFormatInternal(
+static const UChar solidus = 0x2F;
+static const UChar aliasPrefix[] = { 0x6C,0x69,0x73,0x74,0x50,0x61,0x74,0x74,0x65,0x72,0x6E,0x2F }; // "listPattern/"
+enum {
+ kAliasPrefixLen = UPRV_LENGTHOF(aliasPrefix),
+ kStyleLenMax = 24 // longest currently is 14
+};
+
+struct ListFormatter::ListPatternsSink : public ResourceSink {
+ UnicodeString two, start, middle, end;
+#if ((U_PLATFORM == U_PF_AIX) || (U_PLATFORM == U_PF_OS390)) && (U_CPLUSPLUS_VERSION < 11)
+ char aliasedStyle[kStyleLenMax+1];
+ ListPatternsSink() {
+ uprv_memset(aliasedStyle, 0, kStyleLenMax+1);
+ }
+#else
+ char aliasedStyle[kStyleLenMax+1] = {0};
+
+ ListPatternsSink() {}
+#endif
+ virtual ~ListPatternsSink();
+
+ void setAliasedStyle(UnicodeString alias) {
+ int32_t startIndex = alias.indexOf(aliasPrefix, kAliasPrefixLen, 0);
+ if (startIndex < 0) {
+ return;
+ }
+ startIndex += kAliasPrefixLen;
+ int32_t endIndex = alias.indexOf(solidus, startIndex);
+ if (endIndex < 0) {
+ endIndex = alias.length();
+ }
+ alias.extract(startIndex, endIndex-startIndex, aliasedStyle, kStyleLenMax+1, US_INV);
+ aliasedStyle[kStyleLenMax] = 0;
+ }
+
+ void handleValueForPattern(ResourceValue &value, UnicodeString &pattern, UErrorCode &errorCode) {
+ if (pattern.isEmpty()) {
+ if (value.getType() == URES_ALIAS) {
+ if (aliasedStyle[0] == 0) {
+ setAliasedStyle(value.getAliasUnicodeString(errorCode));
+ }
+ } else {
+ pattern = value.getUnicodeString(errorCode);
+ }
+ }
+ }
+
+ virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
+ UErrorCode &errorCode) {
+ aliasedStyle[0] = 0;
+ if (value.getType() == URES_ALIAS) {
+ setAliasedStyle(value.getAliasUnicodeString(errorCode));
+ return;
+ }
+ ResourceTable listPatterns = value.getTable(errorCode);
+ for (int i = 0; U_SUCCESS(errorCode) && listPatterns.getKeyAndValue(i, key, value); ++i) {
+ if (uprv_strcmp(key, "2") == 0) {
+ handleValueForPattern(value, two, errorCode);
+ } else if (uprv_strcmp(key, "end") == 0) {
+ handleValueForPattern(value, end, errorCode);
+ } else if (uprv_strcmp(key, "middle") == 0) {
+ handleValueForPattern(value, middle, errorCode);
+ } else if (uprv_strcmp(key, "start") == 0) {
+ handleValueForPattern(value, start, errorCode);
+ }
+ }
+ }
+};
+
+// Virtual destructors must be defined out of line.
+ListFormatter::ListPatternsSink::~ListPatternsSink() {}
+
+ListFormatInternal* ListFormatter::loadListFormatInternal(
const Locale& locale, const char * style, UErrorCode& errorCode) {
UResourceBundle* rb = ures_open(NULL, locale.getName(), &errorCode);
+ rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
if (U_FAILURE(errorCode)) {
ures_close(rb);
return NULL;
}
- rb = ures_getByKeyWithFallback(rb, "listPattern", rb, &errorCode);
- rb = ures_getByKeyWithFallback(rb, style, rb, &errorCode);
-
- // TODO(Travis Keep): This is a hack until fallbacks can be added for
- // listPattern/duration and listPattern/duration-narrow in CLDR.
- if (errorCode == U_MISSING_RESOURCE_ERROR) {
- errorCode = U_ZERO_ERROR;
- rb = ures_getByKeyWithFallback(rb, "standard", rb, &errorCode);
+ ListFormatter::ListPatternsSink sink;
+ char currentStyle[kStyleLenMax+1];
+ uprv_strncpy(currentStyle, style, kStyleLenMax);
+ currentStyle[kStyleLenMax] = 0;
+
+ for (;;) {
+ ures_getAllItemsWithFallback(rb, currentStyle, sink, errorCode);
+ if (U_FAILURE(errorCode) || sink.aliasedStyle[0] == 0 || uprv_strcmp(currentStyle, sink.aliasedStyle) == 0) {
+ break;
+ }
+ uprv_strcpy(currentStyle, sink.aliasedStyle);
}
+ ures_close(rb);
if (U_FAILURE(errorCode)) {
- ures_close(rb);
return NULL;
}
- UnicodeString two, start, middle, end;
- getStringByKey(rb, "2", two, errorCode);
- getStringByKey(rb, "start", start, errorCode);
- getStringByKey(rb, "middle", middle, errorCode);
- getStringByKey(rb, "end", end, errorCode);
- ures_close(rb);
- if (U_FAILURE(errorCode)) {
+ if (sink.two.isEmpty() || sink.start.isEmpty() || sink.middle.isEmpty() || sink.end.isEmpty()) {
+ errorCode = U_MISSING_RESOURCE_ERROR;
return NULL;
}
- ListFormatInternal* result = new ListFormatInternal(two, start, middle, end);
+ ListFormatInternal* result = new ListFormatInternal(sink.two, sink.start, sink.middle, sink.end, errorCode);
if (result == NULL) {
errorCode = U_MEMORY_ALLOCATION_ERROR;
return NULL;
}
- return result;
-}
-
-static void getStringByKey(const UResourceBundle* rb, const char* key, UnicodeString& result, UErrorCode& errorCode) {
- int32_t len;
- const UChar* ustr = ures_getStringByKeyWithFallback(rb, key, &len, &errorCode);
if (U_FAILURE(errorCode)) {
- return;
+ delete result;
+ return NULL;
}
- result.setTo(ustr, len);
+ return result;
}
ListFormatter* ListFormatter::createInstance(UErrorCode& errorCode) {
return p;
}
-ListFormatter::ListFormatter(const ListFormatData& listFormatData) {
- owned = new ListFormatInternal(listFormatData);
+ListFormatter::ListFormatter(const ListFormatData& listFormatData, UErrorCode &errorCode) {
+ owned = new ListFormatInternal(listFormatData, errorCode);
data = owned;
}
* On entry offset is an offset into first or -1 if offset unspecified.
* On exit offset is offset of second in result if recordOffset was set
* Otherwise if it was >=0 it is set to point into result where it used
- * to point into first.
+ * to point into first. On exit, result is the join of first and second
+ * according to pat. Any previous value of result gets replaced.
*/
-static void joinStrings(
- const SimplePatternFormatter& pat,
+static void joinStringsAndReplace(
+ const SimpleFormatter& pat,
const UnicodeString& first,
const UnicodeString& second,
UnicodeString &result,
}
const UnicodeString *params[2] = {&first, &second};
int32_t offsets[2];
- pat.format(
+ pat.formatAndReplace(
params,
- LENGTHOF(params),
+ UPRV_LENGTHOF(params),
result,
offsets,
- LENGTHOF(offsets),
+ UPRV_LENGTHOF(offsets),
errorCode);
if (U_FAILURE(errorCode)) {
return;
appendTo.append(items[0]);
return appendTo;
}
- if (nItems == 2) {
- if (index == 0) {
- offset = 0;
- }
- joinStrings(
- data->twoPattern,
- items[0],
- items[1],
- appendTo,
- index == 1,
- offset,
- errorCode);
- return appendTo;
- }
- UnicodeString temp[2];
+ UnicodeString result(items[0]);
if (index == 0) {
offset = 0;
}
- joinStrings(
- data->startPattern,
- items[0],
+ joinStringsAndReplace(
+ nItems == 2 ? data->twoPattern : data->startPattern,
+ result,
items[1],
- temp[0],
+ result,
index == 1,
offset,
errorCode);
- int32_t i;
- int32_t pos = 0;
- int32_t npos = 1;
- for (i = 2; i < nItems - 1; ++i) {
- temp[npos].remove();
- joinStrings(
- data->middlePattern,
- temp[pos],
- items[i],
- temp[npos],
- index == i,
- offset,
- errorCode);
- pos = npos;
- npos = (pos + 1) & 1;
- }
- temp[npos].remove();
- joinStrings(
- data->endPattern,
- temp[pos],
- items[nItems - 1],
- temp[npos],
- index == nItems - 1,
- offset,
- errorCode);
+ if (nItems > 2) {
+ for (int32_t i = 2; i < nItems - 1; ++i) {
+ joinStringsAndReplace(
+ data->middlePattern,
+ result,
+ items[i],
+ result,
+ index == i,
+ offset,
+ errorCode);
+ }
+ joinStringsAndReplace(
+ data->endPattern,
+ result,
+ items[nItems - 1],
+ result,
+ index == nItems - 1,
+ offset,
+ errorCode);
+ }
if (U_SUCCESS(errorCode)) {
if (offset >= 0) {
offset += appendTo.length();
}
- appendTo += temp[npos];
+ appendTo += result;
}
return appendTo;
}