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 020package org.crsh.cli.impl.lang; 021 022import org.crsh.cli.descriptor.ArgumentDescriptor; 023import org.crsh.cli.descriptor.CommandDescriptor; 024import org.crsh.cli.descriptor.Description; 025import org.crsh.cli.impl.descriptor.CommandDescriptorImpl; 026import org.crsh.cli.impl.descriptor.IntrospectionException; 027import org.crsh.cli.descriptor.OptionDescriptor; 028import org.crsh.cli.descriptor.ParameterDescriptor; 029import org.crsh.cli.SyntaxException; 030import org.crsh.cli.impl.invocation.CommandInvoker; 031import org.crsh.cli.impl.invocation.InvocationException; 032import org.crsh.cli.impl.invocation.InvocationMatch; 033import org.crsh.cli.impl.invocation.ParameterMatch; 034import org.crsh.cli.impl.invocation.Resolver; 035 036import java.lang.reflect.InvocationTargetException; 037import java.lang.reflect.Method; 038import java.lang.reflect.Type; 039import java.util.Collections; 040import java.util.Map; 041 042class 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 Class<?> type = method.getReturnType(); 116 return getInvoker2(match, type); 117 } 118 119 private <V> CommandInvoker<T, V> getInvoker2(final InvocationMatch<T> _match, final Class<V> returnType) { 120 return new CommandInvoker<T, V>() { 121 @Override 122 public InvocationMatch<T> getMatch() { 123 return _match; 124 } 125 @Override 126 public Class<V> getReturnType() { 127 return returnType; 128 } 129 @Override 130 public Type getGenericReturnType() { 131 return getMethod().getGenericReturnType(); 132 } 133 @Override 134 public Class<?>[] getParameterTypes() { 135 return getMethod().getParameterTypes(); 136 } 137 @Override 138 public Type[] getGenericParameterTypes() { 139 return getMethod().getGenericParameterTypes(); 140 } 141 @Override 142 public V invoke(Resolver resolver, T command) throws InvocationException, SyntaxException { 143 144 // 145 owner.configure(_match.owner(), command); 146 147 // Prepare invocation 148 Method m = getMethod(); 149 Class<?>[] parameterTypes = m.getParameterTypes(); 150 Object[] mArgs = new Object[parameterTypes.length]; 151 for (int i = 0;i < mArgs.length;i++) { 152 ParameterDescriptor parameter = getParameter(i); 153 154 // 155 Class<?> parameterType = parameterTypes[i]; 156 157 Object v; 158 if (parameter == null) { 159 // Attempt to obtain from resolver 160 v = resolver.resolve(parameterType); 161 } else { 162 ParameterMatch match = _match.getParameter(parameter); 163 if (match != null) { 164 v = match.computeValue(); 165 } else { 166 v = null; 167 } 168 } 169 170 // 171 if (v == null) { 172 if (parameterType.isPrimitive() || parameter.isRequired()) { 173 if (parameter instanceof ArgumentDescriptor) { 174 ArgumentDescriptor argument = (ArgumentDescriptor)parameter; 175 throw new SyntaxException("Missing argument " + argument.getName()); 176 } else { 177 OptionDescriptor option = (OptionDescriptor)parameter; 178 throw new SyntaxException("Missing option " + option.getNames()); 179 } 180 } 181 } 182 183 // 184 mArgs[i] = v; 185 } 186 187 // Perform method invocation 188 try { 189 Object ret = m.invoke(command, mArgs); 190 return returnType.cast(ret); 191 } 192 catch (InvocationTargetException e) { 193 Throwable t = e.getTargetException(); 194 if (t instanceof Error) { 195 throw (Error)t; 196 } else { 197 throw new InvocationException(t); 198 } 199 } 200 catch (IllegalAccessException t) { 201 throw new InvocationException(t); 202 } 203 } 204 }; 205 } 206}