001/*****************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved.            *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD      *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file.                                                     *
007 *****************************************************************************/
008package org.picocontainer.visitors;
009
010import org.picocontainer.PicoContainer;
011import org.picocontainer.PicoCompositionException;
012
013import java.io.Serializable;
014import java.lang.reflect.InvocationTargetException;
015import java.lang.reflect.Method;
016import java.util.ArrayList;
017import java.util.Collections;
018import java.util.List;
019
020
021/**
022 * A PicoVisitor implementation, that calls methods on the components of a specific type.
023 * 
024 * @author Aslak Hellesøy
025 * @author Jörg Schaible
026 */
027@SuppressWarnings("serial")
028public class MethodCallingVisitor extends TraversalCheckingVisitor implements Serializable {
029
030    // TODO: we must serialize method with read/writeObject ... and are our parent serializable ???
031    private transient Method method;
032    private final Object[] arguments;
033    private final Class<?> type;
034    private final boolean visitInInstantiationOrder;
035    private final List componentInstances;
036
037    /**
038     * Construct a MethodCallingVisitor for a method with arguments.
039     * 
040     * @param method the {@link Method} to invoke
041     * @param ofType the type of the components, that will be invoked
042     * @param visitInInstantiationOrder <code>true</code> if components are visited in instantiation order
043     * @param arguments the arguments for the method invocation (may be <code>null</code>)
044     * @throws NullPointerException if <tt>method</tt>, or <tt>ofType</tt> is <code>null</code>
045     */
046    public MethodCallingVisitor(Method method, Class<?> ofType, Object[] arguments, boolean visitInInstantiationOrder) {
047        if (method == null) {
048            throw new NullPointerException();
049        }
050        this.method = method;
051        this.arguments = arguments;
052        this.type = ofType;
053        this.visitInInstantiationOrder = visitInInstantiationOrder;
054        this.componentInstances = new ArrayList();
055    }
056
057    /**
058     * Construct a MethodCallingVisitor for standard methods visiting the component in instantiation order.
059     * 
060     * @param method the method to invoke
061     * @param ofType the type of the components, that will be invoked
062     * @param arguments the arguments for the method invocation (may be <code>null</code>)
063     * @throws NullPointerException if <tt>method</tt>, or <tt>ofType</tt> is <code>null</code>
064     */
065    public MethodCallingVisitor(Method method, Class ofType, Object[] arguments) {
066        this(method, ofType, arguments, true);
067    }
068
069    public Object traverse(Object node) {
070        componentInstances.clear();
071        try {
072            super.traverse(node);
073            if (!visitInInstantiationOrder) {
074                Collections.reverse(componentInstances);
075            }
076            for (Object componentInstance : componentInstances) {
077                invoke(componentInstance);
078            }
079        } finally {
080            componentInstances.clear();
081        }
082        return Void.TYPE;
083    }
084
085    public boolean visitContainer(PicoContainer pico) {
086        super.visitContainer(pico);
087        componentInstances.addAll(pico.getComponents(type));
088        return CONTINUE_TRAVERSAL;
089    }
090
091    protected Method getMethod() {
092        return method;
093    }
094
095    protected Object[] getArguments() {
096        return arguments;
097    }
098
099    protected void invoke(final Object[] targets) {
100        for (Object target : targets) {
101            invoke(target);
102        }
103    }
104
105    protected Class<Void> invoke(final Object target) {
106        final Method method = getMethod();
107        try {
108            method.invoke(target, getArguments());
109        } catch (IllegalArgumentException e) {
110            throw new PicoCompositionException("Can't call " + method.getName() + " on " + target, e);
111        } catch (IllegalAccessException e) {
112            throw new PicoCompositionException("Can't call " + method.getName() + " on " + target, e);
113        } catch (InvocationTargetException e) {
114            throw new PicoCompositionException("Failed when calling " + method.getName() + " on " + target, e
115                    .getTargetException());
116        }
117        return Void.TYPE;
118    }
119}