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    }