Index: /trunk/java-utils/src/main/java/de/ugoe/cs/util/console/CommandExecuter.java
===================================================================
--- /trunk/java-utils/src/main/java/de/ugoe/cs/util/console/CommandExecuter.java	(revision 660)
+++ /trunk/java-utils/src/main/java/de/ugoe/cs/util/console/CommandExecuter.java	(revision 661)
@@ -1,15 +1,24 @@
+
 package de.ugoe.cs.util.console;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.net.URL;
 import java.security.InvalidParameterException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
 import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
 import java.util.logging.Level;
 
 /**
  * <p>
- * Executes commands. The commands have to implement the {@link Command}
- * interface and be in packages registered using addCommandPackage().
- * Additionally, default commands are implemented in the
- * de.ugoe.cs.util.console.defaultcommands package.
+ * Executes commands. The commands have to implement the {@link Command} interface and be in
+ * packages registered using addCommandPackage(). Additionally, default commands are implemented in
+ * the de.ugoe.cs.util.console.defaultcommands package.
  * </p>
  * <p>
@@ -22,147 +31,243 @@
 public class CommandExecuter {
 
-	/**
-	 * <p>
-	 * Handle of the CommandExecuter instance.
-	 * </p>
-	 */
-	private final static CommandExecuter theInstance = new CommandExecuter();
-
-	/**
-	 * <p>
-	 * Prefix of all command classes.
-	 * </p>
-	 */
-	private static final String cmdPrefix = "CMD";
-
-	/**
-	 * <p>
-	 * Name of the package for default commands.
-	 * </p>
-	 */
-	private static final String defaultPackage = "de.ugoe.cs.util.console.defaultcommands";
-
-	/**
-	 * <p>
-	 * List of packages in which commands may be defined. The exec methods trys
-	 * to load command from these packages in the order they have been added.
-	 * </p>
-	 * <p>
-	 * The de.ugoe.cs.util.console.defaultcommands package has always lowest
-	 * priority, unless it is specifically added.
-	 * </p>
-	 */
-	private List<String> commandPackageList;
-
-	/**
-	 * <p>
-	 * Returns the instance of CommandExecuter. If no instances exists yet, a
-	 * new one is created.
-	 * </p>
-	 * 
-	 * @return the instance of CommandExecuter
-	 */
-	public static synchronized CommandExecuter getInstance() {
-		return theInstance;
-	}
-
-	/**
-	 * <p>
-	 * Creates a new CommandExecuter. Private to prevent multiple instances
-	 * (Singleton).
-	 * </p>
-	 */
-	private CommandExecuter() {
-		commandPackageList = new ArrayList<String>();
-	}
-
-	/**
-	 * <p>
-	 * Adds a package that will be used by {@link #exec(String)} to load command
-	 * from.
-	 * </p>
-	 * 
-	 * @param pkg
-	 *            package where commands are located
-	 * @throws InvalidParameterException
-	 *             thrown if the package name is null or empty string
-	 */
-	public void addCommandPackage(String pkg) {
-		if ("".equals(pkg) || pkg == null) {
-			throw new InvalidParameterException(
-					"package name must not be null or empty string");
-		}
-		commandPackageList.add(pkg);
-	}
-
-	/**
-	 * <p>
-	 * Executes the command defined by string. A command has the following form
-	 * (mix of EBNF and natural language):
-	 * </p>
-	 * <code>
-	 * &lt;command&gt; := &lt;commandname&gt;&lt;whitespace&gt;{&lt;parameter&gt;}<br>
-	 * &lt;commandname&gt; := String without whitespaces. Has to be a valid Java class name<br>
-	 * &lt;parameter&gt; := &lt;string&gt;|&lt;stringarray&gt;<br>
-	 * &lt;string&gt; := &lt;stringwithoutwhitespaces&gt;|&lt;stringwithwhitespaces&gt;
-	 * &lt;stringwithoutwhitespaces&gt; := a string without whitespaces<br>
-	 * &lt;stringwithoutwhitespaces&gt; := a string, that can have whitespaces, but must be in double quotes<br>
-	 * &lt;stringarray&gt; := "["&lt;string&gt;{&lt;whitespace&gt;&lt;string&gt;"]"
-	 * </code>
-	 * 
-	 * @param command
-	 *            the command as a string
-	 */
-	public void exec(String command) {
-		Console.commandNotification(command);
-		Command cmd = null;
-		CommandParser parser = new CommandParser();
-		parser.parse(command);
-		for (int i = 0; cmd == null && i < commandPackageList.size(); i++) {
-			cmd = loadCMD(commandPackageList.get(i) + "." + cmdPrefix
-					+ parser.getCommandName());
-		}
-		if (cmd == null) { // check if command is available as default command
-			cmd = loadCMD(defaultPackage + "." + cmdPrefix
-					+ parser.getCommandName());
-		}
-		if (cmd == null) {
-			Console.println("Unknown command");
-		} else {
-			try {
-				cmd.run(parser.getParameters());
-			} catch (InvalidParameterException e) {
-				cmd.help();
-			}
-		}
-	}
-
-	/**
-	 * <p>
-	 * Helper method that loads a class and tries to cast it to {@link Command}.
-	 * </p>
-	 * 
-	 * @param className
-	 *            qualified name of the class (including package name)
-	 * @return if class is available and implement {@link Command} and instance
-	 *         of the class, null otherwise
-	 */
-	private Command loadCMD(String className) {
-		Command cmd = null;
-		try {
-			Class<?> cmdClass = Class.forName(className);
-			cmd = (Command) cmdClass.newInstance();
-		} catch (NoClassDefFoundError e) {
-			String[] splitResult = e.getMessage().split("CMD");
-			String correctName = splitResult[splitResult.length - 1].replace(
-					")", "");
-			Console.println("Did you mean " + correctName + "?");
-		} catch (ClassNotFoundException e) {
-		} catch (IllegalAccessException e) {
-		} catch (InstantiationException e) {
-		} catch (ClassCastException e) {
-			Console.traceln(Level.WARNING, className + "found, but does not implement Command");
-		}
-		return cmd;
-	}
+    /**
+     * <p>
+     * Handle of the CommandExecuter instance.
+     * </p>
+     */
+    private final static CommandExecuter theInstance = new CommandExecuter();
+
+    /**
+     * <p>
+     * Prefix of all command classes.
+     * </p>
+     */
+    private static final String cmdPrefix = "CMD";
+
+    /**
+     * <p>
+     * Name of the package for default commands.
+     * </p>
+     */
+    private static final String defaultPackage = "de.ugoe.cs.util.console.defaultcommands";
+
+    /**
+     * <p>
+     * List of packages in which commands may be defined. The exec methods trys to load command from
+     * these packages in the order they have been added.
+     * </p>
+     * <p>
+     * The de.ugoe.cs.util.console.defaultcommands package has always lowest priority, unless it is
+     * specifically added.
+     * </p>
+     */
+    private List<String> commandPackageList;
+
+    /**
+     * <p>
+     * Returns the instance of CommandExecuter. If no instances exists yet, a new one is created.
+     * </p>
+     * 
+     * @return the instance of CommandExecuter
+     */
+    public static synchronized CommandExecuter getInstance() {
+        return theInstance;
+    }
+
+    /**
+     * <p>
+     * Creates a new CommandExecuter. Private to prevent multiple instances (Singleton).
+     * </p>
+     */
+    private CommandExecuter() {
+        commandPackageList = new ArrayList<String>();
+    }
+
+    /**
+     * <p>
+     * Adds a package that will be used by {@link #exec(String)} to load command from.
+     * </p>
+     * 
+     * @param pkg
+     *            package where commands are located
+     * @throws InvalidParameterException
+     *             thrown if the package name is null or empty string
+     */
+    public void addCommandPackage(String pkg) {
+        if ("".equals(pkg) || pkg == null) {
+            throw new InvalidParameterException("package name must not be null or empty string");
+        }
+        commandPackageList.add(pkg);
+    }
+
+    /**
+     * <p>
+     * Executes the command defined by string. A command has the following form (mix of EBNF and
+     * natural language):
+     * </p>
+     * <code>
+     * &lt;command&gt; := &lt;commandname&gt;&lt;whitespace&gt;{&lt;parameter&gt;}<br>
+     * &lt;commandname&gt; := String without whitespaces. Has to be a valid Java class name<br>
+     * &lt;parameter&gt; := &lt;string&gt;|&lt;stringarray&gt;<br>
+     * &lt;string&gt; := &lt;stringwithoutwhitespaces&gt;|&lt;stringwithwhitespaces&gt;
+     * &lt;stringwithoutwhitespaces&gt; := a string without whitespaces<br>
+     * &lt;stringwithoutwhitespaces&gt; := a string, that can have whitespaces, but must be in double quotes<br>
+     * &lt;stringarray&gt; := "["&lt;string&gt;{&lt;whitespace&gt;&lt;string&gt;"]"
+     * </code>
+     * 
+     * @param command
+     *            the command as a string
+     */
+    public void exec(String command) {
+        Console.commandNotification(command);
+        Command cmd = null;
+        CommandParser parser = new CommandParser();
+        parser.parse(command);
+        for (int i = 0; cmd == null && i < commandPackageList.size(); i++) {
+            cmd = loadCMD(commandPackageList.get(i) + "." + cmdPrefix + parser.getCommandName());
+        }
+        if (cmd == null) { // check if command is available as default command
+            cmd = loadCMD(defaultPackage + "." + cmdPrefix + parser.getCommandName());
+        }
+        if (cmd == null) {
+            Console.println("Unknown command");
+        }
+        else {
+            try {
+                cmd.run(parser.getParameters());
+            }
+            catch (InvalidParameterException e) {
+                cmd.help();
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Helper method that loads a class and tries to cast it to {@link Command}.
+     * </p>
+     * 
+     * @param className
+     *            qualified name of the class (including package name)
+     * @return if class is available and implement {@link Command} and instance of the class, null
+     *         otherwise
+     */
+    private Command loadCMD(String className) {
+        Command cmd = null;
+        try {
+            Class<?> cmdClass = Class.forName(className);
+            cmd = (Command) cmdClass.newInstance();
+        }
+        catch (NoClassDefFoundError e) {
+            String[] splitResult = e.getMessage().split("CMD");
+            String correctName = splitResult[splitResult.length - 1].replace(")", "");
+            Console.println("Did you mean " + correctName + "?");
+        }
+        catch (ClassNotFoundException e) {}
+        catch (IllegalAccessException e) {}
+        catch (InstantiationException e) {}
+        catch (ClassCastException e) {
+            Console.traceln(Level.WARNING, className + "found, but does not implement Command");
+        }
+        return cmd;
+    }
+
+    /**
+     * <p>
+     * reads all available commands from the registered command packages and returns a list of
+     * their names
+     * </p>
+     *
+     * @return an array containing the names of the available commands.
+     */
+    public String[] getAvailableCommands() {
+        List<String> commands = new ArrayList<String>();
+        List<String> packages = new ArrayList<String>();
+        packages.addAll(commandPackageList);
+        packages.add(defaultPackage);
+        
+        FilenameFilter filter = new FilenameFilter() {
+            @Override
+            public boolean accept(File dir, String name) {
+                return (name != null) && (name.startsWith(cmdPrefix)) && (name.endsWith(".class"));
+            }
+        };
+        
+        List<String> classNames = new ArrayList<String>();
+        for (String packageName : packages) {
+            String path = packageName.replace('.', '/');
+            try {
+                Enumeration<URL> resources = ClassLoader.getSystemResources(path);
+                
+                while (resources.hasMoreElements()) {
+                    URL resource = resources.nextElement();
+                    File packageDir = new File(resource.getFile());
+                    
+                    if (packageDir.isDirectory()) {
+                        for (File classFile : packageDir.listFiles(filter)) {
+                            String className = classFile.getName().substring
+                                (0, classFile.getName().lastIndexOf('.'));
+                            classNames.add(packageName + "." + className);
+                        }
+                    }
+                    else {
+                        resource.getFile().startsWith("file:");
+                        int index = resource.getFile().lastIndexOf('!');
+                        if ((index > 0) && (resource.getFile().startsWith("file:")) &&
+                            (resource.getFile().endsWith("!/" + path)))
+                        {
+                            String jarFile = resource.getFile().substring("file:".length(), index);
+                            
+                            // we have to read the package content from a jar file
+                            JarInputStream jarInputStream = null;
+                            try {
+                                jarInputStream = new JarInputStream(new FileInputStream(jarFile));
+                            }
+                            catch (Exception e) {
+                                e.printStackTrace();
+                                Console.traceln
+                                    (Level.WARNING, "could not read contents of jar " + jarFile);
+                            }
+
+                            JarEntry entry = null;
+                            do {
+                                entry = jarInputStream.getNextJarEntry();
+                                if ((entry != null) && (!entry.isDirectory()) &&
+                                    (entry.getName().startsWith(path)))
+                                {
+                                    String className = entry.getName().substring
+                                        (path.length(), entry.getName().lastIndexOf('.'));
+                                    classNames.add(packageName + "." + className);
+                                }
+                            }
+                            while (entry != null);
+                        }
+                    }
+                }
+            }
+            catch (IOException e) {
+                Console.traceln(Level.WARNING, "could not read commands of package " + packageName);
+            }
+        }
+        
+        for (String className : classNames) {
+            String commandStr =
+                className.substring(className.lastIndexOf('.') + cmdPrefix.length() + 1);
+            
+            // commands may be found twice as a package may be available twice on the
+            // class path. Therefore check, if the command was already dumped before
+            // dumping it.
+            if (!commands.contains(commandStr)) {
+                // class may still be inner classes. Therefore load the command, to
+                // see if it is really available and a command.
+                if (loadCMD(className) != null) {
+                    commands.add(commandStr);
+                }
+            }
+        }
+        
+        String[] commandArray = commands.toArray(new String[commands.size()]);
+        Arrays.sort(commandArray);
+        return commandArray;
+    }
 }
