Permalink
Browse files

Support MDynamicEnum, bump version

MDynamicEnum is now supported, and these show up properly in the compiler
and reflection mechanisms. This is still not entirely useful, but this
sets the stage for some (now) easily implementable features.
  • Loading branch information...
LadyCailin committed Nov 17, 2018
1 parent 8041089 commit 5433d4d46d365782ccd796bbb6ade7381d8ad2bc
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.sk89q</groupId>
<artifactId>commandhelper</artifactId>
<version>3.3.3-SNAPSHOT</version>
<version>3.3.4-SNAPSHOT</version>
<name>CommandHelper</name>
<description>CommandHelper is a full blown scripting language built right into Minecraft</description>
<inceptionYear>2010</inceptionYear>
@@ -83,6 +83,10 @@ public static void checkForceImplementation() throws Exception {
Set<Class<?>> s = ClassDiscovery.getDefaultInstance().loadClassesThatExtend(superClass);
checkImplements:
for(Class<?> c : s) {
if(c.isInterface()) {
// Interfaces are exempt from the requirement
continue;
}
// First, check if maybe it has a InterfaceRunner for it
findRunner:
for(Class<?> ir : ClassDiscovery.getDefaultInstance().loadClassesWithAnnotation(InterfaceRunnerFor.class)) {
@@ -2,7 +2,6 @@
import com.laytonsmith.PureUtilities.ClassLoading.DynamicEnum;
import com.laytonsmith.annotations.MDynamicEnum;
import com.laytonsmith.annotations.MEnum;
import com.laytonsmith.core.Static;
import java.util.ArrayList;
@@ -70,7 +69,6 @@ public String concreteName() {
return MAP.values();
}
@MEnum("com.commandhelper.VanillaBiomeType")
public enum MCVanillaBiomeType {
OCEAN,
PLAINS,
@@ -3,7 +3,6 @@
import com.laytonsmith.PureUtilities.ClassLoading.DynamicEnum;
import com.laytonsmith.abstraction.MCEntity;
import com.laytonsmith.annotations.MDynamicEnum;
import com.laytonsmith.annotations.MEnum;
import com.laytonsmith.core.Static;
import java.util.ArrayList;
@@ -100,7 +99,6 @@ public boolean isSpawnable() {
return MAP.values();
}
@MEnum("com.commandhelper.VanillaEntityType")
public enum MCVanillaEntityType {
AREA_EFFECT_CLOUD(true),
ARMOR_STAND(true),
@@ -2,7 +2,6 @@
import com.laytonsmith.PureUtilities.ClassLoading.DynamicEnum;
import com.laytonsmith.annotations.MDynamicEnum;
import com.laytonsmith.annotations.MEnum;
import com.laytonsmith.core.Static;
import java.util.ArrayList;
@@ -70,7 +69,6 @@ public String concreteName() {
return MAP.values();
}
@MEnum("com.commandhelper.VanillaParticle")
public enum MCVanillaParticle {
EXPLOSION_NORMAL,
EXPLOSION_LARGE,
@@ -2,7 +2,6 @@
import com.laytonsmith.PureUtilities.ClassLoading.DynamicEnum;
import com.laytonsmith.annotations.MDynamicEnum;
import com.laytonsmith.annotations.MEnum;
import com.laytonsmith.core.Static;
import java.util.ArrayList;
@@ -71,7 +70,6 @@ public String concreteName() {
return MAP.values();
}
@MEnum("com.commandhelper.VanillaSound")
public enum MCVanillaSound {
// After 1.9
AMBIENT_CAVE,
@@ -6,8 +6,10 @@
import java.lang.annotation.Target;
/**
* Tagged onto "enum" classes to allow the reflection mechanism to list out all the values in the enum. MEnum is
* semi-temporary, so this is as well.
* Tagged onto "enum" classes to allow the reflection mechanism to list out all the values in the enum. It is important
* to note that the underlying real enum should not be tagged with the {@link MEnum} annotation. The type in
* MethodScript will be inherited from this annotation, and the values that are listed in the "values" method in this
* class will be used.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@@ -2,24 +2,34 @@
import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscovery;
import com.laytonsmith.PureUtilities.ClassLoading.ClassMirror.ClassMirror;
import com.laytonsmith.PureUtilities.ClassLoading.DynamicEnum;
import com.laytonsmith.PureUtilities.Common.Annotations.InterfaceRunnerFor;
import com.laytonsmith.PureUtilities.Common.ReflectionUtils;
import com.laytonsmith.annotations.MDynamicEnum;
import com.laytonsmith.annotations.MEnum;
import com.laytonsmith.annotations.typeof;
import com.laytonsmith.core.FullyQualifiedClassName;
import com.laytonsmith.core.natives.interfaces.MEnumType;
import com.laytonsmith.core.natives.interfaces.Mixed;
import com.laytonsmith.core.natives.interfaces.MixedInterfaceRunner;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* A utility class for managing the native class lists.
*/
public class NativeTypeList {
// TODO: These methods should add caching, they are called too freqently, and have such a high complexity,
// that this needs to be done.
private static final Object NATIVE_TYPE_LOCK = new Object();
private static volatile Set<String> nativeTypes;
private static Set<FullyQualifiedClassName> fqNativeTypes;
@@ -85,7 +95,11 @@ public static String resolveNativeType(String simpleName) {
nativeTypes.add(name);
}
//TODO: How do DynamicEnums fit into this?
for(ClassMirror<? extends DynamicEnum> c : ClassDiscovery.getDefaultInstance()
.getClassesWithAnnotationThatExtend(MDynamicEnum.class, DynamicEnum.class)) {
String name = (String) c.getAnnotation(MDynamicEnum.class).getValue("value");
nativeTypes.add(name);
}
for(String s : nativeTypes) {
fqNativeTypes.add(FullyQualifiedClassName.forFullyQualifiedClass(s));
@@ -125,7 +139,7 @@ public static String resolveNativeType(String simpleName) {
}
/**
* Returns the java enum type for the given MethodScript enum name.
* Returns the java enum type for the given MethodScript enum name. This does not work with DynamicEnums.
* @param fqcn
* @return
* @throws ClassNotFoundException
@@ -140,7 +154,6 @@ public static String resolveNativeType(String simpleName) {
return (Class<Enum<?>>) c.loadClass();
}
}
// TODO: How do DynamicEnums fit into this?
throw new ClassNotFoundException("Could not find the class of type " + fqcn);
}
@@ -154,8 +167,53 @@ public static MEnumType getNativeEnumType(FullyQualifiedClassName fqcn) throws C
if("ms.lang.enum".equals(fqcn.getFQCN())) {
return MEnumType.getRootEnumType();
}
Class<? extends Enum<?>> e = getNativeEnum(fqcn);
return MEnumType.FromEnum(fqcn, (Class<Enum<?>>) e, null, null);
try {
Class<? extends Enum<?>> e = getNativeEnum(fqcn);
return MEnumType.FromEnum(fqcn, (Class<Enum<?>>) e, null, null);
} catch (ClassNotFoundException ex) {
// Try DynamicEnums
for(ClassMirror<? extends DynamicEnum> c : ClassDiscovery.getDefaultInstance()
.getClassesWithAnnotationThatExtend(MDynamicEnum.class, DynamicEnum.class)) {
if(c.getAnnotation(MDynamicEnum.class).getProxy(MDynamicEnum.class).value().equals(fqcn.getFQCN())) {
ArrayList<?> values;
try {
// This cast currently holds true, which is what we need to access the raw enum value anyways,
// so if this becomes untrue, the change here should be done carefully.
values = (ArrayList<?>) c.getMethod("values", new Class[0])
.loadMethod().invoke(null);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException ex1) {
throw new RuntimeException(ex1);
}
Enum[] constants = values.stream()
.map(new Function<Object, Enum<?>>() {
@Override
public Enum<?> apply(Object e) {
Class<?> cl = e.getClass();
Field candidate = null;
for(Field f : cl.getDeclaredFields()) {
if(Enum.class.isAssignableFrom(f.getType())) {
if(candidate != null) {
throw new RuntimeException("While processing " + c.getClassName()
+ " got multiple instances of Enum candidates");
}
candidate = f;
// Don't break, check all, so we fail fast
}
}
if(candidate == null) {
throw new RuntimeException("While processing " + c.getClassName() + " found no"
+ " instances of Enum");
}
return (Enum<?>) ReflectionUtils.get(cl, e, candidate.getName());
}
})
.collect(Collectors.toList()).toArray(new Enum[values.size()]);
return MEnumType.FromPartialEnum(fqcn, c.loadClass(), constants, null, null);
}
}
}
throw new ClassNotFoundException("Could not find the class of type " + fqcn);
}
/**
@@ -192,7 +192,7 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntime
} else if(args.length == 2) {
FullyQualifiedClassName enumName = FullyQualifiedClassName.forName(args[1].val());
for(ClassMirror<? extends Enum> e : enums) {
if(e.getAnnotation(MEnum.class).getValue("value").equals(enumName)) {
if(e.getAnnotation(MEnum.class).getValue("value").equals(enumName.getFQCN())) {
MEnumType type = MEnumType.FromEnum(enumName, (Class<Enum<?>>) e.loadClass(), null, null);
for(MEnumTypeValue v : type.values()) {
a.push(v, t);
@@ -201,7 +201,7 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntime
}
}
for(ClassMirror<? extends DynamicEnum> d : dEnums) {
if(d.getAnnotation(MDynamicEnum.class).getValue("value").equals(enumName)) {
if(d.getAnnotation(MDynamicEnum.class).getValue("value").equals(enumName.getFQCN())) {
for(DynamicEnum ee : (Collection<DynamicEnum>) ReflectionUtils.invokeMethod(d.loadClass(), null, "values")) {
// TODO: Not sure how to handle this now...
a.push(new CString(ee.name(), t), t);
@@ -34,7 +34,7 @@
public static final CClassType TYPE = CClassType.get("ms.lang.enum");
/**
* Generates a new MEnumType subclass
* Generates a new MEnumType subclass.
* @param fqcn The fully qualified class name. Generally, this should be gathered from the typeof of the MEnum, if
* applicable, but if this is an external enum, or dynamically generated, this may come from other sources.
* @param enumClass The underlying java enum class
@@ -44,9 +44,28 @@
* should be the since tag for the enum class as a whole.
* @return A subclass of MEnumType. This does not register it in the ecosystem.
*/
public static MEnumType FromEnum(FullyQualifiedClassName fqcn, final Class<Enum<?>> enumClass, String docs,
Version since) {
final Enum<?>[] constants = enumClass.getEnumConstants();
public static MEnumType FromEnum(FullyQualifiedClassName fqcn, final Class<Enum<?>> enumClass,
String docs, Version since) {
return FromPartialEnum(fqcn, enumClass, enumClass.getEnumConstants(), docs, since);
}
/**
* Generates a new MEnumType subclass.
* @param fqcn The fully qualified class name. Generally, this should be gathered from the typeof of the MEnum, if
* applicable, but if this is an external enum, or dynamically generated, this may come from other sources.
* @param enumClass The underlying java enum class
* @param values The list of enum constants. This does not have to be the full list of Enum values in the type, or
* indeed, even the enum values from the enumClass. It does have to be an Enum type, however, as we need a
* customizable type for documentation purposes.
* @param docs This may be null if the enum implements {@code public static String enumDocs()}, otherwise this
* should be the docs for the enum class as a whole.
* @param since This may be null if the enum implements {@code public static Version enumSince()}, otherwise this
* should be the since tag for the enum class as a whole.
* @return A subclass of MEnumType. This does not register it in the ecosystem.
*/
public static MEnumType FromPartialEnum(FullyQualifiedClassName fqcn, final Class<?> enumClass,
Enum<?>[] values, String docs, Version since) {
final Enum<?>[] constants = values;
return new MEnumType() {
@Override
public String docs() {
@@ -139,7 +158,7 @@ public MEnumTypeValue get(int index) {
return new MEnumTypeValue() {
@Override
public int ordinal() {
return v.ordinal();
return index;
}
@Override
@@ -154,9 +173,9 @@ public URL getSourceJar() {
@Override
public Class<? extends Documentation>[] seeAlso() {
if(SimpleDocumentation.class.isAssignableFrom(enumClass)) {
if(SimpleDocumentation.class.isAssignableFrom(v.getDeclaringClass())) {
try {
return (Class[]) enumClass.getDeclaredMethod("seeAlso").invoke(v);
return (Class[]) v.getDeclaringClass().getDeclaredMethod("seeAlso").invoke(v);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException ex) {
throw new RuntimeException(ex);
@@ -173,9 +192,9 @@ public String getName() {
@Override
public String docs() {
if(SimpleDocumentation.class.isAssignableFrom(enumClass)) {
if(SimpleDocumentation.class.isAssignableFrom(v.getDeclaringClass())) {
try {
return (String) enumClass.getDeclaredMethod("docs").invoke(v);
return (String) v.getDeclaringClass().getDeclaredMethod("docs").invoke(v);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException ex) {
throw new RuntimeException(ex);
@@ -187,9 +206,9 @@ public String docs() {
@Override
public Version since() {
if(SimpleDocumentation.class.isAssignableFrom(enumClass)) {
if(SimpleDocumentation.class.isAssignableFrom(v.getDeclaringClass())) {
try {
return (Version) enumClass.getDeclaredMethod("since").invoke(v);
return (Version) v.getDeclaringClass().getDeclaredMethod("since").invoke(v);
} catch (NoSuchMethodException | SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException ex) {
throw new RuntimeException(ex);

0 comments on commit 5433d4d

Please sign in to comment.