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 }