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.IntrospectionException; 026 import org.crsh.cli.descriptor.OptionDescriptor; 027 import org.crsh.cli.descriptor.ParameterDescriptor; 028 import org.crsh.cli.impl.SyntaxException; 029 import org.crsh.cli.impl.invocation.CommandInvoker; 030 import org.crsh.cli.impl.invocation.InvocationException; 031 import org.crsh.cli.impl.invocation.InvocationMatch; 032 import org.crsh.cli.impl.invocation.ParameterMatch; 033 034 import java.lang.reflect.InvocationTargetException; 035 import java.lang.reflect.Method; 036 import java.lang.reflect.Type; 037 import java.util.Collections; 038 import java.util.Map; 039 040 class MethodDescriptor<T> extends ObjectCommandDescriptor<T> { 041 042 /** . */ 043 private final ClassDescriptor<T> owner; 044 045 /** . */ 046 private final Method method; 047 048 public MethodDescriptor( 049 ClassDescriptor<T> owner, 050 Method method, 051 String name, 052 Description info) throws IntrospectionException { 053 super(name, info); 054 055 // 056 this.owner = owner; 057 this.method = method; 058 } 059 060 @Override 061 protected void addParameter(ParameterDescriptor parameter) throws IntrospectionException, NullPointerException, IllegalArgumentException { 062 super.addParameter(parameter); 063 } 064 065 @Override 066 public CommandDescriptor<Instance<T>> getOwner() { 067 return owner; 068 } 069 070 @Override 071 public Map<String, ? extends CommandDescriptor<Instance<T>>> getSubordinates() { 072 return Collections.emptyMap(); 073 } 074 075 public Method getMethod() { 076 return method; 077 } 078 079 @Override 080 public CommandInvoker<Instance<T>, ?> getInvoker(InvocationMatch<Instance<T>> match) { 081 Class<?> type = method.getReturnType(); 082 return getInvoker2(match, type); 083 } 084 085 static void bind(InvocationMatch<?> match, Iterable<ParameterDescriptor> parameters, Object target, Object[] args) throws SyntaxException, InvocationException { 086 for (ParameterDescriptor parameter : parameters) { 087 ParameterMatch parameterMatch = match.getParameter(parameter); 088 Object value = parameterMatch != null ? parameterMatch.computeValue() : null; 089 if (value == null) { 090 if (parameter.getDeclaredType().isPrimitive() || parameter.isRequired()) { 091 if (parameter instanceof ArgumentDescriptor) { 092 ArgumentDescriptor argument = (ArgumentDescriptor)parameter; 093 throw new SyntaxException("Missing argument " + argument.getName()); 094 } else { 095 OptionDescriptor option = (OptionDescriptor)parameter; 096 throw new SyntaxException("Missing option " + option.getNames()); 097 } 098 } 099 } else { 100 ((Binding)parameter).set(target, args, value); 101 } 102 } 103 } 104 105 private <V> ObjectCommandInvoker<T, V> getInvoker2(final InvocationMatch<Instance<T>> match, final Class<V> returnType) { 106 return new ObjectCommandInvoker<T, V>(match) { 107 @Override 108 public Class<V> getReturnType() { 109 return returnType; 110 } 111 @Override 112 public Type getGenericReturnType() { 113 return getMethod().getGenericReturnType(); 114 } 115 @Override 116 public Class<?>[] getParameterTypes() { 117 return getMethod().getParameterTypes(); 118 } 119 @Override 120 public Type[] getGenericParameterTypes() { 121 return getMethod().getGenericParameterTypes(); 122 } 123 @Override 124 public V invoke(Instance<T> commandInstance) throws InvocationException, SyntaxException { 125 126 // 127 T command = null; 128 try { 129 command = commandInstance.get(); 130 } 131 catch (Exception e) { 132 throw new InvocationException(e); 133 } 134 135 // 136 if (owner != null) { 137 bind(match.owner(), owner.getParameters(), command, Util.EMPTY_ARGS); 138 } 139 140 // Prepare invocation 141 Method m = getMethod(); 142 Class<?>[] parameterTypes = m.getParameterTypes(); 143 Object[] mArgs = new Object[parameterTypes.length]; 144 145 // Bind method parameter first 146 bind(match, getParameters(), command, mArgs); 147 148 // Fill missing contextual parameters and make primitive check 149 for (int i = 0;i < mArgs.length;i++) { 150 Class<?> parameterType = parameterTypes[i]; 151 if (mArgs[i] == null) { 152 Object v = commandInstance.resolve(parameterType); 153 if (v != null) { 154 mArgs[i] = v; 155 } 156 } 157 if (mArgs[i] == null && parameterType.isPrimitive()) { 158 throw new SyntaxException("Method argument at position " + i + " of " + m + " is missing"); 159 } 160 } 161 162 // Perform method invocation 163 try { 164 Object ret = m.invoke(command, mArgs); 165 return returnType.cast(ret); 166 } 167 catch (InvocationTargetException e) { 168 Throwable t = e.getTargetException(); 169 if (t instanceof Error) { 170 throw (Error)t; 171 } else { 172 throw new InvocationException(t); 173 } 174 } 175 catch (IllegalAccessException t) { 176 throw new InvocationException(t); 177 } 178 } 179 }; 180 } 181 }