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 }