com.ibm.icu.impl.UResource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
*******************************************************************************
* Copyright (C) 2015-2016, 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;
// Class UResource is named consistently with the public class UResourceBundle,
// in case we want to make it public at some point.
/**
* ICU resource bundle key and value types.
*/
public final class UResource {
/**
* 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 {
// 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() {
s = "";
}
/**
* Constructs a resource key object equal to the given string.
*/
public Key(String s) {
setString(s);
}
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 Key setBytes(byte[] keyBytes, int keyOffset) {
bytes = keyBytes;
offset = keyOffset;
for (length = 0; keyBytes[keyOffset + length] != 0; ++length) {}
s = null;
return this;
}
/**
* Mutates this key to an empty resource key string.
*/
public Key setToEmpty() {
bytes = null;
offset = length = 0;
s = "";
return this;
}
/**
* Mutates this key to be equal to the given string.
*/
public Key setString(String s) {
if (s.isEmpty()) {
setToEmpty();
} else {
bytes = new byte[s.length()];
offset = 0;
length = s.length();
for (int i = 0; i < length; ++i) {
char c = s.charAt(i);
if (c <= 0x7f) {
bytes[i] = (byte)c;
} else {
throw new IllegalArgumentException('\"' + s + "\" is not an ASCII string");
}
}
this.s = s;
}
return this;
}
/**
* {@inheritDoc}
* Does not clone the byte array.
*/
@Override
public Key clone() {
try {
return (Key)super.clone();
} catch (CloneNotSupportedException cannotOccur) {
return null;
}
}
@Override
public char charAt(int i) {
assert(0 <= i && i < length);
return (char)bytes[offset + i];
}
@Override
public int length() {
return length;
}
@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.
*/
@Override
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);
}
/**
* @return true if the substring of this key starting from the offset
* contains the same characters as the other sequence.
*/
public boolean regionMatches(int start, CharSequence cs) {
int csLength = cs.length();
return csLength == (length - start) && regionMatches(start, 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;
}
@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 = charAt(i) - cs.charAt(i);
if (diff != 0) {
return diff;
}
}
return length - csLength;
}
}
/**
* Interface for iterating over a resource bundle array resource.
* Does not use Java Iterator to reduce object creations.
*/
public interface Array {
/**
* @return The number of items in the array resource.
*/
public int getSize();
/**
* @param i Array item index.
* @param value Output-only, receives the value of the i'th item.
* @return true if i is non-negative and less than getSize().
*/
public boolean getValue(int i, Value value);
}
/**
* Interface for iterating over a resource bundle table resource.
* Does not use Java Iterator to reduce object creations.
*/
public interface Table {
/**
* @return The number of items in the array resource.
*/
public int getSize();
/**
* @param i Array item index.
* @param key Output-only, receives the key of the i'th item.
* @param value Output-only, receives the value of the i'th item.
* @return true if i is non-negative and less than getSize().
*/
public boolean getKeyAndValue(int i, Key key, Value value);
}
/**
* 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();
/**
* @throws UResourceTypeMismatchException if this is not an alias resource
*/
public abstract String getAliasString();
/**
* @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();
/**
* @throws UResourceTypeMismatchException if this is not an array resource
*/
public abstract Array getArray();
/**
* @throws UResourceTypeMismatchException if this is not a table resource
*/
public abstract Table getTable();
/**
* Is this a no-fallback/no-inheritance marker string?
* Such a marker is used for CLDR no-fallback data values of "∅∅∅"
* when enumerating tables with fallback from the specific resource bundle to root.
*
* @return true if this is a no-inheritance marker string
*/
public abstract boolean isNoInheritanceMarker();
/**
* @return the array of strings in this array resource.
* @see UResourceBundle#getStringArray()
* @throws UResourceTypeMismatchException if this is not an array resource
* or if any of the array items is not a string
*/
public abstract String[] getStringArray();
/**
* Same as
*
* if (getType() == STRING) {
* return new String[] { getString(); }
* } else {
* return getStringArray();
* }
*
*
* @see #getString()
* @see #getStringArray()
* @throws UResourceTypeMismatchException if this is
* neither a string resource nor an array resource containing strings
*/
public abstract String[] getStringArrayOrStringAsArray();
/**
* Same as
*
* if (getType() == STRING) {
* return getString();
* } else {
* return getStringArray()[0];
* }
*
*
* @see #getString()
* @see #getStringArray()
* @throws UResourceTypeMismatchException if this is
* neither a string resource nor an array resource containing strings
*/
public abstract String getStringOrFirstOfArray();
/**
* 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:
return "(array)";
case UResourceBundle.TABLE:
return "(table)";
default: // should not occur
return "???";
}
}
}
/**
* Sink for ICU resource bundle contents.
*/
public static abstract class Sink {
/**
* Called once for each bundle (child-parent-...-root).
* The value is normally an array or table resource,
* and implementations of this method normally iterate over the
* tree of resource items stored there.
*
* @param key Initially the key string of the enumeration-start resource.
* Empty if the enumeration starts at the top level of the bundle.
* Reuse for output values from Array and Table getters.
* @param value Call getArray() or getTable() as appropriate.
* Then reuse for output values from Array and Table getters.
* @param noFallback true if the bundle has no parent;
* that is, its top-level table has the nofallback attribute,
* or it is the root bundle of a locale tree.
*/
public abstract void put(Key key, Value value, boolean noFallback);
}
}