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    package org.crsh.cli.impl.tokenizer;
020    
021    import org.crsh.cli.impl.line.LineParser;
022    import org.crsh.cli.impl.line.Quoting;
023    
024    import java.util.LinkedList;
025    
026    /**
027     * @author Julien Viet
028     */
029    class 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    }