]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/listformatter.cpp
ICU-59173.0.1.tar.gz
[apple/icu.git] / icuSources / common / listformatter.cpp
index 17bc0666d8bd6a030742b35e6ab731a8e3947d5d..d105654755fd1e5a96fa7c157270baeb71ec7402 100644 (file)
@@ -1,12 +1,14 @@
+// © 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
 *
@@ -15,7 +17,7 @@
 */
 
 #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),
@@ -77,17 +79,6 @@ uprv_deleteListFormatInternal(void *obj) {
 
 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) {
@@ -170,50 +161,116 @@ const ListFormatInternal* ListFormatter::getListFormatInternal(
     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) {
@@ -239,8 +296,8 @@ ListFormatter* ListFormatter::createInstance(const Locale& locale, const char *s
     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;
 }
 
@@ -256,10 +313,11 @@ ListFormatter::~ListFormatter() {
  * 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,
@@ -271,12 +329,12 @@ static void joinStrings(
     }
     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;
@@ -327,62 +385,43 @@ UnicodeString& ListFormatter::format(
         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;
 }