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.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) {
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 }