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.Tokenizer;
029    import org.crsh.cli.impl.tokenizer.TokenizerImpl;
030    import org.crsh.cli.impl.parser.Event;
031    import org.crsh.cli.impl.parser.Mode;
032    import org.crsh.cli.impl.parser.Parser;
033    import org.crsh.cli.spi.Completer;
034    
035    import java.util.Collections;
036    import java.util.List;
037    
038    /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
039    public 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    }