View Javadoc

1   /*
2   Karma core - Core of the Karma application
3   Copyright (C) 2004  Toolforge <www.toolforge.nl>
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19  package nl.toolforge.karma.core.cmd;
20  
21  import nl.toolforge.karma.core.cmd.digester.CommandDescriptorCreationFactory;
22  import nl.toolforge.karma.core.cmd.digester.OptionDescriptorCreationFactory;
23  import org.apache.commons.cli.Options;
24  import org.apache.commons.digester.Digester;
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.xml.sax.SAXException;
28  
29  import java.io.IOException;
30  import java.net.URL;
31  import java.util.Enumeration;
32  
33  /***
34   * <p>Loads command-descriptors from an <code>XML</code>-file. The default filename
35   * is <code>commands.xml</code>, which should be available in the classpath. It should have been shipped with
36   * the Karma Core release jar-file.
37   * <p/>
38   *
39   * @author W.M. Oosterom
40   * @author D.A. Smedes
41   * @version $Id: CommandLoader.java,v 1.33 2004/11/10 23:53:08 asmedes Exp $
42   */
43  public final class CommandLoader {
44  
45    //TODO the xml instance should be checked by a DTD or XML Schema document.
46  
47    private static Log logger = LogFactory.getLog(CommandLoader.class);
48    /***
49     * Default filename for the command descriptor file
50     */
51    public static final String DEFAULT_COMMANDS_BASEDIR = "commands";
52  
53    /*** File name for core commands. */
54    public static final String CORE_COMMANDS_FILE = "core-commands.xml";
55  
56    /*** Directory where plugins are located. */
57    public static final String COMMAND_PLUGINS_DIR = "plugins";
58  
59    /*** File name for plugin commands definitions. */
60    public static final String PLUGIN_COMMANDS_FILE = "commands.xml";
61  
62    private CommandLoader() {
63    }
64  
65    private static CommandLoader instance = null;
66  
67    /***
68     * Gets the singleton instance of the <code>CommandLoader</code>.
69     *
70     * @return The singleton instance of the <code>CommandLoader</code>.
71     */
72    public static CommandLoader getInstance() {
73      if (instance == null) {
74        instance = new CommandLoader();
75      }
76      return instance;
77    }
78  
79    /***
80     * Loads command xml files from a predefined base directory. The base directory is determined by
81     * {@link DEFAULT_COMMANDS_BASEDIR}, relative to the runtime classpath. Core commands are considered to be in
82     * <code>core-commands.xml</code>. The rest of the commands are located in plugin directories on the classpath.
83     *
84     * @return The full set of commands for Karma.
85     * @throws CommandLoadException
86     */
87    CommandDescriptorMap load() throws CommandLoadException {
88  
89      // Load the core commands
90      //
91      CommandDescriptorMap uniqueCommands = loadCoreCommands();
92  
93      // Load the plugin commands
94      //
95      Enumeration enum = null;
96      try {
97        String commands = DEFAULT_COMMANDS_BASEDIR + "/" + COMMAND_PLUGINS_DIR + "/" + PLUGIN_COMMANDS_FILE;
98        enum = this.getClass().getClassLoader().getResources(commands);
99      } catch (IOException ioe) {
100       throw new CommandLoadException(CommandLoadException.LOAD_FAILURE_FOR_PLUGIN_COMMANDS_FILE, new Object[]{PLUGIN_COMMANDS_FILE});
101     }
102 
103     while (enum.hasMoreElements()) {
104 
105       CommandDescriptorMap map = (CommandDescriptorMap) load((URL) enum.nextElement());
106 
107 //      for (Iterator i = map.keySet().iterator(); i.hasNext();) {
108 //
109 //        CommandDescriptor descriptor = map.get((String) i.next());
110 //
111 //        for (Iterator j = descriptor.getAliasList().iterator(); j.hasNext();) {
112 //          String alias = (String) j.next();
113 //          if (uniqueCommands.keySet().contains(alias)) {
114 //            throw new CommandLoadException(CommandLoadException.DUPLICATE_COMMAND, new Object[]{alias});
115 //          }
116 //        }
117 //        // At this point, we have not found a duplicate.
118 //        //
119 //        uniqueCommands.add(descriptor);
120 //      }
121 
122       uniqueCommands.addAll(map);
123     }
124 
125     return uniqueCommands;
126   }
127 
128   /***
129    * <p>Loads the <code>xml</code> file containing command descriptors.
130    *
131    * @param resource         The resource url to a command <code>xml</code> file. Use
132    *                         {@link #load} to use the default settings.
133    * @return                 A <code>List</code> of {@link CommandDescriptor} instances.
134    *
135    * @throws CommandLoadException
136    */
137   CommandDescriptorMap load(URL resource) throws CommandLoadException {
138 
139     try {
140       return (CommandDescriptorMap) getCommandDigester().parse(resource.openStream());
141     } catch (IOException e) {
142       logger.error(e);
143       throw new CommandLoadException(CommandLoadException.LOAD_FAILURE_FOR_PLUGIN_COMMANDS_FILE, new Object[]{PLUGIN_COMMANDS_FILE});
144     } catch (SAXException e) {
145       logger.error(e);
146       throw new CommandLoadException(CommandLoadException.LOAD_FAILURE_FOR_PLUGIN_COMMANDS_FILE, new Object[]{PLUGIN_COMMANDS_FILE});
147     }
148   }
149 
150   /***
151    * Loads commands from the Karma default <code>commands.xml</code> file.
152    *
153    * @throws CommandLoadException
154    */
155   private CommandDescriptorMap loadCoreCommands() throws CommandLoadException {
156 
157     try {
158       String defaultCommands = DEFAULT_COMMANDS_BASEDIR + "/" + CORE_COMMANDS_FILE;
159 
160       CommandDescriptorMap cds = (CommandDescriptorMap) getCommandDigester().parse(this.getClass().getClassLoader().getResourceAsStream(defaultCommands));
161 
162       return cds;
163     } catch (IOException e) {
164       logger.error(e);
165       throw new CommandLoadException(CommandLoadException.LOAD_FAILURE_FOR_DEFAULT_COMMANDS, new Object[]{CORE_COMMANDS_FILE});
166     } catch (SAXException e) {
167       logger.error(e);
168       throw new CommandLoadException(CommandLoadException.LOAD_FAILURE_FOR_DEFAULT_COMMANDS, new Object[]{CORE_COMMANDS_FILE});
169     }
170   }
171 
172   private Digester getCommandDigester() {
173 
174     Digester digester = new Digester();
175 
176     digester.addObjectCreate("commands", CommandDescriptorMap.class);
177 
178     digester.addFactoryCreate("*/command", CommandDescriptorCreationFactory.class);
179     digester.addCallMethod("*/command/description", "setDescription", 0);
180     digester.addCallMethod("*/command/classname", "setClassName", 0);
181     digester.addCallMethod("*/command/help", "setHelp", 0);
182 
183     digester.addObjectCreate("*/command/options", Options.class);
184 
185     digester.addFactoryCreate("*/command/options/option", OptionDescriptorCreationFactory.class);
186     digester.addCallMethod("*/command/options/option/arg", "setArgName", 0);
187     digester.addSetNext("*/command/options/option", "addOption");
188 
189     digester.addSetNext("*/command/options", "addOptions"); // Adds an Option to the command.
190 
191     digester.addSetNext("*/command", "add"); // Adds a CommandDescriptor instance to the set.
192 
193     return digester;
194   }
195 }