/* * Copyright 2016 Federico Tomassetti * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.javaparser.symbolsolver.javassistmodel; import com.github.javaparser.ast.AccessSpecifier; import com.github.javaparser.ast.expr.MethodCallExpr; import com.github.javaparser.resolution.MethodUsage; import com.github.javaparser.resolution.UnsolvedSymbolException; import com.github.javaparser.resolution.declarations.*; import com.github.javaparser.resolution.types.ResolvedReferenceType; import com.github.javaparser.resolution.types.ResolvedType; import com.github.javaparser.symbolsolver.core.resolution.Context; import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration; import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic; import com.github.javaparser.symbolsolver.resolution.SymbolSolver; import javassist.CtClass; import javassist.CtField; import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.SyntheticAttribute; import java.lang.reflect.Modifier; import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; /** * @author Federico Tomassetti */ public class JavassistEnumDeclaration extends AbstractTypeDeclaration implements ResolvedEnumDeclaration { private CtClass ctClass; private TypeSolver typeSolver; private JavassistTypeDeclarationAdapter javassistTypeDeclarationAdapter; public JavassistEnumDeclaration(CtClass ctClass, TypeSolver typeSolver) { if (ctClass == null) { throw new IllegalArgumentException(); } if (!ctClass.isEnum()) { throw new IllegalArgumentException("Trying to instantiate a JavassistEnumDeclaration with something which is not an enum: " + ctClass.toString()); } this.ctClass = ctClass; this.typeSolver = typeSolver; this.javassistTypeDeclarationAdapter = new JavassistTypeDeclarationAdapter(ctClass, typeSolver); } @Override public AccessSpecifier accessSpecifier() { return JavassistFactory.modifiersToAccessLevel(ctClass.getModifiers()); } @Override public String getPackageName() { return ctClass.getPackageName(); } @Override public String getClassName() { String name = ctClass.getName().replace('$', '.'); if (getPackageName() != null) { return name.substring(getPackageName().length() + 1, name.length()); } return name; } @Override public String getQualifiedName() { return ctClass.getName().replace('$', '.'); } @Override public List getAncestors() { // Direct ancestors of an enum are java.lang.Enum and interfaces List ancestors = new LinkedList<>(); try { CtClass superClass = ctClass.getSuperclass(); if (superClass != null) { ResolvedType superClassTypeUsage = JavassistFactory.typeUsageFor(superClass, typeSolver); if (superClassTypeUsage.isReferenceType()) { ancestors.add(superClassTypeUsage.asReferenceType()); } } for (CtClass interfaze : ctClass.getInterfaces()) { ResolvedType interfazeTypeUsage = JavassistFactory.typeUsageFor(interfaze, typeSolver); if (interfazeTypeUsage.isReferenceType()) { ancestors.add(interfazeTypeUsage.asReferenceType()); } } } catch (NotFoundException e) { throw new RuntimeException("Ancestor not found for " + ctClass.getName() + ".", e); } return ancestors; } @Override public ResolvedFieldDeclaration getField(String name) { Optional field = javassistTypeDeclarationAdapter.getDeclaredFields().stream().filter(f -> f.getName().equals(name)).findFirst(); return field.orElseThrow(() -> new RuntimeException("Field " + name + " does not exist in " + ctClass.getName() + ".")); } @Override public boolean hasField(String name) { return javassistTypeDeclarationAdapter.getDeclaredFields().stream().anyMatch(f -> f.getName().equals(name)); } @Override public List getAllFields() { return javassistTypeDeclarationAdapter.getDeclaredFields(); } @Override public Set getDeclaredMethods() { return javassistTypeDeclarationAdapter.getDeclaredMethods(); } @Override public boolean isAssignableBy(ResolvedType type) { throw new UnsupportedOperationException(); } @Override public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) { throw new UnsupportedOperationException(); } @Override public boolean hasDirectlyAnnotation(String canonicalName) { throw new UnsupportedOperationException(); } @Override public String getName() { String[] nameElements = ctClass.getSimpleName().replace('$', '.').split("\\."); return nameElements[nameElements.length - 1]; } @Override public List getTypeParameters() { return javassistTypeDeclarationAdapter.getTypeParameters(); } @Override public Optional containerType() { return javassistTypeDeclarationAdapter.containerType(); } public SymbolReference solveMethod(String name, List argumentsTypes, boolean staticOnly) { List candidates = new ArrayList<>(); Predicate staticOnlyCheck = m -> !staticOnly || (staticOnly && Modifier.isStatic(m.getModifiers())); for (CtMethod method : ctClass.getDeclaredMethods()) { boolean isSynthetic = method.getMethodInfo().getAttribute(SyntheticAttribute.tag) != null; boolean isNotBridge = (method.getMethodInfo().getAccessFlags() & AccessFlag.BRIDGE) == 0; if (method.getName().equals(name) && !isSynthetic && isNotBridge && staticOnlyCheck.test(method)) { candidates.add(new JavassistMethodDeclaration(method, typeSolver)); } } try { CtClass superClass = ctClass.getSuperclass(); if (superClass != null) { SymbolReference ref = new JavassistClassDeclaration(superClass, typeSolver).solveMethod(name, argumentsTypes, staticOnly); if (ref.isSolved()) { candidates.add(ref.getCorrespondingDeclaration()); } } } catch (NotFoundException e) { throw new RuntimeException(e); } return MethodResolutionLogic.findMostApplicable(candidates, name, argumentsTypes, typeSolver); } public Optional solveMethodAsUsage(String name, List argumentsTypes, TypeSolver typeSolver, Context invokationContext, List typeParameterValues) { return JavassistUtils.getMethodUsage(ctClass, name, argumentsTypes, typeSolver, invokationContext); } @Override public Set internalTypes() { try { /* Get all internal types of the current class and get their corresponding ReferenceTypeDeclaration. Finally, return them in a Set. */ return Arrays.stream(ctClass.getDeclaredClasses()).map(itype -> JavassistFactory.toTypeDeclaration(itype, typeSolver)).collect(Collectors.toSet()); } catch (NotFoundException e) { throw new RuntimeException(e); } } @Override public ResolvedReferenceTypeDeclaration getInternalType(String name) { /* The name of the ReferenceTypeDeclaration could be composed on the internal class and the outer class, e.g. A$B. That's why we search the internal type in the ending part. In case the name is composed of the internal type only, i.e. f.getName() returns B, it will also works. */ Optional type = this.internalTypes().stream().filter(f -> f.getName().endsWith(name)).findFirst(); return type.orElseThrow(() -> new UnsolvedSymbolException("Internal type not found: " + name)); } @Override public boolean hasInternalType(String name) { /* The name of the ReferenceTypeDeclaration could be composed on the internal class and the outer class, e.g. A$B. That's why we search the internal type in the ending part. In case the name is composed of the internal type only, i.e. f.getName() returns B, it will also works. */ return this.internalTypes().stream().anyMatch(f -> f.getName().endsWith(name)); } public SymbolReference solveSymbol(String name, TypeSolver typeSolver) { for (CtField field : ctClass.getDeclaredFields()) { if (field.getName().equals(name)) { return SymbolReference.solved(new JavassistFieldDeclaration(field, typeSolver)); } } String[] interfaceFQNs = getInterfaceFQNs(); for (String interfaceFQN : interfaceFQNs) { SymbolReference interfaceRef = solveSymbolForFQN(name, typeSolver, interfaceFQN); if (interfaceRef.isSolved()) { return interfaceRef; } } return SymbolReference.unsolved(ResolvedValueDeclaration.class); } private SymbolReference solveSymbolForFQN(String symbolName, TypeSolver typeSolver, String fqn) { if (fqn == null) { return SymbolReference.unsolved(ResolvedValueDeclaration.class); } ResolvedReferenceTypeDeclaration fqnTypeDeclaration = typeSolver.solveType(fqn); return new SymbolSolver(typeSolver).solveSymbolInType(fqnTypeDeclaration, symbolName); } private String[] getInterfaceFQNs() { return ctClass.getClassFile().getInterfaces(); } @Override public List getEnumConstants() { return Arrays.stream(ctClass.getFields()) .filter(f -> (f.getFieldInfo2().getAccessFlags() & AccessFlag.ENUM) != 0) .map(f -> new JavassistEnumConstantDeclaration(f, typeSolver)) .collect(Collectors.toList()); } }