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 }