All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.checkerframework.framework.stub.AnnotationFileUtil Maven / Gradle / Ivy

package org.checkerframework.framework.stub;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.StubUnit;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.ConstructorDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.PrimitiveType;
import com.github.javaparser.ast.type.VoidType;
import com.github.javaparser.ast.type.WildcardType;
import com.github.javaparser.ast.visitor.SimpleVoidVisitor;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.signature.qual.FullyQualifiedName;
import org.checkerframework.javacutil.BugInCF;
import org.checkerframework.javacutil.ElementUtils;
import org.plumelib.util.IPair;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Types;

/** Utility class for annotation files (stub files and ajava files). */
public class AnnotationFileUtil {
    /**
     * The types of files that can contain annotations. Also indicates the file's source, such as
     * from the JDK, built in, or from the command line.
     *
     * 

Stub files have extension ".astub". Ajava files have extension ".ajava". */ public enum AnnotationFileType { /** Stub file in the annotated JDK. */ JDK_STUB, /** Stub file built into a checker. */ BUILTIN_STUB, /** Stub file provided on command line. */ COMMAND_LINE_STUB, /** Ajava file being parsed as if it is a stub file. */ AJAVA_AS_STUB, /** Ajava file provided on command line. */ AJAVA; /** * Returns true if this represents a stub file. * * @return true if this represents a stub file */ public boolean isStub() { switch (this) { case JDK_STUB: case BUILTIN_STUB: case COMMAND_LINE_STUB: case AJAVA_AS_STUB: return true; case AJAVA: return false; default: throw new BugInCF("unhandled case " + this); } } /** * Returns true if this annotation file is built-in (not provided on the command line). * * @return true if this annotation file is built-in (not provided on the command line) */ public boolean isBuiltIn() { switch (this) { case JDK_STUB: case BUILTIN_STUB: return true; case COMMAND_LINE_STUB: case AJAVA_AS_STUB: case AJAVA: return false; default: throw new BugInCF("unhandled case " + this); } } /** * Returns true if this annotation file was provided on the command line (not built-in). * * @return true if this annotation file was provided on the command line (not built-in) */ public boolean isCommandLine() { switch (this) { case JDK_STUB: case BUILTIN_STUB: return false; case COMMAND_LINE_STUB: case AJAVA_AS_STUB: case AJAVA: return true; default: throw new BugInCF("unhandled case " + this); } } } /** * Finds the type declaration with the given class name in a StubUnit. * * @param className fully qualified name of the type declaration to find * @param indexFile a StubUnit to search * @return the declaration in {@code indexFile} with {@code className} if it exists, null * otherwise. */ /*package-private*/ static @Nullable TypeDeclaration findDeclaration( String className, StubUnit indexFile) { int indexOfDot = className.lastIndexOf('.'); if (indexOfDot == -1) { // classes not within a package needs to be the first in the index file CompilationUnit cu = indexFile.getCompilationUnits().get(0); assert !cu.getPackageDeclaration().isPresent(); return findDeclaration(className, cu); } String packageName = className.substring(0, indexOfDot); String simpleName = className.substring(indexOfDot + 1); for (CompilationUnit cu : indexFile.getCompilationUnits()) { if (cu.getPackageDeclaration().isPresent() && cu.getPackageDeclaration().get().getNameAsString().equals(packageName)) { TypeDeclaration type = findDeclaration(simpleName, cu); if (type != null) { return type; } } } // Couldn't find it return null; } /*package-private*/ static TypeDeclaration findDeclaration( TypeElement type, StubUnit indexFile) { return findDeclaration(type.getQualifiedName().toString(), indexFile); } /*package-private*/ static @Nullable FieldDeclaration findDeclaration( VariableElement field, StubUnit indexFile) { TypeDeclaration type = findDeclaration((TypeElement) field.getEnclosingElement(), indexFile); if (type == null) { return null; } for (BodyDeclaration member : type.getMembers()) { if (!(member instanceof FieldDeclaration)) { continue; } FieldDeclaration decl = (FieldDeclaration) member; for (VariableDeclarator var : decl.getVariables()) { if (toString(var).equals(field.getSimpleName().toString())) { return decl; } } } return null; } /*package-private*/ static @Nullable BodyDeclaration findDeclaration( ExecutableElement method, StubUnit indexFile) { TypeDeclaration type = findDeclaration((TypeElement) method.getEnclosingElement(), indexFile); if (type == null) { return null; } String methodRep = toString(method); for (BodyDeclaration member : type.getMembers()) { if (member instanceof MethodDeclaration) { if (toString((MethodDeclaration) member).equals(methodRep)) { return member; } } else if (member instanceof ConstructorDeclaration) { if (toString((ConstructorDeclaration) member).equals(methodRep)) { return member; } } } return null; } /*package-private*/ static @Nullable TypeDeclaration findDeclaration( String simpleName, CompilationUnit cu) { for (TypeDeclaration type : cu.getTypes()) { if (simpleName.equals(type.getNameAsString())) { return type; } } // Couldn't find it return null; } /*package-private*/ static String toString(MethodDeclaration method) { return ElementPrinter.toString(method); } /*package-private*/ static String toString(ConstructorDeclaration constructor) { return ElementPrinter.toString(constructor); } /*package-private*/ static String toString(VariableDeclarator field) { return field.getNameAsString(); } /*package-private*/ static String toString(FieldDeclaration field) { assert field.getVariables().size() == 1; return toString(field.getVariables().get(0)); } /*package-private*/ static String toString(VariableElement element) { assert element.getKind().isField(); return element.getSimpleName().toString(); } /*package-private*/ static @Nullable String toString(Element element) { if (element instanceof ExecutableElement) { return toString((ExecutableElement) element); } else if (element instanceof VariableElement) { return toString((VariableElement) element); } else { return null; } } /** * Split a name (which comes from an import statement) into the part before the last period and * the part after the last period. * * @param imported the name to split * @return a pair of the type name and the field name */ @SuppressWarnings("signature") // string parsing public static IPair<@FullyQualifiedName String, String> partitionQualifiedName( String imported) { @FullyQualifiedName String typeName = imported.substring(0, imported.lastIndexOf(".")); String name = imported.substring(imported.lastIndexOf(".") + 1); IPair typeParts = IPair.of(typeName, name); return typeParts; } private static final class ElementPrinter extends SimpleVoidVisitor { public static String toString(Node n) { ElementPrinter printer = new ElementPrinter(); n.accept(printer, null); return printer.getOutput(); } private final StringBuilder sb = new StringBuilder(); public String getOutput() { return sb.toString(); } @Override public void visit(ConstructorDeclaration n, Void arg) { sb.append(""); sb.append("("); if (n.getParameters() != null) { for (Iterator i = n.getParameters().iterator(); i.hasNext(); ) { Parameter p = i.next(); p.accept(this, arg); if (i.hasNext()) { sb.append(","); } } } sb.append(")"); } @Override public void visit(MethodDeclaration n, Void arg) { sb.append(n.getName()); sb.append("("); if (n.getParameters() != null) { for (Iterator i = n.getParameters().iterator(); i.hasNext(); ) { Parameter p = i.next(); p.accept(this, arg); if (i.hasNext()) { sb.append(","); } } } sb.append(")"); } @Override public void visit(Parameter n, Void arg) { n.getType().accept(this, arg); if (n.isVarArgs()) { sb.append("[]"); } } // Types @Override public void visit(ClassOrInterfaceType n, Void arg) { sb.append(n.getName()); } @Override public void visit(PrimitiveType n, Void arg) { switch (n.getType()) { case BOOLEAN: sb.append("boolean"); break; case BYTE: sb.append("byte"); break; case CHAR: sb.append("char"); break; case DOUBLE: sb.append("double"); break; case FLOAT: sb.append("float"); break; case INT: sb.append("int"); break; case LONG: sb.append("long"); break; case SHORT: sb.append("short"); break; default: throw new BugInCF("AnnotationFileUtil: unknown type: " + n.getType()); } } @Override public void visit(com.github.javaparser.ast.type.ArrayType n, Void arg) { n.getComponentType().accept(this, arg); sb.append("[]"); } @Override public void visit(VoidType n, Void arg) { sb.append("void"); } @Override public void visit(WildcardType n, Void arg) { // We don't write type arguments // TODO: Why? throw new BugInCF("AnnotationFileUtil: don't print type args"); } } /** * Return annotation files found at a given file system location (does not look on classpath). * * @param location an annotation file (stub file or ajava file), a jarfile, or a directory. Look * for it as an absolute file and relative to the current directory. * @param fileType file type of files to collect * @return annotation files with the given file type found in the file system (does not look on * classpath). Returns null if the file system location does not exist; the caller may wish * to issue a warning in that case. */ public static @Nullable List allAnnotationFiles( String location, AnnotationFileType fileType) { File file = new File(location); if (file.exists()) { List resources = new ArrayList<>(); addAnnotationFilesToList(file, resources, fileType); return resources; } // The file doesn't exist. Maybe it is relative to the current working directory, so try // that. file = new File(System.getProperty("user.dir"), location); if (file.exists()) { List resources = new ArrayList<>(); addAnnotationFilesToList(file, resources, fileType); return resources; } return null; } /** * Returns true if the given file is an annotation file of the given type. * * @param f the file to check * @param fileType the type of file to check against * @return true if {@code f} is a file with file type matching {@code fileType}, false otherwise */ private static boolean isAnnotationFile(File f, AnnotationFileType fileType) { return f.isFile() && isAnnotationFile(f.getName(), fileType); } /** * Returns true if the given file is an annotation file of the given kind. * * @param path a file * @param fileType the type of file to check against * @return true if {@code path} represents a file with file type matching {@code fileType}, * false otherwise */ private static boolean isAnnotationFile(String path, AnnotationFileType fileType) { return path.endsWith(fileType.isStub() ? ".astub" : ".ajava"); } private static boolean isJar(File f) { return f.isFile() && f.getName().endsWith(".jar"); } /** * Side-effects {@code resources} by adding annotation files of the given file type to it. * * @param location an annotation file (a stub file or ajava file), a jarfile, or a directory. If * a stub file or ajava file, add it to the {@code resources} list. If a jarfile, use all * annotation files (of type {@code fileType}) contained in it. If a directory, recurse on * all files contained in it. * @param resources the list to add the found files to * @param fileType type of annotation files to add */ @SuppressWarnings({ "JdkObsolete", // JarFile.entries() "nullness:argument", // inference failed in Arrays.sort "builder:required.method.not.called" // ownership passed to list of // JarEntryAnnotationFileResource, where `file` appears in every element of the list }) private static void addAnnotationFilesToList( File location, List resources, AnnotationFileType fileType) { if (isAnnotationFile(location, fileType)) { resources.add(new FileAnnotationFileResource(location)); } else if (isJar(location)) { JarFile file; try { file = new JarFile(location); } catch (IOException e) { System.err.println("AnnotationFileUtil: could not process JAR file: " + location); return; } Enumeration entries = file.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (isAnnotationFile(entry.getName(), fileType)) { resources.add(new JarEntryAnnotationFileResource(file, entry)); } } } else if (location.isDirectory()) { File[] directoryContents = location.listFiles(); Arrays.sort(directoryContents, Comparator.comparing(File::getName)); for (File enclosed : directoryContents) { addAnnotationFilesToList(enclosed, resources, fileType); } } } /** * Returns true if the given {@link ExecutableElement} is the canonical constructor of a record * (i.e., the parameter types of the constructor correspond to the parameter types of the record * components, ignoring annotations). * * @param elt the constructor/method to check * @param types the Types instance to use for comparing types * @return true if elt is the canonical constructor of the record containing it */ public static boolean isCanonicalConstructor(ExecutableElement elt, Types types) { if (elt.getKind() != ElementKind.CONSTRUCTOR) { return false; } TypeElement enclosing = (TypeElement) elt.getEnclosingElement(); if (!ElementUtils.isRecordElement(enclosing)) { return false; } List recordComponents = ElementUtils.getRecordComponents(enclosing); if (recordComponents.size() == elt.getParameters().size()) { for (int i = 0; i < recordComponents.size(); i++) { if (!types.isSameType( recordComponents.get(i).asType(), elt.getParameters().get(i).asType())) { return false; } } return true; } return false; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy