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 }