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 }