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