AbstractTypeDeclaration.java
/*
* Copyright (C) 2015-2016 Federico Tomassetti
* Copyright (C) 2017-2024 The JavaParser Team.
*
* This file is part of JavaParser.
*
* JavaParser can be used either under the terms of
* a) the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* b) the terms of the Apache License
*
* You should have received a copy of both licenses in LICENCE.LGPL and
* LICENCE.APACHE. Please refer to those files for details.
*
* JavaParser 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
* GNU Lesser General Public License for more details.
*/
package com.github.javaparser.symbolsolver.logic;
import com.github.javaparser.resolution.MethodUsage;
import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
import com.github.javaparser.resolution.logic.FunctionalInterfaceLogic;
import com.github.javaparser.resolution.types.ResolvedReferenceType;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.utils.Log;
import com.github.javaparser.utils.Pair;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Common ancestor for most types.
*
* @author Federico Tomassetti
*/
public abstract class AbstractTypeDeclaration implements ResolvedReferenceTypeDeclaration {
/*
* Returns all methods which have distinct "enhanced" signature declared in this type and all members.
* An "enhanced" signature include the return type which is used sometimes to identify functional interfaces.
* This is a different implementation from the previous one which returned all methods which have a distinct
* signature (based on method name and qualified parameter types)
*/
@Override
public final Set<MethodUsage> getAllMethods() {
Set<MethodUsage> methods = new HashSet<>();
Set<String> methodsSignatures = new HashSet<>();
for (ResolvedMethodDeclaration methodDeclaration : getDeclaredMethods()) {
MethodUsage methodUsage = new MethodUsage(methodDeclaration);
methods.add(methodUsage);
String signature = methodUsage.getSignature();
String returnType = methodUsage.getDeclaration().getReturnType().describe();
String enhancedSignature = String.format("%s %s", returnType, signature);
methodsSignatures.add(enhancedSignature);
}
for (ResolvedReferenceType ancestor : getAllAncestors()) {
List<Pair<ResolvedTypeParameterDeclaration, ResolvedType>> typeParametersMap =
ancestor.getTypeParametersMap();
for (MethodUsage mu : ancestor.getDeclaredMethods()) {
// replace type parameters to be able to filter away overridden generified methods
MethodUsage methodUsage = mu;
for (Pair<ResolvedTypeParameterDeclaration, ResolvedType> p : typeParametersMap) {
methodUsage = methodUsage.replaceTypeParameter(p.a, p.b);
}
String signature = methodUsage.getSignature();
String returnType = methodUsage.getDeclaration().getReturnType().describe();
String enhancedSignature = String.format("%s %s", returnType, signature);
if (!methodsSignatures.contains(enhancedSignature)) {
methodsSignatures.add(enhancedSignature);
methods.add(mu);
}
}
}
return methods;
}
@Override
public final boolean isFunctionalInterface() {
return FunctionalInterfaceLogic.getFunctionalMethod(this).isPresent();
}
/**
* With the introduction of records in Java 14 (Preview), the {@code Class.isRecord} method
* was added to check whether a class is a record or not (similar to {@code isEnum} etc.).
* This method cannot be used directly in JavaParser, however, since it will not compile
* on Java versions 8-13 (or 15 if preview features aren't enabled) which are supported
* by the project.
*
* This workaround calls the {@code isRecord} method via reflection which compiles while still
* giving the expected answer. There are 2 cases to consider when this method is called:
*
* 1) JavaParser is invoked using a Java runtime which supports records
* In this case, the {@code isRecord} method exists, so invoking it will give the
* answer as usual.
*
* 2) JavaParser is invoked using an older Java runtime without record support
* In this case, the {@code isRecord} method does not exist, so attempting to invoke
* it will throw a {@code NoSuchMethodException}. This is not a problem since the
* classloader cannot load classes compiled by Java versions greater than that used
* to invoke JavaParser. This means that if JavaParser is invoked with a Java 8 runtime,
* for example, then no classes compiled with Java versions greater than 8 are supported,
* so no class loaded by the classloader could possibly be a record class since it could
* not be compiled in the first place. There may be an edge case here for classes compiled
* with Java 14/15 preview, but most likely these won't load either.
*
* In the case of an {@code NoSuchMethodException}, simply return false as the type could
* not be a record for the reason explained above.
*/
public static boolean isRecordType(Class<?> clazz) {
try {
Method isRecord = Class.class.getMethod("isRecord");
return (Boolean) isRecord.invoke(clazz);
} catch (NoSuchMethodException e) {
return false;
} catch (InvocationTargetException | IllegalAccessException e) {
// These exceptions should never be thrown since a known standard library function is
// being invoked, so if this happens something went wrong.
Log.error("Could not invoke isRecord on " + clazz.getName() + " due to " + e.getMessage());
return false;
}
}
}