/* ******************************************************************* * Copyright (c) 2007-2008 Contributors * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Eclipse Public License v 2.0 * which accompanies this distribution and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt * * Contributors: * Alexandre Vasseur * ******************************************************************/ package org.aspectj.weaver.patterns; import org.aspectj.weaver.Member; /** * A sample toString like visitor that helps understanding the AST tree structure organization * * @author Alexandre Vasseur */ public class DumpPointcutVisitor implements PatternNodeVisitor { private StringBuffer sb = new StringBuffer(); public String get() { return sb.toString(); } private void append(Object o) { sb.append(o.toString()); } private void append(char c) { sb.append(c); } /** * This method helps maintaining the API and raises warning when PatternNode subclasses do not implement the visitor pattern * * @param node * @param data * @return */ public Object visit(PatternNode node, Object data) { System.err.println("Should implement: " + node.getClass()); return null; } public Object visit(AnyTypePattern node, Object data) { append('*'); return null; } public Object visit(NoTypePattern node, Object data) { append(node.toString());// TODO no idea when this one is used return null; } public Object visit(EllipsisTypePattern node, Object data) { append(node.toString()); return null; } public Object visit(AnyWithAnnotationTypePattern node, Object data) { if (node.getAnnotationPattern() != AnnotationTypePattern.ANY) { append('('); } node.annotationPattern.accept(this, data); append(" *"); if (node.getAnnotationPattern() != AnnotationTypePattern.ANY) { append(')'); } return null; } public Object visit(AnyAnnotationTypePattern node, Object data) { // @ANY : ignore append('*'); return null; } public Object visit(EllipsisAnnotationTypePattern node, Object data) { append(".."); return null; } public Object visit(AndAnnotationTypePattern node, Object data) { node.getLeft().accept(this, data); append(' '); node.getRight().accept(this, data); return null; } public Object visit(AndPointcut node, Object data) { append('('); node.getLeft().accept(this, data); append(" && "); node.getRight().accept(this, data); append(')'); return null; } public Object visit(AndTypePattern node, Object data) { append('('); node.getLeft().accept(this, data); append(" && "); node.getRight().accept(this, data); append(')'); return null; } public Object visit(AnnotationPatternList node, Object data) { AnnotationTypePattern[] annotations = node.getAnnotationPatterns(); for (int i = 0; i < annotations.length; i++) { if (i > 0) { append(", ");// Note: list is ",", and is " " separated for annotations } annotations[i].accept(this, data); } return null; } public Object visit(AnnotationPointcut node, Object data) { append("@annotation("); node.getAnnotationTypePattern().accept(this, data); append(')'); return null; } public Object visit(ArgsAnnotationPointcut node, Object data) { append("@args("); node.getArguments().accept(this, data); append(')'); return null; } public Object visit(ArgsPointcut node, Object data) { append("args("); node.getArguments().accept(this, data); append(')'); return null; } public Object visit(BindingAnnotationTypePattern node, Object data) { append(node); return null; } public Object visit(BindingTypePattern node, Object data) { append(node); return null; } public Object visit(CflowPointcut node, Object data) { append(node.isCflowBelow() ? "cflowbelow(" : "cflow("); node.getEntry().accept(this, data); append(')'); return null; } public Object visit(ExactAnnotationTypePattern node, Object data) { // append('@'); // since @annotation(@someAnno) cannot be parsed anymore append(node.getAnnotationType().getName()); return null; } public Object visit(ExactTypePattern node, Object data) { if (node.getAnnotationPattern() != AnnotationTypePattern.ANY) { append('('); node.getAnnotationPattern().accept(this, data); append(' '); } String typeString = node.getType().toString(); if (node.isVarArgs()) { typeString = typeString.substring(0, typeString.lastIndexOf('['));// TODO AV - ugly } append(typeString); if (node.isIncludeSubtypes()) { append('+'); } if (node.isVarArgs()) { append("..."); } if (node.getAnnotationPattern() != AnnotationTypePattern.ANY) { append(')'); } return null; } public Object visit(KindedPointcut node, Object data) { append(node.getKind().getSimpleName()); append('('); node.getSignature().accept(this, data); append(')'); return null; } public Object visit(ModifiersPattern node, Object data) { append(node.toString());// note: node takes care of forbidden mods return null; } public Object visit(NamePattern node, Object data) { append(node.toString()); return null; } public Object visit(NotAnnotationTypePattern node, Object data) { append("!"); node.getNegatedPattern().accept(this, data); return null; } public Object visit(NotPointcut node, Object data) { append("!("); node.getNegatedPointcut().accept(this, data); append(')'); return null; } public Object visit(NotTypePattern node, Object data) { append("!("); node.getNegatedPattern().accept(this, data); append(')'); return null; } public Object visit(OrAnnotationTypePattern node, Object data) { append('('); node.getLeft().accept(this, data); append(" || "); node.getRight().accept(this, data); append(')'); return null; } public Object visit(OrPointcut node, Object data) { append('('); node.getLeft().accept(this, data); append(" || "); node.getRight().accept(this, data); append(')'); return null; } public Object visit(OrTypePattern node, Object data) { append('('); node.getLeft().accept(this, data); append(" || "); node.getRight().accept(this, data); append(')'); return null; } public Object visit(ReferencePointcut node, Object data) { append(node.toString()); return null; } public Object visit(SignaturePattern node, Object data) { if (node.getAnnotationPattern() != AnnotationTypePattern.ANY) { node.getAnnotationPattern().accept(this, data); append(' '); } if (node.getModifiers() != ModifiersPattern.ANY) { node.getModifiers().accept(this, data); append(' '); } if (node.getKind() == Member.STATIC_INITIALIZATION) { node.getDeclaringType().accept(this, data); } else if (node.getKind() == Member.HANDLER) { append("handler("); node.getParameterTypes().get(0).accept(this, data);// Note: we know we have 1 child append(')'); } else { if (!(node.getKind() == Member.CONSTRUCTOR)) { node.getReturnType().accept(this, data); append(' '); } if (node.getDeclaringType() != TypePattern.ANY) { node.getDeclaringType().accept(this, data); append('.'); } if (node.getKind() == Member.CONSTRUCTOR) { append("new"); } else { node.getName().accept(this, data); } if (node.getKind() == Member.METHOD || node.getKind() == Member.CONSTRUCTOR) { append('('); node.getParameterTypes().accept(this, data); append(')'); } if (node.getThrowsPattern() != null) { append(' '); node.getThrowsPattern().accept(this, data); } } return null; } public Object visit(ThisOrTargetAnnotationPointcut node, Object data) { append(node.isThis() ? "@this(" : "@target("); node.getAnnotationTypePattern().accept(this, data); append(')'); return null; } public Object visit(ThisOrTargetPointcut node, Object data) { append(node.isThis() ? "this(" : "target("); node.getType().accept(this, data); append(')'); return null; } // Note: a visitor instance is not thread safe so should not be shared private boolean inThrowsForbidden = false; public Object visit(ThrowsPattern node, Object data) { if (node == ThrowsPattern.ANY) { return null; } append("throws "); node.getRequired().accept(this, data); if (node.getForbidden().size() > 0) { // a hack since throws !(A, B) cannot be parsed try { inThrowsForbidden = true; node.getForbidden().accept(this, data); } finally { inThrowsForbidden = false; } } return null; } public Object visit(TypePatternList node, Object data) { if (node.getTypePatterns().length == 0) { return null; } TypePattern[] typePatterns = node.getTypePatterns(); for (int i = 0; i < typePatterns.length; i++) { TypePattern typePattern = typePatterns[i]; if (i > 0) { append(", "); } if (inThrowsForbidden) { append('!'); } typePattern.accept(this, data); } return null; } public Object visit(WildAnnotationTypePattern node, Object data) { append("@("); node.getTypePattern().accept(this, data); append(')'); return null; } public Object visit(WildTypePattern node, Object data) { if (node.getAnnotationPattern() != AnnotationTypePattern.ANY) { append('('); node.getAnnotationPattern().accept(this, data); append(' '); } NamePattern[] namePatterns = node.getNamePatterns(); for (int i = 0; i < namePatterns.length; i++) { if (namePatterns[i] == null) { append('.');// FIXME mh, error prone, can't we have a nullNamePattern ? } else { if (i > 0) { append('.'); } namePatterns[i].accept(this, data); } } if (node.isIncludeSubtypes()) { append('+'); } if (node.isVarArgs()) { append("..."); } if (node.getAnnotationPattern() != AnnotationTypePattern.ANY) { append(')'); } return null; } public Object visit(WithinAnnotationPointcut node, Object data) { append("@within("); node.getAnnotationTypePattern().accept(this, data); append(')'); return null; } public Object visit(WithinCodeAnnotationPointcut node, Object data) { append("@withincode("); node.getAnnotationTypePattern().accept(this, data); append(')'); return null; } public Object visit(WithinPointcut node, Object data) { append("within("); node.getTypePattern().accept(this, data); append(')'); return null; } public Object visit(WithincodePointcut node, Object data) { append("withincode("); node.getSignature().accept(this, data); append(')'); return null; } public Object visit(Pointcut.MatchesNothingPointcut node, Object data) { append("");// TODO shouldn't that be a "false" ? return null; } // -------------- perX public Object visit(PerCflow node, Object data) { append(node); return null; } public Object visit(PerFromSuper node, Object data) { append(node); return null; } public Object visit(PerObject node, Object data) { append(node); return null; } public Object visit(PerSingleton node, Object data) { append(node); return null; } public Object visit(PerTypeWithin node, Object data) { append(node); return null; } // ------------- declare X public Object visit(DeclareAnnotation node, Object data) { append(node); return null; } public Object visit(DeclareErrorOrWarning node, Object data) { append(node); return null; } public Object visit(DeclareParents node, Object data) { append(node); return null; } public Object visit(DeclarePrecedence node, Object data) { append(node); return null; } public Object visit(DeclareSoft node, Object data) { append(node); return null; } // ----------- misc public Object visit(ConcreteCflowPointcut node, Object data) { append(node); return null; } public Object visit(HandlerPointcut node, Object data) { append(node); return null; } public Object visit(IfPointcut node, Object data) { append(node); return null; } public Object visit(TypeVariablePattern node, Object data) { append(node); return null; } public Object visit(TypeVariablePatternList node, Object data) { append(node); return null; } public Object visit(HasMemberTypePattern node, Object data) { append(node); return null; } public Object visit(TypeCategoryTypePattern node, Object data) { append(node); return null; } public static void check(String s) { check(Pointcut.fromString(s), false); } public static void check(PatternNode pc, boolean isTypePattern) { DumpPointcutVisitor v1 = new DumpPointcutVisitor(); pc.accept(v1, null); DumpPointcutVisitor v2 = new DumpPointcutVisitor(); final PatternNode pc2; if (isTypePattern) { pc2 = new PatternParser(v1.get()).parseTypePattern(); } else { pc2 = Pointcut.fromString(v1.get()); } pc2.accept(v2, null); // at second parsing, the String form stay stable when parsed and parsed again if (!v1.get().equals(v2.get())) { throw new ParserException("Unstable back parsing for '" + pc + "', got '" + v1.get() + "' and '" + v2.get() + "'", null); } } public static void main(String args[]) throws Throwable { String[] s = new String[] { // "@args(Foo, Goo, *, .., Moo)", // "execution(* *())", // "call(* *(int, Integer...))", // "staticinitialization(@(Foo) @(Boo) @(Goo) Moo)", "(if(true) && set(int BaseApp.i))" }; for (String value : s) { check(value); } } }