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