/*
* Copyright (C) 2012 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.crsh.cli.impl.lang;
import org.crsh.cli.descriptor.ArgumentDescriptor;
import org.crsh.cli.descriptor.CommandDescriptor;
import org.crsh.cli.descriptor.Description;
import org.crsh.cli.impl.descriptor.IntrospectionException;
import org.crsh.cli.descriptor.OptionDescriptor;
import org.crsh.cli.descriptor.ParameterDescriptor;
import org.crsh.cli.impl.SyntaxException;
import org.crsh.cli.impl.invocation.CommandInvoker;
import org.crsh.cli.impl.invocation.InvocationException;
import org.crsh.cli.impl.invocation.InvocationMatch;
import org.crsh.cli.impl.invocation.ParameterMatch;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Map;
class MethodDescriptor<T> extends ObjectCommandDescriptor<T> {
/** . */
private final ClassDescriptor<T> owner;
/** . */
private final Method method;
public MethodDescriptor(
ClassDescriptor<T> owner,
Method method,
String name,
Description info) throws IntrospectionException {
super(name, info);
//
this.owner = owner;
this.method = method;
}
@Override
protected void addParameter(ParameterDescriptor parameter) throws IntrospectionException, NullPointerException, IllegalArgumentException {
super.addParameter(parameter);
}
@Override
public CommandDescriptor<Instance<T>> getOwner() {
return owner;
}
@Override
public Map<String, ? extends CommandDescriptor<Instance<T>>> getSubordinates() {
return Collections.emptyMap();
}
public Method getMethod() {
return method;
}
@Override
public CommandInvoker<Instance<T>, ?> getInvoker(InvocationMatch<Instance<T>> match) {
Class<?> type = method.getReturnType();
return getInvoker2(match, type);
}
static void bind(InvocationMatch<?> match, Iterable<ParameterDescriptor> parameters, Object target, Object[] args) throws SyntaxException, InvocationException {
for (ParameterDescriptor parameter : parameters) {
ParameterMatch parameterMatch = match.getParameter(parameter);
Object value = parameterMatch != null ? parameterMatch.computeValue() : null;
if (value == null) {
if (parameter.getDeclaredType().isPrimitive() || parameter.isRequired()) {
if (parameter instanceof ArgumentDescriptor) {
ArgumentDescriptor argument = (ArgumentDescriptor)parameter;
throw new SyntaxException("Missing argument " + argument.getName());
} else {
OptionDescriptor option = (OptionDescriptor)parameter;
throw new SyntaxException("Missing option " + option.getNames());
}
}
} else {
((Binding)parameter).set(target, args, value);
}
}
}
private <V> ObjectCommandInvoker<T, V> getInvoker2(final InvocationMatch<Instance<T>> match, final Class<V> returnType) {
return new ObjectCommandInvoker<T, V>(match) {
@Override
public Class<V> getReturnType() {
return returnType;
}
@Override
public Type getGenericReturnType() {
return getMethod().getGenericReturnType();
}
@Override
public Class<?>[] getParameterTypes() {
return getMethod().getParameterTypes();
}
@Override
public Type[] getGenericParameterTypes() {
return getMethod().getGenericParameterTypes();
}
@Override
public V invoke(Instance<T> commandInstance) throws InvocationException, SyntaxException {
//
T command = null;
try {
command = commandInstance.get();
}
catch (Exception e) {
throw new InvocationException(e);
}
//
if (owner != null) {
bind(match.owner(), owner.getParameters(), command, Util.EMPTY_ARGS);
}
// Prepare invocation
Method m = getMethod();
Class<?>[] parameterTypes = m.getParameterTypes();
Object[] mArgs = new Object[parameterTypes.length];
// Bind method parameter first
bind(match, getParameters(), command, mArgs);
// Fill missing contextual parameters and make primitive check
for (int i = 0;i < mArgs.length;i++) {
Class<?> parameterType = parameterTypes[i];
if (mArgs[i] == null) {
Object v = commandInstance.resolve(parameterType);
if (v != null) {
mArgs[i] = v;
}
}
if (mArgs[i] == null && parameterType.isPrimitive()) {
throw new SyntaxException("Method argument at position " + i + " of " + m + " is missing");
}
}
// Perform method invocation
try {
Object ret = m.invoke(command, mArgs);
return returnType.cast(ret);
}
catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
if (t instanceof Error) {
throw (Error)t;
} else {
throw new InvocationException(t);
}
}
catch (IllegalAccessException t) {
throw new InvocationException(t);
}
}
};
}
}