ClassEnum.java
package it.fulminazzo.yagl;
import it.fulminazzo.fulmicollection.utils.ReflectionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;
/**
* An abstract class that allows to "convert" classes to enums by providing the most three common methods:
* {@link #name()}, {@link #values(Class)} and {@link #valueOf(String, Class)}.
*/
@SuppressWarnings("unchecked")
public abstract class ClassEnum {
private static final Map<Class<?>, EnumCache<?>> CACHE_MAP = new HashMap<>();
/**
* Returns the index of the current object.
*
* @return the index
*/
public int ordinal() {
return getCache().ordinal(this);
}
/**
* Gets the name of the current object.
*
* @return the name
*/
public String name() {
return getCache().name(this);
}
/**
* Gets the field from its index, returned by {@link #ordinal()}..
*
* @param <T> the type parameter
* @param index the index
* @param clazz the clazz
* @return the t
*/
protected static <T extends ClassEnum> T valueOf(final @Range(from = 0, to = Integer.MAX_VALUE) int index,
final @NotNull Class<T> clazz) {
return getCache(clazz).valueOf(index);
}
/**
* Gets the field from its name.
*
* @param <T> the type parameter
* @param name the name
* @param clazz the clazz
* @return the field
*/
protected static <T extends ClassEnum> T valueOf(final @NotNull String name, final @NotNull Class<T> clazz) {
return getCache(clazz).valueOf(name);
}
/**
* Gets all the fields in an array.
*
* @param <T> the type parameter
* @param clazz the clazz
* @return the fields
*/
protected static <T extends ClassEnum> T[] values(final @NotNull Class<T> clazz) {
return getCache(clazz).values();
}
private <T extends ClassEnum> EnumCache<T> getCache() {
return getCache((Class<T>) getClass());
}
private static <T extends ClassEnum> EnumCache<T> getCache(final @NotNull Class<T> clazz) {
return (EnumCache<T>) CACHE_MAP.computeIfAbsent(clazz, c -> new EnumCache<>((Class<T>) c));
}
private static class EnumCache<T extends ClassEnum> {
private final Class<T> clazz;
private final Map<String, T> values;
private EnumCache(Class<T> clazz) {
this.clazz = clazz;
this.values = new LinkedHashMap<>();
}
public int ordinal(final @NotNull T t) {
checkValues();
List<T> values = new LinkedList<>(this.values.values());
for (int i = 0; i < values.size(); i++)
if (t.hashCode() == values.get(i).hashCode())
return i;
throw new IllegalArgumentException(String.format("Could not find ordinal of object '%s'", printObject(t)));
}
public String name(final @NotNull T t) {
checkValues();
for (String k : this.values.keySet())
if (t.hashCode() == this.values.get(k).hashCode())
return k;
throw new IllegalArgumentException(String.format("Could not find name of object '%s'", printObject(t)));
}
public T valueOf(final @Range(from = 0, to = Integer.MAX_VALUE) int index) {
checkValues();
for (T t : this.values.values()) if (t.ordinal() == index) return t;
throw new IllegalArgumentException(String.format("Could not find %s with index '%s'", this.clazz.getSimpleName().toLowerCase(), index));
}
public T valueOf(final @NotNull String name) {
checkValues();
return this.values.get(name.toUpperCase());
}
public T[] values() {
checkValues();
return this.values.values().toArray((T[]) Array.newInstance(this.clazz, 0));
}
@NotNull
private static <T extends ClassEnum> String printObject(@NotNull T t) {
return t.getClass().getCanonicalName() + "@" + t.hashCode();
}
private void checkValues() {
if (!this.values.isEmpty()) return;
for (Field field : this.clazz.getDeclaredFields())
if (field.getType().equals(this.clazz))
ReflectionUtils.get(field, this.clazz).ifPresent(o ->
this.values.put(field.getName().toUpperCase(), (T) o));
}
}
}