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