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    
020    package org.crsh.cli.impl.completion;
021    
022    import org.crsh.cli.descriptor.ArgumentDescriptor;
023    import org.crsh.cli.descriptor.CommandDescriptor;
024    import org.crsh.cli.impl.Delimiter;
025    import org.crsh.cli.descriptor.OptionDescriptor;
026    import org.crsh.cli.completers.EmptyCompleter;
027    import org.crsh.cli.impl.tokenizer.Token;
028    import org.crsh.cli.impl.tokenizer.TokenizerImpl;
029    import org.crsh.cli.impl.parser.Event;
030    import org.crsh.cli.impl.parser.Mode;
031    import org.crsh.cli.impl.parser.Parser;
032    import org.crsh.cli.spi.Completer;
033    
034    import java.util.List;
035    
036    /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
037    public 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    }