package rescuecore2.misc.java; import java.util.List; import java.util.ArrayList; import java.util.jar.Manifest; import java.util.jar.JarEntry; import java.util.jar.Attributes; import java.util.regex.Pattern; import java.util.regex.Matcher; import rescuecore2.registry.MessageFactory; import rescuecore2.registry.EntityFactory; import rescuecore2.registry.PropertyFactory; import rescuecore2.components.Agent; import rescuecore2.components.Simulator; import rescuecore2.components.Viewer; import rescuecore2.components.Component; import rescuecore2.log.Logger; /** This class contains information about a type that can be detected in a jar manifest or class file. For example, a MessageFactory implementation in a jar file can be designated in the jar manifest under a "MessageFactory" attribute, or implementation classes could be inferred by looking for a class name regular expression like "(.*MessageFactory).class". Classes in the jar file can be inspected and checked to see if they extend (or implement) a particular class (interface). */ public class LoadableType { /** A MessageFactory loadable type. */ public static final LoadableType MESSAGE_FACTORY = new LoadableType("MessageFactory", "(.+MessageFactory).class", MessageFactory.class); /** An EntityFactory loadable type. */ public static final LoadableType ENTITY_FACTORY = new LoadableType("EntityFactory", "(.+EntityFactory).class", EntityFactory.class); /** A PropertyFactory loadable type. */ public static final LoadableType PROPERTY_FACTORY = new LoadableType("PropertyFactory", "(.+PropertyFactory).class", PropertyFactory.class); /** An Agent loadable type. */ public static final LoadableType AGENT = new LoadableType("Agent", "(.+(?:FireBrigade|PoliceForce|AmbulanceTeam|Centre|Center|Civilian)).class", Agent.class); /** A Simulator loadable type. */ public static final LoadableType SIMULATOR = new LoadableType("Simulator", "(.+Simulator).class", Simulator.class); /** A Viewer loadable type. */ public static final LoadableType VIEWER = new LoadableType("Viewer", "(.+Viewer).class", Viewer.class); /** A Component loadable type. */ public static final LoadableType COMPONENT = new LoadableType("Component", null, Component.class); private String manifestKey; private Pattern regex; private Class<?> clazz; /** Construct a new LoadableType. @param manifestKey The key to look for in a jar manifest for this type. @param regex A regex to use for determining if a class name should be tested. @param clazz A superclass for checking if candidate classes should be extracted. */ public LoadableType(String manifestKey, String regex, Class<?> clazz) { this.manifestKey = manifestKey; this.regex = regex == null ? null : Pattern.compile(regex); this.clazz = clazz; } /** Inspect a jar manifest and extract the entries in the manifestKey attribute if it exists. This method will check that entries also specify valid class names. @param mf The manifest to check. @return A list of class names. */ public List<String> processManifest(Manifest mf) { Attributes att = mf.getMainAttributes(); String value = att.getValue(manifestKey); List<String> result = new ArrayList<String>(); if (value != null) { for (String next : value.split(" ")) { try { Class<?> testClass = Class.forName(next); if (!clazz.isAssignableFrom(testClass)) { Logger.warn("Manifest entry '" + manifestKey + "' contains invalid class name: '" + next + "' is not a subclass of '" + clazz.getName() + "'"); } else if (testClass.isInterface()) { Logger.warn("Manifest entry '" + manifestKey + "' contains invalid class name: '" + next + "' is an interface"); } else { result.add(next); } } catch (ClassNotFoundException e) { Logger.warn("Manifest entry '" + manifestKey + "' contains invalid class name: '" + next + "' not found"); } } } return result; } /** Inspect an entry in a jar file and see if it names a conformant class. @param e The JarEntry to check. @return The class name, or null if the entry does not name a conformant class. */ public String processJarEntry(JarEntry e) { if (regex == null) { return null; } Matcher m = regex.matcher(e.getName()); if (m.matches()) { String className = null; try { className = m.group(1).replace("/", "."); Class<?> testClass = Class.forName(className); if (clazz.isAssignableFrom(testClass) && !testClass.isInterface()) { return className; } } catch (ClassNotFoundException ex) { Logger.warn("Class " + className + " not found"); } } return null; } }