1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.frontend.cli;
20
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Modifier;
24 import java.text.Format;
25 import java.text.MessageFormat;
26 import java.text.NumberFormat;
27 import java.text.ParseException;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.StringTokenizer;
32
33 import org.apache.commons.lang3.ClassUtils;
34 import org.apache.logging.log4j.LogManager;
35 import org.apache.logging.log4j.Logger;
36 import org.mycore.common.MCRClassTools;
37 import org.mycore.common.MCRException;
38 import org.mycore.common.config.MCRConfigurationException;
39
40
41
42
43
44
45
46
47
48
49 public class MCRCommand {
50
51 private static final Logger LOGGER = LogManager.getLogger(MCRCommand.class);
52
53
54 protected MessageFormat messageFormat;
55
56
57 private Method method;
58
59
60 protected Class<?>[] parameterTypes;
61
62
63 protected String className;
64
65
66 protected String methodName;
67
68
69 protected String suffix;
70
71
72 protected String help;
73
74
75
76
77 protected MCRCommand() {
78 }
79
80
81
82
83
84
85
86
87
88
89
90 public MCRCommand(String format, String methodSignature, String helpText) {
91 StringTokenizer st = new StringTokenizer(methodSignature, " ");
92
93 String token = st.nextToken();
94 int point = token.lastIndexOf(".");
95
96 className = token.substring(0, point);
97 methodName = token.substring(point + 1);
98 int numParameters = st.countTokens();
99 parameterTypes = new Class<?>[numParameters];
100 messageFormat = new MessageFormat(format, Locale.ROOT);
101
102 for (int i = 0; i < numParameters; i++) {
103 token = st.nextToken();
104
105 Format f = null;
106 switch (token) {
107 case "int":
108 parameterTypes[i] = Integer.TYPE;
109 f = NumberFormat.getIntegerInstance(Locale.ROOT);
110 break;
111 case "long":
112 parameterTypes[i] = Long.TYPE;
113 f = NumberFormat.getIntegerInstance(Locale.ROOT);
114 break;
115 case "String":
116 parameterTypes[i] = String.class;
117 break;
118 default:
119 unsupportedArgException(methodSignature, token);
120 }
121 messageFormat.setFormat(i, f);
122 }
123
124 int pos = format.indexOf("{");
125 suffix = pos == -1 ? format : format.substring(0, pos);
126
127 if (helpText != null) {
128 help = helpText;
129 } else {
130 help = "No help text available for this command";
131 }
132 }
133
134 private void unsupportedArgException(String methodSignature, String token) {
135 throw new MCRConfigurationException("Error while parsing command definitions for command line interface:\n"
136 + "Unsupported argument type '" + token + "' in command " + methodSignature);
137 }
138
139 public MCRCommand(Method cmd) {
140 className = cmd.getDeclaringClass().getName();
141 methodName = cmd.getName();
142 parameterTypes = cmd.getParameterTypes();
143 org.mycore.frontend.cli.annotation.MCRCommand cmdAnnotation = cmd
144 .getAnnotation(org.mycore.frontend.cli.annotation.MCRCommand.class);
145 help = cmdAnnotation.help();
146 messageFormat = new MessageFormat(cmdAnnotation.syntax(), Locale.ROOT);
147 setMethod(cmd);
148
149 for (int i = 0; i < parameterTypes.length; i++) {
150 Class<?> paramtype = parameterTypes[i];
151 if (ClassUtils.isAssignable(paramtype, Integer.class, true)
152 || ClassUtils.isAssignable(paramtype, Long.class, true)) {
153 messageFormat.setFormat(i, NumberFormat.getIntegerInstance(Locale.ROOT));
154 } else if (!String.class.isAssignableFrom(paramtype)) {
155 unsupportedArgException(className + "." + methodName, paramtype.getName());
156 }
157 }
158
159 int pos = cmdAnnotation.syntax().indexOf("{");
160 suffix = pos == -1 ? cmdAnnotation.syntax() : cmdAnnotation.syntax().substring(0, pos);
161 }
162
163 private void initMethod(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
164 if (method == null) {
165 setMethod(Class.forName(className, true, classLoader).getMethod(methodName, parameterTypes));
166 }
167 }
168
169
170
171
172
173
174 public String getHelpText() {
175 return help;
176 }
177
178
179
180
181
182
183
184
185
186 protected Object[] parseCommandLine(String commandLine) {
187 try {
188 return messageFormat.parse(commandLine);
189 } catch (ParseException ex) {
190 return null;
191 }
192 }
193
194
195
196
197
198
199
200
201 private void prepareInvocationParameters(Object[] commandParameters) {
202
203 for (int i = 0; i < commandParameters.length; i++) {
204 if (parameterTypes[i] == Integer.TYPE) {
205 commandParameters[i] = ((Number) commandParameters[i]).intValue();
206 }
207 }
208 }
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227 public List<String> invoke(String input) throws IllegalAccessException, InvocationTargetException,
228 ClassNotFoundException, NoSuchMethodException {
229 return invoke(input, MCRClassTools.getClassLoader());
230 }
231
232 @SuppressWarnings({ "unchecked", "rawtypes" })
233 public List<String> invoke(String input, ClassLoader classLoader) throws IllegalAccessException,
234 InvocationTargetException, ClassNotFoundException, NoSuchMethodException {
235 if (!input.startsWith(suffix)) {
236 return null;
237 }
238
239 Object[] commandParameters = parseCommandLine(input);
240
241 if (commandParameters == null) {
242 LOGGER.info("No match for syntax: {}", getSyntax());
243 return null;
244 }
245 LOGGER.info("Syntax matched (executed): {}", getSyntax());
246
247 initMethod(classLoader);
248 prepareInvocationParameters(commandParameters);
249 Object result = method.invoke(null, commandParameters);
250 if (result instanceof List && !((List) result).isEmpty() && ((List) result).get(0) instanceof String) {
251 return (List<String>) result;
252 } else {
253 return new ArrayList<>();
254 }
255 }
256
257
258
259
260
261
262 public final String getSyntax() {
263 return messageFormat.toPattern();
264 }
265
266 public void outputHelp() {
267 MCRCommandLineInterface.output(getSyntax());
268 MCRCommandLineInterface.output(" " + getHelpText());
269 MCRCommandLineInterface.output("");
270 }
271
272
273
274
275
276 public void setMethod(Method method) {
277 if (!Modifier.isStatic(method.getModifiers())) {
278 throw new MCRException("MCRCommand method needs to be static: " + method);
279 }
280 this.method = method;
281 }
282 }