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.lang;
021    
022    import org.crsh.cli.descriptor.ArgumentDescriptor;
023    import org.crsh.cli.descriptor.CommandDescriptor;
024    import org.crsh.cli.descriptor.Description;
025    import org.crsh.cli.impl.descriptor.CommandDescriptorImpl;
026    import org.crsh.cli.impl.descriptor.IntrospectionException;
027    import org.crsh.cli.descriptor.OptionDescriptor;
028    import org.crsh.cli.descriptor.ParameterDescriptor;
029    import org.crsh.cli.SyntaxException;
030    import org.crsh.cli.impl.invocation.CommandInvoker;
031    import org.crsh.cli.impl.invocation.InvocationException;
032    import org.crsh.cli.impl.invocation.InvocationMatch;
033    import org.crsh.cli.impl.invocation.ParameterMatch;
034    import org.crsh.cli.impl.invocation.Resolver;
035    
036    import java.lang.reflect.InvocationTargetException;
037    import java.lang.reflect.Method;
038    import java.lang.reflect.Type;
039    import java.util.Collections;
040    import java.util.Map;
041    
042    class MethodDescriptor<T> extends CommandDescriptorImpl<T> {
043    
044      /** . */
045      private final ClassDescriptor<T> owner;
046    
047      /** . */
048      private final Method method;
049    
050      /** . */
051      private final int size;
052    
053      public MethodDescriptor(
054        ClassDescriptor<T> owner,
055        Method method,
056        String name,
057        Description info) throws IntrospectionException {
058        super(name, info);
059    
060        //
061        this.owner = owner;
062        this.method = method;
063        this.size = method.getParameterTypes().length;
064      }
065    
066      /**
067       * Returns the parameter descriptor for the specified method parameter index.
068       *
069       * @param index the parameter index
070       * @return the parameter descriptor or null if none can be bound
071       * @throws IndexOutOfBoundsException if the index is not valid
072       */
073      ParameterDescriptor getParameter(int index) throws IndexOutOfBoundsException {
074        if (index < 0 || index >= size) {
075          throw new IndexOutOfBoundsException("Bad index value " + index);
076        }
077        for (ParameterDescriptor argument : getParameters()) {
078          if (((MethodArgumentBinding)argument.getBinding()).getIndex() == index) {
079            return argument;
080          }
081        }
082        return null;
083      }
084    
085      @Override
086      protected void addParameter(ParameterDescriptor parameter) throws IntrospectionException, NullPointerException, IllegalArgumentException {
087        super.addParameter(parameter);
088      }
089    
090      @Override
091      public CommandDescriptor<T> getOwner() {
092        return owner;
093      }
094    
095      @Override
096      public Map<String, ? extends CommandDescriptorImpl<T>> getSubordinates() {
097        return Collections.emptyMap();
098      }
099    
100      @Override
101      public CommandDescriptorImpl<T> getSubordinate(String name) {
102        return null;
103      }
104    
105      public Method getMethod() {
106        return method;
107      }
108    
109      @Override
110      public Class<T> getType() {
111        return owner.getType();
112      }
113    
114      public CommandInvoker<T> getInvoker(final InvocationMatch<T> _match) {
115        return new CommandInvoker<T>() {
116          @Override
117          public Class<?> getReturnType() {
118            return getMethod().getReturnType();
119          }
120    
121          @Override
122          public Type getGenericReturnType() {
123            return getMethod().getGenericReturnType();
124          }
125    
126          @Override
127          public Class<?>[] getParameterTypes() {
128            return getMethod().getParameterTypes();
129          }
130    
131          @Override
132          public Type[] getGenericParameterTypes() {
133            return getMethod().getGenericParameterTypes();
134          }
135    
136          @Override
137          public Object invoke(Resolver resolver, T command) throws InvocationException, SyntaxException {
138    
139            //
140            owner.configure(_match.owner(), command);
141    
142            // Prepare invocation
143            Method m = getMethod();
144            Class<?>[] parameterTypes = m.getParameterTypes();
145            Object[] mArgs = new Object[parameterTypes.length];
146            for (int i = 0;i < mArgs.length;i++) {
147              ParameterDescriptor parameter = getParameter(i);
148    
149              //
150              Class<?> parameterType = parameterTypes[i];
151    
152              Object v;
153              if (parameter == null) {
154                // Attempt to obtain from resolver
155                v = resolver.resolve(parameterType);
156              } else {
157                ParameterMatch match = _match.getParameter(parameter);
158                if (match != null) {
159                  v = match.computeValue();
160                } else {
161                  v = null;
162                }
163              }
164    
165              //
166              if (v == null) {
167                if (parameterType.isPrimitive() || parameter.isRequired()) {
168                  if (parameter instanceof ArgumentDescriptor) {
169                    ArgumentDescriptor argument = (ArgumentDescriptor)parameter;
170                    throw new SyntaxException("Missing argument " + argument.getName());
171                  } else {
172                    OptionDescriptor option = (OptionDescriptor)parameter;
173                    throw new SyntaxException("Missing option " + option.getNames());
174                  }
175                }
176              }
177    
178              //
179              mArgs[i] = v;
180            }
181    
182            // Perform method invocation
183            try {
184              return m.invoke(command, mArgs);
185            }
186            catch (InvocationTargetException e) {
187              Throwable t = e.getTargetException();
188              if (t instanceof Error) {
189                throw (Error)t;
190              } else {
191                throw new InvocationException(t);
192              }
193            }
194            catch (IllegalAccessException t) {
195              throw new InvocationException(t);
196            }
197          }
198        };
199      }
200    }