]> granicus.if.org Git - icu/commitdiff
ICU-7434 ICU4J SoftCache with CacheValue that can be null (which were not cached...
authorMarkus Scherer <markus.icu@gmail.com>
Thu, 12 May 2016 22:59:11 +0000 (22:59 +0000)
committerMarkus Scherer <markus.icu@gmail.com>
Thu, 12 May 2016 22:59:11 +0000 (22:59 +0000)
X-SVN-Rev: 38734

icu4j/main/classes/core/src/com/ibm/icu/impl/CacheValue.java [new file with mode: 0644]
icu4j/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundleImpl.java
icu4j/main/classes/core/src/com/ibm/icu/impl/ICUResourceBundleReader.java
icu4j/main/classes/core/src/com/ibm/icu/impl/SoftCache.java
icu4j/main/tests/core/.settings/org.eclipse.jdt.ui.prefs
icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/CacheTest.java [new file with mode: 0644]
icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/TestAll.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/TestAll.java

diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/CacheValue.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/CacheValue.java
new file mode 100644 (file)
index 0000000..24d5336
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2016, International Business Machines Corporation and
+ * others. All Rights Reserved.
+ *******************************************************************************
+ */
+package com.ibm.icu.impl;
+
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+
+import com.ibm.icu.util.ICUException;
+
+/**
+ * Value type for cache items:
+ * Holds a value either via a direct reference or via a {@link Reference},
+ * depending on the current "strength" when {@code getInstance()} was called.
+ *
+ * <p>The value is <i>conceptually<i> immutable.
+ * If it is held via a direct reference, then it is actually immutable.
+ *
+ * <p>A {@code Reference} may be cleared (garbage-collected),
+ * after which {@code get()} returns null.
+ * It can then be reset via {@code resetIfAbsent()}.
+ * The new value should be the same as, or equivalent to, the old value.
+ *
+ * <p>Null values are supported. They can be distinguished from cleared values
+ * via {@code isNull()}.
+ *
+ * @param <V> Cache instance value type
+ */
+public abstract class CacheValue<V> {
+    /**
+     * "Strength" of holding a value in CacheValue instances.
+     * The default strength is {@code SOFT}.
+     */
+    public enum Strength {
+        /**
+         * Subsequent {@code getInstance()}-created objects
+         * will hold direct references to their values.
+         */
+        STRONG,
+        /**
+         * Subsequent {@code getInstance()}-created objects
+         * will hold {@link SoftReference}s to their values.
+         */
+        SOFT
+    };
+    private static volatile Strength strength = Strength.SOFT;
+
+    @SuppressWarnings("rawtypes")
+    private static final CacheValue NULL_VALUE = new NullValue();
+
+    /**
+     * Changes the "strength" of value references for subsequent {@code getInstance()} calls.
+     */
+    public static void setStrength(Strength strength) { CacheValue.strength = strength; }
+
+    /**
+     * Returns true if the "strength" is set to {@code STRONG}.
+     */
+    public static boolean futureInstancesWillBeStrong() { return strength == Strength.STRONG; }
+
+    /**
+     * Returns a CacheValue instance that holds the value.
+     * It holds it directly if the value is null or if the current "strength" is {@code STRONG}.
+     * Otherwise, it holds it via a {@link Reference}.
+     */
+    @SuppressWarnings("unchecked")
+    public static <V> CacheValue<V> getInstance(V value) {
+        if (value == null) {
+            return NULL_VALUE;
+        }
+        return strength == Strength.STRONG ? new StrongValue<V>(value) : new SoftValue<V>(value);
+    }
+
+    /**
+     * Distinguishes a null value from a Reference value that has been cleared.
+     *
+     * @return true if this object represents a null value.
+     */
+    public boolean isNull() { return false; }
+    /**
+     * Returns the value (which can be null),
+     * or null if it was held in a Reference and has been cleared.
+     */
+    public abstract V get();
+    /**
+     * If the value was held via a {@link Reference} which has been cleared,
+     * then it is replaced with a new {@link Reference} to the new value,
+     * and the new value is returned.
+     * The old and new values should be the same or equivalent.
+     *
+     * <p>Otherwise the old value is returned.
+     *
+     * @param value Replacement value, for when the current {@link Reference} has been cleared.
+     * @return The old or new value.
+     */
+    public abstract V resetIfCleared(V value);
+
+    private static final class NullValue<V> extends CacheValue<V> {
+        @Override
+        public boolean isNull() { return true; }
+        @Override
+        public V get() { return null; }
+        @Override
+        public V resetIfCleared(V value) {
+            if (value != null) {
+                throw new ICUException("resetting a null value to a non-null value");
+            }
+            return null;
+        }
+    }
+
+    private static final class StrongValue<V> extends CacheValue<V> {
+        private V value;
+
+        StrongValue(V value) { this.value = value; }
+        @Override
+        public V get() { return value; }
+        @Override
+        public V resetIfCleared(V value) {
+            // value and this.value should be equivalent, but
+            // we do not require equals() to be implemented appropriately.
+            return this.value;
+        }
+    }
+
+    private static final class SoftValue<V> extends CacheValue<V> {
+        private Reference<V> ref;
+
+        SoftValue(V value) { ref = new SoftReference<V>(value); }
+        @Override
+        public V get() { return ref.get(); }
+        @Override
+        public synchronized V resetIfCleared(V value) {
+            V oldValue = ref.get();
+            if (oldValue == null) {
+                ref = new SoftReference<V>(value);
+                return value;
+            } else {
+                // value and oldValue should be equivalent, but
+                // we do not require equals() to be implemented appropriately.
+                return oldValue;
+            }
+        }
+    }
+}
index 2fb6d0c638c5e822302c93fc1b45d925dfc5b44a..efd5b68a55ebb1e4e1d74684ae49102190719f32 100644 (file)
@@ -102,7 +102,8 @@ class ICUResourceBundleImpl extends ICUResourceBundle {
             super(container, key, resource);
             String s = wholeBundle.reader.getString(resource);
             // Allow the reader cache's SoftReference to do its job.
-            if (s.length() < ICUResourceBundleReader.LARGE_SIZE / 2) {
+            if (s.length() < ICUResourceBundleReader.LARGE_SIZE / 2 ||
+                    CacheValue.futureInstancesWillBeStrong()) {
                 value = s;
             }
         }
index 96ddd8a996c2b6596f9f53f257c324ec3f744563..7e5a7f8b00041f05ca1136780ec73cedfd51be39 100644 (file)
@@ -1182,7 +1182,7 @@ public final class ICUResourceBundleReader {
      * <p>This cache uses int[] and Object[] arrays to minimize object creation
      * and avoid auto-boxing.
      *
-     * <p>Large resource objects are stored in SoftReferences.
+     * <p>Large resource objects are usually stored in SoftReferences.
      *
      * <p>For few resources, a small table is used with binary search.
      * When more resources are cached, then the data structure changes to be faster
@@ -1210,11 +1210,18 @@ public final class ICUResourceBundleReader {
         private int levelBitsList;
         private Level rootLevel;
 
+        private static boolean storeDirectly(int size) {
+            return size < LARGE_SIZE || CacheValue.futureInstancesWillBeStrong();
+        }
+
         @SuppressWarnings("unchecked")
         private static final Object putIfCleared(Object[] values, int index, Object item, int size) {
             Object value = values[index];
             if(!(value instanceof SoftReference)) {
-                assert size < LARGE_SIZE;  // Caller should be consistent for each resource.
+                // The caller should be consistent for each resource,
+                // that is, create equivalent objects of equal size every time,
+                // but the CacheValue "strength" may change over time.
+                // assert size < LARGE_SIZE;
                 return value;
             }
             assert size >= LARGE_SIZE;
@@ -1222,7 +1229,8 @@ public final class ICUResourceBundleReader {
             if(value != null) {
                 return value;
             }
-            values[index] = new SoftReference<Object>(item);
+            values[index] = CacheValue.futureInstancesWillBeStrong() ?
+                    item : new SoftReference<Object>(item);
             return item;
         }
 
@@ -1271,7 +1279,7 @@ public final class ICUResourceBundleReader {
                         return level.putIfAbsent(key, item, size);
                     }
                     keys[index] = key;
-                    values[index] = (size >= LARGE_SIZE) ? new SoftReference<Object>(item) : item;
+                    values[index] = storeDirectly(size) ? item : new SoftReference<Object>(item);
                     return item;
                 }
                 // Collision: Add a child level, move the old item there,
@@ -1403,7 +1411,7 @@ public final class ICUResourceBundleReader {
                     }
                     ++length;
                     keys[index] = res;
-                    values[index] = (size >= LARGE_SIZE) ? new SoftReference<Object>(item) : item;
+                    values[index] = storeDirectly(size) ? item : new SoftReference<Object>(item);
                     return item;
                 } else /* not found && length == SIMPLE_LENGTH */ {
                     // Grow to become trie-like.
index e77f86cf2a0cacb8902406d152de75b7e0168e73..f70d94684484c34b9726918f8ef06ed45d5e2b4f 100644 (file)
 /*
 *******************************************************************************
-*   Copyright (C) 2010-2011, International Business Machines
+*   Copyright (C) 2010-2016, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *******************************************************************************
 */
 package com.ibm.icu.impl;
 
-import java.lang.ref.SoftReference;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * Generic, thread-safe cache implementation, storing SoftReferences to cached instances.
+ * Generic, thread-safe cache implementation, usually storing cached instances
+ * in {@link java.lang.ref.Reference}s via {@link CacheValue}s.
  * To use, instantiate a subclass which implements the createInstance() method,
  * and call get() with the key and the data. The get() call will use the data
  * only if it needs to call createInstance(), otherwise the data is ignored.
  *
- * By using SoftReferences to instances, the Java runtime can release instances
- * once they are not used any more at all. If such an instance is then requested again,
- * the get() method will call createInstance() again and also create a new SoftReference.
- * The cache holds on to its map of keys to SoftReferenced instances forever.
+ * <p>When caching instances while the CacheValue "strength" is {@code SOFT},
+ * the Java runtime can later release these instances once they are not used any more at all.
+ * If such an instance is then requested again,
+ * the getInstance() method will call createInstance() again and reset the CacheValue.
+ * The cache holds on to its map of keys to CacheValues forever.
+ *
+ * <p>A value can be null if createInstance() returns null.
+ * In this case, it must do so consistently for the same key and data.
  *
  * @param <K> Cache lookup key type
- * @param <V> Cache instance value type
+ * @param <V> Cache instance value type (must not be a CacheValue)
  * @param <D> Data type for creating a new instance value
  *
  * @author Markus Scherer, Mark Davis
  */
 public abstract class SoftCache<K, V, D> extends CacheBase<K, V, D> {
+    private ConcurrentHashMap<K, Object> map = new ConcurrentHashMap<K, Object>();
+
+    @SuppressWarnings("unchecked")
     @Override
     public final V getInstance(K key, D data) {
-        // We synchronize twice, once on the map and once on valueRef,
+        // We synchronize twice, once in the ConcurrentHashMap and
+        // once in valueRef.resetIfCleared(value),
         // because we prefer the fine-granularity locking of the ConcurrentHashMap
         // over coarser locking on the whole cache instance.
-        // We use a SettableSoftReference (a second level of indirection) because
+        // We use a CacheValue (a second level of indirection) because
         // ConcurrentHashMap.putIfAbsent() never replaces the key's value, and if it were
-        // a simple SoftReference we would not be able to reset its value after it has been cleared.
+        // a simple Reference we would not be able to reset its value after it has been cleared.
         // (And ConcurrentHashMap.put() always replaces the value, which we don't want either.)
-        SettableSoftReference<V> valueRef = map.get(key);
-        V value;
-        if(valueRef != null) {
-            synchronized(valueRef) {
-                value = valueRef.ref.get();
-                if(value != null) {
-                    return value;
-                } else {
-                    // The instance has been evicted, its SoftReference cleared.
-                    // Create and set a new instance.
-                    value = createInstance(key, data);
-                    if (value != null) {
-                        valueRef.ref = new SoftReference<V>(value);
-                    }
-                    return value;
-                }
+        Object mapValue = map.get(key);
+        if(mapValue != null) {
+            if(!(mapValue instanceof CacheValue)) {
+                // The value was stored directly.
+                return (V)mapValue;
             }
-        } else /* valueRef == null */ {
-            // We had never cached an instance for this key.
-            value = createInstance(key, data);
-            if (value == null) {
+            CacheValue<V> cv = (CacheValue<V>)mapValue;
+            if(cv.isNull()) {
                 return null;
             }
-            valueRef = map.putIfAbsent(key, new SettableSoftReference<V>(value));
-            if(valueRef == null) {
-                // Normal "put": Our new value is now cached.
+            V value = cv.get();
+            if(value != null) {
                 return value;
-            } else {
-                // Race condition: Another thread beat us to putting a SettableSoftReference
-                // into the map. Return its value, but just in case the garbage collector
-                // was aggressive, we also offer our new instance for caching.
-                return valueRef.setIfAbsent(value);
             }
-        }
-    }
-    /**
-     * Value type for cache items: Has a SoftReference which can be set
-     * to a new value when the SoftReference has been cleared.
-     * The SoftCache class sometimes accesses the ref field directly.
-     *
-     * @param <V> Cache instance value type
-     */
-    private static final class SettableSoftReference<V> {
-        private SettableSoftReference(V value) {
-            ref = new SoftReference<V>(value);
-        }
-        /**
-         * If the SoftReference has been cleared, then this replaces it with a new SoftReference
-         * for the new value and returns the new value; otherwise returns the current
-         * SoftReference's value.
-         * @param value Replacement value, for when the current reference has been cleared
-         * @return The value that is held by the SoftReference, old or new
-         */
-        private synchronized V setIfAbsent(V value) {
-            V oldValue = ref.get();
-            if(oldValue == null) {
-                ref = new SoftReference<V>(value);
+            // The instance has been evicted, its Reference cleared.
+            // Create and set a new instance.
+            value = createInstance(key, data);
+            return cv.resetIfCleared(value);
+        } else /* valueRef == null */ {
+            // We had never cached an instance for this key.
+            V value = createInstance(key, data);
+            mapValue = (value != null && CacheValue.futureInstancesWillBeStrong()) ?
+                    value : CacheValue.getInstance(value);
+            mapValue = map.putIfAbsent(key, mapValue);
+            if(mapValue == null) {
+                // Normal "put": Our new value is now cached.
                 return value;
-            } else {
-                return oldValue;
             }
+            // Race condition: Another thread beat us to putting a CacheValue
+            // into the map. Return its value, but just in case the garbage collector
+            // was aggressive, we also offer our new instance for caching.
+            if(!(mapValue instanceof CacheValue)) {
+                // The value was stored directly.
+                return (V)mapValue;
+            }
+            CacheValue<V> cv = (CacheValue<V>)mapValue;
+            return cv.resetIfCleared(value);
         }
-        private SoftReference<V> ref;  // never null
     }
-    private ConcurrentHashMap<K, SettableSoftReference<V>> map =
-        new ConcurrentHashMap<K, SettableSoftReference<V>>();
 }
index 96842381ec3666c72fa06c88e3f60f7083302337..f9d431b7e638cc2f6d32d22e90e2de66eda0e832 100644 (file)
@@ -1,4 +1,3 @@
-#Wed Jun 17 11:10:12 EDT 2009
 eclipse.preferences.version=1
 formatter_profile=_ICU4J Standard
 formatter_settings_version=11
@@ -7,4 +6,4 @@ org.eclipse.jdt.ui.importorder=java;javax;org;com;
 org.eclipse.jdt.ui.javadoc=true
 org.eclipse.jdt.ui.ondemandthreshold=99
 org.eclipse.jdt.ui.staticondemandthreshold=99
-org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\r\n * @return the ${bare_field_name}\r\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\r\n * @param ${param} the ${bare_field_name} to set\r\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="false" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*\r\n *******************************************************************************\r\n * Copyright (C) ${year}, International Business Machines Corporation and         *\r\n * others. All Rights Reserved.                                                *\r\n *******************************************************************************\r\n */</template><template autoinsert\="true" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\r\n * @author ${user}\r\n *\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\r\n * \r\n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\r\n * ${tags}\r\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\r\n * ${see_to_overridden}\r\n */</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\r\n * ${tags}\r\n * ${see_to_target}\r\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\r\n${package_declaration}\r\n\r\n${typecomment}\r\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\r\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\r\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\r\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\r\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\r\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\r\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\r\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates>
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\n * @return the ${bare_field_name}\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\n * @param ${param} the ${bare_field_name} to set\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\n * ${tags}\n */</template><template autoinsert\="false" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*\n *******************************************************************************\n * Copyright (C) ${year}, International Business Machines Corporation and\n * others. All Rights Reserved.\n *******************************************************************************\n */</template><template autoinsert\="true" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\n * @author ${user}\n *\n * ${tags}\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\n * \n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\n * ${tags}\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\n * ${see_to_overridden}\n */</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\n * ${tags}\n * ${see_to_target}\n */</template><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates>
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/CacheTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/impl/CacheTest.java
new file mode 100644 (file)
index 0000000..bb6828c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *******************************************************************************
+ * Copyright (C) 2016, International Business Machines Corporation and
+ * others. All Rights Reserved.
+ *******************************************************************************
+ */
+package com.ibm.icu.dev.test.impl;
+
+import com.ibm.icu.dev.test.TestFmwk;
+import com.ibm.icu.impl.CacheValue;
+import com.ibm.icu.impl.CacheValue.Strength;
+
+public class CacheTest extends TestFmwk {
+    public CacheTest() {}
+
+    public static void main(String[] args) throws Exception {
+        new CacheTest().run(args);
+    }
+
+    /** Code coverage for CacheValue. */
+    public void testNullCacheValue() {
+        CacheValue<Object> nv = CacheValue.getInstance(null);
+        assertTrue("null CacheValue isNull()", nv.isNull());
+        assertTrue("null CacheValue get()==null", nv.get() == null);
+        assertTrue("null CacheValue reset==null", nv.resetIfCleared(null) == null);
+        try {
+            Object v = nv.resetIfCleared(this);
+            fail("null CacheValue reset(not null) should throw an Exception, returned " +
+                    v + " instead");
+        } catch(Exception expected) {
+        }
+    }
+
+    /** Code coverage for CacheValue. */
+    public void testStrongCacheValue() {
+        boolean wasStrong = CacheValue.futureInstancesWillBeStrong();
+        CacheValue.setStrength(Strength.STRONG);
+        assertTrue("setStrength(STRONG).futureInstancesWillBeStrong()",
+                CacheValue.futureInstancesWillBeStrong());
+        CacheValue<Object> sv = CacheValue.<Object>getInstance(this);
+        assertFalse("strong CacheValue not isNull()", sv.isNull());
+        assertTrue("strong CacheValue get()==same", sv.get() == this);
+        // A strong CacheValue never changes value.
+        // The implementation does not check that the new value is equal to the old one,
+        // or even of equal type, so it does not matter which new value we pass in.
+        assertTrue("strong CacheValue reset==same", sv.resetIfCleared("") == this);
+        if (!wasStrong) {
+            CacheValue.setStrength(Strength.SOFT);
+        }
+    }
+
+    /** Code coverage for CacheValue. */
+    public void testSoftCacheValue() {
+        boolean wasStrong = CacheValue.futureInstancesWillBeStrong();
+        CacheValue.setStrength(Strength.SOFT);
+        assertFalse("setStrength(SOFT).futureInstancesWillBeStrong()",
+                CacheValue.futureInstancesWillBeStrong());
+        CacheValue<Object> sv = CacheValue.<Object>getInstance(this);
+        assertFalse("soft CacheValue not isNull()", sv.isNull());
+        Object v = sv.get();
+        assertTrue("soft CacheValue get()==same or null", v == this || v == null);
+        assertTrue("soft CacheValue reset==same", sv.resetIfCleared(this) == this);
+        if (wasStrong) {
+            CacheValue.setStrength(Strength.STRONG);
+        }
+    }
+}
index b7ed931e3ba57111e499e00aea4cde651e240507..9a4034afe363b5131dfda9738ae54977685e1277 100644 (file)
@@ -17,18 +17,12 @@ public class TestAll extends TestGroup {
     }
 
     public TestAll() {
-        super("com.ibm.icu.dev.test.util",
-              new String[] {
-                  "ICUServiceTest",
-                  "ICUServiceThreadTest",
-                  "ICUBinaryTest",
-                  "SimpleFormatterTest",
-                  "TextTrieMapTest"
-              },
-              "Test miscellaneous implementation utilities");
+        super(
+            new String[] {
+                "CacheTest"
+            },
+            "Test miscellaneous implementation classes");
     }
 
     public static final String CLASS_TARGET_NAME = "Impl";
 }
-
-
index f7540e01f9f5110ff34da2013922c6642edf8940..2b390f72ed122b05c7d2d759f79c761ff023038a 100644 (file)
@@ -19,6 +19,11 @@ public class TestAll extends TestGroup {
     public TestAll() {
         super(
               new String[] {
+            "ICUServiceTest",
+            "ICUServiceThreadTest",
+            "ICUBinaryTest",
+            "SimpleFormatterTest",
+            "TextTrieMapTest",
             "VersionInfoTest",
             "ICUResourceBundleTest",
             "BytesTrieTest",