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 }