blob: 340b4de48be1470107fcd2b9dec2b12b7bf620cb [file] [log] [blame]
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html#License
/*
*******************************************************************************
* 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;
}
}
}
}