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.PicoVisitor;
011 import org.picocontainer.PicoException;
012
013 import java.lang.reflect.InvocationTargetException;
014 import java.lang.reflect.Method;
015 import java.security.AccessController;
016 import java.security.PrivilegedAction;
017
018 /**
019 * Abstract PicoVisitor implementation. A generic traverse method is implemented, that
020 * accepts any object with a method named "accept", that takes a
021 * {@link PicoVisitor} as argument and and invokes it. Additionally it provides the
022 * {@link #checkTraversal()} method, that throws a {@link PicoVisitorTraversalException},
023 * if currently no traversal is running.
024 *
025 * @author Jörg Schaible
026 */
027 public abstract class AbstractPicoVisitor implements PicoVisitor {
028 private boolean traversal;
029
030 public Object traverse(final Object node) {
031 traversal = true;
032 Object retval =
033 AccessController.doPrivileged(new PrivilegedAction() {
034 public Object run() {
035 try {
036 return node.getClass().getMethod("accept", PicoVisitor.class);
037 } catch (NoSuchMethodException e) {
038 return e;
039 }
040 }
041 });
042 try {
043 if (retval instanceof NoSuchMethodException) {
044 throw (NoSuchMethodException) retval;
045 }
046 Method accept = (Method) retval;
047 accept.invoke(node, this);
048 return Void.TYPE;
049 } catch (NoSuchMethodException e) {
050 } catch (IllegalAccessException e) {
051 } catch (InvocationTargetException e) {
052 Throwable cause = e.getTargetException();
053 if (cause instanceof RuntimeException) {
054 throw (RuntimeException)cause;
055 } else if (cause instanceof Error) {
056 throw (Error)cause;
057 }
058 } finally {
059 traversal = false;
060 }
061 throw new IllegalArgumentException(node.getClass().getName() + " is not a valid type for traversal");
062 }
063
064 /**
065 * Checks the traversal flag, indicating a currently running traversal of the visitor.
066 * @throws PicoVisitorTraversalException if no traversal is active.
067 */
068 protected void checkTraversal() {
069 if (!traversal) {
070 throw new PicoVisitorTraversalException(this);
071 }
072 }
073
074 /**
075 * Exception for a PicoVisitor, that is dependent on a defined starting point of the traversal.
076 * If the traversal is not initiated with a call of {@link PicoVisitor#traverse}
077 *
078 * @author joehni
079 */
080 public static class PicoVisitorTraversalException
081 extends PicoException {
082
083 /**
084 * Construct the PicoVisitorTraversalException.
085 *
086 * @param visitor The visitor casing the exception.
087 */
088 public PicoVisitorTraversalException(PicoVisitor visitor) {
089 super("Traversal for PicoVisitor of type " + visitor.getClass().getName() + " must start with the visitor's traverse method");
090 }
091 }
092
093 }