Skip to content

Instantly share code, notes, and snippets.

@GotoFinal
Created June 24, 2017 20:39
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GotoFinal/2354ca1831aaaefc2a3a45bd71f7d636 to your computer and use it in GitHub Desktop.
Save GotoFinal/2354ca1831aaaefc2a3a45bd71f7d636 to your computer and use it in GitHub Desktop.
/*
* The MIT License (MIT)
*
* Copyright (c) 2017. Diorite (by Bartłomiej Mazur (aka GotoFinal))
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import java.lang.StackWalker.Option;
import java.lang.StackWalker.StackFrame;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Base class for creating dynamic enums, new elements can be added to dynamic enums, but not removed/changed.
*
* @param <T>
* type of enum.
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public abstract class DynamicEnum<T extends DynamicEnum<T>> implements Comparable<T> {
private static final StackWalker walker =
StackWalker.getInstance(Set.of(Option.RETAIN_CLASS_REFERENCE, Option.SHOW_HIDDEN_FRAMES, Option.SHOW_REFLECT_FRAMES));
int ordinal = - 1;
String name = "\0";
private final transient Supplier<String> prettyName = new Supplier<String>() {
private String value;
@Override
public String get() {
if (this.value == null) {
this.value = DynamicEnum.this.createPrettyName();
}
return this.value;
}
};
@SuppressWarnings("StringEquality")
final void validate() {
if ((this.ordinal == - 1) || (this.name == "\0")) {
throw new IllegalStateException("Unregistered enum element: " + this.getClass() + ", " + this.name + ", " + this.ordinal);
}
}
protected DynamicEnum() {
Class<T> enumType = this.getDeclaringClass0();
DynamicEnumType<T> dynamicEnumType = DynamicEnumType.getDynamicEnumType(enumType);
List<StackFrame> stackFrames = walker.walk(s -> s.skip(2).collect(Collectors.toList()));
List<StackFrame> validStack = new ArrayList<>(2);
boolean foundSpecial = false;
for (StackFrame stackFrame : stackFrames) {
if (foundSpecial) {
validStack.add(stackFrame);
if (validStack.size() == 2) {
break;
}
}
if (stackFrame.getDeclaringClass().equals(DynamicEnum.class) && stackFrame.getMethodName().equals("$")) {
foundSpecial = true;
}
}
if (! foundSpecial) {
for (StackFrame stackFrame : stackFrames) {
validStack.add(stackFrame);
if (validStack.size() == 2) {
break;
}
}
}
switch (ValueType.findType(validStack, enumType, this.getClass())) {
case DYNAMIC:
case DYNAMIC_EXTENDED:
return;
case NORMAL:
case EXTENDED:
this.ordinal = dynamicEnumType.counter++;
int index = 0;
for (Field field : enumType.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && (field.getType() == enumType)) {
if (index++ == this.ordinal) {
this.name = field.getName();
dynamicEnumType.addElement((T) this);
return;
}
}
}
throw new IllegalStateException("Can't find enum value declaration");
}
}
protected static <T extends DynamicEnum<T>> T $(Object... arguments) {
StackFrame stackOne = walker.walk(s -> s.skip(1).limit(1).findFirst()).orElseThrow(InternalError::new);
if (! DynamicEnum.class.isAssignableFrom(stackOne.getDeclaringClass())) {
throw new InternalError("This method should be used only to create new instances of enum values!");
}
try {
return findMatchingConstructor((Class<T>) stackOne.getDeclaringClass(), arguments).newInstance(arguments);
}
catch (RuntimeException e) {
if (e.getCause() instanceof InstantiationException) {
throw new InternalError("Can't create enum instance, there was error in constructor!", e.getCause());
}
throw new InternalError("Exception when invoking constructor!", e);
}
catch (Exception e) {
throw new InternalError("Can't create enum instance using default constructor, $() method works only for simple constructor usages", e);
}
}
/**
* Returns ordinal of this enum element.
*
* @return ordinal of this enum element.
*/
public final int ordinal() {
this.validate();
return this.ordinal;
}
/**
* Returns name of this enum element.
*
* @return name of this enum element.
*/
public final String name() {
this.validate();
return this.name;
}
/**
* Returns name of this enum element in PascalCase and removed underscores.
*
* @return pretty name of this enum element.
*/
public final String prettyName() {
return Objects.requireNonNull(this.prettyName.get());
}
private String createPrettyName() {
String toLowerCase = this.name.toLowerCase();
char[] charArray = toLowerCase.toCharArray();
StringBuilder sb = new StringBuilder(toLowerCase.length()).append(Character.toUpperCase(charArray[0]));
char last = '\0';
for (int i = 1; i < charArray.length; i++) {
char c = charArray[i];
if (last == '_') {
sb.append(Character.toUpperCase(c));
}
else if (c != '_') {
sb.append(c);
}
last = c;
}
return sb.toString();
}
/**
* Returns the Class object corresponding to this enum constant's enum type. Two enum constants e1 and e2 are of the same enum type if and only if
* e1.getDeclaringClass() == e2.getDeclaringClass(). (The value returned by this method may differ from the one returned by the {@link Object#getClass}
* method for enum constants with constant-specific class bodies.)
*
* @return the Class object corresponding to this enum constant's enum type
*/
public final Class<T> getDeclaringClass() {
this.validate();
return this.getDeclaringClass0();
}
private Class<T> getDeclaringClass0() {
Class<?> clazz = this.getClass();
Class<?> superClass = clazz.getSuperclass();
return (superClass.getSuperclass() == DynamicEnum.class) ? (Class<T>) superClass : (Class<T>) clazz;
}
/**
* Compares this enum with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less
* than, equal to, or greater than the specified object.
*
* Enum constants are only comparable to other enum constants of the
* same enum type. The natural order implemented by this
* method is the order in which the constants are declared.
*/
@Override
public final int compareTo(T o) {
this.validate();
DynamicEnum<T> self = this;
if ((self.getClass() != ((DynamicEnum<?>) o).getClass()) && // optimization
(self.getDeclaringClass() != ((DynamicEnum<?>) o).getDeclaringClass())) {
throw new ClassCastException();
}
o.validate();
return self.ordinal - o.ordinal;
}
/**
* Returns the name of this enum constant, as contained in the
* declaration. This method may be overridden, though it typically
* isn't necessary or desirable. An enum type should override this
* method when a more "programmer-friendly" string form exists.
*
* @return the name of this enum constant
*/
public String toString() {
this.validate();
return this.name;
}
/**
* Returns true if the specified object is equal to this
* enum constant.
*
* @param other
* the object to be compared for equality with this object.
*
* @return true if the specified object is equal to this enum constant.
*/
public final boolean equals(Object other) {
this.validate();
if (other instanceof DynamicEnum) {
((DynamicEnum) other).validate();
return this == other;
}
else {
return false;
}
}
/**
* Returns a hash code for this enum constant.
*
* @return a hash code for this enum constant.
*/
public final int hashCode() {
this.validate();
return super.hashCode();
}
/**
* Throws CloneNotSupportedException. This guarantees that enums
* are never cloned, which is necessary to preserve their "singleton"
* status.
*
* @return (never returns)
*/
@Override
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
/**
* enum classes cannot have finalize methods.
*/
@Override
protected final void finalize() {}
/**
* Returns array of values of given enum.
*
* @param enumType
* type of enum.
* @param <T>
* type og enum.
*
* @return array of values of given enum.
*/
public static <T extends DynamicEnum<T>> T[] values(Class<T> enumType) {
return DynamicEnumType.getDynamicEnumType(enumType).values.clone();
}
/**
* Adds new enum element to this dynamic enum.
*
* @param enumValue
* enum value.
* @param name
* name of enum value.
* @param <T>
* type of enum.
*
* @return ordinal value of enum.
*/
public static <T extends DynamicEnum<T>> int addEnumElement(String name, T enumValue) {
Class<T> enumValueClass = (Class<T>) enumValue.getClass();
while (enumValueClass.getSuperclass() != DynamicEnum.class) {
enumValueClass = (Class<T>) enumValueClass.getSuperclass();
}
DynamicEnumType<T> dynamicEnumType = DynamicEnumType.getDynamicEnumType(enumValueClass);
dynamicEnumType.addElement(enumValue, name);
return enumValue.ordinal;
}
/**
* Returns the enum constant of the specified enum type with the
* specified name.
* The name must match exactly an identifier used
* to declare an enum constant in this type.
*
* @param <T>
* The enum type whose constant is to be returned
* @param enumType
* the {@code Class} object of the enum type from which to return a constant
* @param name
* the name of the constant to return
*
* @return the enum constant of the specified enum type with the specified name
*
* @exception IllegalArgumentException
* if the specified enum type has no constant with the specified name, or the specified class object does not represent an enum type
*/
public static <T extends DynamicEnum<T>> T valueOf(Class<T> enumType, String name) {
T result = DynamicEnumType.getDynamicEnumType(enumType).elementsMap.get(name);
if (result != null) {
return result;
}
throw new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "." + name);
}
/**
* Returns the enum constant of the specified enum type with the specified ordinal.
*
* @param <T>
* The enum type whose constant is to be returned
* @param enumType
* the {@code Class} object of the enum type from which to return a constant
* @param ordinal
* the name of the constant to return
*
* @return the enum constant of the specified enum type with the specified ordinal
*
* @exception IllegalArgumentException
* if the specified enum type has no constant with the specified ordinal, or the specified class object does not represent an enum type
*/
public static <T extends DynamicEnum<T>> T valueOf(Class<T> enumType, int ordinal) {
T[] values = DynamicEnumType.getDynamicEnumType(enumType).values;
if ((ordinal >= 0) && (ordinal < values.length)) {
T result = values[ordinal];
if (result != null) {
return result;
}
}
throw new IllegalArgumentException("No enum constant " + enumType.getCanonicalName() + "#" + ordinal);
}
private static <T> Constructor<T> findMatchingConstructor(Class<T> type, Object... values) throws NoSuchMethodException, IllegalStateException {
return findMatchingExecutable((Constructor<T>[]) type.getDeclaredConstructors(), values);
}
private static <T> Constructor<T> findMatchingExecutable(Constructor<T>[] executables,
Object... values) throws NoSuchMethodException, IllegalStateException {
if (values.length == 0) {
for (Constructor<T> executable : executables) {
if (executable.getParameterCount() == 0) {
return setAccessible(executable);
}
}
throw new NoSuchMethodException("Can't find no-args constructor.");
}
Class<?>[] paramTypes = new Class<?>[values.length];
for (int i = 0; i < values.length; i++) {
Object value = values[i];
paramTypes[i] = (value == null) ? null : value.getClass();
}
// try to find exact matching constructor, and add any just compatible to collection.
int exactMatches = 0;
Constructor<T> exact = null;
Constructor<T> bestMatch = null;
for (Constructor<T> executable : executables) {
if (executable.getParameterCount() != values.length) {
continue;
}
CompatibleExecutableResults compatibleConstructor = isCompatibleExecutable(executable, paramTypes);
if (compatibleConstructor == CompatibleExecutableResults.EXACT) {
if (exactMatches >= 1) {
throw new IllegalStateException("Ambiguous constructors found " + Arrays.toString(paramTypes));
}
exact = executable;
exactMatches += 1;
}
if (compatibleConstructor != CompatibleExecutableResults.INVALID) {
bestMatch = getMoreSpecialized(bestMatch, executable);
}
}
if (bestMatch == null) {
throw new NoSuchMethodException("Can't find matching constructor for: " + Arrays.toString(paramTypes));
}
if (exact != null) {
if (! bestMatch.equals(exact)) {
throw new IllegalStateException("Ambiguous constructors found " + Arrays.toString(paramTypes));
}
return setAccessible(exact);
}
return setAccessible(bestMatch);
}
private static <T> Constructor<T> setAccessible(Constructor<T> executable) {
executable.setAccessible(true);
return executable;
}
private static <T> Constructor<T> getMoreSpecialized(Constructor<T> a, Constructor<T> b) {
if (a == null) {
return b;
}
if (b == null) {
return a;
}
Class<?>[] aTypes = a.getParameterTypes();
Class<?>[] bTypes = b.getParameterTypes();
int result = 0;
for (int i = 0; i < aTypes.length; i++) {
Class<?> aType = aTypes[i];
Class<?> bType = bTypes[i];
if (aType.equals(bType)) {
continue;
}
// if aType is less specialized than bType
if ((aType.isPrimitive() && ! bType.isPrimitive()) ||
getWrapperClass(aType).isAssignableFrom(getWrapperClass(bType))) {
// one of prev types was less specialized, javac fails to find such constructor, we should too
if (result < 0) {
throw new IllegalStateException("Ambiguous constructors found for: " + Arrays.toString(aTypes) + " and " + Arrays.toString(bTypes));
}
result += 1;
}
else {
if (result > 0) {
throw new IllegalStateException("Ambiguous constructors found for: " + Arrays.toString(aTypes) + " and " + Arrays.toString(bTypes));
}
result -= 1;
}
}
if (result == 0) {
throw new IllegalStateException("Ambiguous constructors found for: " + Arrays.toString(aTypes) + " and " + Arrays.toString(bTypes));
}
if (result < 0) {
return a;
}
return b;
}
private static CompatibleExecutableResults isCompatibleExecutable(Executable constructor, Class<?>[] providedTypes) {
Class<?>[] constructorParameterTypes = constructor.getParameterTypes();
boolean compatible = true;
CompatibleExecutableResults current = CompatibleExecutableResults.EXACT;
for (int i = 0; i < constructorParameterTypes.length; i++) {
Class<?> providedType = providedTypes[i];
Class<?> parameterType = constructorParameterTypes[i];
// null can't be used as primitive
if ((providedType == null) && parameterType.isPrimitive()) {
return CompatibleExecutableResults.INVALID;
}
// handle primitives correctly by using wrapped type as boolean.class.isAssignableFrom(Boolean.class) => false
if ((providedType != null) && ! getWrapperClass(parameterType).isAssignableFrom(providedType)) {
return CompatibleExecutableResults.INVALID;
}
if ((providedType == null) || parameterType.equals(providedType)) {
continue; // sill exact match
}
current = CompatibleExecutableResults.COMPATIBLE;
}
return current;
}
private enum CompatibleExecutableResults {
EXACT,
COMPATIBLE,
INVALID
}
private static Class<?> getWrapperClass(Class<?> clazz) {
if (! clazz.isPrimitive()) {
return clazz;
}
if (clazz == boolean.class) {
return Boolean.class;
}
if (clazz == byte.class) {
return Byte.class;
}
if (clazz == short.class) {
return Short.class;
}
if (clazz == char.class) {
return Character.class;
}
if (clazz == int.class) {
return Integer.class;
}
if (clazz == long.class) {
return Long.class;
}
if (clazz == float.class) {
return Float.class;
}
if (clazz == double.class) {
return Double.class;
}
throw new Error("Unknown primitive type?"); // not possible?
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2017. Diorite (by Bartłomiej Mazur (aka GotoFinal))
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import org.junit.Assert;
import org.junit.Test;
public class DynamicEnumTest
{
@Test
public void testEnum()
{
Assert.assertSame(EnumExample.A, EnumExample.values()[0]);
Assert.assertSame(EnumExample.B, EnumExample.values()[1]);
Assert.assertSame(EnumExample.C, EnumExample.values()[2]);
Assert.assertSame(EnumExample.A, EnumExample.valueOf("A"));
EnumExample d = new EnumExample();
EnumExample e = new EnumExample() {};
Assert.assertSame(3, EnumExample.addEnumElement("D", d));
Assert.assertSame(4, EnumExample.addEnumElement("E", e));
Assert.assertSame(d, EnumExample.values()[3]);
Assert.assertSame(e, EnumExample.values()[4]);
Assert.assertSame(e, EnumExample.valueOf("E"));
// 0 -> A, 1 -> B, 2 -> C, 3 -> D, 4 -> E
for (EnumExample enumExample : EnumExample.values())
{
System.out.println(enumExample.ordinal() + " -> " + enumExample.name());
}
}
static class EnumExample extends DynamicEnum<EnumExample>
{
public static final EnumExample A = $();
public static final EnumExample B = $();
public static final EnumExample C = new EnumExample()
{
public int doSomething() {return 7;}
};
public int doSomething() {return 5;}
public static EnumExample[] values() {return DynamicEnum.values(EnumExample.class);}
public static EnumExample valueOf(String name) {return DynamicEnum.valueOf(EnumExample.class, name);}
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2017. Diorite (by Bartłomiej Mazur (aka GotoFinal))
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
final class DynamicEnumType<T extends DynamicEnum<T>> {
private static final Map<Class<?>, DynamicEnumType<?>> types = new ConcurrentHashMap<>(20);
final Map<String, T> elementsMap = Collections.synchronizedMap(new HashMap<>(10, 0.1f));
private final Class<T> type;
T[] values;
volatile int counter;
@SuppressWarnings("unchecked")
DynamicEnumType(Class<T> type) {
this.type = type;
this.values = (T[]) Array.newInstance(type, 0);
}
synchronized void addElement(T element, String name) {
element.name = name;
element.ordinal = this.counter++;
this.addElement(element);
}
@SuppressWarnings("unchecked")
synchronized void addElement(T element) {
int oldLength = this.values.length;
if (element.ordinal != oldLength) {
throw new IllegalStateException("Expected enum element of id: " + oldLength + " but got: " + element.ordinal);
}
T[] newValues = (T[]) Array.newInstance(this.type, oldLength + 1);
System.arraycopy(this.values, 0, newValues, 0, oldLength);
newValues[oldLength] = element;
element.ordinal = oldLength;
this.values = newValues;
this.elementsMap.put(element.name, element);
}
@SuppressWarnings("unchecked")
static <T extends DynamicEnum<T>> DynamicEnumType<T> getDynamicEnumType(Class<T> type) {
DynamicEnumType<T> enumType = (DynamicEnumType<T>) types.get(type);
if (enumType != null) {
return enumType;
}
enumType = new DynamicEnumType<>(type);
types.putIfAbsent(type, enumType);
return enumType;
}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2017. Diorite (by Bartłomiej Mazur (aka GotoFinal))
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
public class SampleEnum extends DynamicEnum<SampleEnum>
{
public static final SampleEnum A = $("heh");
private final String someProperty;
SampleEnum(String someProperty) {this.someProperty = someProperty;}
public String getSomeProperty() {return this.someProperty;}
public static SampleEnum[] values() {return DynamicEnum.values(SampleEnum.class);}
public static SampleEnum valueOf(String name) {return DynamicEnum.valueOf(SampleEnum.class, name);}
}
/*
* The MIT License (MIT)
*
* Copyright (c) 2017. Diorite (by Bartłomiej Mazur (aka GotoFinal))
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import java.lang.StackWalker.StackFrame;
import java.util.List;
enum ValueType
{
NORMAL,
EXTENDED,
DYNAMIC,
DYNAMIC_EXTENDED;
static ValueType findType(List<StackFrame> frames, Class<?> enumType, Class<?> elementType)
{
StackFrame stackOne = frames.get(0);
if (stackOne.getMethodName().equals("<clinit>") && (stackOne.getDeclaringClass() == enumType))
{
return ValueType.NORMAL;
}
if (frames.size() == 2)
{
StackFrame stackTwo = frames.get(1);
if (stackOne.getMethodName().equals("<init>") && (stackOne.getDeclaringClass() == elementType) && stackTwo.getMethodName().equals("<clinit>") &&
(stackTwo.getDeclaringClass() == enumType))
{
return ValueType.EXTENDED;
}
}
if (stackOne.getDeclaringClass().isAnonymousClass() && stackOne.getMethodName().equals("<init>") && (stackOne.getDeclaringClass() == elementType))
{
return ValueType.DYNAMIC_EXTENDED;
}
return ValueType.DYNAMIC;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment