001 /*
002 *
003 * $Revision: 13085 $ $Date: 2008-02-06 18:27:24 +0100 (Mi, 06 Feb 2008) $
004 *
005 * This file is part of *** M y C o R e ***
006 * See http://www.mycore.de/ for details.
007 *
008 * This program is free software; you can use it, redistribute it
009 * and / or modify it under the terms of the GNU General Public License
010 * (GPL) as published by the Free Software Foundation; either version 2
011 * of the License or (at your option) any later version.
012 *
013 * This program is distributed in the hope that it will be useful, but
014 * WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program, in a file called gpl.txt or license.txt.
020 * If not, write to the Free Software Foundation Inc.,
021 * 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
022 */
023
024 package org.mycore.frontend.cli;
025
026 import java.lang.reflect.InvocationTargetException;
027 import java.lang.reflect.Method;
028 import java.text.Format;
029 import java.text.MessageFormat;
030 import java.text.NumberFormat;
031 import java.text.ParseException;
032 import java.util.ArrayList;
033 import java.util.List;
034 import java.util.StringTokenizer;
035
036 import org.mycore.common.MCRConfigurationException;
037
038 /**
039 * Represents a command understood by the command line interface. A command has
040 * an external input syntax that the user uses to invoke the command and points
041 * to a method in a class that implements the command.
042 *
043 * @see MCRCommandLineInterface
044 *
045 * @author Frank Lützenkirchen
046 * @author Jens Kupferschmidt
047 * @version $Revision: 13085 $ $Date: 2008-02-06 18:27:24 +0100 (Mi, 06 Feb 2008) $
048 */
049 public class MCRCommand {
050 /** The input format used for invoking this command */
051 protected MessageFormat messageFormat;
052
053 /** The java method that implements this command */
054 protected Method method;
055
056 /** The types of the invocation parameters */
057 protected Class[] parameterTypes;
058
059 /** The number of invocation parameters */
060 protected int numParameters;
061
062 /** The class providing the implementation method */
063 protected String className;
064
065 /** The method implementing this command */
066 protected String methodName;
067
068 /** The beginning of the message format up to the first parameter */
069 protected String suffix;
070
071 /** The help text String */
072 protected String help;
073
074 /**
075 * Creates a new MCRCommand.
076 *
077 * @param format
078 * the command syntax, e.g. "save document {0} to directory {1}"
079 * @param methodSignature
080 * the method to invoke, e.g.
081 * "miless.commandline.DocumentCommands.saveDoc int String"
082 * @param helpText
083 * the helpt text for this command
084 */
085 public MCRCommand(String format, String methodSignature, String helpText) {
086 StringTokenizer st = new StringTokenizer(methodSignature, " ");
087
088 String token = st.nextToken();
089 int point = token.lastIndexOf(".");
090
091 className = token.substring(0, point);
092 methodName = token.substring(point + 1);
093 numParameters = st.countTokens();
094 parameterTypes = new Class[numParameters];
095 messageFormat = new MessageFormat(format);
096
097 for (int i = 0; i < numParameters; i++) {
098 token = st.nextToken();
099
100 Format f;
101
102 if (token.equals("int")) {
103 parameterTypes[i] = Integer.TYPE;
104 f = NumberFormat.getIntegerInstance();
105 } else if (token.equals("String")) {
106 parameterTypes[i] = String.class;
107 f = null;
108 } else {
109 throw new MCRConfigurationException("Error while parsing command definitions for command line interface:\n" + "Unsupported argument type '"
110 + token + "' in command " + methodSignature);
111 }
112
113 messageFormat.setFormat(i, f);
114 }
115
116 int pos = format.indexOf("{");
117 suffix = ((pos == -1) ? format : format.substring(0, pos));
118
119 if (helpText != null) {
120 help = helpText;
121 } else {
122 help = "No help text available for this command";
123 }
124 }
125
126 /**
127 * Returns the method implementing the command behavior.
128 *
129 * @return The method to be invoked for executing the command
130 * @throws ClassNotFoundException
131 * when the class that implements the method was not found
132 * @throws NoSuchMethodException
133 * When the method specified in the constructor was not found
134 */
135 protected Method getMethod(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
136 if (method == null) {
137 method = Class.forName(className, true, classLoader).getMethod(methodName, parameterTypes);
138 }
139
140 return method;
141 }
142
143 /**
144 * The method return the helpt text of this command.
145 *
146 * @return the help text as String
147 */
148 protected String getHelpText() {
149 return help;
150 }
151
152 /**
153 * Parses an input string and tries to match it with the message format used
154 * to invoke this command.
155 *
156 * @param commandLine
157 * The input from the command line
158 * @return null, if the input does not match the message format; otherwise
159 * an array holding the parameter values from the command line
160 */
161 protected Object[] parseCommandLine(String commandLine) {
162 try {
163 return messageFormat.parse(commandLine);
164 } catch (ParseException ex) {
165 return null;
166 }
167 }
168
169 /**
170 * Transforms the parameters found by the MessageFormat parse method into
171 * such that can be used to invoke the method implementing this command
172 *
173 * @param commandParameters
174 * The parameters as returned by the
175 * <code>parseCommandLine</code> method
176 * @return The parameters that can be used to invoke the implementing method
177 */
178 protected Object[] buildInvocationParameters(Object[] commandParameters) {
179 Object[] parameters = new Object[numParameters];
180 int j = 0;
181
182 for (int i = 0; i < numParameters; i++) {
183 if (parameterTypes[i] == Integer.TYPE) {
184 parameters[i] = ((Number) commandParameters[j]).intValue();
185 j++;
186 continue;
187 }
188
189 if (parameterTypes[i] == String.class) {
190 parameters[i] = commandParameters[j];
191 j++;
192
193 continue;
194 }
195 }
196
197 return parameters;
198 }
199
200 /**
201 * Tries to invoke the method that implements the behavior of this command
202 * given the user input from the command line. This is only done when the
203 * command line syntax matches the syntax used by this command.
204 *
205 * @return null, if the command syntax did not match and the command was not
206 * invoked, otherwise a List of commands is returned which may be
207 * empty or otherwise contains commands that should be processed
208 * next
209 * @param input
210 * The command entered by the user at the command prompt
211 * @throws IllegalAccessException
212 * when the method can not be invoked
213 * @throws InvocationTargetException
214 * when an exception is thrown by the invoked method
215 * @throws ClassNotFoundException
216 * when the class providing the method could not be found
217 * @throws NoSuchMethodException
218 * when the method specified does not exist
219 */
220 public List<String> invoke(String input) throws IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException {
221 return invoke(input, MCRCommand.class.getClassLoader());
222 }
223
224 @SuppressWarnings("unchecked")
225 public List<String> invoke(String input, ClassLoader classLoader) throws IllegalAccessException, InvocationTargetException, ClassNotFoundException,
226 NoSuchMethodException {
227 if (!input.startsWith(suffix)) {
228 return null;
229 }
230
231 Object[] commandParameters = parseCommandLine(input);
232
233 if (commandParameters == null) {
234 return null;
235 }
236
237 Object result = getMethod(classLoader).invoke(null, buildInvocationParameters(commandParameters));
238 if ((result instanceof List) && (!((List) result).isEmpty()) && (((List) result).get(0) instanceof String))
239 return (List<String>) result;
240 else
241 return new ArrayList<String>();
242 }
243
244 /**
245 * Returns the input syntax to be used for invoking this command from the
246 * command prompt.
247 *
248 * @return the input syntax for this command
249 */
250 public final String showSyntax() {
251 return messageFormat.toPattern();
252 }
253 }