All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.crsh.cmdline.matcher.impl.Status Maven / Gradle / Ivy

/*
 * Copyright (C) 2012 eXo Platform SAS.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.crsh.cmdline.matcher.impl;

import org.crsh.cmdline.*;
import org.crsh.cmdline.matcher.tokenizer.Token;
import org.crsh.cmdline.matcher.tokenizer.Tokenizer;

import java.util.*;

abstract class Status {

  /**
   * The input.
   */
  static class Request {

    /** . */
    final Mode mode;
    
    /** . */
    final String mainName;
    
    /** . */
    Tokenizer tokenizer;

    /** . */
    final CommandDescriptor command;

    Request(Mode mode, String mainName, Tokenizer tokenizer, CommandDescriptor command) {
      this.mode = mode;
      this.mainName = mainName;
      this.tokenizer = tokenizer;
      this.command = command;
    }
  }

  /**
   * The output.
   */
  static class Response {

    /** . */
    Status status;

    /** . */
    LinkedList events;

    /** . */
    CommandDescriptor command;

    Response(Status status) {
      this.status = status;
      this.events = null;
      this.command = null;
    }

    Response() {
      this.status = null;
      this.events = null;
      this.command = null;
    }

    void add(Event event) {
      if (events == null) {
        events = new LinkedList();
      }
      events.add(event);
    }

    void addAll(Collection toAdd) {
      if (events == null) {
        events = new LinkedList();
      }
      events.addAll(toAdd);
    }
  }

  /**
   * Process a request.
   *
   * @param req the request
   * @param  the generic type of the command
   * @return the response
   */
  abstract  Response process(Request req);

  static class ReadingOption extends Status {

     Response process(Request req) {
      Response response = new Response();
      Token token = req.tokenizer.peek();
      if (token == null) {
        response.add(new Event.Stop.Done.Option(req.tokenizer.getIndex()));
      } else if (token instanceof Token.Whitespace) {
        response.add(new Event.Separator((Token.Whitespace) token));
        req.tokenizer.next();
      } else {
        Token.Literal literal = (Token.Literal)token;
        if (literal instanceof Token.Literal.Option) {
          Token.Literal.Option optionToken = (Token.Literal.Option)literal;
          if (optionToken.getName().length() == 0 && optionToken instanceof Token.Literal.Option.Long) {
            req.tokenizer.next();
            if (req.tokenizer.hasNext()) {
              if (req.command instanceof ClassDescriptor) {
                ClassDescriptor classCommand = (ClassDescriptor)req.command;
                MethodDescriptor m = classCommand.getMethod(req.mainName);
                if (m != null) {
                  response.command = m;
                  response.add(new Event.Method.Implicit(m, optionToken));
                }
              }
              response.status = new Status.WantReadArg();
            } else {
              if (req.mode == Mode.INVOKE) {
                if (req.command instanceof ClassDescriptor) {
                  ClassDescriptor classCommand = (ClassDescriptor)req.command;
                  MethodDescriptor m = classCommand.getMethod(req.mainName);
                  if (m != null) {
                    response.command = m;
                    response.add(new Event.Method.Implicit(m, optionToken));
                  }
                }
                response.status = new Status.Done();
                response.add(new Event.Stop.Done.Arg(req.tokenizer.getIndex()));
              } else {
                if (req.command instanceof ClassDescriptor) {
                  ClassDescriptor classCommand = (ClassDescriptor)req.command;
                  MethodDescriptor m = classCommand.getMethod(req.mainName);
                  if (m != null) {
                    response.command = m;
                    response.add(new Event.Method.Implicit(m, optionToken));
                    response.add(new Event.Stop.Unresolved.NoSuchOption.Method(optionToken));
                  } else  {
                    response.add(new Event.Stop.Unresolved.NoSuchOption.Class(optionToken));
                  }
                } else {
                  response.add(new Event.Stop.Unresolved.NoSuchOption.Method(optionToken));
                }
              }
            }
          } else {
            OptionDescriptor desc = req.command.findOption(literal.getValue());
            if (desc != null) {
              req.tokenizer.next();
              int arity = desc.getArity();
              LinkedList values = new LinkedList();
              while (arity > 0) {
                if (req.tokenizer.hasNext()) {
                  Token a = req.tokenizer.peek();
                  if (a instanceof Token.Whitespace) {
                    req.tokenizer.next();
                    if (req.tokenizer.hasNext() && req.tokenizer.peek() instanceof Token.Literal.Word) {
                      // ok
                    } else {
                      req.tokenizer.pushBack();
                      break;
                    }
                  } else {
                    Token.Literal b = (Token.Literal)a;
                    if (b instanceof Token.Literal.Word) {
                      values.addLast((Token.Literal.Word)b);
                      req.tokenizer.next();
                      arity--;
                    } else {
                      req.tokenizer.pushBack();
                      break;
                    }
                  }
                } else {
                  break;
                }
              }
              response.add(new Event.Option(desc, optionToken, values));
            } else {
              // We are reading an unknown option
              // it could match an option of an implicit command
              if (req.command instanceof ClassDescriptor) {
                MethodDescriptor m = ((ClassDescriptor)req.command).getMethod(req.mainName);
                if (m != null) {
                  desc = m.findOption(literal.getValue());
                  if (desc != null) {
                    response.command = m;
                    response.add(new Event.Method.Implicit(m, literal));
                  } else {
                    if (req.command.getOptionNames().size() == 0) {
                      response.command = m;
                      response.add(new Event.Method.Implicit(m, literal));
                    } else {
                      response.add(new Event.Stop.Unresolved.NoSuchOption.Method(optionToken));
                    }
                  }
                } else {
                  response.add(new Event.Stop.Unresolved.NoSuchOption.Class(optionToken));
                }
              } else {
                response.add(new Event.Stop.Unresolved.NoSuchOption.Method(optionToken));
              }
            }
          }
        } else {
          Token.Literal.Word wordLiteral = (Token.Literal.Word)literal;
          if (req.command instanceof ClassDescriptor) {
            ClassDescriptor classCommand = (ClassDescriptor)req.command;
            MethodDescriptor m = classCommand.getMethod(wordLiteral.getValue());
            if (m != null && !m.getName().equals(req.mainName)) {
              response.command = m;
              req.tokenizer.next();
              response.add(new Event.Method.Explicit(m, wordLiteral));
            } else {
              m = classCommand.getMethod(req.mainName);
              if (m != null) {
                response.add(new Event.Method.Implicit(m, wordLiteral));
                response.status = new Status.WantReadArg();
                response.command = m;
              } else {
                response.status = new Status.WantReadArg();
              }
            }
          } else {
            response.status = new Status.WantReadArg();
          }
        }
      }
      return response;
    }

  }

  static class WantReadArg extends Status {
    @Override
     Response process(Request req) {
      switch (req.mode) {
        case INVOKE:
          return new Response(new Status.ComputeArg());
        case COMPLETE:
          return new Response(new Status.ReadingArg());
        default:
          throw new AssertionError();
      }
    }
  }

  static class ComputeArg extends Status {

    @Override
     Response process(Request req) {
      Token token = req.tokenizer.peek();
      Response response = new Response();
      if (token == null) {
        response.add(new Event.Stop.Done.Arg(req.tokenizer.getIndex()));
      } else if (token instanceof Token.Whitespace) {
        response.add(new Event.Separator((Token.Whitespace) token));
        req.tokenizer.next();
      } else {

        //
        List> arguments = req.command.getArguments();

        // Count the number ok remaining non whitespace;
        int tokenCount = 0;
        int wordCount = 0;
        do {
          Token t = req.tokenizer.next();
          if (t instanceof Token.Literal) {
            wordCount++;
          }
          tokenCount++;
        }
        while (req.tokenizer.hasNext());
        req.tokenizer.pushBack(tokenCount);

        //
        int oneCount = 0;
        int zeroOrOneCount = 0;
        int index = 0;
        for (ArgumentDescriptor argument : arguments) {
          Multiplicity multiplicity = argument.getMultiplicity();
          if (multiplicity == Multiplicity.SINGLE) {
            if (argument.isRequired()) {
              if (oneCount + 1 > wordCount) {
                break;
              }
              oneCount++;
            } else {
              zeroOrOneCount++;
            }
          }
          index++;
        }

        // This the number of arguments we can satisfy
        arguments = arguments.subList(0, index);

        // How many words we can consume for zeroOrOne and zeroOrMore
        int toConsume = wordCount - oneCount;

        // Correct the zeroOrOneCount and adjust toConsume
        zeroOrOneCount = Math.min(zeroOrOneCount, toConsume);
        toConsume -= zeroOrOneCount;

        // The remaining
        LinkedList events = new LinkedList();
        for (ArgumentDescriptor argument : arguments) {
          int size;
          switch (argument.getMultiplicity()) {
            case SINGLE:
              if (argument.isRequired()) {
                size = 1;
              } else {
                if (zeroOrOneCount > 0) {
                  zeroOrOneCount--;
                  size = 1;
                } else {
                  size = 0;
                }
              }
              break;
            case MULTI:
              // We consume the remaining
              size = toConsume;
              toConsume = 0;
              break;
            default:
              throw new AssertionError();
          }

          // Now take care of the argument
          if (size > 0) {
            List values = new ArrayList(size);
            while (size > 0) {
              Token t = req.tokenizer.next();
              if (t instanceof Token.Literal) {
                values.add(((Token.Literal)t));
                size--;
              }
            }
            events.addLast(new Event.Argument(argument, values));

            // Add the whitespace if needed
            if (req.tokenizer.hasNext() && req.tokenizer.peek() instanceof Token.Whitespace) {
              events.addLast(new Event.Separator((Token.Whitespace) req.tokenizer.next()));
            }
          }
        }

        //
        events.addLast(new Event.Stop.Done.Arg(req.tokenizer.getIndex()));

        //
        response.status = new Status.Done();
        response.addAll(events);
      }
      return response;
    }
  }

  static class Done extends Status {
    @Override
     Response process(Request req) {
      throw new IllegalStateException();
    }
  }

  static class ReadingArg extends Status {

    /** . */
    private final int index;

    ReadingArg() {
      this(0);
    }

    private ReadingArg(int index) {
      this.index = index;
    }

    ReadingArg next() {
      return new ReadingArg(index + 1);
    }

    @Override
     Response process(Request req) {
      Token token = req.tokenizer.peek();
      Response response = new Response();
      if (token == null) {
        response.add(new Event.Stop.Done.Arg(req.tokenizer.getIndex()));
      } else if (token instanceof Token.Whitespace) {
        response.add(new Event.Separator((Token.Whitespace) token));
        req.tokenizer.next();
      } else {
        final Token.Literal literal = (Token.Literal)token;
        List> arguments = req.command.getArguments();
        if (index < arguments.size()) {
          ArgumentDescriptor argument = arguments.get(index);
          switch (argument.getMultiplicity()) {
            case SINGLE:
              req.tokenizer.next();
              response.add(new Event.Argument(argument, Arrays.asList(literal)));
              response.status = next();
              break;
            case MULTI:
              req.tokenizer.next();
              List values = new ArrayList();
              values.add(literal);
              while (req.tokenizer.hasNext()) {
                Token capture = req.tokenizer.next();
                if (capture instanceof Token.Literal) {
                  values.add(((Token.Literal)capture));
                } else {
                  if (req.tokenizer.hasNext()) {
                    // Ok
                  } else {
                    req.tokenizer.pushBack();
                    break;
                  }
                }
              }
              response.add(new Event.Argument(argument, values));
          }
        } else {
          response.add(new Event.Stop.Unresolved.TooManyArguments(literal));
        }
      }
      return response;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy