001 /*
002 * Copyright (C) 2010 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.cmdline.matcher.impl;
021
022 import org.crsh.cmdline.ArgumentDescriptor;
023 import org.crsh.cmdline.ClassDescriptor;
024 import org.crsh.cmdline.CommandCompletion;
025 import org.crsh.cmdline.CommandDescriptor;
026 import org.crsh.cmdline.Delimiter;
027 import org.crsh.cmdline.MethodDescriptor;
028 import org.crsh.cmdline.OptionDescriptor;
029 import org.crsh.cmdline.binding.ClassFieldBinding;
030 import org.crsh.cmdline.binding.MethodArgumentBinding;
031 import org.crsh.cmdline.matcher.ArgumentMatch;
032 import org.crsh.cmdline.matcher.ClassMatch;
033 import org.crsh.cmdline.matcher.CmdCompletionException;
034 import org.crsh.cmdline.matcher.CommandMatch;
035 import org.crsh.cmdline.matcher.LiteralValue;
036 import org.crsh.cmdline.matcher.Matcher;
037 import org.crsh.cmdline.matcher.MethodMatch;
038 import org.crsh.cmdline.matcher.OptionMatch;
039 import org.crsh.cmdline.matcher.tokenizer.Token;
040 import org.crsh.cmdline.matcher.tokenizer.Tokenizer;
041 import org.crsh.cmdline.spi.Completer;
042
043 import java.util.ArrayList;
044 import java.util.Collections;
045 import java.util.List;
046 import java.util.ListIterator;
047
048 /**
049 * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
050 * @version $Revision$
051 */
052 public class MatcherImpl<T> extends Matcher<T> {
053
054 /** . */
055 private final ClassDescriptor<T> descriptor;
056
057 /** . */
058 private final String mainName;
059
060 public MatcherImpl(ClassDescriptor<T> descriptor) {
061 this(null, descriptor);
062 }
063
064 public MatcherImpl(String mainName, ClassDescriptor<T> descriptor) {
065 this.mainName = mainName;
066 this.descriptor = descriptor;
067 }
068
069 private List<LiteralValue> bilto(List<? extends Token.Literal> literals) {
070 List<LiteralValue> values = new ArrayList<LiteralValue>(literals.size());
071 for (Token.Literal literal : literals) {
072 values.add(new LiteralValue(literal.getRaw(), literal.getValue()));
073 }
074 return values;
075 }
076
077 @Override
078 public CommandMatch<T, ?, ?> match(String s) {
079
080 Tokenizer tokenizer = new Tokenizer(s);
081 Parser<T> parser = new Parser<T>(tokenizer, descriptor, mainName, Mode.INVOKE);
082
083 //
084 List<OptionMatch<ClassFieldBinding>> classOptions = new ArrayList<OptionMatch<ClassFieldBinding>>();
085 List<ArgumentMatch<ClassFieldBinding>> classArguments = new ArrayList<ArgumentMatch<ClassFieldBinding>>();
086 List<OptionMatch<MethodArgumentBinding>> methodOptions = new ArrayList<OptionMatch<MethodArgumentBinding>>();
087 List<ArgumentMatch<MethodArgumentBinding>> methodArguments = new ArrayList<ArgumentMatch<MethodArgumentBinding>>();
088 MethodDescriptor<T> method = null;
089
090 //
091 Integer methodEnd = null;
092 Integer classEnd;
093 Event previous = null;
094 while (true) {
095 Event event = parser.next();
096 if (event instanceof Event.Separator) {
097 //
098 } else if (event instanceof Event.Stop) {
099 // We are done
100 // Check error status and react to it maybe
101 Event.Stop end = (Event.Stop)event;
102 int endIndex;
103 if (previous instanceof Event.Separator) {
104 endIndex = ((Event.Separator)previous).getToken().getFrom();
105 } else {
106 endIndex = end.getIndex();
107 }
108
109 // We try to match the main if none was found
110 if (method == null) {
111 classEnd = endIndex;
112 if (mainName != null) {
113 method = descriptor.getMethod(mainName);
114 }
115 if (method != null) {
116 methodEnd = classEnd;
117 }
118 } else {
119 methodEnd = classEnd = endIndex;
120 }
121 break;
122 } else if (event instanceof Event.Option) {
123 Event.Option optionEvent = (Event.Option)event;
124 OptionDescriptor<?> desc = optionEvent.getDescriptor();
125 List options;
126 if (desc.getOwner() instanceof ClassDescriptor<?>) {
127 options = classOptions;
128 } else {
129 options = methodOptions;
130 }
131 boolean done = false;
132 for (ListIterator<OptionMatch> i = options.listIterator();i.hasNext();) {
133 OptionMatch om = i.next();
134 if (om.getParameter().equals(desc)) {
135 List<LiteralValue> v = new ArrayList<LiteralValue>(om.getValues());
136 v.addAll(bilto(optionEvent.getValues()));
137 List<String> names = new ArrayList<String>(om.getNames());
138 names.add(optionEvent.getToken().getName());
139 i.set(new OptionMatch(desc, names, v));
140 done = true;
141 break;
142 }
143 }
144 if (!done) {
145 OptionMatch match = new OptionMatch(desc, optionEvent.getToken().getName(), bilto(optionEvent.getValues()));
146 options.add(match);
147 }
148 } else if (event instanceof Event.Method) {
149 if (event instanceof Event.Method.Implicit) {
150 Event.Method.Implicit implicit = (Event.Method.Implicit)event;
151 classEnd = implicit.getTrigger().getFrom();
152 method = (MethodDescriptor<T>)implicit.getDescriptor();
153 } else {
154 Event.Method.Explicit explicit = (Event.Method.Explicit)event;
155 classEnd = explicit.getToken().getFrom();
156 method = (MethodDescriptor<T>)explicit.getDescriptor();
157 }
158 } else if (event instanceof Event.Argument) {
159 Event.Argument argumentEvent = (Event.Argument)event;
160 List<Token.Literal> values = argumentEvent.getValues();
161 ArgumentMatch match;
162 if (values.size() > 0) {
163 match = new ArgumentMatch(
164 argumentEvent.getDescriptor(),
165 argumentEvent.getFrom(),
166 argumentEvent.getTo(),
167 bilto(argumentEvent.getValues())
168 );
169 if (argumentEvent.getDescriptor().getOwner() instanceof ClassDescriptor<?>) {
170 classArguments.add(match);
171 } else {
172 methodArguments.add(match);
173 }
174 }
175 }
176 previous = event;
177 }
178
179 //
180 ClassMatch classMatch = new ClassMatch(descriptor, classOptions, classArguments, s.substring(classEnd));
181 if (method != null) {
182 return new MethodMatch(classMatch, method, false, methodOptions, methodArguments, s.substring(methodEnd));
183 } else {
184 return classMatch;
185 }
186 }
187
188 private Completion argument(MethodDescriptor<?> method, Completer completer) {
189 List<? extends ArgumentDescriptor<?>> arguments = method.getArguments();
190 if (arguments.isEmpty()) {
191 return new EmptyCompletion();
192 } else {
193 ArgumentDescriptor<?> argument = arguments.get(0);
194 return new ParameterCompletion("", Delimiter.EMPTY, argument, completer);
195 }
196 }
197
198 @Override
199 public CommandCompletion complete(Completer completer, String s) throws CmdCompletionException {
200 return getCompletion(completer, s).complete();
201 }
202
203 private Completion getCompletion(Completer completer, String s) throws CmdCompletionException {
204
205 Tokenizer tokenizer = new Tokenizer(s);
206 Parser<T> parser = new Parser<T>(tokenizer, descriptor, mainName, Mode.COMPLETE);
207
208 // Last non separator event
209 Event last = null;
210 Event.Separator separator = null;
211 MethodDescriptor<?> method = null;
212 Event.Stop stop;
213
214 //
215 while (true) {
216 Event event = parser.next();
217 if (event instanceof Event.Separator) {
218 separator = (Event.Separator)event;
219 } else if (event instanceof Event.Stop) {
220 stop = (Event.Stop)event;
221 break;
222 } else if (event instanceof Event.Option) {
223 last = event;
224 separator = null;
225 } else if (event instanceof Event.Method) {
226 method = ((Event.Method)event).getDescriptor();
227 last = event;
228 separator = null;
229 } else if (event instanceof Event.Argument) {
230 last = event;
231 separator = null;
232 }/* else if (event instanceof Event.DoubleDash) {
233 last = event;
234 separator = null;
235 }*/
236 }
237
238 //
239 if (stop instanceof Event.Stop.Unresolved.NoSuchOption) {
240 Event.Stop.Unresolved.NoSuchOption nso = (Event.Stop.Unresolved.NoSuchOption)stop;
241 return new OptionCompletion<T>(method != null ? (CommandDescriptor<T, ?>)method : descriptor, nso.getToken());
242 } else if (stop instanceof Event.Stop.Unresolved) {
243 if (stop instanceof Event.Stop.Unresolved.TooManyArguments) {
244 if (method == null) {
245 Event.Stop.Unresolved.TooManyArguments tma = (Event.Stop.Unresolved.TooManyArguments)stop;
246 return new MethodCompletion<T>(descriptor, mainName, s.substring(stop.getIndex()), parser.getDelimiter());
247 } else {
248 return new EmptyCompletion();
249 }
250 } else {
251 return new EmptyCompletion();
252 }
253 } else if (stop instanceof Event.Stop.Done.Option) {
254 // to use ?
255 } else if (stop instanceof Event.Stop.Done.Arg) {
256 // to use ?
257 }
258
259 //
260 if (last == null) {
261 if (method == null) {
262 if (descriptor.getSubordinates().keySet().equals(Collections.singleton(mainName))) {
263 method = descriptor.getMethod(mainName);
264 List<ArgumentDescriptor<MethodArgumentBinding>> args = method.getArguments();
265 if (args.size() > 0) {
266 return new ParameterCompletion("", Delimiter.EMPTY, args.get(0), completer);
267 } else {
268 return new EmptyCompletion();
269 }
270 } else {
271 return new MethodCompletion<T>(descriptor, mainName, s.substring(stop.getIndex()), Delimiter.EMPTY);
272 }
273 } else {
274 return new EmptyCompletion();
275 }
276 }
277
278 //
279 /*if (last instanceof Event.DoubleDash) {
280 Event.DoubleDash dd = (Event.DoubleDash)last;
281 return new OptionCompletion<T>(method != null ? (CommandDescriptor<T, ?>)method : descriptor, dd.token);
282 } else*/
283 if (last instanceof Event.Option) {
284 Event.Option optionEvent = (Event.Option)last;
285 List<Token.Literal.Word> values = optionEvent.getValues();
286 OptionDescriptor<?> option = optionEvent.getDescriptor();
287 if (separator == null) {
288 if (values.size() == 0) {
289 return new SpaceCompletion();
290 } else if (values.size() <= option.getArity()) {
291 Token.Literal.Word word = optionEvent.peekLast();
292 return new ParameterCompletion(word.getValue(), parser.getDelimiter(), option, completer);
293 } else {
294 return new EmptyCompletion();
295 }
296 } else {
297 if (values.size() < option.getArity()) {
298 return new ParameterCompletion("", Delimiter.EMPTY, option, completer);
299 } else {
300 if (method == null) {
301 return new MethodCompletion<T>(descriptor, mainName, s.substring(stop.getIndex()), Delimiter.EMPTY);
302 } else {
303 return argument(method, completer);
304 }
305 }
306 }
307 } else if (last instanceof Event.Argument) {
308 Event.Argument eventArgument = (Event.Argument)last;
309 ArgumentDescriptor<?> argument = eventArgument.getDescriptor();
310 if (separator != null) {
311 switch (argument.getMultiplicity()) {
312 case SINGLE:
313 List<? extends ArgumentDescriptor<?>> arguments = argument.getOwner().getArguments();
314 int index = arguments.indexOf(argument) + 1;
315 if (index < arguments.size()) {
316 ArgumentDescriptor<?> nextArg = arguments.get(index);
317 return new ParameterCompletion("", Delimiter.EMPTY, nextArg, completer);
318 } else {
319 return new EmptyCompletion();
320 }
321 case MULTI:
322 return new ParameterCompletion("", Delimiter.EMPTY, argument, completer);
323 default:
324 throw new AssertionError();
325 }
326 } else {
327 Token.Literal value = eventArgument.peekLast();
328 return new ParameterCompletion(value.getValue(), parser.getDelimiter(), argument, completer);
329 }
330 } else if (last instanceof Event.Method) {
331 if (separator != null) {
332 return argument(method, completer);
333 } else {
334 return new SpaceCompletion();
335 }
336 } else {
337 throw new AssertionError();
338 }
339 }
340 }