    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     *****************************************************************************/
    008    package org.picocontainer.visitors;
    009
    010    import org.picocontainer.PicoContainer;
    011    import org.picocontainer.PicoCompositionException;
    012
    013    import java.io.Serializable;
    014    import java.lang.reflect.InvocationTargetException;
    015    import java.lang.reflect.Method;
    016    import java.util.ArrayList;
    017    import java.util.Collections;
    018    import 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&oslash;y
    025     * @author J&ouml;rg Schaible
    026     */
    027    @SuppressWarnings("serial")
    028    public 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    }