package appeng.items.parts; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.List; import net.minecraft.util.ResourceLocation; import appeng.api.parts.IPartModel; import appeng.core.AELog; /** * Helps with the reflection magic needed to gather all models for AE2 cable bus * parts. */ public class PartModelsHelper { public static List createModels(Class clazz) { List locations = new ArrayList<>(); // Check all static fields for used models Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (field.getAnnotation(PartModels.class) == null) { continue; } if (!Modifier.isStatic(field.getModifiers())) { AELog.error("The @PartModels annotation can only be used on static fields or methods. Was seen on: " + field); continue; } Object value; try { field.setAccessible(true); value = field.get(null); } catch (IllegalAccessException e) { AELog.error(e, "Cannot access field annotated with @PartModels: " + field); continue; } convertAndAddLocation(field, value, locations); } // Check all static methods for the annotation for (Method method : clazz.getDeclaredMethods()) { if (method.getAnnotation(PartModels.class) == null) { continue; } if (!Modifier.isStatic(method.getModifiers())) { AELog.error("The @PartModels annotation can only be used on static fields or methods. Was seen on: " + method); continue; } // Check for parameter count if (method.getParameters().length != 0) { AELog.error( "The @PartModels annotation can only be used on static methods without parameters. Was seen on: " + method); continue; } // Make sure we can handle the return type Class returnType = method.getReturnType(); if (!ResourceLocation.class.isAssignableFrom(returnType) && !Collection.class.isAssignableFrom(returnType)) { AELog.error( "The @PartModels annotation can only be used on static methods that return a ResourceLocation or Collection of " + "ResourceLocations. Was seen on: " + method); continue; } Object value; try { method.setAccessible(true); value = method.invoke(null); } catch (IllegalAccessException | InvocationTargetException e) { AELog.error(e, "Failed to invoke the @PartModels annotated method " + method); continue; } convertAndAddLocation(method, value, locations); } if (clazz.getSuperclass() != null) { locations.addAll(createModels(clazz.getSuperclass())); } return locations; } private static void convertAndAddLocation(Object source, Object value, List locations) { if (value == null) { return; } if (value instanceof ResourceLocation) { locations.add((ResourceLocation) value); } else if (value instanceof IPartModel) { locations.addAll(((IPartModel) value).getModels()); } else if (value instanceof Collection) { // Check that each object is an IPartModel Collection values = (Collection) value; for (Object candidate : values) { if (!(candidate instanceof IPartModel)) { AELog.error("List of locations obtained from {} contains a non resource location: {}", source, candidate); continue; } locations.addAll(((IPartModel) candidate).getModels()); } } } }