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    }