/* * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * bstefanescu */ package org.nuxeo.ecm.automation.core.impl; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Iterator; import java.util.Map; import org.nuxeo.ecm.automation.OperationContext; import org.nuxeo.ecm.automation.OperationException; import org.nuxeo.ecm.automation.OperationType; import org.nuxeo.ecm.automation.OutputCollector; import org.nuxeo.ecm.automation.core.annotations.OperationMethod; /** * A method proxy which accept as input only iterable inputs. At invocation time it iterates over the input elements and * invoke the real method using the current input element as the input. * <p> * The result is collected into a {@link OutputCollector} as specified by the {@link OperationMethod} annotation. * <p> * This proxy is used (instead of the default {@link InvokableMethod}) when an operation is defining an output collector * in the {@link OperationMethod} annotation so that iterable inputs are automatically handled by the chain executor. * <p> * This specialized implementation is declaring the same consume type as its non-iterable counterpart. But at runtime it * consumes any Iterable of the cosume type. * <p> * To correctly generate the operation documentation the {@link OperationTypeImpl} is checking if the method is iterable * or not through {@link #isIterable()} to declare the correct consume type. * * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ public class InvokableIteratorMethod extends InvokableMethod { @SuppressWarnings("rawtypes") protected Class<? extends OutputCollector> collector; public InvokableIteratorMethod(OperationType op, Method method, OperationMethod anno) { super(op, method, anno); collector = anno.collector(); if (collector == OutputCollector.class) { throw new IllegalArgumentException("Not an iterable method"); } // check the collector match the method signature - to early detect invalid // operation definitions. if (consume == Void.TYPE) { throw new IllegalArgumentException("An iterable method must have an argument"); } Type[] ctypes = IterableInputHelper.findCollectorTypes(collector); if (!((Class<?>) ctypes[0]).isAssignableFrom(produce)) { throw new IllegalArgumentException("The collector used on " + method + " doesn't match the method return type"); } // must modify the produced type to fit the real produced type. try { produce = (Class<?>) ctypes[1]; } catch (ArrayIndexOutOfBoundsException e) { throw new IllegalStateException("Invalid output collector: " + collector + ". No getOutput method found."); } // the consumed type is not used in chain compilation so we let it as is // for now. } @Override public boolean isIterable() { return true; } @Override public int inputMatch(Class<?> in) { Class<?> iterableIn = IterableInputHelper.getIterableType(in); if (iterableIn != null) { return super.inputMatch(iterableIn); } return 0; } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override protected Object doInvoke(OperationContext ctx, Map<String, Object> args) throws OperationException, ReflectiveOperationException { Object input = ctx.getInput(); if (!(input instanceof Iterable)) { throw new IllegalStateException("An iterable method was called in a non iterable context"); } OutputCollector list = collector.newInstance(); Iterable<?> iterable = (Iterable<?>) input; Iterator<?> it = iterable.iterator(); while (it.hasNext()) { Object in = it.next(); // update context to use as input the current entry ctx.setInput(in); list.collect(ctx, super.doInvoke(ctx, args)); } return list.getOutput(); } }