Changeset 661


Ignore:
Timestamp:
08/28/12 12:00:07 (12 years ago)
Author:
pharms
Message:
  • added possibility to list available commands
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/java-utils/src/main/java/de/ugoe/cs/util/console/CommandExecuter.java

    r639 r661  
     1 
    12package de.ugoe.cs.util.console; 
    23 
     4import java.io.File; 
     5import java.io.FileInputStream; 
     6import java.io.FilenameFilter; 
     7import java.io.IOException; 
     8import java.net.URL; 
    39import java.security.InvalidParameterException; 
    410import java.util.ArrayList; 
     11import java.util.Arrays; 
     12import java.util.Enumeration; 
    513import java.util.List; 
     14import java.util.jar.JarEntry; 
     15import java.util.jar.JarInputStream; 
    616import java.util.logging.Level; 
    717 
    818/** 
    919 * <p> 
    10  * Executes commands. The commands have to implement the {@link Command} 
    11  * interface and be in packages registered using addCommandPackage(). 
    12  * Additionally, default commands are implemented in the 
    13  * de.ugoe.cs.util.console.defaultcommands package. 
     20 * Executes commands. The commands have to implement the {@link Command} interface and be in 
     21 * packages registered using addCommandPackage(). Additionally, default commands are implemented in 
     22 * the de.ugoe.cs.util.console.defaultcommands package. 
    1423 * </p> 
    1524 * <p> 
     
    2231public class CommandExecuter { 
    2332 
    24         /** 
    25          * <p> 
    26          * Handle of the CommandExecuter instance. 
    27          * </p> 
    28          */ 
    29         private final static CommandExecuter theInstance = new CommandExecuter(); 
    30  
    31         /** 
    32          * <p> 
    33          * Prefix of all command classes. 
    34          * </p> 
    35          */ 
    36         private static final String cmdPrefix = "CMD"; 
    37  
    38         /** 
    39          * <p> 
    40          * Name of the package for default commands. 
    41          * </p> 
    42          */ 
    43         private static final String defaultPackage = "de.ugoe.cs.util.console.defaultcommands"; 
    44  
    45         /** 
    46          * <p> 
    47          * List of packages in which commands may be defined. The exec methods trys 
    48          * to load command from these packages in the order they have been added. 
    49          * </p> 
    50          * <p> 
    51          * The de.ugoe.cs.util.console.defaultcommands package has always lowest 
    52          * priority, unless it is specifically added. 
    53          * </p> 
    54          */ 
    55         private List<String> commandPackageList; 
    56  
    57         /** 
    58          * <p> 
    59          * Returns the instance of CommandExecuter. If no instances exists yet, a 
    60          * new one is created. 
    61          * </p> 
    62          *  
    63          * @return the instance of CommandExecuter 
    64          */ 
    65         public static synchronized CommandExecuter getInstance() { 
    66                 return theInstance; 
    67         } 
    68  
    69         /** 
    70          * <p> 
    71          * Creates a new CommandExecuter. Private to prevent multiple instances 
    72          * (Singleton). 
    73          * </p> 
    74          */ 
    75         private CommandExecuter() { 
    76                 commandPackageList = new ArrayList<String>(); 
    77         } 
    78  
    79         /** 
    80          * <p> 
    81          * Adds a package that will be used by {@link #exec(String)} to load command 
    82          * from. 
    83          * </p> 
    84          *  
    85          * @param pkg 
    86          *            package where commands are located 
    87          * @throws InvalidParameterException 
    88          *             thrown if the package name is null or empty string 
    89          */ 
    90         public void addCommandPackage(String pkg) { 
    91                 if ("".equals(pkg) || pkg == null) { 
    92                         throw new InvalidParameterException( 
    93                                         "package name must not be null or empty string"); 
    94                 } 
    95                 commandPackageList.add(pkg); 
    96         } 
    97  
    98         /** 
    99          * <p> 
    100          * Executes the command defined by string. A command has the following form 
    101          * (mix of EBNF and natural language): 
    102          * </p> 
    103          * <code> 
    104          * &lt;command&gt; := &lt;commandname&gt;&lt;whitespace&gt;{&lt;parameter&gt;}<br> 
    105          * &lt;commandname&gt; := String without whitespaces. Has to be a valid Java class name<br> 
    106          * &lt;parameter&gt; := &lt;string&gt;|&lt;stringarray&gt;<br> 
    107          * &lt;string&gt; := &lt;stringwithoutwhitespaces&gt;|&lt;stringwithwhitespaces&gt; 
    108          * &lt;stringwithoutwhitespaces&gt; := a string without whitespaces<br> 
    109          * &lt;stringwithoutwhitespaces&gt; := a string, that can have whitespaces, but must be in double quotes<br> 
    110          * &lt;stringarray&gt; := "["&lt;string&gt;{&lt;whitespace&gt;&lt;string&gt;"]" 
    111          * </code> 
    112          *  
    113          * @param command 
    114          *            the command as a string 
    115          */ 
    116         public void exec(String command) { 
    117                 Console.commandNotification(command); 
    118                 Command cmd = null; 
    119                 CommandParser parser = new CommandParser(); 
    120                 parser.parse(command); 
    121                 for (int i = 0; cmd == null && i < commandPackageList.size(); i++) { 
    122                         cmd = loadCMD(commandPackageList.get(i) + "." + cmdPrefix 
    123                                         + parser.getCommandName()); 
    124                 } 
    125                 if (cmd == null) { // check if command is available as default command 
    126                         cmd = loadCMD(defaultPackage + "." + cmdPrefix 
    127                                         + parser.getCommandName()); 
    128                 } 
    129                 if (cmd == null) { 
    130                         Console.println("Unknown command"); 
    131                 } else { 
    132                         try { 
    133                                 cmd.run(parser.getParameters()); 
    134                         } catch (InvalidParameterException e) { 
    135                                 cmd.help(); 
    136                         } 
    137                 } 
    138         } 
    139  
    140         /** 
    141          * <p> 
    142          * Helper method that loads a class and tries to cast it to {@link Command}. 
    143          * </p> 
    144          *  
    145          * @param className 
    146          *            qualified name of the class (including package name) 
    147          * @return if class is available and implement {@link Command} and instance 
    148          *         of the class, null otherwise 
    149          */ 
    150         private Command loadCMD(String className) { 
    151                 Command cmd = null; 
    152                 try { 
    153                         Class<?> cmdClass = Class.forName(className); 
    154                         cmd = (Command) cmdClass.newInstance(); 
    155                 } catch (NoClassDefFoundError e) { 
    156                         String[] splitResult = e.getMessage().split("CMD"); 
    157                         String correctName = splitResult[splitResult.length - 1].replace( 
    158                                         ")", ""); 
    159                         Console.println("Did you mean " + correctName + "?"); 
    160                 } catch (ClassNotFoundException e) { 
    161                 } catch (IllegalAccessException e) { 
    162                 } catch (InstantiationException e) { 
    163                 } catch (ClassCastException e) { 
    164                         Console.traceln(Level.WARNING, className + "found, but does not implement Command"); 
    165                 } 
    166                 return cmd; 
    167         } 
     33    /** 
     34     * <p> 
     35     * Handle of the CommandExecuter instance. 
     36     * </p> 
     37     */ 
     38    private final static CommandExecuter theInstance = new CommandExecuter(); 
     39 
     40    /** 
     41     * <p> 
     42     * Prefix of all command classes. 
     43     * </p> 
     44     */ 
     45    private static final String cmdPrefix = "CMD"; 
     46 
     47    /** 
     48     * <p> 
     49     * Name of the package for default commands. 
     50     * </p> 
     51     */ 
     52    private static final String defaultPackage = "de.ugoe.cs.util.console.defaultcommands"; 
     53 
     54    /** 
     55     * <p> 
     56     * List of packages in which commands may be defined. The exec methods trys to load command from 
     57     * these packages in the order they have been added. 
     58     * </p> 
     59     * <p> 
     60     * The de.ugoe.cs.util.console.defaultcommands package has always lowest priority, unless it is 
     61     * specifically added. 
     62     * </p> 
     63     */ 
     64    private List<String> commandPackageList; 
     65 
     66    /** 
     67     * <p> 
     68     * Returns the instance of CommandExecuter. If no instances exists yet, a new one is created. 
     69     * </p> 
     70     *  
     71     * @return the instance of CommandExecuter 
     72     */ 
     73    public static synchronized CommandExecuter getInstance() { 
     74        return theInstance; 
     75    } 
     76 
     77    /** 
     78     * <p> 
     79     * Creates a new CommandExecuter. Private to prevent multiple instances (Singleton). 
     80     * </p> 
     81     */ 
     82    private CommandExecuter() { 
     83        commandPackageList = new ArrayList<String>(); 
     84    } 
     85 
     86    /** 
     87     * <p> 
     88     * Adds a package that will be used by {@link #exec(String)} to load command from. 
     89     * </p> 
     90     *  
     91     * @param pkg 
     92     *            package where commands are located 
     93     * @throws InvalidParameterException 
     94     *             thrown if the package name is null or empty string 
     95     */ 
     96    public void addCommandPackage(String pkg) { 
     97        if ("".equals(pkg) || pkg == null) { 
     98            throw new InvalidParameterException("package name must not be null or empty string"); 
     99        } 
     100        commandPackageList.add(pkg); 
     101    } 
     102 
     103    /** 
     104     * <p> 
     105     * Executes the command defined by string. A command has the following form (mix of EBNF and 
     106     * natural language): 
     107     * </p> 
     108     * <code> 
     109     * &lt;command&gt; := &lt;commandname&gt;&lt;whitespace&gt;{&lt;parameter&gt;}<br> 
     110     * &lt;commandname&gt; := String without whitespaces. Has to be a valid Java class name<br> 
     111     * &lt;parameter&gt; := &lt;string&gt;|&lt;stringarray&gt;<br> 
     112     * &lt;string&gt; := &lt;stringwithoutwhitespaces&gt;|&lt;stringwithwhitespaces&gt; 
     113     * &lt;stringwithoutwhitespaces&gt; := a string without whitespaces<br> 
     114     * &lt;stringwithoutwhitespaces&gt; := a string, that can have whitespaces, but must be in double quotes<br> 
     115     * &lt;stringarray&gt; := "["&lt;string&gt;{&lt;whitespace&gt;&lt;string&gt;"]" 
     116     * </code> 
     117     *  
     118     * @param command 
     119     *            the command as a string 
     120     */ 
     121    public void exec(String command) { 
     122        Console.commandNotification(command); 
     123        Command cmd = null; 
     124        CommandParser parser = new CommandParser(); 
     125        parser.parse(command); 
     126        for (int i = 0; cmd == null && i < commandPackageList.size(); i++) { 
     127            cmd = loadCMD(commandPackageList.get(i) + "." + cmdPrefix + parser.getCommandName()); 
     128        } 
     129        if (cmd == null) { // check if command is available as default command 
     130            cmd = loadCMD(defaultPackage + "." + cmdPrefix + parser.getCommandName()); 
     131        } 
     132        if (cmd == null) { 
     133            Console.println("Unknown command"); 
     134        } 
     135        else { 
     136            try { 
     137                cmd.run(parser.getParameters()); 
     138            } 
     139            catch (InvalidParameterException e) { 
     140                cmd.help(); 
     141            } 
     142        } 
     143    } 
     144 
     145    /** 
     146     * <p> 
     147     * Helper method that loads a class and tries to cast it to {@link Command}. 
     148     * </p> 
     149     *  
     150     * @param className 
     151     *            qualified name of the class (including package name) 
     152     * @return if class is available and implement {@link Command} and instance of the class, null 
     153     *         otherwise 
     154     */ 
     155    private Command loadCMD(String className) { 
     156        Command cmd = null; 
     157        try { 
     158            Class<?> cmdClass = Class.forName(className); 
     159            cmd = (Command) cmdClass.newInstance(); 
     160        } 
     161        catch (NoClassDefFoundError e) { 
     162            String[] splitResult = e.getMessage().split("CMD"); 
     163            String correctName = splitResult[splitResult.length - 1].replace(")", ""); 
     164            Console.println("Did you mean " + correctName + "?"); 
     165        } 
     166        catch (ClassNotFoundException e) {} 
     167        catch (IllegalAccessException e) {} 
     168        catch (InstantiationException e) {} 
     169        catch (ClassCastException e) { 
     170            Console.traceln(Level.WARNING, className + "found, but does not implement Command"); 
     171        } 
     172        return cmd; 
     173    } 
     174 
     175    /** 
     176     * <p> 
     177     * reads all available commands from the registered command packages and returns a list of 
     178     * their names 
     179     * </p> 
     180     * 
     181     * @return an array containing the names of the available commands. 
     182     */ 
     183    public String[] getAvailableCommands() { 
     184        List<String> commands = new ArrayList<String>(); 
     185        List<String> packages = new ArrayList<String>(); 
     186        packages.addAll(commandPackageList); 
     187        packages.add(defaultPackage); 
     188         
     189        FilenameFilter filter = new FilenameFilter() { 
     190            @Override 
     191            public boolean accept(File dir, String name) { 
     192                return (name != null) && (name.startsWith(cmdPrefix)) && (name.endsWith(".class")); 
     193            } 
     194        }; 
     195         
     196        List<String> classNames = new ArrayList<String>(); 
     197        for (String packageName : packages) { 
     198            String path = packageName.replace('.', '/'); 
     199            try { 
     200                Enumeration<URL> resources = ClassLoader.getSystemResources(path); 
     201                 
     202                while (resources.hasMoreElements()) { 
     203                    URL resource = resources.nextElement(); 
     204                    File packageDir = new File(resource.getFile()); 
     205                     
     206                    if (packageDir.isDirectory()) { 
     207                        for (File classFile : packageDir.listFiles(filter)) { 
     208                            String className = classFile.getName().substring 
     209                                (0, classFile.getName().lastIndexOf('.')); 
     210                            classNames.add(packageName + "." + className); 
     211                        } 
     212                    } 
     213                    else { 
     214                        resource.getFile().startsWith("file:"); 
     215                        int index = resource.getFile().lastIndexOf('!'); 
     216                        if ((index > 0) && (resource.getFile().startsWith("file:")) && 
     217                            (resource.getFile().endsWith("!/" + path))) 
     218                        { 
     219                            String jarFile = resource.getFile().substring("file:".length(), index); 
     220                             
     221                            // we have to read the package content from a jar file 
     222                            JarInputStream jarInputStream = null; 
     223                            try { 
     224                                jarInputStream = new JarInputStream(new FileInputStream(jarFile)); 
     225                            } 
     226                            catch (Exception e) { 
     227                                e.printStackTrace(); 
     228                                Console.traceln 
     229                                    (Level.WARNING, "could not read contents of jar " + jarFile); 
     230                            } 
     231 
     232                            JarEntry entry = null; 
     233                            do { 
     234                                entry = jarInputStream.getNextJarEntry(); 
     235                                if ((entry != null) && (!entry.isDirectory()) && 
     236                                    (entry.getName().startsWith(path))) 
     237                                { 
     238                                    String className = entry.getName().substring 
     239                                        (path.length(), entry.getName().lastIndexOf('.')); 
     240                                    classNames.add(packageName + "." + className); 
     241                                } 
     242                            } 
     243                            while (entry != null); 
     244                        } 
     245                    } 
     246                } 
     247            } 
     248            catch (IOException e) { 
     249                Console.traceln(Level.WARNING, "could not read commands of package " + packageName); 
     250            } 
     251        } 
     252         
     253        for (String className : classNames) { 
     254            String commandStr = 
     255                className.substring(className.lastIndexOf('.') + cmdPrefix.length() + 1); 
     256             
     257            // commands may be found twice as a package may be available twice on the 
     258            // class path. Therefore check, if the command was already dumped before 
     259            // dumping it. 
     260            if (!commands.contains(commandStr)) { 
     261                // class may still be inner classes. Therefore load the command, to 
     262                // see if it is really available and a command. 
     263                if (loadCMD(className) != null) { 
     264                    commands.add(commandStr); 
     265                } 
     266            } 
     267        } 
     268         
     269        String[] commandArray = commands.toArray(new String[commands.size()]); 
     270        Arrays.sort(commandArray); 
     271        return commandArray; 
     272    } 
    168273} 
Note: See TracChangeset for help on using the changeset viewer.