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 }