001    package biz.hammurapi.util;
002    
003    import java.lang.reflect.Method;
004    import java.lang.reflect.Modifier;
005    import java.lang.reflect.UndeclaredThrowableException;
006    import java.util.ArrayList;
007    import java.util.Iterator;
008    import java.util.List;
009    
010    public class VisitorConverter {
011            
012            /**
013             * Name of leave method.
014             */
015            public static final String LEAVE = "leave";
016            
017            /**
018             * Name of visit method.
019             */
020            public static final String VISIT = "visit";
021            
022            /**
023             * Converts object to visitor.
024             * If visitor is instance of <code>Visitor</code>, then this method returns
025             * object unchanged. Otherwise it introspects object for public 
026             * <boolean>visit()</boolean> and <boolean>leave()</boolean> methods
027             * with a single argument returning boolean or void. If such methods are found
028             * they are made accessible in order to enable invocation of methods declared in
029             * anonymous and local classes. This method returns an anonymous visitor or polite
030             * visitor (if there are leave() methods). This anonymous (polite) visitor
031             * dispatches invocations to visit() and leave() methods with compatible types. 
032             * @param target Object to be converted to visitor
033             * @return Object argument if it already implements visitor, or dispatcher 
034             * to object's visit() and leave() methods.
035             */
036            public Visitor convert(final Object object) {
037                    if (object == null ) {
038                            return null;
039                    }
040                    
041                    if (object instanceof Visitor) {
042                            return (Visitor) object;
043                    }               
044                    
045                    final List visitMethods = new ArrayList();
046                    final List leaveMethods = new ArrayList();
047    
048                    Method[] methods = object.getClass().getMethods();
049                    for (int i=0; i<methods.length; ++i) {
050                            Method method = methods[i];
051                            if (Modifier.isPublic(method.getModifiers())
052                                            && VISIT.equals(method.getName()) 
053                                            && method.getParameterTypes().length==1
054                                            && (void.class.equals(method.getReturnType()) || boolean.class.equals(method.getReturnType()))) {
055                                    
056                                    if (!method.isAccessible()) {
057                                            method.setAccessible(true);
058                                    }
059                                    visitMethods.add(method);
060                            }
061                            
062                            if (Modifier.isPublic(method.getModifiers()) 
063                                            && LEAVE.equals(method.getName()) 
064                                            && method.getParameterTypes().length==1
065                                            && void.class.equals(method.getReturnType())) {
066                                    
067                                    if (!method.isAccessible()) {
068                                            method.setAccessible(true);
069                                    }
070                                    leaveMethods.add(method);
071                            }
072                    }
073                    
074                    if (visitMethods.isEmpty() && leaveMethods.isEmpty()) {
075                            return null;
076                    }
077                    
078                    final Visitor dispatcher = new Visitor() {
079    
080                            public boolean visit(Object target) {
081                                    Iterator it = visitMethods.iterator();
082                                    while (it.hasNext()) {
083                                            Method visitMethod = (Method) it.next();
084                                            if (visitMethod.getParameterTypes()[0].isInstance(target)) {
085                                                    try {
086                                                            Object ret = visitMethod.invoke(object, new Object[] {target});
087                                                            if (Boolean.FALSE.equals(ret)) {
088                                                                    return false;
089                                                            }
090                                                    } catch (Exception e) {
091                                                            throw new UndeclaredThrowableException(e);
092                                                    }
093                                            }
094                                    }
095                                    
096                                    return true;
097                            }
098                            
099                    };
100                    
101                    if (leaveMethods.isEmpty()) {
102                            return dispatcher;
103                    }
104                    
105                    return new PoliteVisitor() {
106    
107                            public void leave(Object target) {
108                                    Iterator it = visitMethods.iterator();
109                                    while (it.hasNext()) {
110                                            Method visitMethod = (Method) it.next();
111                                            if (visitMethod.getParameterTypes()[0].isInstance(target)) {
112                                                    try {
113                                                            visitMethod.invoke(object, new Object[] {target});
114                                                    } catch (Exception e) {
115                                                            throw new UndeclaredThrowableException(e);
116                                                    }
117                                            }
118                                    }
119                            }
120    
121                            public boolean visit(Object target) {
122                                    return dispatcher.visit(target);
123                            }
124                            
125                    };
126            }
127    
128    }