"Dynamic" Enums
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import static org.slf4j.LoggerFactory.getLogger;
/**
* @author Octavian Theodor NITA (https://github.com/octavian-nita/)
* @version 1.0, Aug 18, 2016
*/
public abstract class TypeValue<TV extends TypeValue<TV>> implements Comparable<TV>, Serializable {
private static final Map<Class<?>, Map<String, ? super Object>> valueCache = new HashMap<>();
private final String value;
private final String description;
protected TypeValue(String value) {
this.value = required(value);
this.description = createDescription(value);
}
/**
* Override when a description can be inferred based on the provided value.
*
* @return description computed based on the provided <code>value</code>; <code>value</code> by default
*/
protected String createDescription(String value) { return value; }
/**
* Factory method ensuring that only one instance of each possible value is ever created.
*
* @param value if <code>null</code>, this method returns <code>null</code>
*/
public static <T> T valueOf(Class<T> klass, String value) {
if (value == null) {
return null;
}
Map<String, ? super Object> tvCache = valueCache.get(klass);
if (tvCache == null) {
valueCache.put(klass, tvCache = new TreeMap<>()); // (almost) free alphabetical ordering of values by name
}
T typeValue = klass.cast(tvCache.get(value));
if (typeValue == null) {
try {
Constructor<T> ctor = klass.getDeclaredConstructor(String.class);
if (!ctor.isAccessible()) {
ctor.setAccessible(true);
}
tvCache.put(value, typeValue = ctor.newInstance(value));
} catch (Throwable throwable) {
getLogger(TypeValue.class).error("cannot instantiate class " + klass.getName(), throwable);
}
}
return typeValue;
}
public String getValue() { return value; }
public String getDescription() { return description; }
@Override
public String toString() {
return "TypeValue{" +
"value='" + value + '\'' +
", description='" + description + '\'' +
'}';
}
@Override
public int compareTo(TV o) { return value.compareTo(o.getValue()); }
@Override
public int hashCode() { return new HashCodeBuilder().append(value).toHashCode(); }
@Override
public boolean equals(Object that) {
return this == that || that != null && that instanceof TypeValue &&
new EqualsBuilder().append(value, ((TypeValue) that).value).isEquals();
}
}