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.descriptor.CommandDescriptor; 023 import org.crsh.cli.impl.SyntaxException; 024 import org.crsh.cli.impl.LiteralValue; 025 import org.crsh.cli.descriptor.OptionDescriptor; 026 import org.crsh.cli.impl.tokenizer.Token; 027 import org.crsh.cli.impl.tokenizer.Tokenizer; 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 033 import java.util.ArrayList; 034 import java.util.Collections; 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 CommandDescriptor<T> descriptor; 044 045 /** . */ 046 private Iterable<Token> tokens; 047 048 public InvocationMatcher(CommandDescriptor<T> descriptor) { 049 this(descriptor, Collections.<Token>emptyList()); 050 } 051 052 private InvocationMatcher(CommandDescriptor<T> descriptor, Iterable<Token> tokens) { 053 this.descriptor = descriptor; 054 this.tokens = tokens; 055 } 056 057 public InvocationMatcher<T> subordinate(String name) throws SyntaxException { 058 TokenList tokens = new TokenList(this.tokens); 059 if (name != null && name.length() > 0) { 060 tokens.add(new Token.Literal.Word(tokens.last(), name)); 061 } 062 return new InvocationMatcher<T>(descriptor, tokens); 063 } 064 065 public InvocationMatcher<T> option(String optionName, List<?> optionValue) throws SyntaxException { 066 return options(Collections.<String, List<?>>singletonMap(optionName, optionValue)); 067 } 068 069 public InvocationMatcher<T> options(Map<String, List<?>> options) throws SyntaxException { 070 TokenList tokens = new TokenList(this.tokens); 071 for (Map.Entry<String, List<?>> option : options.entrySet()) { 072 tokens.addOption(option.getKey(), option.getValue()); 073 } 074 return new InvocationMatcher<T>(descriptor, tokens); 075 } 076 077 public InvocationMatch<T> arguments(List<?> arguments) throws SyntaxException { 078 TokenList tokens = new TokenList(this.tokens); 079 for (Object argument : arguments) { 080 tokens.add(new Token.Literal.Word(tokens.last(), argument.toString())); 081 } 082 return match(tokens); 083 } 084 085 public InvocationMatch<T> parse(String s) throws SyntaxException { 086 ArrayList<Token> tokens = new ArrayList<Token>(); 087 for (Token token : this.tokens) { 088 tokens.add(token); 089 } 090 for (Iterator<Token> i = new TokenizerImpl(s);i.hasNext();) { 091 tokens.add(i.next()); 092 } 093 return match(tokens); 094 } 095 096 private InvocationMatch<T> match(final Iterable<Token> tokens) throws SyntaxException { 097 Tokenizer tokenizer = new Tokenizer() { 098 099 /** . */ 100 Iterator<Token> i = tokens.iterator(); 101 102 @Override 103 protected Token parse() { 104 return i.hasNext() ? i.next() : null; 105 } 106 }; 107 return match(tokenizer); 108 } 109 110 private InvocationMatch<T> match(Tokenizer tokenizer) throws SyntaxException { 111 112 // 113 Parser<T> parser = new Parser<T>(tokenizer, descriptor, Mode.INVOKE); 114 InvocationMatch<T> current = new InvocationMatch<T>(descriptor); 115 116 // 117 while (true) { 118 Event event = parser.next(); 119 if (event instanceof Event.Separator) { 120 // 121 } else if (event instanceof Event.Stop) { 122 break; 123 } else if (event instanceof Event.Option) { 124 Event.Option optionEvent = (Event.Option)event; 125 OptionDescriptor desc = optionEvent.getParameter(); 126 Iterable<OptionMatch> options = current.options(); 127 OptionMatch option = null; 128 for (OptionMatch om : options) { 129 if (om.getParameter().equals(desc)) { 130 List<LiteralValue> v = new ArrayList<LiteralValue>(om.getValues()); 131 v.addAll(bilto(optionEvent.getValues())); 132 List<String> names = new ArrayList<String>(om.getNames()); 133 names.add(optionEvent.getToken().getName()); 134 option = new OptionMatch(desc, names, v); 135 break; 136 } 137 } 138 if (option == null) { 139 option = new OptionMatch(desc, optionEvent.getToken().getName(), bilto(optionEvent.getValues())); 140 } 141 current.option(option); 142 } else if (event instanceof Event.Subordinate) { 143 current = current.subordinate(((Event.Subordinate)event).getDescriptor().getName()); 144 } else if (event instanceof Event.Argument) { 145 Event.Argument argumentEvent = (Event.Argument)event; 146 List<Token.Literal> values = argumentEvent.getValues(); 147 ArgumentMatch match; 148 if (values.size() > 0) { 149 match = new ArgumentMatch( 150 argumentEvent.getParameter(), 151 argumentEvent.getFrom(), 152 argumentEvent.getTo(), 153 bilto(argumentEvent.getValues()) 154 ); 155 if (argumentEvent.getCommand() == current.getDescriptor()) { 156 current.argument(match); 157 } else { 158 throw new AssertionError(); 159 } 160 } 161 } 162 } 163 164 // 165 StringBuilder rest = new StringBuilder(); 166 while (tokenizer.hasNext()) { 167 Token token = tokenizer.next(); 168 rest.append(token.getRaw()); 169 } 170 current.setRest(rest.toString()); 171 172 // 173 return current; 174 } 175 176 private List<LiteralValue> bilto(List<? extends Token.Literal> literals) { 177 List<LiteralValue> values = new ArrayList<LiteralValue>(literals.size()); 178 for (Token.Literal literal : literals) { 179 values.add(new LiteralValue(literal.getRaw(), literal.getValue())); 180 } 181 return values; 182 } 183 }