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