001 package org.crsh.cmdline.matcher.impl;
002
003 import org.crsh.cmdline.*;
004 import org.crsh.cmdline.matcher.tokenizer.Token;
005 import org.crsh.cmdline.matcher.tokenizer.Tokenizer;
006
007 import java.util.*;
008
009 /**
010 * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
011 */
012 abstract class Status {
013
014 /**
015 * The input.
016 */
017 static class Request {
018
019 /** . */
020 final Mode mode;
021
022 /** . */
023 final String mainName;
024
025 /** . */
026 Tokenizer tokenizer;
027
028 /** . */
029 final CommandDescriptor<?, ?> command;
030
031 Request(Mode mode, String mainName, Tokenizer tokenizer, CommandDescriptor<?, ?> command) {
032 this.mode = mode;
033 this.mainName = mainName;
034 this.tokenizer = tokenizer;
035 this.command = command;
036 }
037 }
038
039 /**
040 * The output.
041 */
042 static class Response {
043
044 /** . */
045 Status status;
046
047 /** . */
048 LinkedList<Event> events;
049
050 /** . */
051 CommandDescriptor<?, ?> command;
052
053 Response(Status status) {
054 this.status = status;
055 this.events = null;
056 this.command = null;
057 }
058
059 Response() {
060 this.status = null;
061 this.events = null;
062 this.command = null;
063 }
064
065 void add(Event event) {
066 if (events == null) {
067 events = new LinkedList<Event>();
068 }
069 events.add(event);
070 }
071
072 void addAll(Collection<Event> toAdd) {
073 if (events == null) {
074 events = new LinkedList<Event>();
075 }
076 events.addAll(toAdd);
077 }
078 }
079
080 /**
081 * Process a request.
082 *
083 * @param req the request
084 * @param <T> the generic type of the command
085 * @return the response
086 */
087 abstract <T> Response process(Request req);
088
089 static class ReadingOption extends Status {
090
091 <T> Response process(Request req) {
092 Response response = new Response();
093 Token token = req.tokenizer.peek();
094 if (token == null) {
095 response.add(new Event.Stop.Done.Option(req.tokenizer.getIndex()));
096 } else if (token instanceof Token.Whitespace) {
097 response.add(new Event.Separator((Token.Whitespace) token));
098 req.tokenizer.next();
099 } else {
100 Token.Literal literal = (Token.Literal)token;
101 if (literal instanceof Token.Literal.Option) {
102 Token.Literal.Option optionToken = (Token.Literal.Option)literal;
103 if (optionToken.getName().length() == 0 && optionToken instanceof Token.Literal.Option.Long) {
104 req.tokenizer.next();
105 if (req.tokenizer.hasNext()) {
106 if (req.command instanceof ClassDescriptor<?>) {
107 ClassDescriptor<T> classCommand = (ClassDescriptor<T>)req.command;
108 MethodDescriptor<T> m = classCommand.getMethod(req.mainName);
109 if (m != null) {
110 response.command = m;
111 response.add(new Event.Method.Implicit(m, optionToken));
112 }
113 }
114 response.status = new Status.WantReadArg();
115 } else {
116 if (req.mode == Mode.INVOKE) {
117 if (req.command instanceof ClassDescriptor<?>) {
118 ClassDescriptor<T> classCommand = (ClassDescriptor<T>)req.command;
119 MethodDescriptor<T> m = classCommand.getMethod(req.mainName);
120 if (m != null) {
121 response.command = m;
122 response.add(new Event.Method.Implicit(m, optionToken));
123 }
124 }
125 response.status = new Status.Done();
126 response.add(new Event.Stop.Done.Arg(req.tokenizer.getIndex()));
127 } else {
128 if (req.command instanceof ClassDescriptor<?>) {
129 ClassDescriptor<T> classCommand = (ClassDescriptor<T>)req.command;
130 MethodDescriptor<T> m = classCommand.getMethod(req.mainName);
131 if (m != null) {
132 response.command = m;
133 response.add(new Event.Method.Implicit(m, optionToken));
134 response.add(new Event.Stop.Unresolved.NoSuchOption.Method(optionToken));
135 } else {
136 response.add(new Event.Stop.Unresolved.NoSuchOption.Class(optionToken));
137 }
138 } else {
139 response.add(new Event.Stop.Unresolved.NoSuchOption.Method(optionToken));
140 }
141 }
142 }
143 } else {
144 OptionDescriptor<?> desc = req.command.findOption(literal.getValue());
145 if (desc != null) {
146 req.tokenizer.next();
147 int arity = desc.getArity();
148 LinkedList<Token.Literal.Word> values = new LinkedList<Token.Literal.Word>();
149 while (arity > 0) {
150 if (req.tokenizer.hasNext()) {
151 Token a = req.tokenizer.peek();
152 if (a instanceof Token.Whitespace) {
153 req.tokenizer.next();
154 if (req.tokenizer.hasNext() && req.tokenizer.peek() instanceof Token.Literal.Word) {
155 // ok
156 } else {
157 req.tokenizer.pushBack();
158 break;
159 }
160 } else {
161 Token.Literal b = (Token.Literal)a;
162 if (b instanceof Token.Literal.Word) {
163 values.addLast((Token.Literal.Word)b);
164 req.tokenizer.next();
165 arity--;
166 } else {
167 req.tokenizer.pushBack();
168 break;
169 }
170 }
171 } else {
172 break;
173 }
174 }
175 response.add(new Event.Option(desc, optionToken, values));
176 } else {
177 // We are reading an unknown option
178 // it could match an option of an implicit command
179 if (req.command instanceof ClassDescriptor<?>) {
180 MethodDescriptor<T> m = ((ClassDescriptor<T>)req.command).getMethod(req.mainName);
181 if (m != null) {
182 desc = m.findOption(literal.getValue());
183 if (desc != null) {
184 response.command = m;
185 response.add(new Event.Method.Implicit(m, literal));
186 } else {
187 if (req.command.getOptionNames().size() == 0) {
188 response.command = m;
189 response.add(new Event.Method.Implicit(m, literal));
190 } else {
191 response.add(new Event.Stop.Unresolved.NoSuchOption.Method(optionToken));
192 }
193 }
194 } else {
195 response.add(new Event.Stop.Unresolved.NoSuchOption.Class(optionToken));
196 }
197 } else {
198 response.add(new Event.Stop.Unresolved.NoSuchOption.Method(optionToken));
199 }
200 }
201 }
202 } else {
203 Token.Literal.Word wordLiteral = (Token.Literal.Word)literal;
204 if (req.command instanceof ClassDescriptor<?>) {
205 ClassDescriptor<T> classCommand = (ClassDescriptor<T>)req.command;
206 MethodDescriptor<T> m = classCommand.getMethod(wordLiteral.getValue());
207 if (m != null && !m.getName().equals(req.mainName)) {
208 response.command = m;
209 req.tokenizer.next();
210 response.add(new Event.Method.Explicit(m, wordLiteral));
211 } else {
212 m = classCommand.getMethod(req.mainName);
213 if (m != null) {
214 response.add(new Event.Method.Implicit(m, wordLiteral));
215 response.status = new Status.WantReadArg();
216 response.command = m;
217 } else {
218 response.status = new Status.WantReadArg();
219 }
220 }
221 } else {
222 response.status = new Status.WantReadArg();
223 }
224 }
225 }
226 return response;
227 }
228
229 }
230
231 static class WantReadArg extends Status {
232 @Override
233 <T> Response process(Request req) {
234 switch (req.mode) {
235 case INVOKE:
236 return new Response(new Status.ComputeArg());
237 case COMPLETE:
238 return new Response(new Status.ReadingArg());
239 default:
240 throw new AssertionError();
241 }
242 }
243 }
244
245 static class ComputeArg extends Status {
246
247 @Override
248 <T> Response process(Request req) {
249 Token token = req.tokenizer.peek();
250 Response response = new Response();
251 if (token == null) {
252 response.add(new Event.Stop.Done.Arg(req.tokenizer.getIndex()));
253 } else if (token instanceof Token.Whitespace) {
254 response.add(new Event.Separator((Token.Whitespace) token));
255 req.tokenizer.next();
256 } else {
257
258 //
259 List<? extends ArgumentDescriptor<?>> arguments = req.command.getArguments();
260
261 // Count the number ok remaining non whitespace;
262 int tokenCount = 0;
263 int wordCount = 0;
264 do {
265 Token t = req.tokenizer.next();
266 if (t instanceof Token.Literal) {
267 wordCount++;
268 }
269 tokenCount++;
270 }
271 while (req.tokenizer.hasNext());
272 req.tokenizer.pushBack(tokenCount);
273
274 //
275 int oneCount = 0;
276 int zeroOrOneCount = 0;
277 int index = 0;
278 for (ArgumentDescriptor<?> argument : arguments) {
279 Multiplicity multiplicity = argument.getMultiplicity();
280 if (multiplicity == Multiplicity.SINGLE) {
281 if (argument.isRequired()) {
282 if (oneCount + 1 > wordCount) {
283 break;
284 }
285 oneCount++;
286 } else {
287 zeroOrOneCount++;
288 }
289 }
290 index++;
291 }
292
293 // This the number of arguments we can satisfy
294 arguments = arguments.subList(0, index);
295
296 // How many words we can consume for zeroOrOne and zeroOrMore
297 int toConsume = wordCount - oneCount;
298
299 // Correct the zeroOrOneCount and adjust toConsume
300 zeroOrOneCount = Math.min(zeroOrOneCount, toConsume);
301 toConsume -= zeroOrOneCount;
302
303 // The remaining
304 LinkedList<Event> events = new LinkedList<Event>();
305 for (ArgumentDescriptor<?> argument : arguments) {
306 int size;
307 switch (argument.getMultiplicity()) {
308 case SINGLE:
309 if (argument.isRequired()) {
310 size = 1;
311 } else {
312 if (zeroOrOneCount > 0) {
313 zeroOrOneCount--;
314 size = 1;
315 } else {
316 size = 0;
317 }
318 }
319 break;
320 case MULTI:
321 // We consume the remaining
322 size = toConsume;
323 toConsume = 0;
324 break;
325 default:
326 throw new AssertionError();
327 }
328
329 // Now take care of the argument
330 if (size > 0) {
331 List<Token.Literal> values = new ArrayList<Token.Literal>(size);
332 while (size > 0) {
333 Token t = req.tokenizer.next();
334 if (t instanceof Token.Literal) {
335 values.add(((Token.Literal)t));
336 size--;
337 }
338 }
339 events.addLast(new Event.Argument(argument, values));
340
341 // Add the whitespace if needed
342 if (req.tokenizer.hasNext() && req.tokenizer.peek() instanceof Token.Whitespace) {
343 events.addLast(new Event.Separator((Token.Whitespace) req.tokenizer.next()));
344 }
345 }
346 }
347
348 //
349 events.addLast(new Event.Stop.Done.Arg(req.tokenizer.getIndex()));
350
351 //
352 response.status = new Status.Done();
353 response.addAll(events);
354 }
355 return response;
356 }
357 }
358
359 static class Done extends Status {
360 @Override
361 <T> Response process(Request req) {
362 throw new IllegalStateException();
363 }
364 }
365
366 static class ReadingArg extends Status {
367
368 /** . */
369 private final int index;
370
371 ReadingArg() {
372 this(0);
373 }
374
375 private ReadingArg(int index) {
376 this.index = index;
377 }
378
379 ReadingArg next() {
380 return new ReadingArg(index + 1);
381 }
382
383 @Override
384 <T> Response process(Request req) {
385 Token token = req.tokenizer.peek();
386 Response response = new Response();
387 if (token == null) {
388 response.add(new Event.Stop.Done.Arg(req.tokenizer.getIndex()));
389 } else if (token instanceof Token.Whitespace) {
390 response.add(new Event.Separator((Token.Whitespace) token));
391 req.tokenizer.next();
392 } else {
393 final Token.Literal literal = (Token.Literal)token;
394 List<? extends ArgumentDescriptor<?>> arguments = req.command.getArguments();
395 if (index < arguments.size()) {
396 ArgumentDescriptor<?> argument = arguments.get(index);
397 switch (argument.getMultiplicity()) {
398 case SINGLE:
399 req.tokenizer.next();
400 response.add(new Event.Argument(argument, Arrays.asList(literal)));
401 response.status = next();
402 break;
403 case MULTI:
404 req.tokenizer.next();
405 List<Token.Literal> values = new ArrayList<Token.Literal>();
406 values.add(literal);
407 while (req.tokenizer.hasNext()) {
408 Token capture = req.tokenizer.next();
409 if (capture instanceof Token.Literal) {
410 values.add(((Token.Literal)capture));
411 } else {
412 if (req.tokenizer.hasNext()) {
413 // Ok
414 } else {
415 req.tokenizer.pushBack();
416 break;
417 }
418 }
419 }
420 response.add(new Event.Argument(argument, values));
421 }
422 } else {
423 response.add(new Event.Stop.Unresolved.TooManyArguments(literal));
424 }
425 }
426 return response;
427 }
428 }
429 }