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 }