package cpw.mods.fml.common.registry; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import cpw.mods.fml.common.FMLLog; import cpw.mods.fml.common.discovery.ASMDataTable; import cpw.mods.fml.common.discovery.ASMDataTable.ASMData; import cpw.mods.fml.common.registry.GameRegistry.ObjectHolder; /** * Internal registry for tracking {@link ObjectHolder} references * @author cpw * */ public enum ObjectHolderRegistry { INSTANCE; private List<ObjectHolderRef> objectHolders = Lists.newArrayList(); public void findObjectHolders(ASMDataTable table) { FMLLog.info("Processing ObjectHolder annotations"); Set<ASMData> allObjectHolders = table.getAll(GameRegistry.ObjectHolder.class.getName()); Map<String, String> classModIds = Maps.newHashMap(); Map<String, Class<?>> classCache = Maps.newHashMap(); for (ASMData data : allObjectHolders) { String className = data.getClassName(); String annotationTarget = data.getObjectName(); String value = (String) data.getAnnotationInfo().get("value"); boolean isClass = className.equals(annotationTarget); if (isClass) { scanTarget(classModIds, classCache, className, annotationTarget, value, isClass, false); } } // double pass - get all the class level annotations first, then the field level annotations for (ASMData data : allObjectHolders) { String className = data.getClassName(); String annotationTarget = data.getObjectName(); String value = (String) data.getAnnotationInfo().get("value"); boolean isClass = className.equals(annotationTarget); if (!isClass) { scanTarget(classModIds, classCache, className, annotationTarget, value, isClass, false); } } scanTarget(classModIds, classCache, "net.minecraft.init.Blocks", null, "minecraft", true, true); scanTarget(classModIds, classCache, "net.minecraft.init.Items", null, "minecraft", true, true); FMLLog.info("Found %d ObjectHolder annotations", objectHolders.size()); } private void scanTarget(Map<String, String> classModIds, Map<String, Class<?>> classCache, String className, String annotationTarget, String value, boolean isClass, boolean extractFromValue) { Class<?> clazz; if (classCache.containsKey(className)) { clazz = classCache.get(className); } else { try { clazz = Class.forName(className, true, getClass().getClassLoader()); classCache.put(className, clazz); } catch (Exception ex) { // unpossible? throw Throwables.propagate(ex); } } if (isClass) { scanClassForFields(classModIds, className, value, clazz, extractFromValue); } else { if (value.indexOf(':') == -1) { String prefix = classModIds.get(className); if (prefix == null) { FMLLog.warning("Found an unqualified ObjectHolder annotation (%s) without a modid context at %s.%s, ignoring", value, className, annotationTarget); throw new IllegalStateException("Unqualified reference to ObjectHolder"); } value = prefix + ":" + value; } try { Field f = clazz.getField(annotationTarget); addHolderReference(new ObjectHolderRef(f, value, extractFromValue)); } catch (Exception ex) { // unpossible? throw Throwables.propagate(ex); } } } private void scanClassForFields(Map<String, String> classModIds, String className, String value, Class<?> clazz, boolean extractFromExistingValues) { classModIds.put(className, value); for (Field f : clazz.getFields()) { int mods = f.getModifiers(); boolean isMatch = Modifier.isPublic(mods) && Modifier.isStatic(mods) && Modifier.isFinal(mods); if (!isMatch || f.isAnnotationPresent(ObjectHolder.class)) { continue; } addHolderReference(new ObjectHolderRef(f, value + ":"+ f.getName(), extractFromExistingValues)); } } private void addHolderReference(ObjectHolderRef ref) { if (ref.isValid()) { objectHolders.add(ref); } } public void applyObjectHolders() { FMLLog.info("Applying holder lookups"); for (ObjectHolderRef ohr : objectHolders) { ohr.apply(); } FMLLog.info("Holder lookups applied"); } }