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 response.add(new Event.DoubleDash((Token.Literal.Option.Long) optionToken));
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 OptionDescriptor<?> desc = req.command.findOption(literal.getValue());
117 if (desc != null) {
118 req.tokenizer.next();
119 int arity = desc.getArity();
120 LinkedList<Token.Literal.Word> values = new LinkedList<Token.Literal.Word>();
121 while (arity > 0) {
122 if (req.tokenizer.hasNext()) {
123 Token a = req.tokenizer.peek();
124 if (a instanceof Token.Whitespace) {
125 req.tokenizer.next();
126 if (req.tokenizer.hasNext() && req.tokenizer.peek() instanceof Token.Literal.Word) {
127 // ok
128 } else {
129 req.tokenizer.pushBack();
130 break;
131 }
132 } else {
133 Token.Literal b = (Token.Literal)a;
134 if (b instanceof Token.Literal.Word) {
135 values.addLast((Token.Literal.Word)b);
136 req.tokenizer.next();
137 arity--;
138 } else {
139 req.tokenizer.pushBack();
140 break;
141 }
142 }
143 } else {
144 break;
145 }
146 }
147 response.add(new Event.Option(desc, optionToken, values));
148 } else {
149 // We are reading an unknown option
150 // it could match an option of an implicit command
151 if (req.command instanceof ClassDescriptor<?>) {
152 MethodDescriptor<T> m = ((ClassDescriptor<T>)req.command).getMethod(req.mainName);
153 if (m != null) {
154 desc = m.findOption(literal.getValue());
155 if (desc != null) {
156 response.command = m;
157 response.add(new Event.Method.Implicit(m, literal));
158 } else {
159 response.add(new Event.Stop.Unresolved.NoSuchOption.Method(optionToken));
160 }
161 } else {
162 response.add(new Event.Stop.Unresolved.NoSuchOption.Class(optionToken));
163 }
164 } else {
165 response.add(new Event.Stop.Unresolved.NoSuchOption.Method(optionToken));
166 }
167 }
168 }
169 } else {
170 Token.Literal.Word wordLiteral = (Token.Literal.Word)literal;
171 if (req.command instanceof ClassDescriptor<?>) {
172 ClassDescriptor<T> classCommand = (ClassDescriptor<T>)req.command;
173 MethodDescriptor<T> m = classCommand.getMethod(wordLiteral.getValue());
174 if (m != null && !m.getName().equals(req.mainName)) {
175 response.command = m;
176 req.tokenizer.next();
177 response.add(new Event.Method.Explicit(m, wordLiteral));
178 } else {
179 m = classCommand.getMethod(req.mainName);
180 if (m != null) {
181 response.add(new Event.Method.Implicit(m, wordLiteral));
182 response.status = new Status.WantReadArg();
183 response.command = m;
184 } else {
185 response.status = new Status.WantReadArg();
186 }
187 }
188 } else {
189 response.status = new Status.WantReadArg();
190 }
191 }
192 }
193 return response;
194 }
195
196 }
197
198 static class WantReadArg extends Status {
199 @Override
200 <T> Response process(Request req) {
201 switch (req.mode) {
202 case INVOKE:
203 return new Response(new Status.ComputeArg());
204 case COMPLETE:
205 return new Response(new Status.ReadingArg());
206 default:
207 throw new AssertionError();
208 }
209 }
210 }
211
212 static class ComputeArg extends Status {
213
214 @Override
215 <T> Response process(Request req) {
216 Token token = req.tokenizer.peek();
217 Response response = new Response();
218 if (token == null) {
219 response.add(new Event.Stop.Done.Arg(req.tokenizer.getIndex()));
220 } else if (token instanceof Token.Whitespace) {
221 response.add(new Event.Separator((Token.Whitespace) token));
222 req.tokenizer.next();
223 } else {
224
225 //
226 List<? extends ArgumentDescriptor<?>> arguments = req.command.getArguments();
227
228 // Count the number ok remaining non whitespace;
229 int tokenCount = 0;
230 int wordCount = 0;
231 do {
232 Token t = req.tokenizer.next();
233 if (t instanceof Token.Literal) {
234 wordCount++;
235 }
236 tokenCount++;
237 }
238 while (req.tokenizer.hasNext());
239 req.tokenizer.pushBack(tokenCount);
240
241 //
242 int oneCount = 0;
243 int zeroOrOneCount = 0;
244 int index = 0;
245 for (ArgumentDescriptor<?> argument : arguments) {
246 Multiplicity multiplicity = argument.getMultiplicity();
247 if (multiplicity == Multiplicity.SINGLE) {
248 if (argument.isRequired()) {
249 if (oneCount + 1 > wordCount) {
250 break;
251 }
252 oneCount++;
253 } else {
254 zeroOrOneCount++;
255 }
256 }
257 index++;
258 }
259
260 // This the number of arguments we can satisfy
261 arguments = arguments.subList(0, index);
262
263 // How many words we can consume for zeroOrOne and zeroOrMore
264 int toConsume = wordCount - oneCount;
265
266 // Correct the zeroOrOneCount and adjust toConsume
267 zeroOrOneCount = Math.min(zeroOrOneCount, toConsume);
268 toConsume -= zeroOrOneCount;
269
270 // The remaining
271 LinkedList<Event> events = new LinkedList<Event>();
272 for (ArgumentDescriptor<?> argument : arguments) {
273 int size;
274 switch (argument.getMultiplicity()) {
275 case SINGLE:
276 if (argument.isRequired()) {
277 size = 1;
278 } else {
279 if (zeroOrOneCount > 0) {
280 zeroOrOneCount--;
281 size = 1;
282 } else {
283 size = 0;
284 }
285 }
286 break;
287 case MULTI:
288 // We consume the remaining
289 size = toConsume;
290 toConsume = 0;
291 break;
292 default:
293 throw new AssertionError();
294 }
295
296 // Now take care of the argument
297 if (size > 0) {
298 List<Token.Literal> values = new ArrayList<Token.Literal>(size);
299 while (size > 0) {
300 Token t = req.tokenizer.next();
301 if (t instanceof Token.Literal) {
302 values.add(((Token.Literal)t));
303 size--;
304 }
305 }
306 events.addLast(new Event.Argument(argument, values));
307
308 // Add the whitespace if needed
309 if (req.tokenizer.hasNext() && req.tokenizer.peek() instanceof Token.Whitespace) {
310 events.addLast(new Event.Separator((Token.Whitespace) req.tokenizer.next()));
311 }
312 }
313 }
314
315 //
316 events.addLast(new Event.Stop.Done.Arg(req.tokenizer.getIndex()));
317
318 //
319 response.status = new Status.Done();
320 response.addAll(events);
321 }
322 return response;
323 }
324 }
325
326 static class Done extends Status {
327 @Override
328 <T> Response process(Request req) {
329 throw new IllegalStateException();
330 }
331 }
332
333 static class ReadingArg extends Status {
334
335 /** . */
336 private final int index;
337
338 ReadingArg() {
339 this(0);
340 }
341
342 private ReadingArg(int index) {
343 this.index = index;
344 }
345
346 ReadingArg next() {
347 return new ReadingArg(index + 1);
348 }
349
350 @Override
351 <T> Response process(Request req) {
352 Token token = req.tokenizer.peek();
353 Response response = new Response();
354 if (token == null) {
355 response.add(new Event.Stop.Done.Arg(req.tokenizer.getIndex()));
356 } else if (token instanceof Token.Whitespace) {
357 response.add(new Event.Separator((Token.Whitespace) token));
358 req.tokenizer.next();
359 } else {
360 final Token.Literal literal = (Token.Literal)token;
361 List<? extends ArgumentDescriptor<?>> arguments = req.command.getArguments();
362 if (index < arguments.size()) {
363 ArgumentDescriptor<?> argument = arguments.get(index);
364 switch (argument.getMultiplicity()) {
365 case SINGLE:
366 req.tokenizer.next();
367 response.add(new Event.Argument(argument, Arrays.asList(literal)));
368 response.status = next();
369 break;
370 case MULTI:
371 req.tokenizer.next();
372 List<Token.Literal> values = new ArrayList<Token.Literal>();
373 values.add(literal);
374 while (req.tokenizer.hasNext()) {
375 Token capture = req.tokenizer.next();
376 if (capture instanceof Token.Literal) {
377 values.add(((Token.Literal)capture));
378 } else {
379 if (req.tokenizer.hasNext()) {
380 // Ok
381 } else {
382 req.tokenizer.pushBack();
383 break;
384 }
385 }
386 }
387 response.add(new Event.Argument(argument, values));
388 }
389 } else {
390 response.add(new Event.Stop.Unresolved.TooManyArguments(literal));
391 }
392 }
393 return response;
394 }
395 }
396 }