001    /*
002     @license.text@ 
003     */
004    package biz.hammurapi.antlr;
005    
006    import java.lang.reflect.InvocationTargetException;
007    import java.lang.reflect.Method;
008    import java.lang.reflect.Modifier;
009    
010    import antlr.collections.AST;
011    import biz.hammurapi.util.DispatchException;
012    import biz.hammurapi.util.PoliteVisitor;
013    
014    
015    /**
016     * Visits AST and dispatches invocations by
017     * node names.
018     * @author Pavel Vlasov
019     * @version $Revision: 1.2 $
020     */
021    public class AstVisitor implements PoliteVisitor {
022            private Method[] visitMethods;
023            private Method[] leaveMethods;
024            
025            /**
026             * @param tokenTypeNames - can be obtained from Parser.
027             * @param visitPrefix - visit method name prefix. E.g. if prefix is <code>visit_</code> 
028             * then visit of AST with type name <code>LITERAL_throws</code> will be dispatched
029             * to method <code>visit_LITERAL_throws</code> if such method is present.
030             * @param leavePrefix - leave method name prefix.
031             */
032            public AstVisitor(String[] tokenTypeNames, String visitPrefix, String leavePrefix) {            
033                    Class thisClass=this.getClass();
034                    for (int i=0, mc=thisClass.getMethods().length; i<mc; i++) {
035                            Method m=thisClass.getMethods()[i];
036                            if (!m.getName().equals(visitPrefix) 
037                                            && Modifier.isPublic(m.getModifiers())
038                                            && m.getName().startsWith(visitPrefix) 
039                                            && m.getParameterTypes().length==1 
040                                            && AST.class.isAssignableFrom(m.getParameterTypes()[0])
041                                            && (boolean.class.equals(m.getReturnType()) || void.class.equals(m.getReturnType()))) {
042                                    for (int j=0; j<tokenTypeNames.length; j++) {
043                                            if (m.getName().substring(visitPrefix.length()).equals(tokenTypeNames[j])) {
044                                                    visitMethods[j]=m;
045                                            }
046                                    }
047                            }
048                            
049                            if (!m.getName().equals(leavePrefix) 
050                                            && Modifier.isPublic(m.getModifiers())
051                                            && m.getName().startsWith(leavePrefix) 
052                                            && m.getParameterTypes().length==1 
053                                            && AST.class.isAssignableFrom(m.getParameterTypes()[0])
054                                            && void.class.equals(m.getReturnType())) {
055                                    for (int j=0; j<tokenTypeNames.length; j++) {
056                                            if (m.getName().substring(leavePrefix.length()).equals(tokenTypeNames[j])) {
057                                                    leaveMethods[j]=m;
058                                            }
059                                    }
060                            }                                                                       
061                    }
062            }
063            
064            public boolean visit(Object target) {
065                    if (target instanceof AST) {
066                            Method m=visitMethods[((AST) target).getType()];
067                            if (m==null) {
068                                    return true;
069                            }
070                            
071                            try {
072                                    Object ret = m.invoke(this, new Object[] {target});
073                                    return ret instanceof Boolean ? ((Boolean) ret).booleanValue() : true;
074                            } catch (IllegalArgumentException e) {
075                                    throw new DispatchException(e);
076                            } catch (IllegalAccessException e) {
077                                    throw new DispatchException(e);
078                            } catch (InvocationTargetException e) {
079                                    throw new DispatchException(e);
080                            }
081                    }
082                    
083                    return false;
084            }
085    
086            public void leave(Object target) {
087                    if (target instanceof AST) {
088                            Method m=visitMethods[((AST) target).getType()];
089                            if (m!=null) {
090                                    try {
091                                            m.invoke(this, new Object[] {target});
092                                    } catch (IllegalArgumentException e) {
093                                            throw new DispatchException(e);
094                                    } catch (IllegalAccessException e) {
095                                            throw new DispatchException(e);
096                                    } catch (InvocationTargetException e) {
097                                            throw new DispatchException(e);
098                                    }
099                            }
100                    }
101            }               
102    }