ICU-5293 DateFormat perfomance improvement, including a fix for timezone parsing bug #5290
X-SVN-Rev: 19960
This commit is contained in:
parent
a98c968d72
commit
86ff19728b
@ -1,9 +1,10 @@
|
||||
# Copyright (C) 2005, International Business Machines Corporation and
|
||||
# Copyright (C) 2005-2006, International Business Machines Corporation and
|
||||
# others. All Rights Reserved.
|
||||
src/com/ibm/icu/impl/CollectionUtilities.java
|
||||
src/com/ibm/icu/impl/ICUResourceBundle.java
|
||||
src/com/ibm/icu/impl/ICUResourceBundleImpl.java
|
||||
src/com/ibm/icu/impl/ICUResourceBundleReader.java
|
||||
src/com/ibm/icu/impl/LRUMap.java
|
||||
src/com/ibm/icu/impl/Utility.java
|
||||
src/com/ibm/icu/lang/UCharacter.java
|
||||
src/com/ibm/icu/math/BigDecimal.java
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 1996-2004, International Business Machines Corporation and *
|
||||
* Copyright (C) 1996-2006, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
@ -21,7 +21,9 @@ public class TestAll extends TestGroup {
|
||||
new String[] {
|
||||
"ICUServiceTest",
|
||||
"ICUServiceThreadTest",
|
||||
"ICUBinaryTest"
|
||||
"ICUBinaryTest",
|
||||
"LRUMapTest",
|
||||
"TextTrieMapTest"
|
||||
},
|
||||
"Test miscellaneous implementation utilities");
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 1996-2005, International Business Machines Corporation and *
|
||||
* Copyright (C) 1996-2006, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*
|
||||
@ -13,6 +13,8 @@ import java.util.HashMap;
|
||||
|
||||
import com.ibm.icu.dev.test.TestFmwk;
|
||||
import com.ibm.icu.impl.JDKTimeZone;
|
||||
import com.ibm.icu.impl.LinkedHashMap;
|
||||
import com.ibm.icu.impl.LRUMap;
|
||||
import com.ibm.icu.impl.OlsonTimeZone;
|
||||
import com.ibm.icu.impl.TimeZoneAdapter;
|
||||
import com.ibm.icu.math.BigDecimal;
|
||||
@ -369,6 +371,45 @@ public class SerializableTest extends TestFmwk.TestGroup
|
||||
}
|
||||
}
|
||||
|
||||
private static class LRUMapHandler implements Handler
|
||||
{
|
||||
public Object[] getTestObjects()
|
||||
{
|
||||
LRUMap[] maps = new LRUMap[1];
|
||||
maps[0] = new LRUMap();
|
||||
maps[0].put("1", "a");
|
||||
maps[0].put("2", "b");
|
||||
return maps;
|
||||
}
|
||||
public boolean hasSameBehavior(Object a, Object b)
|
||||
{
|
||||
LRUMap mapA = (LRUMap) a;
|
||||
LRUMap mapB = (LRUMap) b;
|
||||
return mapA.equals(mapB);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LinkedHashMapHandler implements Handler
|
||||
{
|
||||
public Object[] getTestObjects()
|
||||
{
|
||||
LinkedHashMap[] maps = new LinkedHashMap[2];
|
||||
maps[0] = new LinkedHashMap();
|
||||
maps[1] = new LinkedHashMap(16, 0.75F, true);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
maps[i].put("1", "a");
|
||||
maps[i].put("2", "b");
|
||||
}
|
||||
return maps;
|
||||
}
|
||||
public boolean hasSameBehavior(Object a, Object b)
|
||||
{
|
||||
LinkedHashMap mapA = (LinkedHashMap) a;
|
||||
LinkedHashMap mapB = (LinkedHashMap) b;
|
||||
return mapA.equals(mapB);
|
||||
}
|
||||
}
|
||||
|
||||
private static HashMap map = new HashMap();
|
||||
|
||||
static {
|
||||
@ -377,6 +418,8 @@ public class SerializableTest extends TestFmwk.TestGroup
|
||||
map.put("com.ibm.icu.util.ULocale", new ULocaleHandler());
|
||||
map.put("com.ibm.icu.util.Currency", new CurrencyHandler());
|
||||
map.put("com.ibm.icu.impl.JDKTimeZone", new JDKTimeZoneHandler());
|
||||
map.put("com.ibm.icu.impl.LinkedHashMap", new LinkedHashMapHandler());
|
||||
map.put("com.ibm.icu.impl.LRUMap", new LRUMapHandler());
|
||||
map.put("com.ibm.icu.impl.OlsonTimeZone", new OlsonTimeZoneHandler());
|
||||
map.put("com.ibm.icu.impl.TimeZoneAdapter", new TimeZoneAdapterHandler());
|
||||
map.put("com.ibm.icu.math.BigDecimal", new BigDecimalHandler());
|
||||
|
116
icu4j/src/com/ibm/icu/dev/test/util/LRUMapTest.java
Normal file
116
icu4j/src/com/ibm/icu/dev/test/util/LRUMapTest.java
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2006, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.dev.test.util;
|
||||
|
||||
import com.ibm.icu.dev.test.TestFmwk;
|
||||
import com.ibm.icu.impl.LRUMap;
|
||||
|
||||
public class LRUMapTest extends TestFmwk {
|
||||
public static void main(String[] args) throws Exception {
|
||||
LRUMapTest test = new LRUMapTest();
|
||||
test.run(args);
|
||||
}
|
||||
|
||||
public void TestLRUMap() {
|
||||
// Default size - max 64
|
||||
logln("Testing LRUMap with the default size");
|
||||
LRUMap map = new LRUMap();
|
||||
execute(map, 64);
|
||||
|
||||
// max size - 16
|
||||
logln("Testing LRUMap with initial/max size - 4/16");
|
||||
map = new LRUMap(4, 16);
|
||||
execute(map, 16);
|
||||
}
|
||||
|
||||
private void execute(LRUMap map, int maxSize /* maxSize > 6 */) {
|
||||
Integer num;
|
||||
String numStr;
|
||||
|
||||
for (int i = 0; i <= maxSize; i++) {
|
||||
num = new Integer(i);
|
||||
numStr = num.toString();
|
||||
map.put(numStr, num);
|
||||
}
|
||||
// The first key/value should be removed, because
|
||||
// we already put 65 entries
|
||||
num = (Integer)map.get("0");
|
||||
if (num == null) {
|
||||
logln("OK: The entry '0' was removed.");
|
||||
}
|
||||
else {
|
||||
errln("The entry '0' is still available.");
|
||||
}
|
||||
// Access the second entry '1', which is currently
|
||||
// the eldest.
|
||||
num = (Integer)map.get("1");
|
||||
if (num == null) {
|
||||
errln("The eldest entry '1' was removed.");
|
||||
}
|
||||
else {
|
||||
logln("OK: The eldest entry '1' is available.");
|
||||
}
|
||||
// Put another entry - because the entry '1' was
|
||||
// accessed above, the entry '2' is the eldest and
|
||||
// putting new entry should remove the entry.
|
||||
num = new Integer(maxSize + 1);
|
||||
map.put(num.toString(), num);
|
||||
num = (Integer)map.get("2");
|
||||
if (num == null) {
|
||||
logln("OK: The entry '2' was removed.");
|
||||
}
|
||||
else {
|
||||
errln("The entry '2' is still available.");
|
||||
}
|
||||
// The entry '3' is the eldest for now.
|
||||
boolean b = map.containsKey("3");
|
||||
if (b) {
|
||||
logln("OK: The eldest entry '3' is available.");
|
||||
}
|
||||
else {
|
||||
errln("The eldest entry '3' was removed.");
|
||||
}
|
||||
// contansKey should not affect the access order
|
||||
num = new Integer(maxSize + 2);
|
||||
map.put(num.toString(), num);
|
||||
num = (Integer)map.get("3");
|
||||
if (num == null) {
|
||||
logln("OK: The entry '3' was removed.");
|
||||
}
|
||||
else {
|
||||
errln("The entry '3' is still available.");
|
||||
}
|
||||
// Putting existing entry with new value
|
||||
num = (Integer)map.put("4", new Integer(-4));
|
||||
if (num == null) {
|
||||
errln("The entry '4' no longer exists");
|
||||
}
|
||||
if (num.intValue() != 4) {
|
||||
errln("The value for '4' was not 4");
|
||||
}
|
||||
// The entry '5' is the eldest for now, because
|
||||
// the entry '4' was updated above.
|
||||
num = new Integer(maxSize + 3);
|
||||
map.put(num.toString(), num);
|
||||
num = (Integer)map.get("5");
|
||||
if (num == null) {
|
||||
logln("OK: The entry '5' was removed.");
|
||||
}
|
||||
else {
|
||||
errln("The entry '5' is still available.");
|
||||
}
|
||||
// Clear the map
|
||||
map.clear();
|
||||
num = (Integer)map.get("6");
|
||||
if (num == null) {
|
||||
logln("OK: The entry '6' was removed.");
|
||||
}
|
||||
else {
|
||||
errln("The entry '6' is still available.");
|
||||
}
|
||||
}
|
||||
}
|
180
icu4j/src/com/ibm/icu/dev/test/util/TextTrieMapTest.java
Normal file
180
icu4j/src/com/ibm/icu/dev/test/util/TextTrieMapTest.java
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2006, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.dev.test.util;
|
||||
|
||||
import com.ibm.icu.dev.test.TestFmwk;
|
||||
import com.ibm.icu.impl.TextTrieMap;
|
||||
|
||||
public class TextTrieMapTest extends TestFmwk {
|
||||
|
||||
private static final Integer SUN = new Integer(1);
|
||||
private static final Integer MON = new Integer(2);
|
||||
private static final Integer TUE = new Integer(3);
|
||||
private static final Integer WED = new Integer(4);
|
||||
private static final Integer THU = new Integer(5);
|
||||
private static final Integer FRI = new Integer(6);
|
||||
private static final Integer SAT = new Integer(7);
|
||||
|
||||
private static final Integer FOO = new Integer(-1);
|
||||
private static final Integer BAR = new Integer(-2);
|
||||
|
||||
private static final Object[][] TESTDATA = {
|
||||
{"Sunday", SUN},
|
||||
{"Monday", MON},
|
||||
{"Tuesday", TUE},
|
||||
{"Wednesday", WED},
|
||||
{"Thursday", THU},
|
||||
{"Friday", FRI},
|
||||
{"Saturday", SAT},
|
||||
{"Sun", SUN},
|
||||
{"Mon", MON},
|
||||
{"Tue", TUE},
|
||||
{"Wed", WED},
|
||||
{"Thu", THU},
|
||||
{"Fri", FRI},
|
||||
{"Sat", SAT},
|
||||
{"S", SUN},
|
||||
{"M", MON},
|
||||
{"T", TUE},
|
||||
{"W", WED},
|
||||
{"T", THU},
|
||||
{"F", FRI},
|
||||
{"S", SAT}
|
||||
};
|
||||
|
||||
private static final Object[][] TESTCASES = {
|
||||
{"Sunday", SUN, SUN},
|
||||
{"sunday", null, SUN},
|
||||
{"Mo", MON, MON},
|
||||
{"mo", null, MON},
|
||||
{"Thursday Friday", THU, THU},
|
||||
{"T", THU, THU},
|
||||
{"TEST", THU, THU},
|
||||
{"SUN", SAT, SUN},
|
||||
{"super", null, SAT},
|
||||
{"NO", null, null}
|
||||
};
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
TextTrieMapTest test = new TextTrieMapTest();
|
||||
test.run(args);
|
||||
}
|
||||
|
||||
public void TestCaseSensitive() {
|
||||
TextTrieMap map = new TextTrieMap(false);
|
||||
for (int i = 0; i < TESTDATA.length; i++) {
|
||||
map.put((String)TESTDATA[i][0], TESTDATA[i][1]);
|
||||
}
|
||||
|
||||
logln("Test for get(String)");
|
||||
for (int i = 0; i < TESTCASES.length; i++) {
|
||||
Object value = map.get((String)TESTCASES[i][0]);
|
||||
if (!eql(value, TESTCASES[i][1])) {
|
||||
errln("Invalid search results - Expected:" + TESTCASES[i][1] + " Actual:" + value);
|
||||
}
|
||||
}
|
||||
|
||||
logln("Test for get(String, int)");
|
||||
StringBuffer textBuf = new StringBuffer();
|
||||
for (int i = 0; i < TESTCASES.length; i++) {
|
||||
textBuf.setLength(0);
|
||||
for (int j = 0; j < i; j++) {
|
||||
textBuf.append('X');
|
||||
}
|
||||
textBuf.append(TESTCASES[i][0]);
|
||||
Object value = map.get(textBuf.toString(), i);
|
||||
if (!eql(value, TESTCASES[i][1])) {
|
||||
errln("Invalid search results - Expected:" + TESTCASES[i][1] + " Actual:" + value);
|
||||
}
|
||||
}
|
||||
|
||||
// Add duplicated entry
|
||||
Object prev = map.put("Sunday", FOO);
|
||||
if (!eql(prev, SUN)) {
|
||||
errln("The previous value of duplicated entry is not valid - Expected:" + SUN + " Actual:" + prev);
|
||||
}
|
||||
// Make sure the value is updated
|
||||
Object value = map.get("Sunday");
|
||||
if (!eql(value, FOO)) {
|
||||
errln("The map value is not valid - Expected:" + FOO + " Actual:" + value);
|
||||
}
|
||||
|
||||
// Add duplicated entry with different casing
|
||||
prev = map.put("sunday", BAR);
|
||||
if (!eql(prev, null)) {
|
||||
errln("The value should be new in the trie map - Expected:null" + " Actual:" + prev);
|
||||
}
|
||||
// Make sure the value is valid
|
||||
value = map.get("sunday");
|
||||
if (!eql(value, BAR)) {
|
||||
errln("The map value is not valid - Expected:" + BAR + " Actual:" + value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void TestCaseInsensitive() {
|
||||
TextTrieMap map = new TextTrieMap(true);
|
||||
for (int i = 0; i < TESTDATA.length; i++) {
|
||||
map.put((String)TESTDATA[i][0], TESTDATA[i][1]);
|
||||
}
|
||||
|
||||
logln("Test for get(String)");
|
||||
for (int i = 0; i < TESTCASES.length; i++) {
|
||||
Object value = map.get((String)TESTCASES[i][0]);
|
||||
if (!eql(value, TESTCASES[i][2])) {
|
||||
errln("Invalid search results - Expected:" + TESTCASES[i][2] + " Actual:" + value);
|
||||
}
|
||||
}
|
||||
|
||||
logln("Test for get(String, int)");
|
||||
StringBuffer textBuf = new StringBuffer();
|
||||
for (int i = 0; i < TESTCASES.length; i++) {
|
||||
textBuf.setLength(0);
|
||||
for (int j = 0; j < i; j++) {
|
||||
textBuf.append('X');
|
||||
}
|
||||
textBuf.append(TESTCASES[i][0]);
|
||||
Object value = map.get(textBuf.toString(), i);
|
||||
if (!eql(value, TESTCASES[i][2])) {
|
||||
errln("Invalid search results - Expected:" + TESTCASES[i][2] + " Actual:" + value);
|
||||
}
|
||||
}
|
||||
|
||||
// Add duplicated entry
|
||||
Object prev = map.put("Sunday", FOO);
|
||||
if (!eql(prev, SUN)) {
|
||||
errln("The previous value of duplicated entry is not valid - Expected:" + SUN + " Actual:" + prev);
|
||||
}
|
||||
// Make sure the value is updated
|
||||
Object value = map.get("Sunday");
|
||||
if (!eql(value, FOO)) {
|
||||
errln("The map value is not valid - Expected:" + FOO + " Actual:" + value);
|
||||
}
|
||||
|
||||
// Add duplicated entry with different casing
|
||||
prev = map.put("sunday", BAR);
|
||||
if (!eql(prev, FOO)) {
|
||||
errln("The value should be new in the trie map - Expected:" + FOO + " Actual:" + prev);
|
||||
}
|
||||
// Make sure the value is updated
|
||||
value = map.get("sunday");
|
||||
if (!eql(value, BAR)) {
|
||||
errln("The map value is not valid - Expected:" + BAR + " Actual:" + value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean eql(Object o1, Object o2) {
|
||||
if (o1 == null || o2 == null) {
|
||||
if (o1 == null && o2 == null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return o1.equals(o2);
|
||||
}
|
||||
}
|
53
icu4j/src/com/ibm/icu/impl/LRUMap.java
Normal file
53
icu4j/src/com/ibm/icu/impl/LRUMap.java
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2006, International Business Machines Corporation and others.
|
||||
* All Rights Reserved.
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
//#ifndef FOUNDATION
|
||||
import java.util.LinkedHashMap;
|
||||
//#endif
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* Simple LRU (Least Recent Used) Map implementation
|
||||
*/
|
||||
public class LRUMap extends LinkedHashMap {
|
||||
private static final long serialVersionUID = -8178106459089682120L;
|
||||
|
||||
private static final int DEFAULT_MAXCAPACITY = 64;
|
||||
private static final int DEFAULT_INITIALCAPACITY = 16;
|
||||
private static final float DEFAULT_LOADFACTOR = 0.75F;
|
||||
|
||||
private final int maxCapacity;
|
||||
|
||||
/**
|
||||
* Construct a new LRU map with the default initial
|
||||
* capacity(16) and the maximum capacity(64).
|
||||
*/
|
||||
public LRUMap() {
|
||||
super(DEFAULT_INITIALCAPACITY, DEFAULT_LOADFACTOR, true);
|
||||
maxCapacity = DEFAULT_MAXCAPACITY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new LRU map with the specified initial
|
||||
* capacity and the maximum capacity
|
||||
*
|
||||
* @param initialCapacity initial capacity of the map
|
||||
* @param maxCapacity maximum capacity of the map
|
||||
*/
|
||||
public LRUMap(int initialCapacity, int maxCapacity) {
|
||||
super(initialCapacity, DEFAULT_LOADFACTOR, true);
|
||||
this.maxCapacity = maxCapacity;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the eldest entry when the size exceeds the limit
|
||||
*/
|
||||
protected boolean removeEldestEntry(Map.Entry eldest) {
|
||||
return (size() > maxCapacity);
|
||||
}
|
||||
}
|
175
icu4j/src/com/ibm/icu/impl/LinkedHashMap.java
Normal file
175
icu4j/src/com/ibm/icu/impl/LinkedHashMap.java
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2006, International Business Machines Corporation and others.
|
||||
* All Rights Reserved.
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* JDK1.4 LinkedHashMap equivalent implementation for
|
||||
* Java foundation profile support. This class is used
|
||||
* by <code>com.ibm.icu.impl.LRUMap</code> on eclipse
|
||||
* distributions, which require JDK1.3/Java Foundation
|
||||
* profile support.
|
||||
*/
|
||||
public class LinkedHashMap extends HashMap {
|
||||
private static final long serialVersionUID = -2497823480436618075L;
|
||||
|
||||
private boolean accessOrder = false;
|
||||
private LinkedList keyList = new LinkedList();
|
||||
|
||||
public LinkedHashMap() {
|
||||
super();
|
||||
}
|
||||
|
||||
public LinkedHashMap(int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
}
|
||||
|
||||
public LinkedHashMap(int initialCapacity, float loadFactor) {
|
||||
super(initialCapacity, loadFactor);
|
||||
}
|
||||
|
||||
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
|
||||
super(initialCapacity, loadFactor);
|
||||
this.accessOrder = accessOrder;
|
||||
}
|
||||
|
||||
public LinkedHashMap(Map m) {
|
||||
super();
|
||||
putAll(m);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
super.clear();
|
||||
keyList.clear();
|
||||
}
|
||||
|
||||
public Object remove(Object key) {
|
||||
Object value = super.remove(key);
|
||||
if (value == null) {
|
||||
// null might be returned for a map entry
|
||||
// for null value. So we need to check if
|
||||
// the key is actually available or not.
|
||||
// If the key list contains the key, then
|
||||
// remove the key from the list.
|
||||
int index = getKeyIndex(key);
|
||||
if (index >= 0) {
|
||||
keyList.remove(index);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public Object get(Object key) {
|
||||
Object value = super.get(key);
|
||||
if (accessOrder) {
|
||||
// When accessOrder is true, move the key
|
||||
// to the end of the list
|
||||
int index = getKeyIndex(key);
|
||||
if (index >= 0) {
|
||||
if (index != keyList.size() - 1) {
|
||||
keyList.remove(index);
|
||||
keyList.addLast(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public void putAll(Map m) {
|
||||
Set keySet = m.keySet();
|
||||
Iterator it = keySet.iterator();
|
||||
while (it.hasNext()) {
|
||||
Object key = it.next();
|
||||
Object value = m.get(key);
|
||||
put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Object put(Object key, Object value) {
|
||||
Object oldValue = super.put(key, value);
|
||||
|
||||
// Move the key to the end of key list
|
||||
// if it exists. If not, append the key
|
||||
// to the end of key list
|
||||
int index = getKeyIndex(key);
|
||||
if (index >= 0) {
|
||||
if (index != keyList.size() - 1) {
|
||||
keyList.remove(index);
|
||||
keyList.addLast(key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
keyList.addLast(key);
|
||||
}
|
||||
|
||||
// Check if we need to remove the eldest
|
||||
// entry.
|
||||
Object eldestKey = keyList.getFirst();
|
||||
Object eldestValue = super.get(eldestKey);
|
||||
MapEntry entry = new MapEntry(eldestKey, eldestValue);
|
||||
if (removeEldestEntry(entry)) {
|
||||
keyList.removeFirst();
|
||||
super.remove(eldestKey);
|
||||
}
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
protected boolean removeEldestEntry(Map.Entry eldest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private int getKeyIndex(Object key) {
|
||||
int index = -1;
|
||||
for (int i = 0; i < keyList.size(); i++) {
|
||||
Object o = keyList.get(i);
|
||||
if (o.equals(key)) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
protected static class MapEntry implements Map.Entry {
|
||||
private Object key;
|
||||
private Object value;
|
||||
|
||||
private MapEntry(Object key, Object value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
Object otherKey = ((Map.Entry)o).getKey();
|
||||
Object otherValue = ((Map.Entry)o).getValue();
|
||||
if (key.equals(otherKey) && value.equals(otherValue)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Object setValue(Object value) {
|
||||
Object oldValue = this.value;
|
||||
this.value = value;
|
||||
return oldValue;
|
||||
}
|
||||
}
|
||||
}
|
118
icu4j/src/com/ibm/icu/impl/SoftCache.java
Normal file
118
icu4j/src/com/ibm/icu/impl/SoftCache.java
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2006, International Business Machines Corporation and others.
|
||||
* All Rights Reserved.
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.SoftReference;
|
||||
|
||||
/**
|
||||
* LRU hash map using softly-referenced values
|
||||
*/
|
||||
public class SoftCache {
|
||||
private final LRUMap map;
|
||||
private final ReferenceQueue queue = new ReferenceQueue();
|
||||
|
||||
/**
|
||||
* Construct a SoftCache with default cache size
|
||||
*/
|
||||
public SoftCache() {
|
||||
map = new LRUMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a SoftCache with sepcified initial/max size
|
||||
*
|
||||
* @param initialSize the initial cache size
|
||||
* @param maxSize the maximum cache size
|
||||
*/
|
||||
public SoftCache(int initialSize, int maxSize) {
|
||||
map = new LRUMap(initialSize, maxSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an object to the cache
|
||||
* @param key key object
|
||||
* @param value value object
|
||||
* @return the value previously put, null when not matching key is found.
|
||||
*/
|
||||
public synchronized Object put(Object key, Object value) {
|
||||
if (key == null || value == null) {
|
||||
throw new IllegalArgumentException("Key and value must not be null");
|
||||
}
|
||||
ProcessQueue();
|
||||
Object obj = map.put(key, new SoftMapEntry(key, value, queue));
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object from the cache
|
||||
* @param key key object
|
||||
* @return the cached value, null when the value is not found.
|
||||
*/
|
||||
public synchronized Object get(Object key) {
|
||||
ProcessQueue();
|
||||
Object obj = null;
|
||||
SoftMapEntry entry = (SoftMapEntry)map.get(key);
|
||||
if (entry != null) {
|
||||
obj = entry.get();
|
||||
if (obj == null) {
|
||||
// It is unlikely to enter into this block, because
|
||||
// ProcessQueue() should already remove a map entrie
|
||||
// whose value was deleted by the garbage collactor.
|
||||
map.remove(key);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a cache entry from the cache
|
||||
* @param key key object
|
||||
* @return the value of cache entry which is removed from this cache,
|
||||
* or null when no entry for the key was no found.
|
||||
*/
|
||||
public synchronized Object remove(Object key) {
|
||||
return map.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache contents
|
||||
*/
|
||||
public synchronized void clear() {
|
||||
ProcessQueue();
|
||||
map.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove map entries which no longer have value
|
||||
*/
|
||||
private void ProcessQueue() {
|
||||
while (true) {
|
||||
SoftMapEntry entry = (SoftMapEntry)queue.poll();
|
||||
if (entry == null) {
|
||||
break;
|
||||
}
|
||||
map.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class for map entry with soft-referenced value
|
||||
*/
|
||||
private static class SoftMapEntry extends SoftReference {
|
||||
private final Object key;
|
||||
|
||||
private SoftMapEntry(Object key, Object value, ReferenceQueue queue) {
|
||||
super(value, queue);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
private Object getKey() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
222
icu4j/src/com/ibm/icu/impl/TextTrieMap.java
Normal file
222
icu4j/src/com/ibm/icu/impl/TextTrieMap.java
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2006, International Business Machines Corporation and others.
|
||||
* All Rights Reserved.
|
||||
* *****************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.ibm.icu.lang.UCharacter;
|
||||
import com.ibm.icu.text.UTF16;
|
||||
|
||||
/**
|
||||
* TextTrieMap is a trie implementation for supporting
|
||||
* fast prefix match for the key.
|
||||
*/
|
||||
public class TextTrieMap {
|
||||
/**
|
||||
* Costructs a TextTrieMap object.
|
||||
*
|
||||
* @param ignoreCase true to use case insensitive match
|
||||
*/
|
||||
public TextTrieMap(boolean ignoreCase) {
|
||||
this.ignoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the text key and its associated object in this object.
|
||||
*
|
||||
* @param text The text.
|
||||
* @param o The object associated with the text.
|
||||
* @return The previous value associated with specified text,
|
||||
* or null if there was no mapping for the text.
|
||||
*/
|
||||
public synchronized Object put(String text, Object o) {
|
||||
CharacterNode node = root;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
int ch = UTF16.charAt(text, i);
|
||||
node = node.addChildNode(ch);
|
||||
if (UTF16.getCharCount(ch) == 2) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
Object prevObj = node.getObject();
|
||||
node.setObject(o);
|
||||
return prevObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object associated with the longest prefix
|
||||
* matching string key.
|
||||
*
|
||||
* @param text The text to be matched with prefixes.
|
||||
* @return The object associated with the longet prefix matching
|
||||
* matching key, or null if no matching entry is found.
|
||||
*/
|
||||
public Object get(String text) {
|
||||
return get(root, text, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object associated with the longest prefix
|
||||
* matching string key starting at the specified position.
|
||||
*
|
||||
* @param text The text to be matched with prefixes.
|
||||
* @param start The start index of of the text
|
||||
* @return The object associated with the longet prefix matching
|
||||
* matching key, or null if no matching entry is found.
|
||||
*/
|
||||
public Object get(String text, int start) {
|
||||
return get(root, text, start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object associated with the longet prefix
|
||||
* matching string key under the specified node.
|
||||
*
|
||||
* @param node The character node in this trie.
|
||||
* @param text The text to be matched with prefixes.
|
||||
* @param index The current index within the text.
|
||||
* @return The object associated with the longest prefix
|
||||
* match under the node.
|
||||
*/
|
||||
private synchronized Object get(CharacterNode node, String text, int index) {
|
||||
Object obj = node.getObject();
|
||||
if (index < text.length()) {
|
||||
List childNodes = node.getChildNodes();
|
||||
if (childNodes == null) {
|
||||
return obj;
|
||||
}
|
||||
int ch = UTF16.charAt(text, index);
|
||||
int chLen = UTF16.getCharCount(ch);
|
||||
for (int i = 0; i < childNodes.size(); i++) {
|
||||
CharacterNode child = (CharacterNode)childNodes.get(i);
|
||||
if (compare(ch, child.getCharacter())) {
|
||||
Object tmp = get(child, text, index + chLen);
|
||||
if (tmp != null) {
|
||||
obj = tmp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* A private method used for comparing two characters.
|
||||
*
|
||||
* @param ch1 The first character.
|
||||
* @param ch2 The second character.
|
||||
* @return true if the first character matches the second.
|
||||
*/
|
||||
private boolean compare(int ch1, int ch2) {
|
||||
if (ch1 == ch2) {
|
||||
return true;
|
||||
}
|
||||
else if (ignoreCase) {
|
||||
if (UCharacter.toLowerCase(ch1) == UCharacter.toLowerCase(ch2)) {
|
||||
return true;
|
||||
}
|
||||
else if (UCharacter.toUpperCase(ch1) == UCharacter.toUpperCase(ch2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The root node of this trie
|
||||
private CharacterNode root = new CharacterNode(0);
|
||||
|
||||
// Character matching option
|
||||
boolean ignoreCase;
|
||||
|
||||
/**
|
||||
* Inner class representing a character node in the trie.
|
||||
*/
|
||||
private class CharacterNode {
|
||||
int character;
|
||||
List children;
|
||||
Object obj;
|
||||
|
||||
/**
|
||||
* Constructs a node for the character.
|
||||
*
|
||||
* @param ch The character associated with this node.
|
||||
*/
|
||||
public CharacterNode(int ch) {
|
||||
character = ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character associated with this node.
|
||||
*
|
||||
* @return The character
|
||||
*/
|
||||
public int getCharacter() {
|
||||
return character;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object to the node. Only a leaf node has
|
||||
* the reference of an object associated with a key.
|
||||
*
|
||||
* @param obj The object set in the leaf node.
|
||||
*/
|
||||
public void setObject(Object obj) {
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object associated the leaf node.
|
||||
*
|
||||
* @return The object.
|
||||
*/
|
||||
public Object getObject() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child node for the characer under this character
|
||||
* node in the trie. When the matching child node already
|
||||
* exists, the reference of the existing child node is
|
||||
* returned.
|
||||
*
|
||||
* @param ch The character associated with a child node.
|
||||
* @return The child node.
|
||||
*/
|
||||
public CharacterNode addChildNode(int ch) {
|
||||
if (children == null) {
|
||||
children = new ArrayList();
|
||||
CharacterNode newNode = new CharacterNode(ch);
|
||||
children.add(newNode);
|
||||
return newNode;
|
||||
}
|
||||
CharacterNode node = null;
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
CharacterNode cur = (CharacterNode)children.get(i);
|
||||
if (compare(ch, cur.getCharacter())) {
|
||||
node = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (node == null) {
|
||||
node = new CharacterNode(ch);
|
||||
children.add(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of child nodes under this node.
|
||||
*
|
||||
* @return The list of child nodes.
|
||||
*/
|
||||
public List getChildNodes() {
|
||||
return children;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
**********************************************************************
|
||||
* Copyright (c) 2003-2005, International Business Machines
|
||||
* Copyright (c) 2003-2006, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
**********************************************************************
|
||||
* Author: Alan Liu
|
||||
@ -466,6 +466,7 @@ public final class ZoneMeta {
|
||||
private static final String kCUSTOM_ID= "Custom";
|
||||
//private static ICUResourceBundle zoneBundle = null;
|
||||
private static java.util.Enumeration idEnum = null;
|
||||
private static SoftCache zoneCache = new SoftCache();
|
||||
/**
|
||||
* The Olson data is stored the "zoneinfo" resource bundle.
|
||||
* Sub-resources are organized into three ranges of data: Zones, final
|
||||
@ -505,16 +506,20 @@ public final class ZoneMeta {
|
||||
* found, return 0.
|
||||
*/
|
||||
public static TimeZone getSystemTimeZone(String id) {
|
||||
TimeZone z = (TimeZone)zoneCache.get(id);
|
||||
if (z == null) {
|
||||
try{
|
||||
ICUResourceBundle top = (ICUResourceBundle)ICUResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "zoneinfo", ICUResourceBundle.ICU_DATA_CLASS_LOADER);
|
||||
ICUResourceBundle res = openOlsonResource(id);
|
||||
TimeZone z = new OlsonTimeZone(top, res);
|
||||
z = new OlsonTimeZone(top, res);
|
||||
z.setID(id);
|
||||
return z;
|
||||
zoneCache.put(id, z);
|
||||
}catch(Exception ex){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return (TimeZone)z.clone();
|
||||
}
|
||||
|
||||
public static TimeZone getGMT(){
|
||||
TimeZone z = new SimpleTimeZone(0, kGMT_ID);
|
||||
|
@ -9,6 +9,7 @@ package com.ibm.icu.text;
|
||||
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.impl.CalendarData;
|
||||
import com.ibm.icu.impl.TextTrieMap;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.GregorianCalendar;
|
||||
@ -16,10 +17,12 @@ import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
import com.ibm.icu.impl.ZoneMeta;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.impl.SoftCache;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
@ -774,10 +777,13 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
public String[][] getZoneStrings() {
|
||||
if(zoneStrings==null){
|
||||
initZoneStrings();
|
||||
String[][] strings = zoneStrings;
|
||||
if(strings == null){
|
||||
// get the default zone strings
|
||||
ZoneItemInfo zii = getDefaultZoneItemInfo();
|
||||
strings = zii.tzStrings;
|
||||
}
|
||||
return duplicate(zoneStrings);
|
||||
return duplicate(strings);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -787,12 +793,8 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
*/
|
||||
public void setZoneStrings(String[][] newZoneStrings) {
|
||||
zoneStrings = duplicate(newZoneStrings);
|
||||
if(zoneStringsHash==null){
|
||||
zoneStringsHash = new HashMap();
|
||||
}
|
||||
initZoneStrings(newZoneStrings);
|
||||
// reset the zone strings.
|
||||
zoneStrings=null;
|
||||
// need to update local zone item info
|
||||
localZoneItemInfo = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -839,15 +841,15 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
public int hashCode() {
|
||||
int hashcode = 0;
|
||||
hashcode ^= requestedLocale.toString().hashCode();
|
||||
if(zoneStringsHash!=null){
|
||||
for(Iterator iter=zoneStringsHash.keySet().iterator(); iter.hasNext();){
|
||||
String key = (String)iter.next();
|
||||
String[] strings = (String[])zoneStringsHash.get(key);
|
||||
hashcode ^= key.hashCode();
|
||||
for(int i=0; i< strings.length; i++){
|
||||
if(strings[i]!=null){
|
||||
hashcode ^= strings[i].hashCode();
|
||||
String[][] tzStrings = zoneStrings;
|
||||
if (tzStrings == null){
|
||||
ZoneItemInfo zii = getDefaultZoneItemInfo();
|
||||
tzStrings = zii.tzStrings;
|
||||
}
|
||||
for(int i = 0; i < tzStrings.length; i++) {
|
||||
for (int j = 0; j < tzStrings[i].length; j++) {
|
||||
if (tzStrings[i][j] != null) {
|
||||
hashcode ^= tzStrings[i][j].hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -878,7 +880,7 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
&& Utility.arrayEquals(standaloneShortWeekdays, that.standaloneShortWeekdays)
|
||||
&& Utility.arrayEquals(standaloneNarrowWeekdays, that.standaloneNarrowWeekdays)
|
||||
&& Utility.arrayEquals(ampms, that.ampms)
|
||||
&& hashEquals(zoneStringsHash, that.zoneStringsHash)
|
||||
&& arrayOfArrayEquals(zoneStrings, that.zoneStrings)
|
||||
// getDiplayName maps deprecated country and language codes to the current ones
|
||||
// too bad there is no way to get the current codes!
|
||||
// I thought canolicalize() would map the codes but .. alas! it doesn't.
|
||||
@ -1085,62 +1087,216 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
ULocale uloc = rb.getULocale();
|
||||
setLocale(uloc, uloc);
|
||||
}
|
||||
private static final boolean hashEquals(HashMap h1, HashMap h2){
|
||||
if(h1==h2){ // both are null
|
||||
|
||||
private static final boolean arrayOfArrayEquals(Object[][] aa1, Object[][]aa2) {
|
||||
if (aa1 == aa2) { // both are null
|
||||
return true;
|
||||
}
|
||||
if(h1==null || h2==null){ // one is null and the other is not
|
||||
if (aa1 == null || aa2 == null) { // one is null and the other is not
|
||||
return false;
|
||||
}
|
||||
if(h1.size() != h2.size()){
|
||||
if (aa1.length != aa2.length) {
|
||||
return false;
|
||||
}
|
||||
Iterator i1 = h1.keySet().iterator();
|
||||
Iterator i2 = h2.keySet().iterator();
|
||||
boolean equal = true;
|
||||
for (int i = 0; i < aa1.length; i++) {
|
||||
equal = Utility.arrayEquals(aa1[i], aa2[i]);
|
||||
if (!equal) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return equal;
|
||||
}
|
||||
|
||||
while(i1.hasNext()){
|
||||
String key = (String) i1.next();
|
||||
String[] s1 = (String[])h1.get(key);
|
||||
String[] s2 = (String[])h2.get(key);
|
||||
if(Utility.arrayEquals(s1, s2)==false){
|
||||
return false;
|
||||
/**
|
||||
* Package private: used by SimpleDateFormat.
|
||||
* Gets the string for the specified time zone.
|
||||
* @param zid The time zone ID
|
||||
* @param type The type of zone string
|
||||
* @return The zone string, or null if not available.
|
||||
*/
|
||||
String getZoneString(String zid, int type) {
|
||||
// Try local zone item info first
|
||||
String zoneString = getZoneString(getLocalZoneItemInfo(), zid, type);
|
||||
if (zoneString == null) {
|
||||
// Fallback to the default info
|
||||
zoneString = getZoneString(getDefaultZoneItemInfo(), zid, type);
|
||||
}
|
||||
return zoneString;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private void initZoneStrings(){
|
||||
if(zoneStringsHash==null){
|
||||
initZoneStringsHash();
|
||||
}
|
||||
zoneStrings = new String[zoneStringsHash.size()][8];
|
||||
int i = 0;
|
||||
for (Iterator it = zoneStringsKeyList.iterator(); it.hasNext();) {
|
||||
String key = (String)it.next();
|
||||
String[] strings = (String[])zoneStringsHash.get(key);
|
||||
zoneStrings[i][0] = key;
|
||||
zoneStrings[i][1] = strings[TIMEZONE_LONG_STANDARD];
|
||||
zoneStrings[i][2] = strings[TIMEZONE_SHORT_STANDARD];
|
||||
zoneStrings[i][3] = strings[TIMEZONE_LONG_DAYLIGHT];
|
||||
zoneStrings[i][4] = strings[TIMEZONE_SHORT_DAYLIGHT];
|
||||
zoneStrings[i][5] = strings[TIMEZONE_EXEMPLAR_CITY];
|
||||
if(zoneStrings[i][5]==null){
|
||||
zoneStrings[i][5] = strings[TIMEZONE_LONG_GENERIC];
|
||||
}else{
|
||||
zoneStrings[i][6] = strings[TIMEZONE_LONG_GENERIC];
|
||||
}
|
||||
if(zoneStrings[i][6]==null){
|
||||
zoneStrings[i][6] = strings[TIMEZONE_SHORT_GENERIC];
|
||||
}else{
|
||||
zoneStrings[i][7] = strings[TIMEZONE_SHORT_GENERIC];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
private void initZoneStringsHash(){
|
||||
|
||||
zoneStringsHash = new HashMap();
|
||||
zoneStringsKeyList = new ArrayList();
|
||||
for (ULocale tempLocale = requestedLocale; tempLocale != null; tempLocale = tempLocale.getFallback()) {
|
||||
/**
|
||||
* Gets the zone string from the specified zone item info
|
||||
*/
|
||||
private String getZoneString(ZoneItemInfo zinfo, String zid, int type) {
|
||||
if (zinfo == null) {
|
||||
return null;
|
||||
}
|
||||
String[] names = (String[])zinfo.tzidMap.get(zid);
|
||||
if (names != null) {
|
||||
// get name for the type
|
||||
int index = -1;
|
||||
switch (type) {
|
||||
case TIMEZONE_LONG_STANDARD:
|
||||
index = 1;
|
||||
break;
|
||||
case TIMEZONE_SHORT_STANDARD:
|
||||
index = 2;
|
||||
break;
|
||||
case TIMEZONE_LONG_DAYLIGHT:
|
||||
index = 3;
|
||||
break;
|
||||
case TIMEZONE_SHORT_DAYLIGHT:
|
||||
index = 4;
|
||||
break;
|
||||
case TIMEZONE_EXEMPLAR_CITY:
|
||||
if (names.length == 6 || names.length == 8) {
|
||||
index = 5;
|
||||
}
|
||||
break;
|
||||
case TIMEZONE_LONG_GENERIC:
|
||||
if (names.length == 8) {
|
||||
index = 6;
|
||||
} else {
|
||||
index = 5;
|
||||
}
|
||||
break;
|
||||
case TIMEZONE_SHORT_GENERIC:
|
||||
if (names.length == 8) {
|
||||
index = 7;
|
||||
} else {
|
||||
index = 6;
|
||||
}
|
||||
}
|
||||
if (index < names.length) {
|
||||
return names[index];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class ZoneItem{
|
||||
String value;
|
||||
int type;
|
||||
String zid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Package private: used by SimpleDateformat
|
||||
* Gets the ZoneItem instance which has zone strings
|
||||
* which matches the specified text.
|
||||
* @param text The text which contains a zone string
|
||||
* @param start The start position of zone string in the text
|
||||
* @return A ZonItem instance for the longest matching zone
|
||||
* string.
|
||||
*/
|
||||
ZoneItem findZoneIDTypeValue(String text, int start){
|
||||
ZoneItem item = null;
|
||||
int textLength = text.length() - start;
|
||||
if (lastZoneItem != null && textLength == lastZoneItem.value.length()) {
|
||||
if (text.regionMatches(true, start, lastZoneItem.value, 0, textLength)) {
|
||||
item = new ZoneItem();
|
||||
item.type = lastZoneItem.type;
|
||||
item.value = lastZoneItem.value;
|
||||
item.zid = lastZoneItem.zid;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
ZoneItemInfo zinfo = getLocalZoneItemInfo();
|
||||
if (zinfo != null) {
|
||||
// look up the zone string in localZoneItemInfo first
|
||||
item = (ZoneItem)zinfo.tzStringMap.get(text, start);
|
||||
}
|
||||
|
||||
// look up the zone string in default ZoneItemInfo for the locale
|
||||
zinfo = getDefaultZoneItemInfo();
|
||||
ZoneItem itemForLocale = (ZoneItem)zinfo.tzStringMap.get(text, start);
|
||||
if (itemForLocale != null) {
|
||||
// we want to use longer match
|
||||
if (item == null || itemForLocale.value.length() > item.value.length()) {
|
||||
item = itemForLocale;
|
||||
}
|
||||
}
|
||||
|
||||
if (item != null && textLength == item.value.length()) {
|
||||
// clone the last match for next time
|
||||
// only when the substring completely matches
|
||||
// with the value resolved
|
||||
lastZoneItem = new ZoneItem();
|
||||
lastZoneItem.type = item.type;
|
||||
lastZoneItem.value = item.value;
|
||||
lastZoneItem.zid = item.zid;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* A class holds zone strings and searchable indice
|
||||
*/
|
||||
private class ZoneItemInfo {
|
||||
String[][] tzStrings;
|
||||
HashMap tzidMap;
|
||||
TextTrieMap tzStringMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* A cache for ZoneItemInfo objects, shared by class instances.
|
||||
*/
|
||||
private static SoftCache zoneItemInfoCache = new SoftCache();
|
||||
|
||||
/**
|
||||
* A ZoneItemInfo instance which holds custom timezone strings
|
||||
*/
|
||||
private transient ZoneItemInfo localZoneItemInfo;
|
||||
|
||||
/**
|
||||
* Single entry cache for findZoneTypeValue()
|
||||
*/
|
||||
private transient ZoneItem lastZoneItem;
|
||||
|
||||
/**
|
||||
* Gets the ZoneItemInfo instance for the locale used by this object.
|
||||
* If it does not exist, create new one and register in the static cache.
|
||||
*/
|
||||
private ZoneItemInfo getDefaultZoneItemInfo() {
|
||||
ZoneItemInfo zii = (ZoneItemInfo)zoneItemInfoCache.get(requestedLocale);
|
||||
if (zii != null) {
|
||||
return zii;
|
||||
}
|
||||
zii = getZoneItemInfo(getDefaultZoneStrings(requestedLocale));
|
||||
// Add fallback display names
|
||||
String[] zoneIDs = TimeZone.getAvailableIDs();
|
||||
for (int i = 0; i < zoneIDs.length; i++) {
|
||||
Object o = zii.tzidMap.get(zoneIDs[i]);
|
||||
if (o != null) {
|
||||
// Already has names
|
||||
continue;
|
||||
}
|
||||
String value = ZoneMeta.displayFallback(zoneIDs[i], null, requestedLocale);
|
||||
if (value != null) {
|
||||
String[] strings = new String[8];
|
||||
strings[5] = value;
|
||||
zii.tzidMap.put(zoneIDs[i], strings);
|
||||
|
||||
ZoneItem item = new ZoneItem();
|
||||
item.zid = zoneIDs[i];
|
||||
item.value = value;
|
||||
item.type = TIMEZONE_EXEMPLAR_CITY;
|
||||
zii.tzStringMap.put(item.value, item);
|
||||
}
|
||||
}
|
||||
zoneItemInfoCache.put(requestedLocale, zii);
|
||||
return zii;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of zone strings for the specified locale.
|
||||
*/
|
||||
private static String[][] getDefaultZoneStrings(ULocale locale) {
|
||||
ArrayList tmpList = new ArrayList();
|
||||
HashSet tmpSet = new HashSet();
|
||||
for (ULocale tempLocale = locale; tempLocale != null; tempLocale = tempLocale.getFallback()) {
|
||||
ICUResourceBundle bundle = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, tempLocale);
|
||||
ICUResourceBundle zoneStringsBundle = bundle.getWithFallback("zoneStrings");
|
||||
for(int i = 0; i < zoneStringsBundle.getSize(); i++){
|
||||
@ -1150,198 +1306,130 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
if(key.length() == 0|| zoneTable.getType() != ICUResourceBundle.TABLE){
|
||||
continue;
|
||||
}
|
||||
String[] strings = new String[TIMEZONE_COUNT];
|
||||
if (tmpSet.contains(key)) {
|
||||
// only add if we don't have already
|
||||
continue;
|
||||
}
|
||||
String[] strings = new String[8];
|
||||
strings[0] = key;
|
||||
try {
|
||||
strings[TIMEZONE_SHORT_GENERIC] = zoneTable.getStringWithFallback(SHORT_GENERIC);
|
||||
strings[1] = zoneTable.getStringWithFallback(LONG_STANDARD);
|
||||
} catch (MissingResourceException ex) {
|
||||
// throw away the exception
|
||||
}
|
||||
try {
|
||||
strings[TIMEZONE_SHORT_STANDARD] = zoneTable.getStringWithFallback(SHORT_STANDARD);
|
||||
strings[2] = zoneTable.getStringWithFallback(SHORT_STANDARD);
|
||||
} catch (MissingResourceException ex) {
|
||||
// throw away the exception
|
||||
}
|
||||
try {
|
||||
strings[TIMEZONE_SHORT_DAYLIGHT] = zoneTable.getStringWithFallback(SHORT_DAYLIGHT);
|
||||
strings[3] = zoneTable.getStringWithFallback(LONG_DAYLIGHT);
|
||||
} catch (MissingResourceException ex) {
|
||||
// throw away the exception
|
||||
}
|
||||
try {
|
||||
strings[TIMEZONE_LONG_GENERIC] = zoneTable.getStringWithFallback(LONG_GENERIC);
|
||||
}catch( MissingResourceException ex){
|
||||
// throw away the exception
|
||||
}
|
||||
try{
|
||||
strings[TIMEZONE_LONG_STANDARD] = zoneTable.getStringWithFallback(LONG_STANDARD);
|
||||
}catch( MissingResourceException ex){
|
||||
// throw away the exception
|
||||
}
|
||||
try{
|
||||
strings[TIMEZONE_LONG_DAYLIGHT] = zoneTable.getStringWithFallback(LONG_DAYLIGHT);
|
||||
strings[4] = zoneTable.getStringWithFallback(SHORT_DAYLIGHT);
|
||||
} catch (MissingResourceException ex) {
|
||||
// throw away the exception
|
||||
}
|
||||
try {
|
||||
String city = zoneTable.getStringWithFallback(EXEMPLAR_CITY);
|
||||
|
||||
strings[TIMEZONE_EXEMPLAR_CITY] = ZoneMeta.displayFallback(key, city, tempLocale);
|
||||
strings[5] = ZoneMeta.displayFallback(key, city, tempLocale);
|
||||
} catch (MissingResourceException ex) {
|
||||
// throw away the exception
|
||||
}
|
||||
if(!zoneStringsHash.containsKey(key)){
|
||||
zoneStringsHash.put(key, strings); // only add if we don't have already
|
||||
zoneStringsKeyList.add(key);
|
||||
}
|
||||
try {
|
||||
strings[6] = zoneTable.getStringWithFallback(LONG_GENERIC);
|
||||
} catch (MissingResourceException ex) {
|
||||
// throw away the exception
|
||||
}
|
||||
try {
|
||||
strings[7] = zoneTable.getStringWithFallback(SHORT_GENERIC);
|
||||
} catch (MissingResourceException ex) {
|
||||
// throw away the exception
|
||||
}
|
||||
tmpList.add(strings);
|
||||
}
|
||||
}
|
||||
String[][] array = new String[tmpList.size()][8];
|
||||
tmpList.toArray(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
private ArrayList zoneStringsKeyList;
|
||||
private void initZoneStrings(String[][] newZoneStrings){
|
||||
if(newZoneStrings==null){
|
||||
return;
|
||||
/**
|
||||
* Gets the array of zone strings for the custom zone strings
|
||||
*/
|
||||
private ZoneItemInfo getLocalZoneItemInfo() {
|
||||
if (localZoneItemInfo == null && zoneStrings != null) {
|
||||
localZoneItemInfo = getZoneItemInfo(zoneStrings);
|
||||
}
|
||||
zoneStringsKeyList = new ArrayList();
|
||||
for(int row=0; row<newZoneStrings.length; row++){
|
||||
String key = newZoneStrings[row][0];
|
||||
String[] strings = (String[])zoneStringsHash.get(key);
|
||||
if(strings == null){
|
||||
strings = new String[TIMEZONE_COUNT];
|
||||
return localZoneItemInfo;
|
||||
}
|
||||
int colCount = zoneStrings[row].length;
|
||||
for (int col=1; col<colCount; ++col) {
|
||||
// fastCopyFrom() - see assignArray comments
|
||||
switch (col){
|
||||
|
||||
/**
|
||||
* Creates a new ZoneItemInfo instance from the array of time zone
|
||||
* strings.
|
||||
*/
|
||||
private ZoneItemInfo getZoneItemInfo(String[][] strings) {
|
||||
ZoneItemInfo zii = new ZoneItemInfo();
|
||||
zii.tzStrings = strings;
|
||||
zii.tzidMap = new HashMap();
|
||||
zii.tzStringMap = new TextTrieMap(true);
|
||||
for (int i = 0; i < strings.length; i++) {
|
||||
String zid = strings[i][0];
|
||||
if (zid != null && zid.length() > 0) {
|
||||
zii.tzidMap.put(zid, strings[i]);
|
||||
int nameCount = strings[i].length < 8 ? strings[i].length : 8;
|
||||
for (int j = 1; j < nameCount; j++) {
|
||||
if (strings[i][j] != null) {
|
||||
// map zoneStrings array index to timezone name type
|
||||
int type = -1;
|
||||
switch (j) {
|
||||
case 1:
|
||||
strings[TIMEZONE_LONG_STANDARD] = newZoneStrings[row][col];
|
||||
type = TIMEZONE_LONG_STANDARD;
|
||||
break;
|
||||
case 2:
|
||||
strings[TIMEZONE_SHORT_STANDARD] = newZoneStrings[row][col];
|
||||
type = TIMEZONE_SHORT_STANDARD;
|
||||
break;
|
||||
case 3:
|
||||
strings[TIMEZONE_LONG_DAYLIGHT] = newZoneStrings[row][col];
|
||||
type = TIMEZONE_LONG_DAYLIGHT;
|
||||
break;
|
||||
case 4:
|
||||
strings[TIMEZONE_SHORT_DAYLIGHT] = newZoneStrings[row][col];
|
||||
type = TIMEZONE_SHORT_DAYLIGHT;
|
||||
break;
|
||||
case 5:
|
||||
if(colCount==6 || colCount==8){
|
||||
strings[TIMEZONE_EXEMPLAR_CITY] = newZoneStrings[row][col];
|
||||
if (nameCount == 6 || nameCount == 8) {
|
||||
type = TIMEZONE_EXEMPLAR_CITY;
|
||||
} else {
|
||||
strings[TIMEZONE_LONG_GENERIC] = newZoneStrings[row][col];
|
||||
type = TIMEZONE_LONG_GENERIC;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if(colCount==8){
|
||||
strings[TIMEZONE_LONG_GENERIC] = newZoneStrings[row][col];
|
||||
if (nameCount == 8) {
|
||||
type = TIMEZONE_LONG_GENERIC;
|
||||
} else {
|
||||
strings[TIMEZONE_SHORT_GENERIC] = newZoneStrings[row][col];
|
||||
type = TIMEZONE_SHORT_GENERIC;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
strings[TIMEZONE_SHORT_GENERIC] = newZoneStrings[row][col];
|
||||
type = TIMEZONE_SHORT_GENERIC;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
zoneStringsHash.put(key, strings);
|
||||
zoneStringsKeyList.add(key);
|
||||
}
|
||||
}
|
||||
Iterator getZoneStringIDs(){
|
||||
return zoneStringsHash.keySet().iterator();
|
||||
}
|
||||
|
||||
String getZoneString(String zid, int type){
|
||||
|
||||
if(zoneStringsHash == null){
|
||||
//lazy initialization
|
||||
initZoneStringsHash();
|
||||
}
|
||||
|
||||
String[] stringsArray = (String[])zoneStringsHash.get(zid);
|
||||
if(stringsArray != null){
|
||||
return stringsArray[type];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
String getZoneID(String zid){
|
||||
if(zoneStringsHash == null){
|
||||
initZoneStringsHash();
|
||||
}
|
||||
String[] strings = (String[])zoneStringsHash.get(zid);
|
||||
if (strings != null) {
|
||||
return zid;
|
||||
}
|
||||
try{
|
||||
// Do a search through the equivalency group for the given ID
|
||||
int n = TimeZone.countEquivalentIDs(zid);
|
||||
if (n > 1) {
|
||||
int i;
|
||||
for (i=0; i<n; ++i) {
|
||||
String equivID = TimeZone.getEquivalentID(zid, i);
|
||||
if (equivID != zid) {
|
||||
strings = (String[])zoneStringsHash.get(equivID);
|
||||
if (strings != null) {
|
||||
return equivID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}catch(MissingResourceException ex){
|
||||
// throw away the exception
|
||||
}
|
||||
return null;
|
||||
}
|
||||
class ZoneItem{
|
||||
String value;
|
||||
int type;
|
||||
String zid;
|
||||
}
|
||||
ZoneItem getZoneItem(String zid, String text, int start){
|
||||
if(zoneStringsHash == null){
|
||||
initZoneStringsHash();
|
||||
// never occur
|
||||
continue;
|
||||
}
|
||||
ZoneItem item = new ZoneItem();
|
||||
|
||||
String[] strings = (String[])zoneStringsHash.get(zid);
|
||||
if(strings != null){
|
||||
for(int j=0; j<TIMEZONE_COUNT; j++){
|
||||
if(strings[j] != null && text.regionMatches(true, start, strings[j], 0, strings[j].length())){
|
||||
item.type = j;
|
||||
item.value = strings[j];
|
||||
item.zid = zid;
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
ZoneItem findZoneIDTypeValue(String text, int start){
|
||||
if(zoneStringsHash == null){
|
||||
initZoneStringsHash();
|
||||
}
|
||||
ZoneItem item = new ZoneItem();
|
||||
for (Iterator it = zoneStringsHash.keySet().iterator(); it.hasNext();) {
|
||||
String key = (String)it.next();
|
||||
String[] strings = (String[])zoneStringsHash.get(key);
|
||||
if(strings != null){
|
||||
for(int j=0; j<TIMEZONE_COUNT; j++){
|
||||
if(strings[j] != null && text.regionMatches(true, start, strings[j], 0, strings[j].length())){
|
||||
item.type = j;
|
||||
item.value = strings[j];
|
||||
item.zid = key;
|
||||
return item;
|
||||
item.value = strings[i][j];
|
||||
item.type = type;
|
||||
zii.tzStringMap.put(strings[i][j], item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return zii;
|
||||
}
|
||||
|
||||
private HashMap zoneStringsHash;
|
||||
/**
|
||||
* save the input locale
|
||||
*/
|
||||
@ -1455,11 +1543,10 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
dst.standaloneShortWeekdays = duplicate(src.standaloneShortWeekdays);
|
||||
dst.standaloneNarrowWeekdays = duplicate(src.standaloneNarrowWeekdays);
|
||||
dst.ampms = duplicate(src.ampms);
|
||||
if(src.zoneStringsHash != null){
|
||||
dst.zoneStringsHash = (HashMap)src.zoneStringsHash.clone();
|
||||
if (src.zoneStrings != null) {
|
||||
dst.zoneStrings = duplicate(src.zoneStrings);
|
||||
}
|
||||
dst.requestedLocale = new ULocale(src.requestedLocale.toString());
|
||||
//dst.zoneStrings = duplicate(src.zoneStrings);
|
||||
dst.localPatternChars = new String (src.localPatternChars);
|
||||
}
|
||||
|
||||
|
@ -287,6 +287,13 @@ public class SimpleDateFormat extends DateFormat {
|
||||
*/
|
||||
private transient boolean useFastFormat;
|
||||
|
||||
/**
|
||||
* If true, this object supports fast number format
|
||||
*/
|
||||
private transient boolean useFastZeroPaddingNumber;
|
||||
private transient char zeroDigit;
|
||||
private char[] decimalBuf = new char[10]; // 10 digit is good enough to store Interger.MAX_VALUE
|
||||
|
||||
/**
|
||||
* Construct a SimpleDateFormat using the default pattern for the default
|
||||
* locale. <b>Note:</b> Not all locales support SimpleDateFormat; for full
|
||||
@ -467,11 +474,17 @@ public class SimpleDateFormat extends DateFormat {
|
||||
// TODO: convert to use ULocale APIs when we get to the text package
|
||||
numberFormat = NumberFormat.getInstance(loc);
|
||||
numberFormat.setGroupingUsed(false);
|
||||
useFastZeroPaddingNumber = false;
|
||||
///CLOVER:OFF
|
||||
// difficult to test for case where NumberFormat.getInstance does not
|
||||
// return a DecimalFormat
|
||||
if (numberFormat instanceof DecimalFormat)
|
||||
if (numberFormat instanceof DecimalFormat) {
|
||||
((DecimalFormat)numberFormat).setDecimalSeparatorAlwaysShown(false);
|
||||
zeroDigit = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getZeroDigit();
|
||||
if (numberFormat.getClass().getName().equals("com.ibm.icu.text.DecimalFormat")) {
|
||||
useFastZeroPaddingNumber = true;
|
||||
}
|
||||
}
|
||||
///CLOVER:ON
|
||||
numberFormat.setParseIntegerOnly(true); /* So that dd.MM.yy can be parsed */
|
||||
numberFormat.setMinimumFractionDigits(0); // To prevent "Jan 1.00, 1997.00"
|
||||
@ -884,7 +897,7 @@ public class SimpleDateFormat extends DateFormat {
|
||||
}
|
||||
val = (val / 3) * 5 + (val % 60); // minutes => KKmm
|
||||
buf.append(sign);
|
||||
buf.append(new DecimalFormat("0000").format(val));
|
||||
fastZeroPaddingNubmer(buf, (int)val, 4, 4, '0');
|
||||
} else {
|
||||
// long form, localized GMT pattern
|
||||
// not in 3.4 locale data, need to add, so use same default as for general time zone names
|
||||
@ -994,12 +1007,53 @@ public class SimpleDateFormat extends DateFormat {
|
||||
*/
|
||||
protected void zeroPaddingNumber(StringBuffer buf, int value,
|
||||
int minDigits, int maxDigits) {
|
||||
if (useFastZeroPaddingNumber) {
|
||||
fastZeroPaddingNubmer(buf, value, minDigits, maxDigits, zeroDigit);
|
||||
return;
|
||||
}
|
||||
FieldPosition pos = new FieldPosition(-1);
|
||||
numberFormat.setMinimumIntegerDigits(minDigits);
|
||||
numberFormat.setMaximumIntegerDigits(maxDigits);
|
||||
numberFormat.format(value, buf, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal faster method. This method does not use NumberFormat
|
||||
* to format digits.
|
||||
* @internal
|
||||
*/
|
||||
private void fastZeroPaddingNubmer(StringBuffer buf, int value,
|
||||
int minDigits, int maxDigits, char zero) {
|
||||
value = value < 0 ? -value : value; //??
|
||||
minDigits = minDigits < maxDigits ? minDigits : maxDigits;
|
||||
int limit = decimalBuf.length < maxDigits ? decimalBuf.length : maxDigits;
|
||||
int index = limit - 1;
|
||||
while (true) {
|
||||
decimalBuf[index] = (char)((value % 10) + zero);
|
||||
value /= 10;
|
||||
if (index == 0 || value == 0) {
|
||||
break;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
int padding = minDigits - (limit - index);
|
||||
for (; padding > 0; padding--) {
|
||||
decimalBuf[--index] = zero;
|
||||
}
|
||||
buf.append(decimalBuf, index, limit - index);
|
||||
}
|
||||
|
||||
public void setNumberFormat(NumberFormat newNumberFormat) {
|
||||
super.setNumberFormat(newNumberFormat);
|
||||
if (newNumberFormat instanceof DecimalFormat) {
|
||||
zeroDigit = ((DecimalFormat)newNumberFormat).getDecimalFormatSymbols().getZeroDigit();
|
||||
useFastZeroPaddingNumber = true;
|
||||
}
|
||||
else {
|
||||
useFastZeroPaddingNumber = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a number with the specified minimum and maximum number of digits.
|
||||
* @stable ICU 2.0
|
||||
@ -1377,34 +1431,7 @@ public class SimpleDateFormat extends DateFormat {
|
||||
TimeZone tz = null;
|
||||
String zid = null, value = null;
|
||||
int type = -1;
|
||||
zid = formatData.getZoneID(getTimeZone().getID());
|
||||
if (zid != null) {
|
||||
DateFormatSymbols.ZoneItem item = formatData.getZoneItem(zid, text, start);
|
||||
if (item != null) {
|
||||
zid = item.zid;
|
||||
value = item.value;
|
||||
type = item.type;
|
||||
tz = (TimeZone) getTimeZone().clone();
|
||||
}
|
||||
}
|
||||
|
||||
// optimize for default time zone, assume different from caller
|
||||
if (tz == null) {
|
||||
TimeZone defaultZone = TimeZone.getDefault();
|
||||
zid = formatData.getZoneID(defaultZone.getID());
|
||||
if (zid != null) {
|
||||
DateFormatSymbols.ZoneItem item = formatData.getZoneItem(zid, text, start);
|
||||
if (item != null) {
|
||||
zid = item.zid;
|
||||
value = item.value;
|
||||
type = item.type;
|
||||
tz = defaultZone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// still no luck, check all time zone strings
|
||||
if (tz == null) {
|
||||
DateFormatSymbols.ZoneItem item = formatData.findZoneIDTypeValue(text, start);
|
||||
if (item != null) {
|
||||
zid = item.zid;
|
||||
@ -1414,7 +1441,6 @@ public class SimpleDateFormat extends DateFormat {
|
||||
if (zid != null) {
|
||||
tz = TimeZone.getTimeZone(zid);
|
||||
}
|
||||
}
|
||||
|
||||
if (tz != null) { // Matched any ?
|
||||
// always set zone offset, needed to get correct hour in wall time
|
||||
|
Loading…
Reference in New Issue
Block a user