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