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 }