tools/javac/Api.java (147 lines of code) (raw):

/* * Copyright 2000-2024 JetBrains s.r.o. * * 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. */ import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import java.io.Serial; import java.io.Serializable; import java.util.*; /** * Serialized version of the module API. */ public class Api implements Serializable { @Serial private static final long serialVersionUID = 1L; public final HashMap<Type, Type> types = new HashMap<>(); public static class Module extends Api implements Serializable { @Serial private static final long serialVersionUID = 1L; public Version version; public int hash; } public static class Type extends Api implements Serializable { @Serial private static final long serialVersionUID = 1L; public final Api parent; public final String qualifiedName; public final EnumSet<Modifier> modifiers = EnumSet.noneOf(Modifier.class); public ElementKind kind; public final HashSet<String> supertypes = new HashSet<>(); // Recursive public TypeParameter[] typeParameters; public Deprecation deprecation; public Usage usage; public final HashMap<Field, Field> fields = new HashMap<>(); public final HashMap<Method, Method> methods = new HashMap<>(); public Type(Api parent, String qualifiedName) { this.parent = parent; this.qualifiedName = qualifiedName; } // Type is distinguishable by its name, so use it in equals and hashCode @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; return qualifiedName.equals(((Type) o).qualifiedName); } @Override public int hashCode() { return qualifiedName.hashCode(); } @Override public String toString() { return qualifiedName; } } public static class Field implements Serializable { @Serial private static final long serialVersionUID = 1L; public final Type parent; public final String name; public final EnumSet<Modifier> modifiers = EnumSet.noneOf(Modifier.class); public String type; public Serializable constantValue; public Deprecation deprecation; public Field(Type parent, String name) { this.parent = parent; this.name = name; } // Field is distinguishable by its name, so use it in equals and hashCode @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; return name.equals(((Field) o).name); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return type + " " + name; } } public static class Method implements Serializable { @Serial private static final long serialVersionUID = 1L; public final Type parent; public final String name; public final String[] parameterTypes; public final EnumSet<Modifier> modifiers = EnumSet.noneOf(Modifier.class); public String returnType; public HashSet<String> thrownTypes; public TypeParameter[] typeParameters; public Deprecation deprecation; public String extension; public Method(Type parent, String name, String[] parameterTypes) { this.parent = parent; this.name = name; this.parameterTypes = parameterTypes; } // Method is distinguishable by its signature, so use it in equals and hashCode @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Method methodApi = (Method) o; if (!name.equals(methodApi.name)) return false; return Arrays.equals(parameterTypes, methodApi.parameterTypes); } @Override public int hashCode() { int result = name.hashCode(); result = 31 * result + Arrays.hashCode(parameterTypes); return result; } @Override public String toString() { return name + "(" + String.join(", ", parameterTypes) + ")"; } } public record TypeParameter(String name, HashSet<String> bounds) implements Serializable {} enum Deprecation { NONE, DEPRECATED, FOR_REMOVAL } enum Usage { NONE(false, false), // No annotations. Non-annotated API types must either be final, or be inherited by some other type. SERVICE(true, false), // @Service & @Provided PROVIDED(true, false), // @Provided PROVIDES(false, true), // @Provides TWO_WAY(true, true); // @Provided & @Provides public final boolean inheritableByBackend, inheritableByClient; Usage(boolean inheritableByBackend, boolean inheritableByClient) { this.inheritableByBackend = inheritableByBackend; this.inheritableByClient = inheritableByClient; } } public record Version(int major, int minor, int patch) implements Serializable { public static Version parse(String value) { String[] c = value.split("\\."); if (c.length != 3) throw new IllegalArgumentException("Invalid version format"); return new Version(parseComponent(c[0]), parseComponent(c[1]), parseComponent(c[2])); } private static int parseComponent(String value) { try { if (value.length() > 0 && value.charAt(0) != '+') return Integer.parseUnsignedInt(value); } catch (NumberFormatException ignore) {} throw new IllegalArgumentException("Invalid version component: " + value); } @Override public String toString() { return major + "." + minor + "." + patch; } } }