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 */
019package org.crsh.cli.impl.tokenizer;
020
021import org.crsh.cli.impl.line.LineParser;
022import org.crsh.cli.impl.line.Quoting;
023
024import java.util.LinkedList;
025
026/**
027 * @author Julien Viet
028 */
029class Automaton extends LineParser.Visitor {
030
031  /** . */
032  private Status status = Status.WHITESPACE;
033
034  /** . */
035  private final StringBuilder buffer = new StringBuilder();
036
037  /** . */
038  final LinkedList<Token> tokens = new LinkedList<Token>();
039
040  /** . */
041  private int from = 0;
042
043  /** . */
044  private int lastWhitespace = 0;
045
046  /** . */
047  private final CharSequence s;
048
049  Automaton(CharSequence s) {
050    this.s = s;
051  }
052
053  void close() {
054    if (buffer.length() > 0) {
055      if (status == Status.WHITESPACE) {
056        next(lastWhitespace);
057      } else {
058        next(s.length());
059      }
060    }
061  }
062
063  private void next(int current) {
064    Token token;
065    switch (status) {
066      case WHITESPACE:
067        token = new Token.Whitespace(from, s.subSequence(from, current).toString());
068        break;
069      case WORD:
070        token = new Token.Literal.Word(from, s.subSequence(from, current).toString(), buffer.toString());
071        break;
072      case SHORT_OPTION:
073        token = new Token.Literal.Option.Short(from, s.subSequence(from, current).toString(), buffer.toString());
074        break;
075      case LONG_OPTION:
076        token = new Token.Literal.Option.Long(from, s.subSequence(from, current).toString(), buffer.toString());
077        break;
078      default:
079        throw new AssertionError();
080    }
081    tokens.add(token);
082    status = Status.WHITESPACE;
083    buffer.setLength(0);
084    from = current;
085  }
086
087  @Override
088  public void onChar(int index, Quoting quoting, boolean backslash, char c) {
089    if (Character.isWhitespace(c) && quoting == null && !backslash) {
090      lastWhitespace = index + 1;
091      if (status != Status.WHITESPACE) {
092        next(index);
093      }
094      buffer.append(c);
095    } else {
096      switch (status) {
097        case WHITESPACE:
098          if (buffer.length() > 0) {
099            next(lastWhitespace);
100          }
101          buffer.append(c);
102          if (c == '-') {
103            status = Status.SHORT_OPTION;
104          } else {
105            status = Status.WORD;
106          }
107          break;
108        case WORD:
109          buffer.append(c);
110          break;
111        case SHORT_OPTION:
112          if (c == '-') {
113            buffer.append('-');
114            status = Status.LONG_OPTION;
115          } else if (Character.isLetter(c)) {
116            buffer.append(c);
117          } else {
118            status = Status.WORD;
119            buffer.append(c);
120          }
121          break;
122        case LONG_OPTION:
123          if (c == '-') {
124            if (buffer.length() > 2) {
125              buffer.append(c);
126            } else {
127              status = Status.WORD;
128              buffer.append(c);
129            }
130          } else if (Character.isLetter(c)) {
131            buffer.append(c);
132          } else {
133            status = Status.WORD;
134            buffer.append(c);
135          }
136      }
137    }
138  }
139
140  enum Status { WHITESPACE, WORD, SHORT_OPTION, LONG_OPTION }
141
142}