/******************************************************************************* * Copyright (c) 2006-2012 * Software Technology Group, Dresden University of Technology * DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026 * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Software Technology Group - TU Dresden, Germany; * DevBoost GmbH - Berlin, Germany * - initial API and implementation ******************************************************************************/ package org.emftext.language.java.test; import java.math.BigInteger; import org.eclipse.jdt.core.dom.ASTMatcher; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.ArrayAccess; import org.eclipse.jdt.core.dom.ArrayCreation; import org.eclipse.jdt.core.dom.ArrayInitializer; import org.eclipse.jdt.core.dom.ArrayType; import org.eclipse.jdt.core.dom.AssertStatement; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.BlockComment; import org.eclipse.jdt.core.dom.BooleanLiteral; import org.eclipse.jdt.core.dom.BreakStatement; import org.eclipse.jdt.core.dom.CastExpression; import org.eclipse.jdt.core.dom.CatchClause; import org.eclipse.jdt.core.dom.CharacterLiteral; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.ConditionalExpression; import org.eclipse.jdt.core.dom.ConstructorInvocation; import org.eclipse.jdt.core.dom.ContinueStatement; import org.eclipse.jdt.core.dom.DoStatement; import org.eclipse.jdt.core.dom.EmptyStatement; import org.eclipse.jdt.core.dom.EnhancedForStatement; import org.eclipse.jdt.core.dom.EnumConstantDeclaration; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.ExpressionStatement; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.ForStatement; import org.eclipse.jdt.core.dom.IfStatement; import org.eclipse.jdt.core.dom.ImportDeclaration; import org.eclipse.jdt.core.dom.InfixExpression; import org.eclipse.jdt.core.dom.Initializer; import org.eclipse.jdt.core.dom.InstanceofExpression; import org.eclipse.jdt.core.dom.Javadoc; import org.eclipse.jdt.core.dom.LabeledStatement; import org.eclipse.jdt.core.dom.LineComment; import org.eclipse.jdt.core.dom.MarkerAnnotation; import org.eclipse.jdt.core.dom.MemberRef; import org.eclipse.jdt.core.dom.MemberValuePair; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.MethodRef; import org.eclipse.jdt.core.dom.MethodRefParameter; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.NormalAnnotation; import org.eclipse.jdt.core.dom.NullLiteral; import org.eclipse.jdt.core.dom.NumberLiteral; import org.eclipse.jdt.core.dom.PackageDeclaration; import org.eclipse.jdt.core.dom.ParameterizedType; import org.eclipse.jdt.core.dom.ParenthesizedExpression; import org.eclipse.jdt.core.dom.PostfixExpression; import org.eclipse.jdt.core.dom.PrefixExpression; import org.eclipse.jdt.core.dom.PrimitiveType; import org.eclipse.jdt.core.dom.QualifiedName; import org.eclipse.jdt.core.dom.QualifiedType; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SimpleType; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.StringLiteral; import org.eclipse.jdt.core.dom.SuperConstructorInvocation; import org.eclipse.jdt.core.dom.SuperFieldAccess; import org.eclipse.jdt.core.dom.SuperMethodInvocation; import org.eclipse.jdt.core.dom.SwitchCase; import org.eclipse.jdt.core.dom.SwitchStatement; import org.eclipse.jdt.core.dom.SynchronizedStatement; import org.eclipse.jdt.core.dom.TagElement; import org.eclipse.jdt.core.dom.TextElement; import org.eclipse.jdt.core.dom.ThisExpression; import org.eclipse.jdt.core.dom.ThrowStatement; import org.eclipse.jdt.core.dom.TryStatement; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.TypeDeclarationStatement; import org.eclipse.jdt.core.dom.TypeLiteral; import org.eclipse.jdt.core.dom.TypeParameter; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.dom.WhileStatement; import org.eclipse.jdt.core.dom.WildcardType; import org.emftext.language.java.resource.java.IJavaTokenResolveResult; import org.emftext.language.java.resource.java.analysis.JavaDECIMAL_DOUBLE_LITERALTokenResolver; import org.emftext.language.java.resource.java.analysis.JavaDECIMAL_FLOAT_LITERALTokenResolver; import org.emftext.language.java.resource.java.analysis.JavaHEX_DOUBLE_LITERALTokenResolver; import org.emftext.language.java.resource.java.analysis.JavaHEX_FLOAT_LITERALTokenResolver; import org.emftext.language.java.resource.java.analysis.JavaHEX_INTEGER_LITERALTokenResolver; import org.emftext.language.java.resource.java.analysis.JavaHEX_LONG_LITERALTokenResolver; import org.emftext.language.java.resource.java.mopp.JavaTokenResolveResult; import org.emftext.language.java.util.CharacterEscaper; /** * An extension of the JDT ASTMatcher that prints information * whenever the matching of two ASTs fails. In addition some * normalizations are applied to number literals to removed * redundant characters that can cause comparison failures * even though the numbers are the same. */ public class TalkativeASTMatcher extends ASTMatcher { public TalkativeASTMatcher(boolean b) { super(b); } @Override public boolean match(AnnotationTypeDeclaration node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(AnnotationTypeMemberDeclaration node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(AnonymousClassDeclaration node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ArrayAccess node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ArrayCreation node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ArrayInitializer node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ArrayType node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(AssertStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(Assignment node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(Block node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(BlockComment node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(BooleanLiteral node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(BreakStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(CastExpression node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(CatchClause node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(CharacterLiteral node, Object other) { if (!(other instanceof CharacterLiteral)) { return false; } CharacterLiteral o = (CharacterLiteral) other; String oToken = o.getEscapedValue(); String nToken = node.getEscapedValue(); //octal notation and escaped characters if (oToken.matches("\\'\\\\.+\\'")) { oToken = oToken.substring(1, oToken.length() - 1); oToken = "'" + CharacterEscaper.unescapeEscapedCharacters(oToken) + "'"; } if (nToken.matches("\\'\\\\.+\\'")) { nToken = nToken.substring(1, nToken.length() - 1); nToken = "'" + CharacterEscaper.unescapeEscapedCharacters(nToken) + "'"; } return setDiff(node, other, safeEquals(nToken, oToken)); } @Override public boolean match(ClassInstanceCreation node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(CompilationUnit node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ConditionalExpression node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ConstructorInvocation node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ContinueStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(DoStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(EmptyStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(EnhancedForStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(EnumConstantDeclaration node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(EnumDeclaration node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ExpressionStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(FieldAccess node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(FieldDeclaration node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ForStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(IfStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ImportDeclaration node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(InfixExpression node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(Initializer node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(InstanceofExpression node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(Javadoc node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(LabeledStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(LineComment node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(MarkerAnnotation node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(MemberRef node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(MemberValuePair node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(MethodDeclaration node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(MethodInvocation node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(MethodRef node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(MethodRefParameter node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(Modifier node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(NormalAnnotation node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(NullLiteral node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(NumberLiteral node, Object other) { String oToken = null; if (other instanceof PrefixExpression) { oToken = ((PrefixExpression)other).toString(); } else if (other instanceof NumberLiteral) { oToken = ((NumberLiteral)other).getToken(); } else { return setDiff(node, other, false); } String nToken = node.getToken(); return setDiff(node, other, numberMatch(nToken, oToken)); } protected boolean numberMatch(String nToken, String oToken) { nToken = normalizeNumberToken(nToken); oToken = normalizeNumberToken(oToken); boolean equals = safeEquals(nToken, oToken); return equals; } private String normalizeNumberToken(String token) { //System.out.println("normalizeNumberToken(" + token + ")"); //HEX normalization token = normalizeHexNumberToken(token); if (token.toLowerCase().startsWith("-0x")) { token = token.substring(1); token = "-" + normalizeHexNumberToken(token); } if (token.toLowerCase().endsWith("l")) { token = token.substring(0, token.length() - 1); } //OCTAL normalization if (token.matches("0[0-9]+")) { token = new BigInteger(token, 8).toString(); } //condition that indicates that this is float or double --> parse if(token.toLowerCase().endsWith("f") || token.toLowerCase().endsWith("d") || token.toLowerCase().contains("e") || token.toLowerCase().contains("p") || token.toLowerCase().contains(".")) { if (token.toLowerCase().endsWith("f")) { token = token.substring(0, token.length() - 1); IJavaTokenResolveResult result = new JavaTokenResolveResult(); JavaDECIMAL_FLOAT_LITERALTokenResolver.parseToFloat(token, result); token = "" + result.getResolvedToken(); } else { // ends with 'd' or has no suffix if (token.toLowerCase().endsWith("d")) { token = token.substring(0, token.length() - 1); } IJavaTokenResolveResult result = new JavaTokenResolveResult(); JavaDECIMAL_DOUBLE_LITERALTokenResolver.parseToDouble(token, result); token = "" + result.getResolvedToken(); } } if (token.startsWith("- ")) { token = "-" + token.substring(2); } //System.out.println("TalkativeASTMatcher.normalizeNumberToken() result : " + token); return token; } private String normalizeHexNumberToken(String hexToken) { try { boolean hasHexSuffix = hexToken.toLowerCase().startsWith("0x"); boolean endWithL = hexToken.toLowerCase().endsWith("l"); boolean endsWithF = hexToken.toLowerCase().endsWith("f"); boolean containsP = hexToken.toLowerCase().contains("p"); //boolean containsE = hexToken.toLowerCase().contains("e"); boolean containsExp = containsP; boolean containsDot = hexToken.toLowerCase().contains("."); if (hasHexSuffix && endWithL) { IJavaTokenResolveResult result = new JavaTokenResolveResult(); new JavaHEX_LONG_LITERALTokenResolver().resolve(hexToken, null, result); hexToken = result.getResolvedToken().toString(); } else if (hasHexSuffix && (containsExp || containsDot) && endsWithF) { IJavaTokenResolveResult result = new JavaTokenResolveResult(); new JavaHEX_FLOAT_LITERALTokenResolver().resolve(hexToken, null, result); hexToken = result.getResolvedToken().toString(); } else if (hasHexSuffix && (containsExp || containsDot)) { IJavaTokenResolveResult result = new JavaTokenResolveResult(); new JavaHEX_DOUBLE_LITERALTokenResolver().resolve(hexToken, null, result); hexToken = result.getResolvedToken().toString(); } else if (hasHexSuffix) { IJavaTokenResolveResult result = new JavaTokenResolveResult(); new JavaHEX_INTEGER_LITERALTokenResolver().resolve(hexToken, null, result); hexToken = result.getResolvedToken().toString(); } } catch (NumberFormatException nfe) { nfe.printStackTrace(); } return hexToken; } @Override public boolean match(PackageDeclaration node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ParameterizedType node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ParenthesizedExpression node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(PostfixExpression node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(PrefixExpression node, Object other) { if (other instanceof NumberLiteral) { String nToken = node.toString(); String oToken = ((NumberLiteral)other).getToken(); return setDiff(node, other, numberMatch(nToken, oToken)); } else { return setDiff(node, other, super.match(node, other)); } } @Override public boolean match(PrimitiveType node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(QualifiedName node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(QualifiedType node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ReturnStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(SimpleName node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(SimpleType node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(SingleMemberAnnotation node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(SingleVariableDeclaration node, Object other) { return setDiff(node, other, super.match(node, other)); } public boolean match(StringLiteral node, Object other) { if (!(other instanceof StringLiteral)) { return false; } StringLiteral o = (StringLiteral) other; String nString = node.getEscapedValue(); String oString = o.getEscapedValue(); //normalize escaped strings nString = CharacterEscaper.unescapeEscapedCharacters(nString); oString = CharacterEscaper.unescapeEscapedCharacters(oString); return setDiff(node, other, safeEquals(nString, oString)); } @Override public boolean match(SuperConstructorInvocation node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(SuperFieldAccess node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(SuperMethodInvocation node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(SwitchCase node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(SwitchStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(SynchronizedStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(TagElement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(TextElement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ThisExpression node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(ThrowStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(TryStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(TypeDeclaration node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(TypeDeclarationStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(TypeLiteral node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(TypeParameter node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(VariableDeclarationExpression node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(VariableDeclarationFragment node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(VariableDeclarationStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(WhileStatement node, Object other) { return setDiff(node, other, super.match(node, other)); } @Override public boolean match(WildcardType node, Object other) { return setDiff(node, other, super.match(node, other)); } protected String diff = ""; protected boolean setDiff(Object o1, Object o2, boolean result) { if (!result && "".equals(diff)) { diff += ("\nORIGINAL: \n"); if (o1 instanceof ASTNode) { diff += "(POSITION: " + ((ASTNode) o1).getStartPosition() + ")\n"; } diff += (o1.toString()); diff += ("\nREPRINT:\n"); if (o2 instanceof ASTNode) { diff += "(POSITION: " + ((ASTNode) o2).getStartPosition() + ")\n"; } diff += (o2.toString()); diff += ("\n--------\n"); } return result; } public String getDiff() { return diff; } }