/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * bstefanescu */ package org.eclipse.ecr.automation.core.impl; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; import org.eclipse.ecr.automation.OperationContext; import org.eclipse.ecr.automation.OperationException; import org.eclipse.ecr.automation.OperationType; import org.eclipse.ecr.automation.core.annotations.OperationMethod; /** * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ public class InvokableMethod { public static final int VOID_PRIORITY = 1; public static final int ADAPTABLE_PRIORITY = 2; public static final int ISTANCE_OF_PRIORITY = 3; public static final int EXACT_MATCH_PRIORITY = 4; // priorities from 1 to 16 are reserved for internal use. public static final int USER_PRIORITY = 16; protected OperationType op; protected Method method; protected Class<?> produce; protected Class<?> consume; protected int priority; public InvokableMethod(OperationType op, Method method, OperationMethod anno) { produce = method.getReturnType(); Class<?>[] p = method.getParameterTypes(); if (p.length > 1) { throw new IllegalArgumentException( "Operation method must accept at most one argument: " + method); } // if produce is Void => a control operation // if (produce == Void.TYPE) { // throw new IllegalArgumentException("Operation method must return a // value: "+method); // } this.op = op; this.method = method; this.priority = anno.priority(); if (priority > 0) { priority += USER_PRIORITY; } consume = p.length == 0 ? Void.TYPE : p[0]; } public boolean isIterable() { return false; } public int getPriority() { return priority; } public OperationType getOperation() { return op; } public final Class<?> getOutputType() { return produce; } public final Class<?> getInputType() { return consume; } /** * Return 0 for no match. */ public int inputMatch(Class<?> in) { if (consume == in) { return priority > 0 ? priority : EXACT_MATCH_PRIORITY; } if (consume.isAssignableFrom(in)) { return priority > 0 ? priority : ISTANCE_OF_PRIORITY; } if (op.getService().isTypeAdaptable(in, consume)) { return priority > 0 ? priority : ADAPTABLE_PRIORITY; } if (consume == Void.TYPE) { return priority > 0 ? priority : VOID_PRIORITY; } return 0; } protected Object doInvoke(OperationContext ctx, Map<String, Object> args, Object input) throws Exception { Object target = op.newInstance(ctx, args); if (consume == Void.TYPE) { // preserve last output for void methods Object out = method.invoke(target); return produce == Void.TYPE ? input : out; } else { if (input != null && !consume.isAssignableFrom(input.getClass())) { // try to adapt input = op.getService().getAdaptedValue(ctx, input, consume); } return method.invoke(target, input); } } public Object invoke(OperationContext ctx, Map<String, Object> args) throws OperationException { try { return doInvoke(ctx, args, ctx.getInput()); } catch (OperationException e) { throw e; } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof OperationException) { throw (OperationException)t; } else { throw new OperationException("Failed to invoke operation " + op.getId(), t); } } catch (Throwable t) { throw new OperationException("Failed to invoke operation " + op.getId(), t); } } }