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    @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    }