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 }