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.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) {
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 }