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.cmdline.matcher;
021    
022    import org.crsh.cmdline.ArgumentDescriptor;
023    import org.crsh.cmdline.OptionDescriptor;
024    import org.crsh.cmdline.binding.MethodArgumentBinding;
025    import org.crsh.cmdline.MethodDescriptor;
026    import org.crsh.cmdline.ParameterDescriptor;
027    
028    import java.io.IOException;
029    import java.lang.reflect.InvocationTargetException;
030    import java.lang.reflect.Method;
031    import java.util.ArrayList;
032    import java.util.HashSet;
033    import java.util.List;
034    import java.util.Map;
035    import java.util.Set;
036    
037    public class MethodMatch<T> extends CommandMatch<T, MethodDescriptor<T>, MethodArgumentBinding> {
038    
039      /** . */
040      private final MethodDescriptor<T> descriptor;
041    
042      /** . */
043      private final ClassMatch<T> owner;
044    
045      /** . */
046      private final boolean implicit;
047    
048      public MethodMatch(
049        ClassMatch<T> owner,
050        MethodDescriptor<T> descriptor,
051        boolean implicit,
052        List<OptionMatch<MethodArgumentBinding>> optionMatches,
053        List<ArgumentMatch<MethodArgumentBinding>> argumentMatches,
054        String rest) {
055        super(optionMatches, argumentMatches, rest);
056    
057        //
058        this.owner = owner;
059        this.descriptor = descriptor;
060        this.implicit = implicit;
061      }
062    
063      public boolean isImplicit() {
064        return implicit;
065      }
066    
067      @Override
068      public MethodDescriptor<T> getDescriptor() {
069        return descriptor;
070      }
071    
072      public ClassMatch<T> getOwner() {
073        return owner;
074      }
075    
076      @Override
077      public void printMan(Appendable writer) throws IOException {
078        if (implicit) {
079          getOwner().printMan(writer);
080        } else {
081          descriptor.printMan(writer);
082        }
083      }
084    
085      @Override
086      public void printUsage(Appendable writer) throws IOException {
087        if (implicit) {
088          getOwner().printUsage(writer);
089        } else {
090          descriptor.printUsage(writer);
091        }
092      }
093    
094      @Override
095      public Set<ParameterDescriptor<?>> getParameters() {
096        Set<ParameterDescriptor<?>> unused = new HashSet<ParameterDescriptor<?>>();
097        unused.addAll(descriptor.getArguments());
098        unused.addAll(descriptor.getOptions());
099        unused.addAll(owner.getDescriptor().getOptions());
100        return unused;
101      }
102    
103      @Override
104      public List<ParameterMatch<?, ?>> getParameterMatches() {
105        List<ParameterMatch<?, ?>> matches = new ArrayList<ParameterMatch<?, ?>>();
106        matches.addAll(getOptionMatches());
107        matches.addAll(getArgumentMatches());
108        matches.addAll(owner.getOptionMatches());
109        return matches;
110      }
111    
112      @Override
113      protected Object doInvoke(Resolver context, T command, Map<ParameterDescriptor<?>, Object> values) throws CmdInvocationException, CmdSyntaxException {
114    
115        // Prepare invocation
116        MethodDescriptor<T> descriptor = getDescriptor();
117        Method m = descriptor.getMethod();
118        Class<?>[] parameterTypes = m.getParameterTypes();
119        Object[] mArgs = new Object[parameterTypes.length];
120        for (int i = 0;i < mArgs.length;i++) {
121          ParameterDescriptor<MethodArgumentBinding> parameter = descriptor.getParameter(i);
122    
123          //
124          Class<?> parameterType = parameterTypes[i];
125    
126          //
127          Object v;
128          if (parameter == null) {
129            // Attempt to obtain from invocation context
130            v = context.resolve(parameterType);
131          } else {
132            v = values.get(parameter);
133          }
134    
135          //
136          if (v == null) {
137            if (parameterType.isPrimitive() || parameter.isRequired()) {
138              if (parameter instanceof ArgumentDescriptor) {
139                ArgumentDescriptor<?> argument = (ArgumentDescriptor<?>)parameter;
140                throw new CmdSyntaxException("Missing argument " + argument.getName());
141              } else {
142                OptionDescriptor<?> option = (OptionDescriptor<?>)parameter;
143                throw new CmdSyntaxException("Missing option " + option.getNames());
144              }
145            }
146          }
147    
148          //
149          mArgs[i] = v;
150        }
151    
152        // First configure command
153        owner.doInvoke(context, command, values);
154    
155        // Perform method invocation
156        try {
157          return m.invoke(command, mArgs);
158        }
159        catch (InvocationTargetException e) {
160          Throwable t = e.getTargetException();
161          if (t instanceof Error) {
162            throw (Error)t;
163          } else {
164            throw new CmdInvocationException(t);
165          }
166        }
167        catch (IllegalAccessException t) {
168          throw new CmdInvocationException(t);
169        }
170      }
171    }