--- /dev/null
+/*
+ *******************************************************************************
+ * Copyright (C) 2015, International Business Machines Corporation and
+ * others. All Rights Reserved.
+ *******************************************************************************
+ */
+package com.ibm.icu.impl;
+
+import java.nio.ByteBuffer;
+
+import com.ibm.icu.util.UResourceBundle;
+import com.ibm.icu.util.UResourceTypeMismatchException;
+
+/**
+ * ICU resource bundle key and value types.
+ */
+public final class ICUResource {
+ /**
+ * Represents a resource bundle item's key string.
+ * Avoids object creations as much as possible.
+ * Mutable, not thread-safe.
+ * For permanent storage, use clone() or toString().
+ */
+ public static final class Key implements CharSequence, Cloneable, Comparable<Key> {
+ // Stores a reference to the resource bundle key string bytes array,
+ // with an offset of the key, to avoid creating a String object
+ // until one is really needed.
+ // Alternatively, we could try to always just get the key String object,
+ // and cache it in the reader, and see if that performs better or worse.
+ private byte[] bytes;
+ private int offset;
+ private int length;
+ private String s;
+
+ /**
+ * Constructs an empty resource key string object.
+ */
+ public Key() {}
+
+ private Key(byte[] keyBytes, int keyOffset, int keyLength) {
+ bytes = keyBytes;
+ offset = keyOffset;
+ length = keyLength;
+ }
+
+ /**
+ * Mutates this key for a new NUL-terminated resource key string.
+ * The corresponding ASCII-character bytes are not copied and
+ * must not be changed during the lifetime of this key
+ * (or until the next setBytes() call)
+ * and lifetimes of subSequences created from this key.
+ *
+ * @param keyBytes new key string byte array
+ * @param keyOffset new key string offset
+ */
+ public void setBytes(byte[] keyBytes, int keyOffset) {
+ bytes = keyBytes;
+ offset = keyOffset;
+ for (length = 0; keyBytes[keyOffset + length] != 0; ++length) {}
+ s = null;
+ }
+
+ /**
+ * Mutates this key to an empty resource key string.
+ */
+ public void setToEmpty() {
+ bytes = null;
+ offset = length = 0;
+ s = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ * Does not clone the byte array.
+ */
+ @Override
+ public Key clone() {
+ try {
+ return (Key)super.clone();
+ } catch (CloneNotSupportedException cannotOccur) {
+ return null;
+ }
+ }
+
+ // TODO: Java 6: @Override
+ public char charAt(int i) {
+ assert(0 <= i && i < length);
+ return (char)bytes[offset + i];
+ }
+
+ // TODO: Java 6: @Override
+ public int length() {
+ return length;
+ }
+
+ // TODO: Java 6: @Override
+ public Key subSequence(int start, int end) {
+ assert(0 <= start && start < length);
+ assert(start <= end && end <= length);
+ return new Key(bytes, offset + start, end - start);
+ }
+
+ /**
+ * Creates/caches/returns this resource key string as a Java String.
+ */
+ public String toString() {
+ if (s == null) {
+ s = internalSubString(0, length);
+ }
+ return s;
+ }
+
+ private String internalSubString(int start, int end) {
+ StringBuilder sb = new StringBuilder(end - start);
+ for (int i = start; i < end; ++i) {
+ sb.append((char)bytes[offset + i]);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Creates a new Java String for a sub-sequence of this resource key string.
+ */
+ public String substring(int start) {
+ assert(0 <= start && start < length);
+ return internalSubString(start, length);
+ }
+
+ /**
+ * Creates a new Java String for a sub-sequence of this resource key string.
+ */
+ public String substring(int start, int end) {
+ assert(0 <= start && start < length);
+ assert(start <= end && end <= length);
+ return internalSubString(start, end);
+ }
+
+ private boolean regionMatches(byte[] otherBytes, int otherOffset, int n) {
+ for (int i = 0; i < n; ++i) {
+ if (bytes[offset + i] != otherBytes[otherOffset + i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean regionMatches(int start, CharSequence cs, int n) {
+ for (int i = 0; i < n; ++i) {
+ if (bytes[offset + start + i] != cs.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ } else if (this == other) {
+ return true;
+ } else if (other instanceof Key) {
+ Key otherKey = (Key)other;
+ return length == otherKey.length &&
+ regionMatches(otherKey.bytes, otherKey.offset, length);
+ } else {
+ return false;
+ }
+ }
+
+ public boolean contentEquals(CharSequence cs) {
+ if (cs == null) {
+ return false;
+ }
+ return this == cs || (cs.length() == length && regionMatches(0, cs, length));
+ }
+
+ public boolean startsWith(CharSequence cs) {
+ int csLength = cs.length();
+ return csLength <= length && regionMatches(0, cs, csLength);
+ }
+
+ public boolean endsWith(CharSequence cs) {
+ int csLength = cs.length();
+ return csLength <= length && regionMatches(length - csLength, cs, csLength);
+ }
+
+ @Override
+ public int hashCode() {
+ // Never return s.hashCode(), so that
+ // Key.hashCode() is the same whether we have cached s or not.
+ if (length == 0) {
+ return 0;
+ }
+
+ int h = bytes[offset];
+ for (int i = 1; i < length; ++i) {
+ h = 37 * h + bytes[offset];
+ }
+ return h;
+ }
+
+ // TODO: Java 6: @Override
+ public int compareTo(Key other) {
+ return compareTo((CharSequence)other);
+ }
+
+ public int compareTo(CharSequence cs) {
+ int csLength = cs.length();
+ int minLength = length <= csLength ? length : csLength;
+ for (int i = 0; i < minLength; ++i) {
+ int diff = (int)charAt(i) - (int)cs.charAt(i);
+ if (diff != 0) {
+ return diff;
+ }
+ }
+ return length - csLength;
+ }
+ }
+
+ /**
+ * Represents a resource bundle item's value.
+ * Avoids object creations as much as possible.
+ * Mutable, not thread-safe.
+ */
+ public static abstract class Value {
+ protected Value() {}
+
+ /**
+ * @return ICU resource type like {@link UResourceBundle#getType()},
+ * for example, {@link UResourceBundle#STRING}
+ */
+ public abstract int getType();
+
+ /**
+ * @see UResourceBundle#getString()
+ * @throws UResourceTypeMismatchException if this is not a string resource
+ */
+ public abstract String getString();
+
+ /**
+ * @see UResourceBundle#getInt()
+ * @throws UResourceTypeMismatchException if this is not an integer resource
+ */
+ public abstract int getInt();
+
+ /**
+ * @see UResourceBundle#getUInt()
+ * @throws UResourceTypeMismatchException if this is not an integer resource
+ */
+ public abstract int getUInt();
+
+ /**
+ * @see UResourceBundle#getIntVector()
+ * @throws UResourceTypeMismatchException if this is not an intvector resource
+ */
+ public abstract int[] getIntVector();
+
+ /**
+ * @see UResourceBundle#getBinary()
+ * @throws UResourceTypeMismatchException if this is not a binary-blob resource
+ */
+ public abstract ByteBuffer getBinary();
+
+ /**
+ * Only for debugging.
+ */
+ @Override
+ public String toString() {
+ switch(getType()) {
+ case UResourceBundle.STRING:
+ return getString();
+ case UResourceBundle.INT:
+ return Integer.toString(getInt());
+ case UResourceBundle.INT_VECTOR:
+ int[] iv = getIntVector();
+ StringBuilder sb = new StringBuilder("[");
+ sb.append(iv.length).append("]{");
+ if (iv.length != 0) {
+ sb.append(iv[0]);
+ for (int i = 1; i < iv.length; ++i) {
+ sb.append(", ").append(iv[i]);
+ }
+ }
+ return sb.append('}').toString();
+ case UResourceBundle.BINARY:
+ return "(binary blob)";
+ case UResourceBundle.ARRAY: // should not occur
+ return "(array)";
+ case UResourceBundle.TABLE: // should not occur
+ return "(table)";
+ default: // should not occur
+ return "???";
+ }
+ }
+ }
+
+ /**
+ * Sink for ICU resource array contents.
+ * The base class does nothing.
+ *
+ * <p>Nested arrays and tables are stored as nested sinks,
+ * never put() as {@link Value} items.
+ */
+ public static class ArraySink {
+ /**
+ * Adds a value from a resource array.
+ *
+ * @param index of the resource array item
+ * @param value resource value
+ */
+ public void put(int index, Value value) {}
+
+ /**
+ * Returns a nested resource array at the array index as another sink.
+ * Creates the sink if none exists for the key.
+ * Returns null if nested arrays are not supported.
+ * The default implementation always returns null.
+ *
+ * @param index of the resource array item
+ * @param size number of array items
+ * @return nested-array sink, or null
+ */
+ public ArraySink getOrCreateArraySink(int index, int size) {
+ return null;
+ }
+
+ /**
+ * Returns a nested resource table at the array index as another sink.
+ * Creates the sink if none exists for the key.
+ * Returns null if nested tables are not supported.
+ * The default implementation always returns null.
+ *
+ * @param index of the resource array item
+ * @param initialSize size hint for creating the sink if necessary
+ * @return nested-table sink, or null
+ */
+ public TableSink getOrCreateTableSink(int index, int initialSize) {
+ return null;
+ }
+ }
+
+ /**
+ * Sink for ICU resource table contents.
+ * The base class does nothing.
+ *
+ * <p>Nested arrays and tables are stored as nested sinks,
+ * never put() as {@link Value} items.
+ */
+ public static class TableSink {
+ /**
+ * Adds a key-value pair from a resource table.
+ *
+ * @param key resource key string
+ * @param value resource value
+ */
+ public void put(Key key, Value value) {}
+
+ /**
+ * Removes any value for this key.
+ * Typically used for CLDR no-fallback data values of "∅∅∅"
+ * when enumerating tables with fallback from root to the specific resource bundle.
+ *
+ * <p>The default implementation does nothing.
+ *
+ * @param key to be removed
+ */
+ public void remove(Key key) {}
+
+ /**
+ * Returns a nested resource array for the key as another sink.
+ * Creates the sink if none exists for the key.
+ * Returns null if nested arrays are not supported.
+ * The default implementation always returns null.
+ *
+ * @param key resource key string
+ * @param size number of array items
+ * @return nested-array sink, or null
+ */
+ public ArraySink getOrCreateArraySink(Key key, int size) {
+ return null;
+ }
+
+ /**
+ * Returns a nested resource table for the key as another sink.
+ * Creates the sink if none exists for the key.
+ * Returns null if nested tables are not supported.
+ * The default implementation always returns null.
+ *
+ * @param key resource key string
+ * @param initialSize size hint for creating the sink if necessary
+ * @return nested-table sink, or null
+ */
+ public TableSink getOrCreateTableSink(Key key, int initialSize) {
+ return null;
+ }
+ }
+}
import java.util.ResourceBundle;
import java.util.Set;
+import com.ibm.icu.impl.ICUResourceBundleReader.ReaderValue;
import com.ibm.icu.impl.URLHandler.URLVisitor;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
@Deprecated
public static final String ICU_ZONE_BASE_NAME = ICUData.ICU_ZONE_BASE_NAME;
+ /**
+ * CLDR string value "∅∅∅" prevents fallback to the parent bundle.
+ */
private static final String NO_INHERITANCE_MARKER = "\u2205\u2205\u2205";
/**
return result;
}
+ public void getAllArrayItemsWithFallback(String path, ICUResource.ArraySink sink)
+ throws MissingResourceException {
+ getAllContainerItemsWithFallback(path, sink, null);
+ }
+
+ public void getAllTableItemsWithFallback(String path, ICUResource.TableSink sink)
+ throws MissingResourceException {
+ getAllContainerItemsWithFallback(path, null, sink);
+ }
+
+ private void getAllContainerItemsWithFallback(
+ String path, ICUResource.ArraySink arraySink, ICUResource.TableSink tableSink)
+ throws MissingResourceException {
+ // Collect existing and parsed key objects into an array of keys,
+ // rather than assembling and parsing paths.
+ int numPathKeys = countPathKeys(path); // How much deeper does the path go?
+ ICUResourceBundle rb;
+ if (numPathKeys == 0) {
+ rb = this;
+ } else {
+ // Get the keys for finding the target.
+ int depth = getResDepth(); // How deep are we in this bundle?
+ String[] pathKeys = new String[depth + numPathKeys];
+ getResPathKeys(path, numPathKeys, pathKeys, depth);
+ rb = findResourceWithFallback(pathKeys, depth, this, null);
+ if (rb == null) {
+ throw new MissingResourceException(
+ "Can't find resource for bundle "
+ + this.getClass().getName() + ", key " + getType(),
+ path, getKey());
+ }
+ }
+ int expectedType = arraySink != null ? ARRAY : TABLE;
+ if (rb.getType() != expectedType) {
+ throw new UResourceTypeMismatchException("");
+ }
+ // Get all table items with fallback.
+ ICUResource.Key key = new ICUResource.Key();
+ ReaderValue readerValue = new ReaderValue();
+ rb.getAllContainerItemsWithFallback(key, readerValue, arraySink, tableSink);
+ }
+
+ private void getAllContainerItemsWithFallback(
+ ICUResource.Key key, ReaderValue readerValue,
+ ICUResource.ArraySink arraySink, ICUResource.TableSink tableSink) {
+ // We recursively enumerate parent-first,
+ // overriding parent items with child items.
+ // When we see the no-inheritance marker, then we remove the parent's item.
+ //
+ // It would be possible to recursively enumerate child-first,
+ // only storing parent items in the absence of child items,
+ // but then we would need to store the no-inheritance marker
+ // (or some placeholder for it)
+ // to prevent a parent item from being stored.
+ int expectedType = arraySink != null ? ARRAY : TABLE;
+ if (parent != null) {
+ ICUResourceBundle parentBundle = (ICUResourceBundle)parent;
+ ICUResourceBundle rb;
+ int depth = getResDepth();
+ if (depth == 0) {
+ rb = parentBundle;
+ } else {
+ // Re-fetch the path keys: They may differ from the original ones
+ // if we had followed an alias.
+ String[] pathKeys = new String[depth];
+ getResPathKeys(pathKeys, depth);
+ rb = findResourceWithFallback(pathKeys, 0, parentBundle, null);
+ }
+ if (rb != null && rb.getType() == expectedType) {
+ rb.getAllContainerItemsWithFallback(key, readerValue, arraySink, tableSink);
+ }
+ }
+ if (getType() == expectedType) {
+ if (arraySink != null) {
+ ((ICUResourceBundleImpl.ResourceArray)this).getAllItems(key, readerValue, arraySink);
+ } else if (tableSink != null) {
+ ((ICUResourceBundleImpl.ResourceTable)this).getAllItems(key, readerValue, tableSink);
+ }
+ }
+ }
+
/**
* Return a set of the locale names supported by a collection of resource
* bundles.
if (path.length() == 0) {
return null;
}
- ICUResourceBundle sub = null;
- if (requested == null) {
- requested = actualBundle;
- }
-
ICUResourceBundle base = (ICUResourceBundle) actualBundle;
// Collect existing and parsed key objects into an array of keys,
// rather than assembling and parsing paths.
assert numPathKeys > 0;
String[] keys = new String[depth + numPathKeys];
getResPathKeys(path, numPathKeys, keys, depth);
+ return findResourceWithFallback(keys, depth, base, requested);
+ }
+
+ private static final ICUResourceBundle findResourceWithFallback(
+ String[] keys, int depth,
+ ICUResourceBundle base, UResourceBundle requested) {
+ if (requested == null) {
+ requested = base;
+ }
for (;;) { // Iterate over the parent bundles.
for (;;) { // Iterate over the keys on the requested path, within a bundle.
String subKey = keys[depth++];
- sub = (ICUResourceBundle) base.handleGet(subKey, null, requested);
+ ICUResourceBundle sub = (ICUResourceBundle) base.handleGet(subKey, null, requested);
if (sub == null) {
--depth;
break;
/*
*******************************************************************************
- * Copyright (C) 2004-2014, International Business Machines Corporation and
+ * Copyright (C) 2004-2015, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*/
import java.util.Set;
import java.util.TreeSet;
+import com.ibm.icu.impl.ICUResourceBundleReader.ReaderValue;
import com.ibm.icu.util.UResourceBundle;
import com.ibm.icu.util.UResourceTypeMismatchException;
super(wholeBundle);
}
}
- private static class ResourceArray extends ResourceContainer {
+ static class ResourceArray extends ResourceContainer {
public int getType() {
return ARRAY;
}
UResourceBundle requested) {
return createBundleObject(index, Integer.toString(index), aliasesVisited, requested);
}
+ /**
+ * @param key will be set during enumeration; input contents is ignored
+ * @param readerValue will be set during enumeration; input contents is ignored
+ * @param sink receives all array item values
+ */
+ void getAllItems(ICUResource.Key key, ReaderValue readerValue, ICUResource.ArraySink sink) {
+ ICUResourceBundleReader reader = wholeBundle.reader;
+ readerValue.reader = reader;
+ ((ICUResourceBundleReader.Array)value).getAllItems(reader, key, readerValue, sink);
+ }
ResourceArray(ICUResourceBundleImpl container, String key, int resource) {
super(container, key);
value = wholeBundle.reader.getArray(resource);
}
return reader.getString(value.getContainerResource(reader, index));
}
+ /**
+ * @param key will be set during enumeration; input contents is ignored
+ * @param readerValue will be set during enumeration; input contents is ignored
+ * @param sink receives all table item key-value pairs
+ */
+ void getAllItems(ICUResource.Key key, ReaderValue readerValue, ICUResource.TableSink sink) {
+ ICUResourceBundleReader reader = wholeBundle.reader;
+ readerValue.reader = reader;
+ ((ICUResourceBundleReader.Table)value).getAllItems(reader, key, readerValue, sink);
+ }
ResourceTable(ICUResourceBundleImpl container, String key, int resource) {
super(container, key);
value = wholeBundle.reader.getTable(resource);
import java.nio.CharBuffer;
import java.nio.IntBuffer;
+import com.ibm.icu.impl.ICUResource.ArraySink;
+import com.ibm.icu.impl.ICUResource.TableSink;
import com.ibm.icu.util.ICUException;
import com.ibm.icu.util.ICUUncheckedIOException;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.UResourceBundle;
+import com.ibm.icu.util.UResourceTypeMismatchException;
import com.ibm.icu.util.VersionInfo;
/**
private static final char[] emptyChars = new char[0];
private static final int[] emptyInts = new int[0];
private static final String emptyString = "";
- private static final Container EMPTY_ARRAY = new Container();
+ private static final Array EMPTY_ARRAY = new Array();
private static final Table EMPTY_TABLE = new Table();
private char[] getChars(int offset, int count) {
return makeKeyStringFromBytes(poolBundleReader.keyBytes, keyOffset & 0x7fffffff);
}
}
+ private void setKeyFromKey16(int keyOffset, ICUResource.Key key) {
+ if(keyOffset < localKeyLimit) {
+ key.setBytes(keyBytes, keyOffset);
+ } else {
+ key.setBytes(poolBundleReader.keyBytes, keyOffset - localKeyLimit);
+ }
+ }
+ private void setKeyFromKey32(int keyOffset, ICUResource.Key key) {
+ if(keyOffset >= 0) {
+ key.setBytes(keyBytes, keyOffset);
+ } else {
+ key.setBytes(poolBundleReader.keyBytes, keyOffset & 0x7fffffff);
+ }
+ }
private int compareKeys(CharSequence key, char keyOffset) {
if(keyOffset < localKeyLimit) {
return ICUBinary.compareKeys(key, keyBytes, keyOffset);
return (String)resourceCache.putIfAbsent(res, s, s.length() * 2);
}
+ /**
+ * CLDR string value "∅∅∅"=="\u2205\u2205\u2205" prevents fallback to the parent bundle.
+ */
+ private boolean isNoInheritanceMarker(int res) {
+ int offset = RES_GET_OFFSET(res);
+ if (offset == 0) {
+ // empty string
+ } else if (res == offset) {
+ offset = getResourceByteOffset(offset);
+ return getInt(offset) == 3 && bytes.getChar(offset + 4) == 0x2205 &&
+ bytes.getChar(offset + 6) == 0x2205 && bytes.getChar(offset + 8) == 0x2205;
+ } else if (RES_GET_TYPE(res) == ICUResourceBundle.STRING_V2) {
+ if (offset < poolStringIndexLimit) {
+ return poolBundleReader.isStringV2NoInheritanceMarker(offset);
+ } else {
+ return isStringV2NoInheritanceMarker(offset - poolStringIndexLimit);
+ }
+ }
+ return false;
+ }
+
+ private boolean isStringV2NoInheritanceMarker(int offset) {
+ int first = b16BitUnits.charAt(offset);
+ if (first == 0x2205) { // implicit length
+ return b16BitUnits.charAt(offset + 1) == 0x2205 &&
+ b16BitUnits.charAt(offset + 2) == 0x2205 &&
+ b16BitUnits.charAt(offset + 3) == 0;
+ } else if (first == 0xdc03) { // explicit length 3 (should not occur)
+ return b16BitUnits.charAt(offset + 1) == 0x2205 &&
+ b16BitUnits.charAt(offset + 2) == 0x2205 &&
+ b16BitUnits.charAt(offset + 3) == 0x2205;
+ } else {
+ // Assume that the string has not been stored with more length units than necessary.
+ return false;
+ }
+ }
+
String getAlias(int res) {
int offset=RES_GET_OFFSET(res);
int length;
}
}
- Container getArray(int res) {
+ private int getArrayLength(int res) {
+ int offset = RES_GET_OFFSET(res);
+ if(offset == 0) {
+ return 0;
+ }
+ int type = RES_GET_TYPE(res);
+ if(type == UResourceBundle.ARRAY) {
+ offset = getResourceByteOffset(offset);
+ return getInt(offset);
+ } else if(type == ICUResourceBundle.ARRAY16) {
+ return b16BitUnits.charAt(offset);
+ } else {
+ return 0;
+ }
+ }
+
+ Array getArray(int res) {
int type=RES_GET_TYPE(res);
if(!URES_IS_ARRAY(type)) {
return null;
}
Object value = resourceCache.get(res);
if(value != null) {
- return (Container)value;
+ return (Array)value;
+ }
+ Array array = (type == UResourceBundle.ARRAY) ?
+ new Array32(this, offset) : new Array16(this, offset);
+ return (Array)resourceCache.putIfAbsent(res, array, 0);
+ }
+
+ private int getTableLength(int res) {
+ int offset = RES_GET_OFFSET(res);
+ if(offset == 0) {
+ return 0;
+ }
+ int type = RES_GET_TYPE(res);
+ if(type == UResourceBundle.TABLE) {
+ offset = getResourceByteOffset(offset);
+ return bytes.getChar(offset);
+ } else if(type == ICUResourceBundle.TABLE16) {
+ return b16BitUnits.charAt(offset);
+ } else if(type == ICUResourceBundle.TABLE32) {
+ offset = getResourceByteOffset(offset);
+ return getInt(offset);
+ } else {
+ return 0;
}
- Container array = (type == UResourceBundle.ARRAY) ?
- new Array(this, offset) : new Array16(this, offset);
- return (Container)resourceCache.putIfAbsent(res, array, 0);
}
Table getTable(int res) {
return (Table)resourceCache.putIfAbsent(res, table, size);
}
+ // ICUResource.Value --------------------------------------------------- ***
+
+ /**
+ * From C++ uresdata.c gPublicTypes[URES_LIMIT].
+ */
+ private static int PUBLIC_TYPES[] = {
+ UResourceBundle.STRING,
+ UResourceBundle.BINARY,
+ UResourceBundle.TABLE,
+ ICUResourceBundle.ALIAS,
+
+ UResourceBundle.TABLE, /* URES_TABLE32 */
+ UResourceBundle.TABLE, /* URES_TABLE16 */
+ UResourceBundle.STRING, /* URES_STRING_V2 */
+ UResourceBundle.INT,
+
+ UResourceBundle.ARRAY,
+ UResourceBundle.ARRAY, /* URES_ARRAY16 */
+ UResourceBundle.NONE,
+ UResourceBundle.NONE,
+
+ UResourceBundle.NONE,
+ UResourceBundle.NONE,
+ UResourceBundle.INT_VECTOR,
+ UResourceBundle.NONE
+ };
+
+ static class ReaderValue extends ICUResource.Value {
+ ICUResourceBundleReader reader;
+ private int res;
+
+ @Override
+ public int getType() {
+ return PUBLIC_TYPES[RES_GET_TYPE(res)];
+ }
+
+ @Override
+ public String getString() {
+ String s = reader.getString(res);
+ if (s == null) {
+ throw new UResourceTypeMismatchException("");
+ }
+ return s;
+ }
+
+ @Override
+ public int getInt() {
+ if (RES_GET_TYPE(res) != UResourceBundle.INT) {
+ throw new UResourceTypeMismatchException("");
+ }
+ return RES_GET_INT(res);
+ }
+
+ @Override
+ public int getUInt() {
+ if (RES_GET_TYPE(res) != UResourceBundle.INT) {
+ throw new UResourceTypeMismatchException("");
+ }
+ return RES_GET_UINT(res);
+ }
+
+ @Override
+ public int[] getIntVector() {
+ int[] iv = reader.getIntVector(res);
+ if (iv == null) {
+ throw new UResourceTypeMismatchException("");
+ }
+ return iv;
+ }
+
+ @Override
+ public ByteBuffer getBinary() {
+ ByteBuffer bb = reader.getBinary(res);
+ if (bb == null) {
+ throw new UResourceTypeMismatchException("");
+ }
+ return bb;
+ }
+ }
+
// Container value classes --------------------------------------------- ***
static class Container {
Container() {
}
}
- private static final class Array extends Container {
+ static class Array extends Container {
+ Array() {}
+ void getAllItems(ICUResourceBundleReader reader,
+ ICUResource.Key key, ReaderValue value, ArraySink sink) {
+ for (int i = 0; i < size; ++i) {
+ int res = getContainerResource(reader, i);
+ int type = RES_GET_TYPE(res);
+ if (URES_IS_ARRAY(type)) {
+ int numItems = reader.getArrayLength(res);
+ ArraySink subSink = sink.getOrCreateArraySink(i, numItems);
+ if (subSink != null) {
+ Array array = reader.getArray(res);
+ assert(array.size == numItems);
+ array.getAllItems(reader, key, value, subSink);
+ }
+ } else if (URES_IS_TABLE(type)) {
+ int numItems = reader.getTableLength(res);
+ TableSink subSink = sink.getOrCreateTableSink(i, numItems);
+ if (subSink != null) {
+ Table table = reader.getTable(res);
+ assert(table.size == numItems);
+ table.getAllItems(reader, key, value, subSink);
+ }
+ } else if (type == ICUResourceBundle.ALIAS) {
+ throw new UnsupportedOperationException(
+ "aliases not handled in resource enumeration");
+ } else {
+ value.res = res;
+ sink.put(i, value);
+ }
+ }
+ }
+ }
+ private static final class Array32 extends Array {
@Override
int getContainerResource(ICUResourceBundleReader reader, int index) {
return getContainer32Resource(reader, index);
}
- Array(ICUResourceBundleReader reader, int offset) {
+ Array32(ICUResourceBundleReader reader, int offset) {
offset = reader.getResourceByteOffset(offset);
size = reader.getInt(offset);
itemsOffset = offset + 4;
}
}
- private static final class Array16 extends Container {
+ private static final class Array16 extends Array {
@Override
int getContainerResource(ICUResourceBundleReader reader, int index) {
return getContainer16Resource(reader, index);
int getResource(ICUResourceBundleReader reader, String resKey) {
return getContainerResource(reader, findTableItem(reader, resKey));
}
+ void getAllItems(ICUResourceBundleReader reader,
+ ICUResource.Key key, ReaderValue value, TableSink sink) {
+ for (int i = 0; i < size; ++i) {
+ if (keyOffsets != null) {
+ reader.setKeyFromKey16(keyOffsets[i], key);
+ } else {
+ reader.setKeyFromKey32(key32Offsets[i], key);
+ }
+ int res = getContainerResource(reader, i);
+ int type = RES_GET_TYPE(res);
+ if (URES_IS_ARRAY(type)) {
+ int numItems = reader.getArrayLength(res);
+ ArraySink subSink = sink.getOrCreateArraySink(key, numItems);
+ if (subSink != null) {
+ Array array = reader.getArray(res);
+ assert(array.size == numItems);
+ array.getAllItems(reader, key, value, subSink);
+ }
+ } else if (URES_IS_TABLE(type)) {
+ int numItems = reader.getTableLength(res);
+ TableSink subSink = sink.getOrCreateTableSink(key, numItems);
+ if (subSink != null) {
+ Table table = reader.getTable(res);
+ assert(table.size == numItems);
+ table.getAllItems(reader, key, value, subSink);
+ }
+ } else if (type == ICUResourceBundle.ALIAS) {
+ throw new UnsupportedOperationException(
+ "aliases not handled in resource enumeration");
+ } else if (reader.isNoInheritanceMarker(res)) {
+ sink.remove(key);
+ } else {
+ value.res = res;
+ sink.put(key, value);
+ }
+ }
+ }
Table() {
}
}
/*
*******************************************************************************
- * Copyright (C) 2011-2014, International Business Machines Corporation and *
- * others. All Rights Reserved. *
+ * Copyright (C) 2011-2015, International Business Machines Corporation and
+ * others. All Rights Reserved.
*******************************************************************************
*/
package com.ibm.icu.impl;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
+import com.ibm.icu.impl.ICUResource.TableSink;
import com.ibm.icu.impl.TextTrieMap.ResultHandler;
import com.ibm.icu.text.TimeZoneNames;
import com.ibm.icu.util.TimeZone;
private static final String ZONE_STRINGS_BUNDLE = "zoneStrings";
private static final String MZ_PREFIX = "meta:";
+ private static final NameType[] NAME_TYPE_VALUES = NameType.values();
private static volatile Set<String> METAZONE_IDS;
private static final TZ2MZsCache TZ_TO_MZS_CACHE = new TZ2MZsCache();
// and it's stored in SoftCache, so we do not need to worry about the
// footprint much.
private transient ConcurrentHashMap<String, ZNames> _mzNamesMap;
- private transient ConcurrentHashMap<String, TZNames> _tzNamesMap;
+ private transient ConcurrentHashMap<String, ZNames> _tzNamesMap;
+ private transient boolean _namesFullyLoaded;
private transient TextTrieMap<NameInfo> _namesTrie;
private transient boolean _namesTrieFullyLoaded;
if (mzID == null || mzID.length() == 0) {
return null;
}
- return loadMetaZoneNames(mzID).getName(type);
+ return loadMetaZoneNames(null, mzID).getName(type);
}
/*
if (tzID == null || tzID.length() == 0) {
return null;
}
- return loadTimeZoneNames(tzID).getName(type);
+ return loadTimeZoneNames(null, tzID).getName(type);
}
/* (non-Javadoc)
if (tzID == null || tzID.length() == 0) {
return null;
}
- String locName = loadTimeZoneNames(tzID).getName(NameType.EXEMPLAR_LOCATION);
+ String locName = loadTimeZoneNames(null, tzID).getName(NameType.EXEMPLAR_LOCATION);
return locName;
}
}
// All names are not yet loaded into the trie
+ internalLoadAllDisplayNames();
+ addAllNamesIntoTrie();
- // time zone names
+ // Set default time zone location names
+ // for time zones without explicit display names.
Set<String> tzIDs = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL, null, null);
for (String tzID : tzIDs) {
- loadTimeZoneNames(tzID);
- }
-
- // meta zone names
- Set<String> mzIDs = getAvailableMetaZoneIDs();
- for (String mzID : mzIDs) {
- loadMetaZoneNames(mzID);
+ if (!_tzNamesMap.containsKey(tzID)) {
+ tzID = tzID.intern();
+ ZNames tznames = ZNames.getInstance(null, tzID);
+ tznames.addNamesIntoTrie(null, tzID, _namesTrie);
+ _tzNamesMap.put(tzID, tznames);
+ }
}
_namesTrieFullyLoaded = true;
return handler.getMatches();
}
+ @Override
+ public synchronized void loadAllDisplayNames() {
+ internalLoadAllDisplayNames();
+ }
+
+ @Override
+ public String[] getDisplayNames(String tzID, long date, NameType... types) {
+ String[] names = new String[types.length];
+ if (tzID == null || tzID.length() == 0) {
+ return names;
+ }
+ ZNames tzNames = loadTimeZoneNames(null, tzID);
+ ZNames mzNames = null;
+ for (int i = 0; i < types.length; ++i) {
+ NameType type = types[i];
+ String name = tzNames.getName(type);
+ if (name == null) {
+ if (mzNames == null) {
+ String mzID = getMetaZoneID(tzID, date);
+ if (mzID == null || mzID.length() == 0) {
+ mzNames = ZNames.EMPTY_ZNAMES;
+ } else {
+ mzNames = loadMetaZoneNames(null, mzID);
+ }
+ }
+ name = mzNames.getName(type);
+ }
+ names[i] = name;
+ }
+ return names;
+ }
+
+ /** Caller must synchronize. */
+ private void internalLoadAllDisplayNames() {
+ if (!_namesFullyLoaded) {
+ new ZoneStringsLoader().load();
+ _namesFullyLoaded = true;
+ }
+ }
+
+ /** Caller must synchronize. */
+ private void addAllNamesIntoTrie() {
+ for (Map.Entry<String, ZNames> entry : _tzNamesMap.entrySet()) {
+ entry.getValue().addNamesIntoTrie(null, entry.getKey(), _namesTrie);
+ }
+ for (Map.Entry<String, ZNames> entry : _mzNamesMap.entrySet()) {
+ entry.getValue().addNamesIntoTrie(entry.getKey(), null, _namesTrie);
+ }
+ }
+
+ /**
+ * Loads all meta zone and time zone names for this TimeZoneNames' locale.
+ */
+ private final class ZoneStringsLoader extends ICUResource.TableSink {
+ /**
+ * Prepare for several hundred time zones and meta zones.
+ * _zoneStrings.getSize() is ineffective in a sparsely populated locale like en-GB.
+ */
+ private static final int INITIAL_NUM_ZONES = 300;
+ private HashMap<ICUResource.Key, ZNamesLoader> keyToLoader =
+ new HashMap<ICUResource.Key, ZNamesLoader>(INITIAL_NUM_ZONES);
+ private StringBuilder sb = new StringBuilder(32);
+
+ /** Caller must synchronize. */
+ void load() {
+ _zoneStrings.getAllTableItemsWithFallback("", this);
+ for (Map.Entry<ICUResource.Key, ZNamesLoader> entry : keyToLoader.entrySet()) {
+ ICUResource.Key key = entry.getKey();
+ ZNamesLoader loader = entry.getValue();
+ if (key.startsWith(MZ_PREFIX)) {
+ String mzID = mzIDFromKey(key).intern();
+ ZNames mzNames = ZNames.getInstance(loader.getNames(), null);
+ _mzNamesMap.put(mzID, mzNames);
+ } else {
+ String tzID = tzIDFromKey(key).intern();
+ ZNames tzNames = ZNames.getInstance(loader.getNames(), tzID);
+ _tzNamesMap.put(tzID, tzNames);
+ }
+ }
+ }
+
+ @Override
+ public TableSink getOrCreateTableSink(ICUResource.Key key, int initialSize) {
+ ZNamesLoader loader = keyToLoader.get(key);
+ if (loader != null) {
+ return loader;
+ }
+ if (key.startsWith(MZ_PREFIX)) {
+ String mzID = mzIDFromKey(key);
+ if (_mzNamesMap.containsKey(mzID)) {
+ return null; // We have already loaded the names for this meta zone.
+ }
+ loader = ZNamesLoader.forMetaZoneNames();
+ } else {
+ String tzID = tzIDFromKey(key);
+ if (_tzNamesMap.containsKey(tzID)) {
+ return null; // We have already loaded the names for this time zone.
+ }
+ loader = ZNamesLoader.forTimeZoneNames();
+ }
+ keyToLoader.put(key.clone(), loader);
+ return loader;
+ }
+
+ @Override
+ public void remove(ICUResource.Key key) {
+ keyToLoader.remove(key);
+ }
+
+ /**
+ * Equivalent to key.substring(MZ_PREFIX.length())
+ * except reuses our StringBuilder.
+ */
+ private String mzIDFromKey(ICUResource.Key key) {
+ sb.setLength(0);
+ for (int i = MZ_PREFIX.length(); i < key.length(); ++i) {
+ sb.append(key.charAt(i));
+ }
+ return sb.toString();
+ }
+
+ private String tzIDFromKey(ICUResource.Key key) {
+ sb.setLength(0);
+ for (int i = 0; i < key.length(); ++i) {
+ char c = key.charAt(i);
+ if (c == ':') {
+ c = '/';
+ }
+ sb.append(c);
+ }
+ return sb.toString();
+ }
+ }
+
/**
* Initialize the transient fields, called from the constructor and
* readObject.
ICUResourceBundle.ICU_ZONE_BASE_NAME, locale);
_zoneStrings = (ICUResourceBundle)bundle.get(ZONE_STRINGS_BUNDLE);
- _tzNamesMap = new ConcurrentHashMap<String, TZNames>();
+ // TODO: Access is synchronized, can we use a non-concurrent map?
+ _tzNamesMap = new ConcurrentHashMap<String, ZNames>();
_mzNamesMap = new ConcurrentHashMap<String, ZNames>();
+ _namesFullyLoaded = false;
_namesTrie = new TextTrieMap<NameInfo>(true);
_namesTrieFullyLoaded = false;
if (tzCanonicalID == null || tzCanonicalID.length() == 0) {
return;
}
- loadTimeZoneNames(tzCanonicalID);
+ loadTimeZoneNames(null, tzCanonicalID);
+ ZNamesLoader loader = ZNamesLoader.forMetaZoneNames();
Set<String> mzIDs = getAvailableMetaZoneIDs(tzCanonicalID);
for (String mzID : mzIDs) {
- loadMetaZoneNames(mzID);
+ loadMetaZoneNames(loader, mzID);
}
+ addAllNamesIntoTrie();
}
/*
* @param mzID the meta zone ID
* @return An instance of ZNames that includes a set of meta zone display names.
*/
- private synchronized ZNames loadMetaZoneNames(String mzID) {
+ private synchronized ZNames loadMetaZoneNames(ZNamesLoader loader, String mzID) {
ZNames znames = _mzNamesMap.get(mzID);
if (znames == null) {
- znames = ZNames.getInstance(_zoneStrings, MZ_PREFIX + mzID);
- // put names into the trie
+ if (loader == null) {
+ loader = ZNamesLoader.forMetaZoneNames();
+ }
+ znames = ZNames.getInstance(loader, _zoneStrings, MZ_PREFIX + mzID, null);
mzID = mzID.intern();
- for (NameType t : NameType.values()) {
- String name = znames.getName(t);
- if (name != null) {
- NameInfo info = new NameInfo();
- info.mzID = mzID;
- info.type = t;
- _namesTrie.put(name, info);
- }
+ if (_namesTrieFullyLoaded) {
+ znames.addNamesIntoTrie(mzID, null, _namesTrie);
}
- ZNames tmpZnames = _mzNamesMap.putIfAbsent(mzID, znames);
- znames = (tmpZnames == null) ? znames : tmpZnames;
+ _mzNamesMap.put(mzID, znames);
}
return znames;
}
* @param tzID the canonical time zone ID
* @return An instance of TZNames that includes a set of time zone display names.
*/
- private synchronized TZNames loadTimeZoneNames(String tzID) {
- TZNames tznames = _tzNamesMap.get(tzID);
+ private synchronized ZNames loadTimeZoneNames(ZNamesLoader loader, String tzID) {
+ ZNames tznames = _tzNamesMap.get(tzID);
if (tznames == null) {
- tznames = TZNames.getInstance(_zoneStrings, tzID.replace('/', ':'), tzID);
- // put names into the trie
+ if (loader == null) {
+ loader = ZNamesLoader.forTimeZoneNames();
+ }
+ tznames = ZNames.getInstance(loader, _zoneStrings, tzID.replace('/', ':'), tzID);
tzID = tzID.intern();
- for (NameType t : NameType.values()) {
- String name = tznames.getName(t);
- if (name != null) {
- NameInfo info = new NameInfo();
- info.tzID = tzID;
- info.type = t;
- _namesTrie.put(name, info);
- }
+ if (_namesTrieFullyLoaded) {
+ tznames.addNamesIntoTrie(null, tzID, _namesTrie);
}
- TZNames tmpTznames = _tzNamesMap.putIfAbsent(tzID, tznames);
- tznames = (tmpTznames == null) ? tznames : tmpTznames;
+ _tzNamesMap.put(tzID, tznames);
}
return tznames;
}
}
}
- /**
- * This class stores name data for a meta zone
- */
- private static class ZNames {
- private static final ZNames EMPTY_ZNAMES = new ZNames(null);
+ private static final class ZNamesLoader extends ICUResource.TableSink {
+ private static int NUM_META_ZONE_NAMES = 6;
+ private static int NUM_TIME_ZONE_NAMES = 7; // incl. EXEMPLAR_LOCATION
- private String[] _names;
+ private String[] names;
+ private int numNames;
- private static final String[] KEYS = {"lg", "ls", "ld", "sg", "ss", "sd"};
+ private ZNamesLoader(int numNames) {
+ this.numNames = numNames;
+ }
- protected ZNames(String[] names) {
- _names = names;
+ static ZNamesLoader forMetaZoneNames() {
+ return new ZNamesLoader(NUM_META_ZONE_NAMES);
}
- public static ZNames getInstance(ICUResourceBundle zoneStrings, String key) {
- String[] names = loadData(zoneStrings, key);
- if (names == null) {
- return EMPTY_ZNAMES;
- }
- return new ZNames(names);
+ static ZNamesLoader forTimeZoneNames() {
+ return new ZNamesLoader(NUM_TIME_ZONE_NAMES);
}
- public String getName(NameType type) {
- if (_names == null) {
+ String[] load(ICUResourceBundle zoneStrings, String key) {
+ if (zoneStrings == null || key == null || key.length() == 0) {
return null;
}
- String name = null;
- switch (type) {
- case LONG_GENERIC:
- name = _names[0];
- break;
- case LONG_STANDARD:
- name = _names[1];
- break;
- case LONG_DAYLIGHT:
- name = _names[2];
- break;
- case SHORT_GENERIC:
- name = _names[3];
- break;
- case SHORT_STANDARD:
- name = _names[4];
- break;
- case SHORT_DAYLIGHT:
- name = _names[5];
- break;
- case EXEMPLAR_LOCATION:
- name = null; // implemented by subclass
- break;
+
+ try {
+ zoneStrings.getAllTableItemsWithFallback(key, this);
+ } catch (MissingResourceException e) {
+ return null;
}
- return name;
+ return getNames();
}
- protected static String[] loadData(ICUResourceBundle zoneStrings, String key) {
- if (zoneStrings == null || key == null || key.length() == 0) {
+ private static NameType nameTypeFromKey(ICUResource.Key key) {
+ // Avoid key.toString() object creation.
+ if (key.length() != 2) {
return null;
}
+ char c0 = key.charAt(0);
+ char c1 = key.charAt(1);
+ if (c0 == 'l') {
+ return c1 == 'g' ? NameType.LONG_GENERIC :
+ c1 == 's' ? NameType.LONG_STANDARD :
+ c1 == 'd' ? NameType.LONG_DAYLIGHT : null;
+ } else if (c0 == 's') {
+ return c1 == 'g' ? NameType.SHORT_GENERIC :
+ c1 == 's' ? NameType.SHORT_STANDARD :
+ c1 == 'd' ? NameType.SHORT_DAYLIGHT : null;
+ } else if (c0 == 'e' && c1 == 'c') {
+ return NameType.EXEMPLAR_LOCATION;
+ }
+ return null;
+ }
- ICUResourceBundle table = null;
- try {
- table = zoneStrings.getWithFallback(key);
- } catch (MissingResourceException e) {
- return null;
+ @Override
+ public void put(ICUResource.Key key, ICUResource.Value value) {
+ if (value.getType() == UResourceBundle.STRING) {
+ NameType type = nameTypeFromKey(key);
+ if (type != null && type.ordinal() < numNames) {
+ if (names == null) {
+ names = new String[numNames];
+ }
+ names[type.ordinal()] = value.getString();
+ }
}
+ }
- boolean isEmpty = true;
- String[] names = new String[KEYS.length];
- for (int i = 0; i < names.length; i++) {
- try {
- names[i] = table.getStringWithFallback(KEYS[i]);
- isEmpty = false;
- } catch (MissingResourceException e) {
- names[i] = null;
+ @Override
+ public void remove(ICUResource.Key key) {
+ if (names != null) {
+ NameType type = nameTypeFromKey(key);
+ if (type != null && type.ordinal() < numNames) {
+ names[type.ordinal()] = null;
}
}
+ }
- if (isEmpty) {
+ private String[] getNames() {
+ if (names == null) {
return null;
}
-
- return names;
+ int length = names.length;
+ while (names[length - 1] == null) {
+ if (--length == 0) {
+ return null; // no names
+ }
+ }
+ if (length == names.length || numNames == NUM_TIME_ZONE_NAMES) {
+ // Return the full array if the last name is set.
+ // Also return the full *time* zone names array,
+ // so that the exemplar location can be set.
+ String[] result = names;
+ names = null;
+ return result;
+ }
+ // Return a shorter array for permanent storage.
+ // *Move* all names into a minimal array.
+ String[] result = new String[length];
+ do {
+ --length;
+ result[length] = names[length];
+ names[length] = null; // Reset for loading another set of names.
+ } while (length > 0);
+ return result;
}
}
/**
- * This class stores name data for a single time zone
+ * This class stores name data for a meta zone or time zone.
*/
- private static class TZNames extends ZNames {
- private String _locationName;
-
- private static final TZNames EMPTY_TZNAMES = new TZNames(null, null);
+ private static class ZNames {
+ private static final ZNames EMPTY_ZNAMES = new ZNames(null);
+ // A meta zone names instance never has an exemplar location string.
+ private static final int EX_LOC_INDEX = NameType.EXEMPLAR_LOCATION.ordinal();
- public static TZNames getInstance(ICUResourceBundle zoneStrings, String key, String tzID) {
- if (zoneStrings == null || key == null || key.length() == 0) {
- return EMPTY_TZNAMES;
- }
+ private String[] _names;
- String[] names = loadData(zoneStrings, key);
- String locationName = null;
+ protected ZNames(String[] names) {
+ _names = names;
+ }
- ICUResourceBundle table = null;
- try {
- table = zoneStrings.getWithFallback(key);
- locationName = table.getStringWithFallback("ec");
- } catch (MissingResourceException e) {
- // fall through
+ public static ZNames getInstance(String[] names, String tzID) {
+ if (tzID != null && (names == null || names[EX_LOC_INDEX] == null)) {
+ String locationName = getDefaultExemplarLocationName(tzID);
+ if (locationName != null) {
+ if (names == null) {
+ names = new String[EX_LOC_INDEX + 1];
+ }
+ names[EX_LOC_INDEX] = locationName;
+ }
}
- if (locationName == null) {
- locationName = getDefaultExemplarLocationName(tzID);
+ if (names == null) {
+ return EMPTY_ZNAMES;
}
+ return new ZNames(names);
+ }
- if (locationName == null && names == null) {
- return EMPTY_TZNAMES;
- }
- return new TZNames(names, locationName);
+ public static ZNames getInstance(ZNamesLoader loader,
+ ICUResourceBundle zoneStrings, String key, String tzID) {
+ return getInstance(loader.load(zoneStrings, key), tzID);
}
public String getName(NameType type) {
- if (type == NameType.EXEMPLAR_LOCATION) {
- return _locationName;
+ if (_names != null && type.ordinal() < _names.length) {
+ return _names[type.ordinal()];
+ } else {
+ return null;
}
- return super.getName(type);
}
- private TZNames(String[] names, String locationName) {
- super(names);
- _locationName = locationName;
+ public void addNamesIntoTrie(String mzID, String tzID, TextTrieMap<NameInfo> trie) {
+ if (_names == null) {
+ return;
+ }
+ for (int i = 0; i < _names.length; ++ i) {
+ String name = _names[i];
+ if (name != null) {
+ NameInfo info = new NameInfo();
+ info.mzID = mzID;
+ info.tzID = tzID;
+ info.type = NAME_TYPE_VALUES[i];
+ trie.put(name, info);
+ }
+ }
}
}
-
//
// Canonical time zone ID -> meta zone ID
//
/*
*******************************************************************************
- * Copyright (C) 2011-2014, International Business Machines Corporation and *
- * others. All Rights Reserved. *
+ * Copyright (C) 2011-2015, International Business Machines Corporation and
+ * others. All Rights Reserved.
*******************************************************************************
*/
package com.ibm.icu.text;
}
}
+ /**
+ * @author Markus
+ * @internal For specific users only until proposed publicly.
+ */
+ public void loadAllDisplayNames() {}
+
+ /**
+ * @author Markus
+ * @internal For specific users only until proposed publicly.
+ */
+ public String[] getDisplayNames(String tzID, long date, NameType... types) {
+ String[] names = new String[types.length];
+ if (tzID == null || tzID.length() == 0) {
+ return names;
+ }
+ String mzID = null;
+ for (int i = 0; i < types.length; ++i) {
+ NameType type = types[i];
+ String name = getTimeZoneDisplayName(tzID, type);
+ if (name == null) {
+ if (mzID == null) {
+ mzID = getMetaZoneID(tzID, date);
+ }
+ name = getMetaZoneDisplayName(mzID, type);
+ }
+ names[i] = name;
+ }
+ return names;
+ }
+
/**
* Sole constructor for invocation by subclass constructors.
*