/* * 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.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.model.typesystem.ReferenceTypeImpl; 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 JavassistInterfaceDeclaration extends AbstractTypeDeclaration implements ResolvedInterfaceDeclaration { private CtClass ctClass; private TypeSolver typeSolver; private JavassistTypeDeclarationAdapter javassistTypeDeclarationAdapter; @Override public String toString() { return "JavassistInterfaceDeclaration{" + "ctClass=" + ctClass.getName() + ", typeSolver=" + typeSolver + '}'; } public JavassistInterfaceDeclaration(CtClass ctClass, TypeSolver typeSolver) { if (!ctClass.isInterface()) { throw new IllegalArgumentException("Not an interface: " + ctClass.getName()); } this.ctClass = ctClass; this.typeSolver = typeSolver; this.javassistTypeDeclarationAdapter = new JavassistTypeDeclarationAdapter(ctClass, typeSolver); } @Override public List getInterfacesExtended() { try { return Arrays.stream(ctClass.getInterfaces()).map(i -> new JavassistInterfaceDeclaration(i, typeSolver)) .map(i -> new ReferenceTypeImpl(i, typeSolver)).collect(Collectors.toList()); } catch (NotFoundException e) { throw new RuntimeException(e); } } @Override public String getPackageName() { return ctClass.getPackageName(); } @Override public String getClassName() { String className = ctClass.getName().replace('$', '.'); if (getPackageName() != null) { return className.substring(getPackageName().length() + 1, className.length()); } return className; } @Override public String getQualifiedName() { return ctClass.getName().replace('$', '.'); } @Deprecated public Optional solveMethodAsUsage(String name, List argumentsTypes, TypeSolver typeSolver, Context invokationContext, List typeParameterValues) { return JavassistUtils.getMethodUsage(ctClass, name, argumentsTypes, typeSolver, invokationContext); } @Deprecated 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); } try { for (CtClass interfaze : ctClass.getInterfaces()) { SymbolReference ref = new JavassistInterfaceDeclaration(interfaze, 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); } @Override public boolean isAssignableBy(ResolvedType type) { throw new UnsupportedOperationException(); } @Override public List getAllFields() { return javassistTypeDeclarationAdapter.getDeclaredFields(); } @Override public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) { throw new UnsupportedOperationException(); } @Override public List getAncestors() { List ancestors = new ArrayList<>(); try { for (CtClass interfaze : ctClass.getInterfaces()) { ResolvedReferenceType superInterfaze = JavassistFactory.typeUsageFor(interfaze, typeSolver).asReferenceType(); ancestors.add(superInterfaze); } } catch (NotFoundException e) { throw new RuntimeException(e); } ancestors = ancestors.stream().filter(a -> a.getQualifiedName() != Object.class.getCanonicalName()) .collect(Collectors.toList()); ancestors.add(new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver)); return ancestors; } @Override public Set getDeclaredMethods() { return Arrays.stream(ctClass.getDeclaredMethods()) .map(m -> new JavassistMethodDeclaration(m, typeSolver)) .collect(Collectors.toSet()); } @Override public boolean hasDirectlyAnnotation(String canonicalName) { try { for (Object annotationRaw : ctClass.getAnnotations()) { if (annotationRaw.getClass().getCanonicalName().equals(canonicalName)) { return true; } if (Arrays.stream(annotationRaw.getClass().getInterfaces()).anyMatch(it -> it.getCanonicalName().equals(canonicalName))) { return true; } } } catch (ClassNotFoundException e) { throw new RuntimeException(e); } return false; } @Override public String getName() { String[] nameElements = ctClass.getSimpleName().replace('$', '.').split("\\."); return nameElements[nameElements.length - 1]; } @Override public List getTypeParameters() { return javassistTypeDeclarationAdapter.getTypeParameters(); } @Override public AccessSpecifier accessSpecifier() { return JavassistFactory.modifiersToAccessLevel(ctClass.getModifiers()); } @Override public ResolvedInterfaceDeclaration asInterface() { return this; } @Deprecated 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 Optional containerType() { return javassistTypeDeclarationAdapter.containerType(); } @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)); } }