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 }