package com.myselia.stem.control; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; import com.myselia.stem.control.commands.Command; import com.myselia.stem.control.commands.CommandHelp; public class CommandSystem { private static int index = 0; private static String[] history = new String[20]; private static String[] commandparts; private static String command; private static ReflectionCommand[] commandClasses = null; private static boolean debug = true; /** * Scans all classes accessible from the context class loader which belong * to the given package and subpackages. Fetches all the useful fields for * later use and store them in commandClasses * * @author Victor Tatai Adapted for project needs by * @author Philippe Hebert * @source http://dzone.com/snippets/get-all-classes-within-package * @param packageName * @throws ClassNotFoundException * @throws IOException */ public static void setClasses(String packageName) throws ClassNotFoundException, IOException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { ClassLoader classLoader = Thread.currentThread() .getContextClassLoader(); // Error #001 Verification if (classLoader == null) { System.err.println("CommandSystem error. Please force kill the application and investigate."); } ArrayList<Class<Command>> classes = new ArrayList<Class<Command>>(); List<File> dirs = new ArrayList<File>(); if (debug) { String path = packageName.replace('.', '/'); Enumeration<URL> resources = classLoader.getResources(path); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); dirs.add(new File(resource.getFile())); } for (File directory : dirs) { classes.addAll(findClasses(directory, packageName)); } } else { // TESTING SHIT - IGNORE START TESTgetClasses("myseliaStem.jar", classes); // TESTING SHIT - IGNORE END } classes.trimToSize(); // We don't want abstract classes or interfaces in commandClasses. int abstract_count = 0; for (Class<Command> c : classes) { if (Modifier.isAbstract(c.getModifiers())) abstract_count++; } //System.out.println("Populating commandClasses"); // Populating commandClasses and its members. commandClasses = new ReflectionCommand[classes.size() - abstract_count]; for (int i = 0, j = 0; i < classes.size(); i++) { Class<Command> current = classes.get(i); //System.out.println(current); if (Modifier.isAbstract(current.getModifiers())) continue; Method[] methods = { current.getMethod("getCommandSignature"), current.getMethod("action", new Class[] { String.class }) }; Command c_obj = (Command) current.getConstructor().newInstance(); String comm_current = (String) methods[0].invoke(c_obj); commandClasses[j] = new ReflectionCommand(current, comm_current, methods); j++; } CommandHelp.setCommands(commandClasses); } /** * Recursive method used to find all classes in a given directory and * subdirs * * @author Victor Tatai * @source http://dzone.com/snippets/get-all-classes-within-package * @param directory * @param packageName * @return * @throws ClassNotFoundException */ @SuppressWarnings("unchecked") private static List<Class<Command>> findClasses(File directory, String packageName) throws ClassNotFoundException, IOException { List<Class<Command>> classes = new ArrayList<Class<Command>>(); if (!directory.exists()) { return classes; } File[] files = directory.listFiles(); for (File file : files) { if (file.isDirectory()) { // assert !file.getName().contains("."); classes.addAll(findClasses(file, packageName + "." + file.getName())); } else if (file.getName().endsWith(".class")) { classes.add((Class<Command>) Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6))); } } return classes; } /** * Verifies if command is valid by comparing first word with static variable * of AbstractCommand subclasses. Makes use of quasi-anonymous objects. * Hardly needs any maintenance - adapts to the number of Command classes in * cms.controller.command. Calls the proper method and print() method. * * @author Philippe Hebert * @param str */ public static void command(String str) { history(str); str = str.trim(); commandparts = str.split(" "); command = str; /* * Debug System.out.print("command received: "); for(String s : * commandparts){ System.out.print("'" + s + "' "); } */ int j = 0; for (ReflectionCommand c : commandClasses) { if (c == null) System.out.println("c" + j + " is null"); try { /* * Debug System.out.println("" + methods[0].invoke(c_obj)); */ if (commandparts[0].equalsIgnoreCase(c.getCommand())) { print(true); Method[] methods = c.getMethods(); Command c_obj = (Command) c.getClassField() .getConstructor().newInstance(); methods[1].invoke(c_obj, command); return; } } catch (Exception e) { System.err.println("CommandSystem error. Please force kill the application and investigate."); e.printStackTrace(); return; } } print(false); } private static void print(boolean good) { if (good) { //System.out.println(command); } else { System.err.println("no such command \"" + command + "\""); } } private static void history(String last) { for (int i = (history.length - 1); i > 1; i--) { history[i] = history[i - 1]; } history[1] = last; history[0] = ""; } public static void index_increase() { if (index < history.length) { index++; } } public static void index_decrease() { if (index > 0) { index--; } } public static void index_reset() { index = -1; } public static String get_command() { if (index < 0) { index = 0; } return history[index]; } // Naughty Testing Stuff public static List getClasseNames(String jarName) { ArrayList classes = new ArrayList(); if (debug) System.out.println("Jar " + jarName); try { JarInputStream jarFile = new JarInputStream(new FileInputStream( jarName)); JarEntry jarEntry; while (true) { jarEntry = jarFile.getNextJarEntry(); if (jarEntry == null) { break; } if (jarEntry.getName().endsWith(".class")) { if (debug) System.out.println("Found " + jarEntry.getName().replaceAll("/", "\\.")); classes.add(jarEntry.getName().replaceAll("/", "\\.")); } } } catch (Exception e) { e.printStackTrace(); } return classes; } public static void TESTgetClasses(String jarName, ArrayList classes) throws IOException, ClassNotFoundException { JarFile jarFile = new JarFile(jarName); Enumeration e = jarFile.entries(); URL[] urls = { new URL("jar:file:" + jarName + "!/") }; URLClassLoader cl = URLClassLoader.newInstance(urls); while (e.hasMoreElements()) { JarEntry je = (JarEntry) e.nextElement(); if (je.isDirectory() || !je.getName().endsWith(".class")) { continue; } // -6 because of .class if (je.getName().startsWith("cms/control")) { String className = je.getName().substring(0, je.getName().length() - 6); className = className.replace('/', '.'); System.out.println("FOUND: " + className); //Class c = cl.loadClass(className); classes.add((Class<Command>) Class.forName(className)); } } } }