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.invocation; 021 022 import org.crsh.cli.impl.descriptor.CommandDescriptorImpl; 023 import org.crsh.cli.SyntaxException; 024 import org.crsh.cli.impl.Delimiter; 025 import org.crsh.cli.impl.LiteralValue; 026 import org.crsh.cli.descriptor.OptionDescriptor; 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 034 import java.util.ArrayList; 035 import java.util.Iterator; 036 import java.util.List; 037 import java.util.Map; 038 039 /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */ 040 public class InvocationMatcher<T> { 041 042 /** . */ 043 private final CommandDescriptorImpl<T> descriptor; 044 045 /** . */ 046 private final String mainName; 047 048 public InvocationMatcher(CommandDescriptorImpl<T> descriptor, String mainName) { 049 this.descriptor = descriptor; 050 this.mainName = mainName; 051 } 052 053 public InvocationMatch<T> match(String name, Map<String, ?> options, List<?> arguments) throws SyntaxException { 054 class TokenizerImpl extends ArrayList<Token> { 055 int last() { 056 return size() > 0 ? get(size() - 1).getTo() : 0; 057 } 058 @Override 059 public boolean add(Token token) { 060 if (size() > 0) { 061 super.add(new Token.Whitespace(last(), " ")); 062 } 063 return super.add(token); 064 } 065 066 public void addOption(String name) { 067 if (name.length() == 1) { 068 add(new Token.Literal.Option.Short(last(), "-" + name)); 069 } else { 070 add(new Token.Literal.Option.Long(last(), "--" + name)); 071 } 072 } 073 } 074 final TokenizerImpl t = new TokenizerImpl(); 075 076 // Add name 077 if (name != null && name.length() > 0) { 078 t.add(new Token.Literal.Word(t.last(), name)); 079 } 080 081 // Add options 082 for (Map.Entry<String, ?> option : options.entrySet()) { 083 if (option.getValue() instanceof Boolean) { 084 if ((Boolean)option.getValue()) { 085 t.addOption(option.getKey()); 086 } 087 } else { 088 t.addOption(option.getKey()); 089 t.add(new Token.Literal.Word(t.last(), option.getValue().toString())); 090 } 091 } 092 093 // 094 for (Object argument : arguments) { 095 t.add(new Token.Literal.Word(t.last(), argument.toString())); 096 } 097 098 // 099 Tokenizer tokenizer = new Tokenizer() { 100 101 Iterator<Token> i = t.iterator(); 102 103 @Override 104 protected Token parse() { 105 return i.hasNext() ? i.next() : null; 106 } 107 108 @Override 109 public Delimiter getDelimiter() { 110 return Delimiter.EMPTY; 111 } 112 }; 113 114 // 115 return match(tokenizer); 116 } 117 118 public InvocationMatch<T> match(String s) throws SyntaxException { 119 return match(new TokenizerImpl(s)); 120 } 121 122 private InvocationMatch<T> match(Tokenizer tokenizer) throws SyntaxException { 123 124 // 125 Parser<T> parser = new Parser<T>(tokenizer, descriptor, mainName, Mode.INVOKE); 126 InvocationMatch<T> current = new InvocationMatch<T>(descriptor); 127 128 // 129 while (true) { 130 Event event = parser.next(); 131 if (event instanceof Event.Separator) { 132 // 133 } else if (event instanceof Event.Stop) { 134 while (true) { 135 InvocationMatch<T> sub = current.subordinate(mainName); 136 if (sub != null) { 137 current = sub; 138 } else { 139 break; 140 } 141 } 142 break; 143 } else if (event instanceof Event.Option) { 144 Event.Option optionEvent = (Event.Option)event; 145 OptionDescriptor desc = optionEvent.getParameter(); 146 Iterable<OptionMatch> options = current.options(); 147 OptionMatch option = null; 148 for (OptionMatch om : options) { 149 if (om.getParameter().equals(desc)) { 150 List<LiteralValue> v = new ArrayList<LiteralValue>(om.getValues()); 151 v.addAll(bilto(optionEvent.getValues())); 152 List<String> names = new ArrayList<String>(om.getNames()); 153 names.add(optionEvent.getToken().getName()); 154 option = new OptionMatch(desc, names, v); 155 break; 156 } 157 } 158 if (option == null) { 159 option = new OptionMatch(desc, optionEvent.getToken().getName(), bilto(optionEvent.getValues())); 160 } 161 current.option(option); 162 } else if (event instanceof Event.Subordinate) { 163 current = current.subordinate(((Event.Subordinate)event).getDescriptor().getName()); 164 } else if (event instanceof Event.Argument) { 165 Event.Argument argumentEvent = (Event.Argument)event; 166 List<Token.Literal> values = argumentEvent.getValues(); 167 ArgumentMatch match; 168 if (values.size() > 0) { 169 match = new ArgumentMatch( 170 argumentEvent.getParameter(), 171 argumentEvent.getFrom(), 172 argumentEvent.getTo(), 173 bilto(argumentEvent.getValues()) 174 ); 175 if (argumentEvent.getCommand() == current.getDescriptor()) { 176 current.argument(match); 177 } else { 178 throw new AssertionError(); 179 } 180 } 181 } 182 } 183 184 // 185 StringBuilder rest = new StringBuilder(); 186 while (tokenizer.hasNext()) { 187 Token token = tokenizer.next(); 188 rest.append(token.getRaw()); 189 } 190 current.setRest(rest.toString()); 191 192 // 193 return current; 194 } 195 196 private List<LiteralValue> bilto(List<? extends Token.Literal> literals) { 197 List<LiteralValue> values = new ArrayList<LiteralValue>(literals.size()); 198 for (Token.Literal literal : literals) { 199 values.add(new LiteralValue(literal.getRaw(), literal.getValue())); 200 } 201 return values; 202 } 203 }