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øy
025     * @author Jörg Schaible
026     */
027    public class MethodCallingVisitor extends TraversalCheckingVisitor implements Serializable {
028    
029        // TODO: we must serialize method with read/writeObject ... and are our parent serializable ???
030        private transient Method method;
031        private final Object[] arguments;
032        private final Class type;
033        private final boolean visitInInstantiationOrder;
034        private final List componentInstances;
035    
036        /**
037         * Construct a MethodCallingVisitor for a method with arguments.
038         * 
039         * @param method the {@link Method} to invoke
040         * @param ofType the type of the components, that will be invoked
041         * @param visitInInstantiationOrder <code>true</code> if components are visited in instantiation order
042         * @param arguments the arguments for the method invocation (may be <code>null</code>)
043         * @throws NullPointerException if <tt>method</tt>, or <tt>ofType</tt> is <code>null</code>
044         */
045        public MethodCallingVisitor(Method method, Class ofType, Object[] arguments, boolean visitInInstantiationOrder) {
046            if (method == null) {
047                throw new NullPointerException();
048            }
049            this.method = method;
050            this.arguments = arguments;
051            this.type = ofType;
052            this.visitInInstantiationOrder = visitInInstantiationOrder;
053            this.componentInstances = new ArrayList();
054        }
055    
056        /**
057         * Construct a MethodCallingVisitor for standard methods visiting the component in instantiation order.
058         * 
059         * @param method the method to invoke
060         * @param ofType the type of the components, that will be invoked
061         * @param arguments the arguments for the method invocation (may be <code>null</code>)
062         * @throws NullPointerException if <tt>method</tt>, or <tt>ofType</tt> is <code>null</code>
063         */
064        public MethodCallingVisitor(Method method, Class ofType, Object[] arguments) {
065            this(method, ofType, arguments, true);
066        }
067    
068        public Object traverse(Object node) {
069            componentInstances.clear();
070            try {
071                super.traverse(node);
072                if (!visitInInstantiationOrder) {
073                    Collections.reverse(componentInstances);
074                }
075                for (Object componentInstance : componentInstances) {
076                    invoke(componentInstance);
077                }
078            } finally {
079                componentInstances.clear();
080            }
081            return Void.TYPE;
082        }
083    
084        public void visitContainer(PicoContainer pico) {
085            super.visitContainer(pico);
086            componentInstances.addAll(pico.getComponents(type));
087        }
088    
089        protected Method getMethod() {
090            return method;
091        }
092    
093        protected Object[] getArguments() {
094            return arguments;
095        }
096    
097        protected void invoke(final Object[] targets) {
098            for (Object target : targets) {
099                invoke(target);
100            }
101        }
102    
103        protected Class<Void> invoke(final Object target) {
104            final Method method = getMethod();
105            try {
106                method.invoke(target, getArguments());
107            } catch (IllegalArgumentException e) {
108                throw new PicoCompositionException("Can't call " + method.getName() + " on " + target, e);
109            } catch (IllegalAccessException e) {
110                throw new PicoCompositionException("Can't call " + method.getName() + " on " + target, e);
111            } catch (InvocationTargetException e) {
112                throw new PicoCompositionException("Failed when calling " + method.getName() + " on " + target, e
113                        .getTargetException());
114            }
115            return Void.TYPE;
116        }
117    }