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