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
020package org.crsh.cli.impl.completion;
021
022import org.crsh.cli.descriptor.ArgumentDescriptor;
023import org.crsh.cli.descriptor.CommandDescriptor;
024import org.crsh.cli.impl.Delimiter;
025import org.crsh.cli.descriptor.OptionDescriptor;
026import org.crsh.cli.completers.EmptyCompleter;
027import org.crsh.cli.impl.tokenizer.Token;
028import org.crsh.cli.impl.tokenizer.TokenizerImpl;
029import org.crsh.cli.impl.parser.Event;
030import org.crsh.cli.impl.parser.Mode;
031import org.crsh.cli.impl.parser.Parser;
032import org.crsh.cli.spi.Completer;
033
034import java.util.List;
035
036/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
037public final class CompletionMatcher<T> {
038
039  /** . */
040  private final CommandDescriptor<T> descriptor;
041
042  public CompletionMatcher(CommandDescriptor<T> descriptor) {
043    this.descriptor = descriptor;
044  }
045
046  public final CompletionMatch match(String s) throws CompletionException {
047    return match(EmptyCompleter.getInstance(), s);
048  }
049
050  public CompletionMatch match(Completer completer, String s) throws CompletionException {
051    return getCompletion(completer, s).complete();
052  }
053
054  private Completion argument(CommandDescriptor<?> method, Completer completer, Delimiter delimiter) {
055    List<? extends ArgumentDescriptor> arguments = method.getArguments();
056    if (arguments.isEmpty()) {
057      return new EmptyCompletion();
058    } else {
059      ArgumentDescriptor argument = arguments.get(0);
060      return new ParameterCompletion("", delimiter, argument, completer);
061    }
062  }
063
064  private Completion getCompletion(Completer completer, String s) throws CompletionException {
065
066    // Find delimiter
067    CommandDescriptor<T> foo = this.descriptor;
068
069    TokenizerImpl tokenizer = new TokenizerImpl(s);
070    Delimiter delimiter = tokenizer.getEndingDelimiter();
071    Parser<T> parser = new Parser<T>(tokenizer, foo, Mode.COMPLETE);
072
073    // Last non separator event
074    Event last = null;
075    Event.Separator separator = null;
076    Event.Stop stop;
077
078    //
079    while (true) {
080      Event event = parser.next();
081      if (event instanceof Event.Separator) {
082        separator = (Event.Separator)event;
083      } else if (event instanceof Event.Stop) {
084        stop = (Event.Stop)event;
085        break;
086      } else if (event instanceof Event.Option) {
087        last = event;
088        separator = null;
089      } else if (event instanceof Event.Subordinate) {
090        // ABUSE!!! fixme
091        foo = (CommandDescriptor<T>)((Event.Subordinate)event).getDescriptor();
092        last = event;
093        separator = null;
094      } else if (event instanceof Event.Argument) {
095        last = event;
096        separator = null;
097      }
098    }
099
100    //
101    if (stop instanceof Event.Stop.Unresolved.NoSuchOption) {
102      Event.Stop.Unresolved.NoSuchOption nso = (Event.Stop.Unresolved.NoSuchOption)stop;
103      return new OptionCompletion<T>(foo, nso.getToken());
104    } else if (stop instanceof Event.Stop.Unresolved) {
105      if (stop instanceof Event.Stop.Unresolved.TooManyArguments) {
106        Event.Stop.Unresolved.TooManyArguments tma = (Event.Stop.Unresolved.TooManyArguments)stop;
107        return new CommandCompletion<T>(foo, s.substring(stop.getIndex()), delimiter);
108      } else {
109        return new EmptyCompletion();
110      }
111    } else if (stop instanceof Event.Stop.Done) {
112      // to use ?
113    }
114
115    if (last == null) {
116      if (foo.getSubordinates().size() > 0) {
117        return new CommandCompletion<T>(foo, s.substring(stop.getIndex()), Delimiter.EMPTY);
118      } else {
119        List<ArgumentDescriptor> args = foo.getArguments();
120        if (args.size() > 0) {
121          return new ParameterCompletion("", delimiter, args.get(0), completer);
122        } else {
123          return new EmptyCompletion();
124        }
125      }
126    } else if (last instanceof Event.Option) {
127      Event.Option optionEvent = (Event.Option)last;
128      List<Token.Literal.Word> values = optionEvent.getValues();
129      OptionDescriptor option = optionEvent.getParameter();
130      if (separator == null) {
131        if (values.size() == 0) {
132          return new SpaceCompletion();
133        } else if (values.size() <= option.getArity()) {
134          Token.Literal.Word word = optionEvent.peekLast();
135          return new ParameterCompletion(word.getValue(), delimiter, option, completer);
136        } else {
137          return new EmptyCompletion();
138        }
139      } else {
140        if (values.size() < option.getArity()) {
141          return new ParameterCompletion("", delimiter, option, completer);
142        } else {
143          return argument(foo, completer, delimiter);
144        }
145      }
146    } else if (last instanceof Event.Argument) {
147      Event.Argument eventArgument = (Event.Argument)last;
148      ArgumentDescriptor argument = eventArgument.getParameter();
149      if (separator != null) {
150        switch (argument.getMultiplicity()) {
151          case SINGLE:
152            List<? extends ArgumentDescriptor> arguments = eventArgument.getCommand().getArguments();
153            int index = arguments.indexOf(argument) + 1;
154            if (index < arguments.size()) {
155              ArgumentDescriptor nextArg = arguments.get(index);
156              return new ParameterCompletion("", delimiter, nextArg, completer);
157            } else {
158              return new EmptyCompletion();
159            }
160          case MULTI:
161            return new ParameterCompletion("", delimiter, argument, completer);
162          default:
163            throw new AssertionError();
164        }
165      } else {
166        Token.Literal value = eventArgument.peekLast();
167        return new ParameterCompletion(value.getValue(), delimiter, argument, completer);
168      }
169    } else if (last instanceof Event.Subordinate) {
170      if (separator != null) {
171        return argument(foo, completer, delimiter);
172      } else {
173        return new SpaceCompletion();
174      }
175    } else {
176      throw new AssertionError();
177    }
178  }
179}