/** * Copyright (C) 2006-2017 INRIA and contributors * Spoon - http://spoon.gforge.inria.fr/ * * This software is governed by the CeCILL-C License under French law and * abiding by the rules of distribution of free software. You can use, modify * and/or redistribute the software under the terms of the CeCILL-C license as * circulated by CEA, CNRS and INRIA at http://www.cecill.info. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-C license and that you accept its terms. */ package spoon.support.compiler.jdt; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Assignment; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.OperatorIds; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import spoon.reflect.code.BinaryOperatorKind; import spoon.reflect.code.UnaryOperatorKind; import spoon.reflect.declaration.CtAnnotatedElementType; import spoon.reflect.declaration.ModifierKind; import java.util.EnumSet; import java.util.Set; /** Helper class for JDTTreeBuilder */ class JDTTreeBuilderQuery { private JDTTreeBuilderQuery() { } /** * Searches a type from an entry-point according to a simple name. * * @param type * Entry-point to search. * @param simpleName * Expected type name. * @return type binding. */ static TypeBinding searchTypeBinding(ReferenceBinding type, String simpleName) { if (simpleName == null || type == null) { return null; } if (type.memberTypes() != null) { for (ReferenceBinding memberType : type.memberTypes()) { if (simpleName.equals(CharOperation.charToString(memberType.sourceName()))) { return memberType; } } } return searchTypeBinding(type.superclass(), simpleName); } /** * Searches a type used in units declared in a compilation unit. * * @param qualifiedName * Qualified name of the expected type. * @param unitsToProcess * Search the type expected in units. * @return type binding. */ static TypeBinding searchTypeBinding(String qualifiedName, CompilationUnitDeclaration[] unitsToProcess) { if (qualifiedName == null) { return null; } for (CompilationUnitDeclaration unitToProcess : unitsToProcess) { for (TypeDeclaration type : unitToProcess.types) { if (qualifiedName.equals(CharOperation.toString(type.binding.compoundName))) { return type.binding; } if (type.memberTypes != null) { for (TypeDeclaration memberType : type.memberTypes) { if (qualifiedName.equals(CharOperation.toString(memberType.binding.compoundName))) { return type.binding; } } } } } return null; } /** * Searches a type declared in imports. * * @param typeName * Expected type name. * @param imports * Search the type in imports. * @return qualified name of the expected type. */ static String searchType(String typeName, ImportReference[] imports) { if (typeName == null) { return null; } else if (imports == null) { return null; } for (ImportReference anImport : imports) { final String importType = CharOperation.charToString(anImport.getImportName()[anImport.getImportName().length - 1]); if (importType != null && importType.equals(typeName)) { return CharOperation.toString(anImport.getImportName()); } } return null; } /** * Searches a package used in units declared in a compilation unit. * * @param packageName * Package name. * @param unitsToProcess * Search the package expected in units. * @return import reference which correspond to the package expected. */ static ImportReference searchPackage(char[][] packageName, CompilationUnitDeclaration[] unitsToProcess) { for (CompilationUnitDeclaration unit : unitsToProcess) { final ImportReference currentPackage = unit.currentPackage; if (currentPackage == null) { continue; } final char[][] tokens = currentPackage.tokens; if (packageName.length > tokens.length) { continue; } boolean isFound = true; for (int i = 0; i < packageName.length; i++) { char[] chars = packageName[i]; if (!CharOperation.equals(chars, tokens[i])) { isFound = false; break; } } if (isFound) { return currentPackage; } } return null; } /** * Checks in an annotation if a given type is present. * * @param a * An annotation. * @param elementType * Expected element type of the annotation. * @return true if the annotation is compatible with the given element type. */ static boolean hasAnnotationWithType(Annotation a, CtAnnotatedElementType elementType) { if (a.resolvedType == null) { return false; } for (AnnotationBinding annotation : a.resolvedType.getAnnotations()) { if (!"Target".equals(CharOperation.charToString(annotation.getAnnotationType().sourceName()))) { continue; } Object value = annotation.getElementValuePairs()[0].value; if (value == null || !value.getClass().isArray()) { continue; } Object[] fields = (Object[]) value; for (Object field : fields) { if (field instanceof FieldBinding && elementType.name().equals(CharOperation.charToString(((FieldBinding) field).name))) { return true; } } } return false; } /** * Checks if the qualified name reference is a problem field binding and have a valid field. * * @param qualifiedNameReference * Reference which should contains a problem field binding. * @return true if the qualified name reference is a valid problem field binding. */ static boolean isValidProblemBindingField(QualifiedNameReference qualifiedNameReference) { return qualifiedNameReference.binding instanceof ProblemFieldBinding && !((FieldBinding) qualifiedNameReference.binding).declaringClass.isAnonymousType() && qualifiedNameReference.tokens.length - 1 == ((FieldBinding) qualifiedNameReference.binding).declaringClass.compoundName.length && CharOperation .equals(CharOperation.subarray(qualifiedNameReference.tokens, 0, qualifiedNameReference.tokens.length - 1), ((FieldBinding) qualifiedNameReference.binding).declaringClass.compoundName); } /** * Checks if the last node in the stack in the context is an assignment and have a lhs equals to the given expression. * * @param context * Context of the {@link JDTTreeBuilder}. * @param lhs * Potential lhs of the assignment. * @return true if the lhs is equals to the given expression. */ static boolean isLhsAssignment(ContextBuilder context, Expression lhs) { return context.stack.peek().node instanceof Assignment && ((Assignment) context.stack.peek().node).lhs.equals(lhs); } /** * Converts the unary operator from JDT to Spoon. * * @param operator * Identifier of the unary operator. * @return enum value of {@link UnaryOperatorKind}. */ static UnaryOperatorKind getUnaryOperator(int operator) { switch (operator) { case OperatorIds.PLUS: return UnaryOperatorKind.POS; case OperatorIds.MINUS: return UnaryOperatorKind.NEG; case OperatorIds.NOT: return UnaryOperatorKind.NOT; case OperatorIds.TWIDDLE: return UnaryOperatorKind.COMPL; } return null; } /** * Converts the binary operator from JDT to Spoon. * * @param operator * Identifier of the binary operator. * @return enum value of {@link BinaryOperatorKind}. */ static BinaryOperatorKind getBinaryOperatorKind(int operator) { switch (operator) { case OperatorIds.EQUAL_EQUAL: return BinaryOperatorKind.EQ; case OperatorIds.LESS_EQUAL: return BinaryOperatorKind.LE; case OperatorIds.GREATER_EQUAL: return BinaryOperatorKind.GE; case OperatorIds.NOT_EQUAL: return BinaryOperatorKind.NE; case OperatorIds.LEFT_SHIFT: return BinaryOperatorKind.SL; case OperatorIds.RIGHT_SHIFT: return BinaryOperatorKind.SR; case OperatorIds.UNSIGNED_RIGHT_SHIFT: return BinaryOperatorKind.USR; case OperatorIds.OR_OR: return BinaryOperatorKind.OR; case OperatorIds.AND_AND: return BinaryOperatorKind.AND; case OperatorIds.PLUS: return BinaryOperatorKind.PLUS; case OperatorIds.MINUS: return BinaryOperatorKind.MINUS; case OperatorIds.NOT: return BinaryOperatorKind.NE; case OperatorIds.REMAINDER: return BinaryOperatorKind.MOD; case OperatorIds.XOR: return BinaryOperatorKind.BITXOR; case OperatorIds.AND: return BinaryOperatorKind.BITAND; case OperatorIds.MULTIPLY: return BinaryOperatorKind.MUL; case OperatorIds.OR: return BinaryOperatorKind.BITOR; case OperatorIds.DIVIDE: return BinaryOperatorKind.DIV; case OperatorIds.GREATER: return BinaryOperatorKind.GT; case OperatorIds.LESS: return BinaryOperatorKind.LT; case OperatorIds.QUESTIONCOLON: throw new RuntimeException("Unknown operator"); case OperatorIds.EQUAL: return BinaryOperatorKind.EQ; } return null; } /** * Converts the modifier from JDT to Spoon. * * @param modifier * Identifier of the modifier. * @return Set of enum value of {@link ModifierKind}. */ static Set<ModifierKind> getModifiers(int modifier) { Set<ModifierKind> modifiers = EnumSet.noneOf(ModifierKind.class); if ((modifier & ClassFileConstants.AccPublic) != 0) { modifiers.add(ModifierKind.PUBLIC); } if ((modifier & ClassFileConstants.AccPrivate) != 0) { modifiers.add(ModifierKind.PRIVATE); } if ((modifier & ClassFileConstants.AccProtected) != 0) { modifiers.add(ModifierKind.PROTECTED); } if ((modifier & ClassFileConstants.AccStatic) != 0) { modifiers.add(ModifierKind.STATIC); } if ((modifier & ClassFileConstants.AccFinal) != 0) { modifiers.add(ModifierKind.FINAL); } if ((modifier & ClassFileConstants.AccSynchronized) != 0) { modifiers.add(ModifierKind.SYNCHRONIZED); } if ((modifier & ClassFileConstants.AccVolatile) != 0) { modifiers.add(ModifierKind.VOLATILE); } if ((modifier & ClassFileConstants.AccTransient) != 0) { modifiers.add(ModifierKind.TRANSIENT); } if ((modifier & ClassFileConstants.AccAbstract) != 0) { modifiers.add(ModifierKind.ABSTRACT); } if ((modifier & ClassFileConstants.AccStrictfp) != 0) { modifiers.add(ModifierKind.STRICTFP); } if ((modifier & ClassFileConstants.AccNative) != 0) { modifiers.add(ModifierKind.NATIVE); } return modifiers; } }