blob: bb5de2390f8805e89b56c953a430edee39bd0334 [file] [log] [blame]
// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.keepanno.utils;
import static com.android.tools.r8.references.Reference.classFromClass;
import static com.android.tools.r8.references.Reference.classFromTypeName;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.cfmethodgeneration.CodeGenerationBase;
import com.android.tools.r8.keepanno.annotations.KeepItemKind;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.StringUtils.BraceType;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class KeepItemAnnotationGenerator {
public static void main(String[] args) throws IOException {
Generator.class.getClassLoader().setDefaultAssertionStatus(true);
Generator.run(
(file, content) -> {
try {
Files.write(file, content.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
public static class EnumReference {
public final ClassReference enumClass;
public final String enumValue;
public EnumReference(ClassReference enumClass, String enumValue) {
this.enumClass = enumClass;
this.enumValue = enumValue;
}
public String name() {
return enumValue;
}
}
private static final ClassReference JAVA_STRING = classFromClass(String.class);
private static final ClassReference JAVA_RETENTION_POLICY = classFromClass(RetentionPolicy.class);
private static final String PKG = "com.android.tools.r8.keepanno.";
private static final String AST_PKG = PKG + "ast.";
private static final String ANNO_PKG = PKG + "annotations.";
private static final ClassReference ANNOTATION_CONSTANTS = astClass("AnnotationConstants");
static final ClassReference STRING_PATTERN = annoClass("StringPattern");
static final ClassReference TYPE_PATTERN = annoClass("TypePattern");
static final ClassReference CLASS_NAME_PATTERN = annoClass("ClassNamePattern");
static final ClassReference INSTANCE_OF_PATTERN = annoClass("InstanceOfPattern");
static final ClassReference ANNOTATION_PATTERN = annoClass("AnnotationPattern");
static final ClassReference USES_REFLECTION = annoClass("UsesReflection");
static final ClassReference USED_BY_REFLECTION = annoClass("UsedByReflection");
static final ClassReference USED_BY_NATIVE = annoClass("UsedByNative");
static final ClassReference CHECK_REMOVED = annoClass("CheckRemoved");
static final ClassReference CHECK_OPTIMIZED_OUT = annoClass("CheckOptimizedOut");
static final ClassReference EXTRACTED_KEEP_ANNOTATIONS = annoClass("ExtractedKeepAnnotations");
static final ClassReference EXTRACTED_KEEP_ANNOTATION = annoClass("ExtractedKeepAnnotation");
static final ClassReference KEEP_EDGE = annoClass("KeepEdge");
static final ClassReference KEEP_BINDING = annoClass("KeepBinding");
static final ClassReference KEEP_TARGET = annoClass("KeepTarget");
static final ClassReference KEEP_CONDITION = annoClass("KeepCondition");
static final ClassReference KEEP_FOR_API = annoClass("KeepForApi");
public static final ClassReference KEEP_ITEM_KIND = annoClass("KeepItemKind");
private static final EnumReference KIND_ONLY_CLASS = enumRef(KEEP_ITEM_KIND, "ONLY_CLASS");
private static final EnumReference KIND_ONLY_MEMBERS = enumRef(KEEP_ITEM_KIND, "ONLY_MEMBERS");
private static final EnumReference KIND_ONLY_METHODS = enumRef(KEEP_ITEM_KIND, "ONLY_METHODS");
private static final EnumReference KIND_ONLY_FIELDS = enumRef(KEEP_ITEM_KIND, "ONLY_FIELDS");
private static final EnumReference KIND_CLASS_AND_MEMBERS =
enumRef(KEEP_ITEM_KIND, "CLASS_AND_MEMBERS");
private static final EnumReference KIND_CLASS_AND_METHODS =
enumRef(KEEP_ITEM_KIND, "CLASS_AND_METHODS");
private static final EnumReference KIND_CLASS_AND_FIELDS =
enumRef(KEEP_ITEM_KIND, "CLASS_AND_FIELDS");
public static final List<EnumReference> KEEP_ITEM_KIND_VALUES =
ImmutableList.of(
KIND_ONLY_CLASS,
KIND_ONLY_MEMBERS,
KIND_ONLY_METHODS,
KIND_ONLY_FIELDS,
KIND_CLASS_AND_MEMBERS,
KIND_CLASS_AND_METHODS,
KIND_CLASS_AND_FIELDS);
static final ClassReference KEEP_CONSTRAINT = annoClass("KeepConstraint");
private static final EnumReference CONSTRAINT_LOOKUP = enumRef(KEEP_CONSTRAINT, "LOOKUP");
private static final EnumReference CONSTRAINT_NAME = enumRef(KEEP_CONSTRAINT, "NAME");
private static final EnumReference CONSTRAINT_VISIBILITY_RELAX =
enumRef(KEEP_CONSTRAINT, "VISIBILITY_RELAX");
private static final EnumReference CONSTRAINT_VISIBILITY_RESTRICT =
enumRef(KEEP_CONSTRAINT, "VISIBILITY_RESTRICT");
private static final EnumReference CONSTRAINT_VISIBILITY_INVARIANT =
enumRef(KEEP_CONSTRAINT, "VISIBILITY_INVARIANT");
private static final EnumReference CONSTRAINT_CLASS_INSTANTIATE =
enumRef(KEEP_CONSTRAINT, "CLASS_INSTANTIATE");
private static final EnumReference CONSTRAINT_METHOD_INVOKE =
enumRef(KEEP_CONSTRAINT, "METHOD_INVOKE");
private static final EnumReference CONSTRAINT_FIELD_GET = enumRef(KEEP_CONSTRAINT, "FIELD_GET");
private static final EnumReference CONSTRAINT_FIELD_SET = enumRef(KEEP_CONSTRAINT, "FIELD_SET");
private static final EnumReference CONSTRAINT_METHOD_REPLACE =
enumRef(KEEP_CONSTRAINT, "METHOD_REPLACE");
private static final EnumReference CONSTRAINT_FIELD_REPLACE =
enumRef(KEEP_CONSTRAINT, "FIELD_REPLACE");
private static final EnumReference CONSTRAINT_NEVER_INLINE =
enumRef(KEEP_CONSTRAINT, "NEVER_INLINE");
private static final EnumReference CONSTRAINT_CLASS_OPEN_HIERARCHY =
enumRef(KEEP_CONSTRAINT, "CLASS_OPEN_HIERARCHY");
static final List<EnumReference> KEEP_CONSTRAINT_VALUES =
ImmutableList.of(
CONSTRAINT_LOOKUP,
CONSTRAINT_NAME,
CONSTRAINT_VISIBILITY_RELAX,
CONSTRAINT_VISIBILITY_RESTRICT,
CONSTRAINT_VISIBILITY_INVARIANT,
CONSTRAINT_CLASS_INSTANTIATE,
CONSTRAINT_METHOD_INVOKE,
CONSTRAINT_FIELD_GET,
CONSTRAINT_FIELD_SET,
CONSTRAINT_METHOD_REPLACE,
CONSTRAINT_FIELD_REPLACE,
CONSTRAINT_NEVER_INLINE,
CONSTRAINT_CLASS_OPEN_HIERARCHY);
static final ClassReference MEMBER_ACCESS_FLAGS = annoClass("MemberAccessFlags");
private static final EnumReference MEMBER_ACCESS_PUBLIC = enumRef(MEMBER_ACCESS_FLAGS, "PUBLIC");
private static final EnumReference MEMBER_ACCESS_PROTECTED =
enumRef(MEMBER_ACCESS_FLAGS, "PROTECTED");
private static final EnumReference MEMBER_ACCESS_PACKAGE_PRIVATE =
enumRef(MEMBER_ACCESS_FLAGS, "PACKAGE_PRIVATE");
private static final EnumReference MEMBER_ACCESS_PRIVATE =
enumRef(MEMBER_ACCESS_FLAGS, "PRIVATE");
private static final EnumReference MEMBER_ACCESS_STATIC = enumRef(MEMBER_ACCESS_FLAGS, "STATIC");
private static final EnumReference MEMBER_ACCESS_FINAL = enumRef(MEMBER_ACCESS_FLAGS, "FINAL");
private static final EnumReference MEMBER_ACCESS_SYNTHETIC =
enumRef(MEMBER_ACCESS_FLAGS, "SYNTHETIC");
static final List<EnumReference> MEMBER_ACCESS_VALUES =
ImmutableList.of(
MEMBER_ACCESS_PUBLIC,
MEMBER_ACCESS_PROTECTED,
MEMBER_ACCESS_PACKAGE_PRIVATE,
MEMBER_ACCESS_PRIVATE,
MEMBER_ACCESS_STATIC,
MEMBER_ACCESS_FINAL,
MEMBER_ACCESS_SYNTHETIC);
static final ClassReference METHOD_ACCESS_FLAGS = annoClass("MethodAccessFlags");
private static final EnumReference METHOD_ACCESS_SYNCHRONIZED =
enumRef(METHOD_ACCESS_FLAGS, "SYNCHRONIZED");
private static final EnumReference METHOD_ACCESS_BRIDGE = enumRef(METHOD_ACCESS_FLAGS, "BRIDGE");
private static final EnumReference METHOD_ACCESS_NATIVE = enumRef(METHOD_ACCESS_FLAGS, "NATIVE");
private static final EnumReference METHOD_ACCESS_ABSTRACT =
enumRef(METHOD_ACCESS_FLAGS, "ABSTRACT");
private static final EnumReference METHOD_ACCESS_STRICT_FP =
enumRef(METHOD_ACCESS_FLAGS, "STRICT_FP");
static final List<EnumReference> METHOD_ACCESS_VALUES =
ImmutableList.of(
METHOD_ACCESS_SYNCHRONIZED,
METHOD_ACCESS_BRIDGE,
METHOD_ACCESS_NATIVE,
METHOD_ACCESS_ABSTRACT,
METHOD_ACCESS_STRICT_FP);
static final ClassReference FIELD_ACCESS_FLAGS = annoClass("FieldAccessFlags");
private static final EnumReference FIELD_ACCESS_VOLATILE =
enumRef(FIELD_ACCESS_FLAGS, "VOLATILE");
private static final EnumReference FIELD_ACCESS_TRANSIENT =
enumRef(FIELD_ACCESS_FLAGS, "TRANSIENT");
static final List<EnumReference> FIELD_ACCESS_VALUES =
ImmutableList.of(FIELD_ACCESS_VOLATILE, FIELD_ACCESS_TRANSIENT);
private static final String DEFAULT_INVALID_STRING_PATTERN =
"@" + simpleName(STRING_PATTERN) + "(exact = \"\")";
private static final String DEFAULT_INVALID_TYPE_PATTERN =
"@" + simpleName(TYPE_PATTERN) + "(name = \"\")";
private static final String DEFAULT_INVALID_CLASS_NAME_PATTERN =
"@" + simpleName(CLASS_NAME_PATTERN) + "(simpleName = \"\")";
private static final String DEFAULT_ANY_INSTANCE_OF_PATTERN =
"@" + simpleName(INSTANCE_OF_PATTERN) + "()";
private static ClassReference astClass(String simpleName) {
return classFromTypeName(AST_PKG + simpleName);
}
private static ClassReference annoClass(String simpleName) {
return classFromTypeName(ANNO_PKG + simpleName);
}
private static EnumReference enumRef(ClassReference enumClass, String valueName) {
return new EnumReference(enumClass, valueName);
}
public static String quote(String str) {
return "\"" + str + "\"";
}
public static String simpleName(ClassReference clazz) {
String binaryName = clazz.getBinaryName();
return binaryName.substring(binaryName.lastIndexOf('/') + 1);
}
public static class GroupMember extends DocPrinterBase<GroupMember> {
final String name;
String valueType = null;
String valueDefault = null;
GroupMember(String name) {
this.name = name;
}
public GroupMember setType(String type) {
valueType = type;
return this;
}
public GroupMember setValue(String value) {
valueDefault = value;
return this;
}
@Override
public GroupMember self() {
return this;
}
void generate(Generator generator) {
printDoc(generator::println);
if (isDeprecated()) {
generator.println("@Deprecated");
}
if (valueDefault == null) {
generator.println(valueType + " " + name + "();");
} else {
generator.println(valueType + " " + name + "() default " + valueDefault + ";");
}
}
public void generateConstants(Generator generator) {
generator.println("public static final String " + name + " = " + quote(name) + ";");
}
public GroupMember requiredValue(ClassReference type) {
assert valueDefault == null;
return setType(simpleName(type));
}
public GroupMember requiredArrayValue(ClassReference type) {
assert valueDefault == null;
return setType(simpleName(type) + "[]");
}
public GroupMember requiredStringValue() {
return requiredValue(JAVA_STRING);
}
public GroupMember defaultBooleanValue(boolean value) {
setType("boolean");
return setValue(value ? "true" : "false");
}
public GroupMember defaultValue(ClassReference type, String value) {
setType(simpleName(type));
return setValue(value);
}
public GroupMember defaultArrayValue(ClassReference type, String value) {
setType(simpleName(type) + "[]");
return setValue("{" + value + "}");
}
public GroupMember defaultEmptyString() {
return defaultValue(JAVA_STRING, quote(""));
}
public GroupMember defaultObjectClass() {
return setType("Class<?>").setValue("Object.class");
}
public GroupMember defaultArrayEmpty(ClassReference type) {
return defaultArrayValue(type, "");
}
}
public static class Group {
final String name;
final List<GroupMember> members = new ArrayList<>();
final List<String> footers = new ArrayList<>();
final LinkedHashMap<String, Group> mutuallyExclusiveGroups = new LinkedHashMap<>();
boolean mutuallyExclusiveWithOtherGroups = false;
private Group(String name) {
this.name = name;
}
Group allowMutuallyExclusiveWithOtherGroups() {
mutuallyExclusiveWithOtherGroups = true;
return this;
}
Group addMember(GroupMember member) {
members.add(member);
return this;
}
Group addDocFooterParagraph(String footer) {
footers.add(footer);
return this;
}
void generate(Generator generator) {
assert !members.isEmpty();
for (GroupMember member : members) {
if (member != members.get(0)) {
generator.println();
}
List<String> mutuallyExclusiveProperties = new ArrayList<>();
for (GroupMember other : members) {
if (!member.name.equals(other.name)) {
mutuallyExclusiveProperties.add(other.name);
}
}
mutuallyExclusiveGroups.forEach(
(unused, group) -> {
group.members.forEach(m -> mutuallyExclusiveProperties.add(m.name));
});
if (mutuallyExclusiveProperties.size() == 1) {
member.addParagraph(
"Mutually exclusive with the property `"
+ mutuallyExclusiveProperties.get(0)
+ "` also defining "
+ name
+ ".");
} else if (mutuallyExclusiveProperties.size() > 1) {
member.addParagraph(
"Mutually exclusive with the following other properties defining " + name + ":");
member.addUnorderedList(mutuallyExclusiveProperties);
}
footers.forEach(member::addParagraph);
member.generate(generator);
}
}
void generateConstants(Generator generator) {
if (mutuallyExclusiveWithOtherGroups || members.size() > 1) {
StringBuilder camelCaseName = new StringBuilder();
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (c == '-') {
c = Character.toUpperCase(name.charAt(++i));
}
camelCaseName.append(c);
}
generator.println(
"public static final String " + camelCaseName + "Group = " + quote(name) + ";");
}
for (GroupMember member : members) {
member.generateConstants(generator);
}
}
public void addMutuallyExclusiveGroups(Group... groups) {
for (Group group : groups) {
assert mutuallyExclusiveWithOtherGroups || group.mutuallyExclusiveWithOtherGroups;
mutuallyExclusiveGroups.computeIfAbsent(
group.name,
k -> {
// Mutually exclusive is bidirectional so link in with other group.
group.mutuallyExclusiveGroups.put(name, this);
return group;
});
}
}
}
public static class Generator {
private static final List<Class<?>> ANNOTATION_IMPORTS =
ImmutableList.of(ElementType.class, Retention.class, RetentionPolicy.class, Target.class);
private final PrintStream writer;
private int indent = 0;
public Generator(PrintStream writer) {
this.writer = writer;
}
public void withIndent(Runnable fn) {
indent += 2;
fn.run();
indent -= 2;
}
private void println() {
println("");
}
public void println(String line) {
// Don't indent empty lines.
if (line.length() > 0) {
writer.print(Strings.repeat(" ", indent));
}
writer.print(line);
writer.print('\n');
}
private void printCopyRight(int year) {
println(
CodeGenerationBase.getHeaderString(
year, KeepItemAnnotationGenerator.class.getSimpleName()));
}
private void printPackage(String pkg) {
println("package com.android.tools.r8.keepanno." + pkg + ";");
println();
}
private void printImports(Class<?>... imports) {
printImports(Arrays.asList(imports));
}
private void printImports(List<Class<?>> imports) {
for (Class<?> clazz : imports) {
println("import " + clazz.getCanonicalName() + ";");
}
println();
}
private static String KIND_GROUP = "kind";
private static String CONSTRAINTS_GROUP = "constraints";
private static String CLASS_GROUP = "class";
private static String CLASS_NAME_GROUP = "class-name";
private static String INSTANCE_OF_GROUP = "instance-of";
private static String CLASS_ANNOTATED_BY_GROUP = "class-annotated-by";
private static String MEMBER_ANNOTATED_BY_GROUP = "member-annotated-by";
private static String METHOD_ANNOTATED_BY_GROUP = "method-annotated-by";
private static String FIELD_ANNOTATED_BY_GROUP = "field-annotated-by";
private static String ANNOTATION_NAME_GROUP = "annotation-name";
private Group createDescriptionGroup() {
return new Group("description")
.addMember(
new GroupMember("description")
.setDocTitle("Optional description to document the reason for this annotation.")
.setDocReturn("The descriptive message. Defaults to no description.")
.defaultEmptyString());
}
private Group createBindingsGroup() {
return new Group("bindings")
.addMember(new GroupMember("bindings").defaultArrayEmpty(KEEP_BINDING));
}
private Group createPreconditionsGroup() {
return new Group("preconditions")
.addMember(
new GroupMember("preconditions")
.setDocTitle(
"Conditions that should be satisfied for the annotation to be in effect.")
.setDocReturn(
"The list of preconditions. "
+ "Defaults to no conditions, thus trivially/unconditionally satisfied.")
.defaultArrayEmpty(KEEP_CONDITION));
}
private Group createConsequencesGroup() {
return new Group("consequences")
.addMember(
new GroupMember("consequences")
.setDocTitle("Consequences that must be kept if the annotation is in effect.")
.setDocReturn("The list of target consequences.")
.requiredArrayValue(KEEP_TARGET));
}
private Group createConsequencesAsValueGroup() {
return new Group("consequences")
.addMember(
new GroupMember("value")
.setDocTitle("Consequences that must be kept if the annotation is in effect.")
.setDocReturn("The list of target consequences.")
.requiredArrayValue(KEEP_TARGET));
}
private Group createAdditionalPreconditionsGroup() {
return new Group("additional-preconditions")
.addMember(
new GroupMember("additionalPreconditions")
.setDocTitle("Additional preconditions for the annotation to be in effect.")
.setDocReturn(
"The list of additional preconditions. "
+ "Defaults to no additional preconditions.")
.defaultArrayEmpty(KEEP_CONDITION));
}
private Group createAdditionalTargetsGroup(String docTitle) {
return new Group("additional-targets")
.addMember(
new GroupMember("additionalTargets")
.setDocTitle(docTitle)
.setDocReturn(
"List of additional target consequences. "
+ "Defaults to no additional target consequences.")
.defaultArrayEmpty(KEEP_TARGET));
}
private Group stringPatternExactGroup() {
return new Group("string-exact-pattern")
.allowMutuallyExclusiveWithOtherGroups()
.addMember(
new GroupMember("exact")
.setDocTitle("Exact string content.")
.addParagraph("For example, {@code \"foo\"} or {@code \"java.lang.String\"}.")
.defaultEmptyString());
}
private Group stringPatternPrefixGroup() {
return new Group("string-prefix-pattern")
.addMember(
new GroupMember("startsWith")
.setDocTitle("Matches strings beginning with the given prefix.")
.addParagraph(
"For example, {@code \"get\"} to match strings such as {@code"
+ " \"getMyValue\"}.")
.defaultEmptyString());
}
private Group stringPatternSuffixGroup() {
return new Group("string-suffix-pattern")
.addMember(
new GroupMember("endsWith")
.setDocTitle("Matches strings ending with the given suffix.")
.addParagraph(
"For example, {@code \"Setter\"} to match strings such as {@code"
+ " \"myValueSetter\"}.")
.defaultEmptyString());
}
public Group typePatternGroup() {
return new Group("type-pattern")
.addMember(
new GroupMember("name")
.setDocTitle("Exact type name as a string.")
.addParagraph("For example, {@code \"long\"} or {@code \"java.lang.String\"}.")
.defaultEmptyString())
.addMember(
new GroupMember("constant")
.setDocTitle("Exact type from a class constant.")
.addParagraph("For example, {@code String.class}.")
.defaultObjectClass())
.addMember(
new GroupMember("classNamePattern")
.setDocTitle("Classes matching the class-name pattern.")
.defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN));
// TODO(b/248408342): Add more injections on type pattern variants.
// /** Exact type name as a string to match any array with that type as member. */
// String arrayOf() default "";
//
// /** Exact type as a class constant to match any array with that type as member. */
// Class<?> arrayOfConstant() default TypePattern.class;
//
// /** If true, the pattern matches any primitive type. Such as, boolean, int, etc. */
// boolean anyPrimitive() default false;
//
// /** If true, the pattern matches any array type. */
// boolean anyArray() default false;
//
// /** If true, the pattern matches any class type. */
// boolean anyClass() default false;
//
// /** If true, the pattern matches any reference type, namely: arrays or classes. */
// boolean anyReference() default false;
}
private Group instanceOfPatternInclusive() {
return new Group("instance-of-inclusive")
.addMember(
new GroupMember("inclusive")
.setDocTitle("True if the pattern should include the directly matched classes.")
.addParagraph(
"If false, the pattern is exclusive and only matches classes that are",
"strict subclasses of the pattern.")
.defaultBooleanValue(true));
}
private Group instanceOfPatternClassNamePattern() {
return new Group("instance-of-class-name-pattern")
.addMember(
new GroupMember("classNamePattern")
.setDocTitle("Instances of classes matching the class-name pattern.")
.defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN));
}
private Group classNamePatternFullNameGroup() {
return new Group(CLASS_NAME_GROUP)
.allowMutuallyExclusiveWithOtherGroups()
.addMember(
new GroupMember("name")
.setDocTitle(
"Define the " + CLASS_NAME_GROUP + " pattern by fully qualified class name.")
.setDocReturn("The qualified class name that defines the class.")
.defaultEmptyString())
.addMember(
new GroupMember("constant")
.setDocTitle(
"Define the "
+ CLASS_NAME_GROUP
+ " pattern by reference to a Class constant.")
.setDocReturn("The class-constant that defines the class.")
.defaultObjectClass());
}
private Group classNamePatternSimpleNameGroup() {
return new Group("class-simple-name")
.addMember(
new GroupMember("simpleName")
.setDocTitle("Exact simple name of the class or interface.")
.addParagraph(
"For example, the simple name of {@code com.example.MyClass} is {@code"
+ " MyClass}.")
.addParagraph("The default matches any simple name.")
.defaultEmptyString());
}
private Group classNamePatternPackageGroup() {
return new Group("class-package-name")
.addMember(
new GroupMember("packageName")
.setDocTitle("Exact package name of the class or interface.")
.addParagraph(
"For example, the package of {@code com.example.MyClass} is {@code"
+ " com.example}.")
.addParagraph("The default matches any package.")
.defaultEmptyString());
}
private Group getKindGroup() {
return new Group(KIND_GROUP).addMember(getKindMember());
}
private static GroupMember getKindMember() {
return new GroupMember("kind")
.defaultValue(KEEP_ITEM_KIND, "KeepItemKind.DEFAULT")
.setDocTitle("Specify the kind of this item pattern.")
.setDocReturn("The kind for this pattern.")
.addParagraph("Possible values are:")
.addUnorderedList(
docEnumLink(KIND_ONLY_CLASS),
docEnumLink(KIND_ONLY_MEMBERS),
docEnumLink(KIND_ONLY_METHODS),
docEnumLink(KIND_ONLY_FIELDS),
docEnumLink(KIND_CLASS_AND_MEMBERS),
docEnumLink(KIND_CLASS_AND_METHODS),
docEnumLink(KIND_CLASS_AND_FIELDS))
.addParagraph(
"If unspecified the default kind for an item depends on its member patterns:")
.addUnorderedList(
docEnumLink(KIND_ONLY_CLASS) + " if no member patterns are defined",
docEnumLink(KIND_ONLY_METHODS) + " if method patterns are defined",
docEnumLink(KIND_ONLY_FIELDS) + " if field patterns are defined",
docEnumLink(KIND_ONLY_MEMBERS) + " otherwise.");
}
private void forEachKeepConstraintGroups(Consumer<Group> fn) {
fn.accept(getKeepConstraintsGroup());
fn.accept(new Group("constrain-annotations").addMember(constrainAnnotations()));
}
private Group getKeepConstraintsGroup() {
return new Group(CONSTRAINTS_GROUP).addMember(constraints()).addMember(constraintAdditions());
}
private static GroupMember constraints() {
return new GroupMember("constraints")
.setDocTitle("Define the usage constraints of the target.")
.addParagraph("The specified constraints must remain valid for the target.")
.addParagraph(
"The default constraints depend on the kind of the target.",
"For all targets the default constraints include:")
.addUnorderedList(
docEnumLink(CONSTRAINT_LOOKUP),
docEnumLink(CONSTRAINT_NAME),
docEnumLink(CONSTRAINT_VISIBILITY_RELAX))
.addParagraph("For classes the default constraints also include:")
.addUnorderedList(docEnumLink(CONSTRAINT_CLASS_INSTANTIATE))
.addParagraph("For methods the default constraints also include:")
.addUnorderedList(docEnumLink(CONSTRAINT_METHOD_INVOKE))
.addParagraph("For fields the default constraints also include:")
.addUnorderedList(docEnumLink(CONSTRAINT_FIELD_GET), docEnumLink(CONSTRAINT_FIELD_SET))
.setDocReturn("Usage constraints for the target.")
.defaultArrayEmpty(KEEP_CONSTRAINT);
}
private static GroupMember constraintAdditions() {
return new GroupMember("constraintAdditions")
.setDocTitle("Add additional usage constraints of the target.")
.addParagraph(
"The specified constraints must remain valid for the target",
"in addition to the default constraints.")
.addParagraph("The default constraints are documented in " + docLink(constraints()))
.setDocReturn("Additional usage constraints for the target.")
.defaultArrayEmpty(KEEP_CONSTRAINT);
}
private static GroupMember constrainAnnotations() {
return new GroupMember("constrainAnnotations")
.setDocTitle("Patterns for annotations that must remain on the item.")
.addParagraph(
"The annotations matching any of the patterns must remain on the item",
"if the annotation types remain in the program.")
.addParagraph(
"Note that if the annotation types themselves are unused/removed,",
"then their references on the item will be removed too.",
"If the annotation types themselves are used reflectively then they too need a",
"keep annotation or rule to ensure they remain in the program.")
.addParagraph(
"By default no annotation patterns are defined and no annotations are required to",
"remain.")
.setDocReturn("Annotation patterns")
.defaultArrayEmpty(ANNOTATION_PATTERN);
}
private Group annotationNameGroup() {
return new Group(ANNOTATION_NAME_GROUP)
.addMember(annotationName())
.addMember(annotationConstant())
.addMember(annotationNamePattern())
.addDocFooterParagraph(
"If none are specified the default is to match any annotation name.");
}
private GroupMember annotationName() {
return new GroupMember("name")
.setDocTitle(
"Define the " + ANNOTATION_NAME_GROUP + " pattern by fully qualified class name.")
.setDocReturn("The qualified class name that defines the annotation.")
.defaultEmptyString();
}
private GroupMember annotationConstant() {
return new GroupMember("constant")
.setDocTitle(
"Define the "
+ ANNOTATION_NAME_GROUP
+ " pattern by reference to a {@code Class} constant.")
.setDocReturn("The Class constant that defines the annotation.")
.defaultObjectClass();
}
private GroupMember annotationNamePattern() {
return new GroupMember("namePattern")
.setDocTitle(
"Define the "
+ ANNOTATION_NAME_GROUP
+ " pattern by reference to a class-name pattern.")
.setDocReturn("The class-name pattern that defines the annotation.")
.defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN);
}
private static GroupMember annotationRetention() {
return new GroupMember("retention")
.setDocTitle("Specify which retention policies must be set for the annotations.")
.addParagraph("Matches annotations with matching retention policies")
.setDocReturn("Retention policies. By default {@code RetentionPolicy.RUNTIME}.")
.defaultArrayValue(JAVA_RETENTION_POLICY, "RetentionPolicy.RUNTIME");
}
private GroupMember bindingName() {
return new GroupMember("bindingName")
.setDocTitle(
"Name with which other bindings, conditions or targets "
+ "can reference the bound item pattern.")
.setDocReturn("Name of the binding.")
.requiredStringValue();
}
private GroupMember classFromBinding() {
return new GroupMember("classFromBinding")
.setDocTitle("Define the " + CLASS_GROUP + " pattern by reference to a binding.")
.setDocReturn("The name of the binding that defines the class.")
.defaultEmptyString();
}
private Group createClassBindingGroup() {
return new Group(CLASS_GROUP)
.allowMutuallyExclusiveWithOtherGroups()
.addMember(classFromBinding())
.addDocFooterParagraph("If none are specified the default is to match any class.");
}
private GroupMember className() {
return new GroupMember("className")
.setDocTitle("Define the " + CLASS_NAME_GROUP + " pattern by fully qualified class name.")
.setDocReturn("The qualified class name that defines the class.")
.defaultEmptyString();
}
private GroupMember classConstant() {
return new GroupMember("classConstant")
.setDocTitle(
"Define the " + CLASS_NAME_GROUP + " pattern by reference to a Class constant.")
.setDocReturn("The class-constant that defines the class.")
.defaultObjectClass();
}
private GroupMember classNamePattern() {
return new GroupMember("classNamePattern")
.setDocTitle(
"Define the " + CLASS_NAME_GROUP + " pattern by reference to a class-name pattern.")
.setDocReturn("The class-name pattern that defines the class.")
.defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN);
}
private Group createClassNamePatternGroup() {
return new Group(CLASS_NAME_GROUP)
.addMember(className())
.addMember(classConstant())
.addMember(classNamePattern())
.addDocFooterParagraph("If none are specified the default is to match any class name.");
}
private GroupMember instanceOfClassName() {
return new GroupMember("instanceOfClassName")
.setDocTitle(
"Define the "
+ INSTANCE_OF_GROUP
+ " pattern as classes that are instances of the fully qualified class name.")
.setDocReturn("The qualified class name that defines what instance-of the class must be.")
.defaultEmptyString();
}
private GroupMember instanceOfClassConstant() {
return new GroupMember("instanceOfClassConstant")
.setDocTitle(
"Define the "
+ INSTANCE_OF_GROUP
+ " pattern as classes that are instances the referenced Class constant.")
.setDocReturn("The class constant that defines what instance-of the class must be.")
.defaultObjectClass();
}
private String getInstanceOfExclusiveDoc() {
return "The pattern is exclusive in that it does not match classes that are"
+ " instances of the pattern, but only those that are instances of classes that"
+ " are subclasses of the pattern.";
}
private GroupMember instanceOfClassNameExclusive() {
return new GroupMember("instanceOfClassNameExclusive")
.setDocTitle(
"Define the "
+ INSTANCE_OF_GROUP
+ " pattern as classes that are instances of the fully qualified class name.")
.setDocReturn("The qualified class name that defines what instance-of the class must be.")
.addParagraph(getInstanceOfExclusiveDoc())
.defaultEmptyString();
}
private GroupMember instanceOfClassConstantExclusive() {
return new GroupMember("instanceOfClassConstantExclusive")
.setDocTitle(
"Define the "
+ INSTANCE_OF_GROUP
+ " pattern as classes that are instances the referenced Class constant.")
.addParagraph(getInstanceOfExclusiveDoc())
.setDocReturn("The class constant that defines what instance-of the class must be.")
.defaultObjectClass();
}
private GroupMember instanceOfPattern() {
return new GroupMember("instanceOfPattern")
.setDocTitle("Define the " + INSTANCE_OF_GROUP + " with a pattern.")
.setDocReturn("The pattern that defines what instance-of the class must be.")
.defaultValue(INSTANCE_OF_PATTERN, DEFAULT_ANY_INSTANCE_OF_PATTERN);
}
private Group createClassInstanceOfPatternGroup() {
return new Group(INSTANCE_OF_GROUP)
.addMember(instanceOfClassName())
.addMember(instanceOfClassNameExclusive())
.addMember(instanceOfClassConstant())
.addMember(instanceOfClassConstantExclusive())
.addMember(instanceOfPattern())
.addDocFooterParagraph(
"If none are specified the default is to match any class instance.");
}
private String annotatedByDefaultDocFooter(String name) {
return "If none are specified the default is to match any "
+ name
+ " regardless of what the "
+ name
+ " is annotated by.";
}
private Group createAnnotatedByPatternGroup(String name, String groupName) {
return new Group(groupName)
.addMember(
new GroupMember(name + "AnnotatedByClassName")
.setDocTitle(
"Define the " + groupName + " pattern by fully qualified class name.")
.setDocReturn("The qualified class name that defines the annotation.")
.defaultEmptyString())
.addMember(
new GroupMember(name + "AnnotatedByClassConstant")
.setDocTitle(
"Define the " + groupName + " pattern by reference to a Class constant.")
.setDocReturn("The class-constant that defines the annotation.")
.defaultObjectClass())
.addMember(
new GroupMember(name + "AnnotatedByClassNamePattern")
.setDocTitle(
"Define the " + groupName + " pattern by reference to a class-name pattern.")
.setDocReturn("The class-name pattern that defines the annotation.")
.defaultValue(CLASS_NAME_PATTERN, DEFAULT_INVALID_CLASS_NAME_PATTERN));
}
private Group createClassAnnotatedByPatternGroup() {
String name = "class";
return createAnnotatedByPatternGroup(name, CLASS_ANNOTATED_BY_GROUP)
.addDocFooterParagraph(annotatedByDefaultDocFooter(name));
}
private Group createMemberBindingGroup() {
return new Group("member")
.allowMutuallyExclusiveWithOtherGroups()
.addMember(
new GroupMember("memberFromBinding")
.setDocTitle("Define the member pattern in full by a reference to a binding.")
.addParagraph(
"Mutually exclusive with all other class and member pattern properties.",
"When a member binding is referenced this item is defined to be that item,",
"including its class and member patterns.")
.setDocReturn("The binding name that defines the member.")
.defaultEmptyString());
}
private Group createMemberAnnotatedByGroup() {
String name = "member";
return createAnnotatedByPatternGroup(name, MEMBER_ANNOTATED_BY_GROUP)
.addDocFooterParagraph(getMutuallyExclusiveForMemberProperties())
.addDocFooterParagraph(annotatedByDefaultDocFooter(name));
}
private Group createMemberAccessGroup() {
return new Group("member-access")
.addMember(
new GroupMember("memberAccess")
.setDocTitle("Define the member-access pattern by matching on access flags.")
.addParagraph(getMutuallyExclusiveForMemberProperties())
.setDocReturn("The member access-flag constraints that must be met.")
.defaultArrayEmpty(MEMBER_ACCESS_FLAGS));
}
private String getMutuallyExclusiveForMemberProperties() {
return "Mutually exclusive with all field and method properties "
+ "as use restricts the match to both types of members.";
}
private String getMutuallyExclusiveForMethodProperties() {
return "Mutually exclusive with all field properties.";
}
private String getMutuallyExclusiveForFieldProperties() {
return "Mutually exclusive with all method properties.";
}
private String getMethodDefaultDoc(String suffix) {
return "If none, and other properties define this item as a method, the default matches "
+ suffix
+ ".";
}
private String getFieldDefaultDoc(String suffix) {
return "If none, and other properties define this item as a field, the default matches "
+ suffix
+ ".";
}
private Group createMethodAnnotatedByGroup() {
String name = "method";
return createAnnotatedByPatternGroup(name, METHOD_ANNOTATED_BY_GROUP)
.addDocFooterParagraph(getMutuallyExclusiveForMethodProperties())
.addDocFooterParagraph(annotatedByDefaultDocFooter(name));
}
private Group createMethodAccessGroup() {
return new Group("method-access")
.addMember(
new GroupMember("methodAccess")
.setDocTitle("Define the method-access pattern by matching on access flags.")
.addParagraph(getMutuallyExclusiveForMethodProperties())
.addParagraph(getMethodDefaultDoc("any method-access flags"))
.setDocReturn("The method access-flag constraints that must be met.")
.defaultArrayEmpty(METHOD_ACCESS_FLAGS));
}
private Group createMethodNameGroup() {
return new Group("method-name")
.addMember(
new GroupMember("methodName")
.setDocTitle("Define the method-name pattern by an exact method name.")
.addParagraph(getMutuallyExclusiveForMethodProperties())
.addParagraph(getMethodDefaultDoc("any method name"))
.setDocReturn("The exact method name of the method.")
.defaultEmptyString())
.addMember(
new GroupMember("methodNamePattern")
.setDocTitle("Define the method-name pattern by a string pattern.")
.addParagraph(getMutuallyExclusiveForMethodProperties())
.addParagraph(getMethodDefaultDoc("any method name"))
.setDocReturn("The string pattern of the method name.")
.defaultValue(STRING_PATTERN, DEFAULT_INVALID_STRING_PATTERN));
}
private Group createMethodReturnTypeGroup() {
return new Group("return-type")
.addMember(
new GroupMember("methodReturnType")
.setDocTitle(
"Define the method return-type pattern by a fully qualified type or 'void'.")
.addParagraph(getMutuallyExclusiveForMethodProperties())
.addParagraph(getMethodDefaultDoc("any return type"))
.setDocReturn("The qualified type name of the method return type.")
.defaultEmptyString())
.addMember(
new GroupMember("methodReturnTypeConstant")
.setDocTitle("Define the method return-type pattern by a class constant.")
.addParagraph(getMutuallyExclusiveForMethodProperties())
.addParagraph(getMethodDefaultDoc("any return type"))
.setDocReturn("A class constant denoting the type of the method return type.")
.defaultObjectClass())
.addMember(
new GroupMember("methodReturnTypePattern")
.setDocTitle("Define the method return-type pattern by a type pattern.")
.addParagraph(getMutuallyExclusiveForMethodProperties())
.addParagraph(getMethodDefaultDoc("any return type"))
.setDocReturn("The pattern of the method return type.")
.defaultValue(TYPE_PATTERN, DEFAULT_INVALID_TYPE_PATTERN));
}
private Group createMethodParametersGroup() {
return new Group("parameters")
.addMember(
new GroupMember("methodParameters")
.setDocTitle(
"Define the method parameters pattern by a list of fully qualified types.")
.addParagraph(getMutuallyExclusiveForMethodProperties())
.addParagraph(getMethodDefaultDoc("any parameters"))
.setDocReturn("The list of qualified type names of the method parameters.")
.defaultArrayValue(JAVA_STRING, quote("")))
.addMember(
new GroupMember("methodParameterTypePatterns")
.setDocTitle(
"Define the method parameters pattern by a list of patterns on types.")
.addParagraph(getMutuallyExclusiveForMethodProperties())
.addParagraph(getMethodDefaultDoc("any parameters"))
.setDocReturn("The list of type patterns for the method parameters.")
.defaultArrayValue(TYPE_PATTERN, DEFAULT_INVALID_TYPE_PATTERN));
}
private Group createFieldAnnotatedByGroup() {
String name = "field";
return createAnnotatedByPatternGroup(name, FIELD_ANNOTATED_BY_GROUP)
.addDocFooterParagraph(getMutuallyExclusiveForFieldProperties())
.addDocFooterParagraph(annotatedByDefaultDocFooter(name));
}
private Group createFieldAccessGroup() {
return new Group("field-access")
.addMember(
new GroupMember("fieldAccess")
.setDocTitle("Define the field-access pattern by matching on access flags.")
.addParagraph(getMutuallyExclusiveForFieldProperties())
.addParagraph(getFieldDefaultDoc("any field-access flags"))
.setDocReturn("The field access-flag constraints that must be met.")
.defaultArrayEmpty(FIELD_ACCESS_FLAGS));
}
private Group createFieldNameGroup() {
return new Group("field-name")
.addMember(
new GroupMember("fieldName")
.setDocTitle("Define the field-name pattern by an exact field name.")
.addParagraph(getMutuallyExclusiveForFieldProperties())
.addParagraph(getFieldDefaultDoc("any field name"))
.setDocReturn("The exact field name of the field.")
.defaultEmptyString())
.addMember(
new GroupMember("fieldNamePattern")
.setDocTitle("Define the field-name pattern by a string pattern.")
.addParagraph(getMutuallyExclusiveForFieldProperties())
.addParagraph(getFieldDefaultDoc("any field name"))
.setDocReturn("The string pattern of the field name.")
.defaultValue(STRING_PATTERN, DEFAULT_INVALID_STRING_PATTERN));
}
private Group createFieldTypeGroup() {
return new Group("field-type")
.addMember(
new GroupMember("fieldType")
.setDocTitle("Define the field-type pattern by a fully qualified type.")
.addParagraph(getMutuallyExclusiveForFieldProperties())
.addParagraph(getFieldDefaultDoc("any type"))
.setDocReturn("The qualified type name for the field type.")
.defaultEmptyString())
.addMember(
new GroupMember("fieldTypeConstant")
.setDocTitle("Define the field-type pattern by a class constant.")
.addParagraph(getMutuallyExclusiveForFieldProperties())
.addParagraph(getFieldDefaultDoc("any type"))
.setDocReturn("The class constant for the field type.")
.defaultObjectClass())
.addMember(
new GroupMember("fieldTypePattern")
.setDocTitle("Define the field-type pattern by a pattern on types.")
.addParagraph(getMutuallyExclusiveForFieldProperties())
.addParagraph(getFieldDefaultDoc("any type"))
.setDocReturn("The type pattern for the field type.")
.defaultValue(TYPE_PATTERN, DEFAULT_INVALID_TYPE_PATTERN));
}
private void generateClassAndMemberPropertiesWithClassAndMemberBinding() {
internalGenerateClassAndMemberPropertiesWithBinding(true);
}
private void generateClassAndMemberPropertiesWithClassBinding() {
internalGenerateClassAndMemberPropertiesWithBinding(false);
}
private void internalGenerateClassAndMemberPropertiesWithBinding(boolean includeMemberBinding) {
// Class properties.
{
Group bindingGroup = createClassBindingGroup();
Group classNameGroup = createClassNamePatternGroup();
Group classInstanceOfGroup = createClassInstanceOfPatternGroup();
Group classAnnotatedByGroup = createClassAnnotatedByPatternGroup();
bindingGroup.addMutuallyExclusiveGroups(
classNameGroup, classInstanceOfGroup, classAnnotatedByGroup);
bindingGroup.generate(this);
println();
classNameGroup.generate(this);
println();
classInstanceOfGroup.generate(this);
println();
classAnnotatedByGroup.generate(this);
println();
}
// Member binding properties.
Group memberBindingGroup = null;
if (includeMemberBinding) {
memberBindingGroup = createMemberBindingGroup();
memberBindingGroup.generate(this);
println();
}
// The remaining member properties.
internalGenerateMemberPropertiesNoBinding(memberBindingGroup);
}
private Group maybeLink(Group group, Group maybeExclusiveGroup) {
if (maybeExclusiveGroup != null) {
maybeExclusiveGroup.addMutuallyExclusiveGroups(group);
}
return group;
}
private void generateMemberPropertiesNoBinding() {
internalGenerateMemberPropertiesNoBinding(null);
}
private void internalGenerateMemberPropertiesNoBinding(Group memberBindingGroup) {
// General member properties.
maybeLink(createMemberAnnotatedByGroup(), memberBindingGroup).generate(this);
println();
maybeLink(createMemberAccessGroup(), memberBindingGroup).generate(this);
println();
// Method properties.
maybeLink(createMethodAnnotatedByGroup(), memberBindingGroup).generate(this);
println();
maybeLink(createMethodAccessGroup(), memberBindingGroup).generate(this);
println();
maybeLink(createMethodNameGroup(), memberBindingGroup).generate(this);
println();
maybeLink(createMethodReturnTypeGroup(), memberBindingGroup).generate(this);
println();
maybeLink(createMethodParametersGroup(), memberBindingGroup).generate(this);
println();
// Field properties.
maybeLink(createFieldAnnotatedByGroup(), memberBindingGroup).generate(this);
println();
maybeLink(createFieldAccessGroup(), memberBindingGroup).generate(this);
println();
maybeLink(createFieldNameGroup(), memberBindingGroup).generate(this);
println();
maybeLink(createFieldTypeGroup(), memberBindingGroup).generate(this);
}
private void generateStringPattern() {
printCopyRight(2024);
printPackage("annotations");
printImports(ANNOTATION_IMPORTS);
DocPrinter.printer()
.setDocTitle("A pattern structure for matching strings.")
.addParagraph("If no properties are set, the default pattern matches any string.")
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
println("public @interface " + simpleName(STRING_PATTERN) + " {");
println();
withIndent(
() -> {
Group exactGroup = stringPatternExactGroup();
Group prefixGroup = stringPatternPrefixGroup();
Group suffixGroup = stringPatternSuffixGroup();
exactGroup.addMutuallyExclusiveGroups(prefixGroup, suffixGroup);
exactGroup.generate(this);
println();
prefixGroup.generate(this);
println();
suffixGroup.generate(this);
});
println();
println("}");
}
private void generateTypePattern() {
printCopyRight(2023);
printPackage("annotations");
printImports(ANNOTATION_IMPORTS);
DocPrinter.printer()
.setDocTitle("A pattern structure for matching types.")
.addParagraph("If no properties are set, the default pattern matches any type.")
.addParagraph("All properties on this annotation are mutually exclusive.")
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
println("public @interface " + simpleName(TYPE_PATTERN) + " {");
println();
withIndent(() -> typePatternGroup().generate(this));
println();
println("}");
}
private void generateClassNamePattern() {
printCopyRight(2023);
printPackage("annotations");
printImports(ANNOTATION_IMPORTS);
DocPrinter.printer()
.setDocTitle("A pattern structure for matching names of classes and interfaces.")
.addParagraph(
"If no properties are set, the default pattern matches any name of a class or"
+ " interface.")
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
println("public @interface " + simpleName(CLASS_NAME_PATTERN) + " {");
println();
withIndent(
() -> {
Group exactNameGroup = classNamePatternFullNameGroup();
Group simpleNameGroup = classNamePatternSimpleNameGroup();
Group packageGroup = classNamePatternPackageGroup();
exactNameGroup.addMutuallyExclusiveGroups(simpleNameGroup, packageGroup);
exactNameGroup.generate(this);
println();
simpleNameGroup.generate(this);
println();
packageGroup.generate(this);
});
println();
println("}");
}
private void generateInstanceOfPattern() {
printCopyRight(2024);
printPackage("annotations");
printImports(ANNOTATION_IMPORTS);
DocPrinter.printer()
.setDocTitle("A pattern structure for matching instances of classes and interfaces.")
.addParagraph("If no properties are set, the default pattern matches any instance.")
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
println("public @interface " + simpleName(INSTANCE_OF_PATTERN) + " {");
println();
withIndent(
() -> {
instanceOfPatternInclusive().generate(this);
println();
instanceOfPatternClassNamePattern().generate(this);
});
println();
println("}");
}
private void generateAnnotationPattern() {
printCopyRight(2024);
printPackage("annotations");
printImports(ANNOTATION_IMPORTS);
DocPrinter.printer()
.setDocTitle("A pattern structure for matching annotations.")
.addParagraph(
"If no properties are set, the default pattern matches any annotation",
"with a runtime retention policy.")
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
println("public @interface " + simpleName(ANNOTATION_PATTERN) + " {");
println();
withIndent(
() -> {
annotationNameGroup().generate(this);
println();
annotationRetention().generate(this);
});
println();
println("}");
}
private void generateKeepBinding() {
printCopyRight(2022);
printPackage("annotations");
printImports(ANNOTATION_IMPORTS);
DocPrinter.printer()
.setDocTitle("A binding of a keep item.")
.addParagraph(
"Bindings allow referencing the exact instance of a match from a condition in other "
+ " conditions and/or targets. It can also be used to reduce duplication of"
+ " targets by sharing patterns.")
.addParagraph("An item can be:")
.addUnorderedList(
"a pattern on classes;", "a pattern on methods; or", "a pattern on fields.")
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
println("public @interface KeepBinding {");
println();
withIndent(
() -> {
bindingName().generate(this);
println();
getKindGroup().generate(this);
println();
generateClassAndMemberPropertiesWithClassBinding();
});
println();
println("}");
}
private void generateKeepTarget() {
printCopyRight(2022);
printPackage("annotations");
printImports(ANNOTATION_IMPORTS);
DocPrinter.printer()
.setDocTitle("A target for a keep edge.")
.addParagraph(
"The target denotes an item along with options for what to keep. An item can be:")
.addUnorderedList(
"a pattern on classes;", "a pattern on methods; or", "a pattern on fields.")
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
println("public @interface KeepTarget {");
println();
withIndent(
() -> {
getKindGroup().generate(this);
println();
forEachKeepConstraintGroups(
g -> {
g.generate(this);
println();
});
generateClassAndMemberPropertiesWithClassAndMemberBinding();
});
println();
println("}");
}
private void generateKeepCondition() {
printCopyRight(2022);
printPackage("annotations");
printImports(ANNOTATION_IMPORTS);
DocPrinter.printer()
.setDocTitle("A condition for a keep edge.")
.addParagraph(
"The condition denotes an item used as a precondition of a rule. An item can be:")
.addUnorderedList(
"a pattern on classes;", "a pattern on methods; or", "a pattern on fields.")
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
println("public @interface KeepCondition {");
println();
withIndent(
() -> {
generateClassAndMemberPropertiesWithClassAndMemberBinding();
});
println();
println("}");
}
private void generateKeepForApi() {
printCopyRight(2023);
printPackage("annotations");
printImports(ANNOTATION_IMPORTS);
DocPrinter.printer()
.setDocTitle(
"Annotation to mark a class, field or method as part of a library API surface.")
.addParagraph(
"When a class is annotated, member patterns can be used to define which members are"
+ " to be kept. When no member patterns are specified the default pattern matches"
+ " all public and protected members.")
.addParagraph(
"When a member is annotated, the member patterns cannot be used as the annotated"
+ " member itself fully defines the item to be kept (i.e., itself).")
.printDoc(this::println);
println(
"@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,"
+ " ElementType.CONSTRUCTOR})");
println("@Retention(RetentionPolicy.CLASS)");
println("public @interface KeepForApi {");
println();
withIndent(
() -> {
createDescriptionGroup().generate(this);
println();
createAdditionalTargetsGroup(
"Additional targets to be kept as part of the API surface.")
.generate(this);
println();
GroupMember kindProperty = getKindMember();
kindProperty
.clearDocLines()
.addParagraph(
"Default kind is",
docEnumLink(KIND_CLASS_AND_MEMBERS) + ",",
"meaning the annotated class and/or member is to be kept.",
"When annotating a class this can be set to",
docEnumLink(KIND_ONLY_CLASS),
"to avoid patterns on any members.",
"That can be useful when the API members are themselves explicitly annotated.")
.addParagraph(
"It is not possible to use",
docEnumLink(KIND_ONLY_CLASS),
"if annotating a member. Also, it is never valid to use kind",
docEnumLink(KIND_ONLY_MEMBERS),
"as the API surface must keep the class if any member is to be accessible.")
.generate(this);
println();
generateMemberPropertiesNoBinding();
});
println();
println("}");
}
private void generateUsesReflection() {
printCopyRight(2022);
printPackage("annotations");
printImports(ANNOTATION_IMPORTS);
DocPrinter.printer()
.setDocTitle(
"Annotation to declare the reflective usages made by a class, method or field.")
.addParagraph(
"The annotation's 'value' is a list of targets to be kept if the annotated item is"
+ " used. The annotated item is a precondition for keeping any of the specified"
+ " targets. Thus, if an annotated method is determined to be unused by the"
+ " program, the annotation itself will not be in effect and the targets will not"
+ " be kept (assuming nothing else is otherwise keeping them).")
.addParagraph(
"The annotation's 'additionalPreconditions' is optional and can specify additional"
+ " conditions that should be satisfied for the annotation to be in effect.")
.addParagraph(
"The translation of the "
+ docLink(USES_REFLECTION)
+ " annotation into a "
+ docLink(KEEP_EDGE)
+ " is as follows:")
.addParagraph(
"Assume the item of the annotation is denoted by 'CTX' and referred to as its"
+ " context.")
.addCodeBlock(
annoSimpleName(USES_REFLECTION)
+ "(value = targets, [additionalPreconditions = preconditions])",
"==>",
annoSimpleName(KEEP_EDGE) + "(",
" consequences = targets,",
" preconditions = {createConditionFromContext(CTX)} + preconditions",
")",
"",
"where",
" KeepCondition createConditionFromContext(ctx) {",
" if (ctx.isClass()) {",
" return new KeepCondition(classTypeName = ctx.getClassTypeName());",
" }",
" if (ctx.isMethod()) {",
" return new KeepCondition(",
" classTypeName = ctx.getClassTypeName(),",
" methodName = ctx.getMethodName(),",
" methodReturnType = ctx.getMethodReturnType(),",
" methodParameterTypes = ctx.getMethodParameterTypes());",
" }",
" if (ctx.isField()) {",
" return new KeepCondition(",
" classTypeName = ctx.getClassTypeName(),",
" fieldName = ctx.getFieldName()",
" fieldType = ctx.getFieldType());",
" }",
" // unreachable",
" }")
.printDoc(this::println);
println(
"@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,"
+ " ElementType.CONSTRUCTOR})");
println("@Retention(RetentionPolicy.CLASS)");
println("public @interface " + simpleName(USES_REFLECTION) + " {");
println();
withIndent(
() -> {
createDescriptionGroup().generate(this);
println();
createConsequencesAsValueGroup().generate(this);
println();
createAdditionalPreconditionsGroup().generate(this);
});
println("}");
}
private void generateUsedByX(String annotationClassName, String doc) {
printCopyRight(2023);
printPackage("annotations");
printImports(ANNOTATION_IMPORTS);
DocPrinter.printer()
.setDocTitle("Annotation to mark a class, field or method as being " + doc + ".")
.addParagraph(
"Note: Before using this annotation, consider if instead you can annotate the code"
+ " that is doing reflection with "
+ docLink(USES_REFLECTION)
+ ". Annotating the"
+ " reflecting code is generally more clear and maintainable, and it also"
+ " naturally gives rise to edges that describe just the reflected aspects of the"
+ " program. The "
+ docLink(USED_BY_REFLECTION)
+ " annotation is suitable for cases where"
+ " the reflecting code is not under user control, or in migrating away from"
+ " rules.")
.addParagraph(
"When a class is annotated, member patterns can be used to define which members are"
+ " to be kept. When no member patterns are specified the default pattern is to"
+ " match just the class.")
.addParagraph(
"When a member is annotated, the member patterns cannot be used as the annotated"
+ " member itself fully defines the item to be kept (i.e., itself).")
.printDoc(this::println);
println(
"@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,"
+ " ElementType.CONSTRUCTOR})");
println("@Retention(RetentionPolicy.CLASS)");
println("public @interface " + annotationClassName + " {");
println();
withIndent(
() -> {
createDescriptionGroup().generate(this);
println();
createPreconditionsGroup().generate(this);
println();
createAdditionalTargetsGroup(
"Additional targets to be kept in addition to the annotated class/members.")
.generate(this);
println();
GroupMember kindProperty = getKindMember();
kindProperty
.clearDocLines()
.addParagraph("If unspecified the default kind depends on the annotated item.")
.addParagraph("When annotating a class the default kind is:")
.addUnorderedList(
docEnumLink(KIND_ONLY_CLASS) + " if no member patterns are defined;",
docEnumLink(KIND_CLASS_AND_METHODS) + " if method patterns are defined;",
docEnumLink(KIND_CLASS_AND_FIELDS) + " if field patterns are defined;",
docEnumLink(KIND_CLASS_AND_MEMBERS) + "otherwise.")
.addParagraph(
"When annotating a method the default kind is: "
+ docEnumLink(KIND_ONLY_METHODS))
.addParagraph(
"When annotating a field the default kind is: " + docEnumLink(KIND_ONLY_FIELDS))
.addParagraph(
"It is not possible to use "
+ docEnumLink(KIND_ONLY_CLASS)
+ " if annotating a member.")
.generate(this);
println();
forEachKeepConstraintGroups(
g -> {
g.generate(this);
println();
});
generateMemberPropertiesNoBinding();
});
println();
println("}");
}
private static String annoSimpleName(ClassReference clazz) {
return "@" + simpleName(clazz);
}
private static String docLink(ClassReference clazz) {
return "{@link " + simpleName(clazz) + "}";
}
private static String docLink(GroupMember member) {
return "{@link #" + member.name + "}";
}
private static String docEnumLink(EnumReference enumRef) {
return "{@link " + simpleName(enumRef.enumClass) + "#" + enumRef.enumValue + "}";
}
private static String docEnumLinkList(EnumReference... values) {
return StringUtils.join(", ", values, v -> docEnumLink(v), BraceType.TUBORG);
}
private void generateConstants() {
printCopyRight(2023);
printPackage("ast");
printImports();
DocPrinter.printer()
.setDocTitle(
"Utility class for referencing the various keep annotations and their structure.")
.addParagraph(
"Use of these references avoids polluting the Java namespace with imports of the java"
+ " annotations which overlap in name with the actual semantic AST types.")
.printDoc(this::println);
println("public final class AnnotationConstants {");
withIndent(
() -> {
// Root annotations.
generateExtractedKeepAnnotationsConstants();
generateKeepEdgeConstants();
generateKeepForApiConstants();
generateUsesReflectionConstants();
generateUsedByReflectionConstants();
generateUsedByNativeConstants();
generateCheckRemovedConstants();
generateCheckOptimizedOutConstants();
// Common item fields.
generateItemConstants();
// Inner annotation classes.
generateBindingConstants();
generateConditionConstants();
generateTargetConstants();
generateKindConstants();
generateConstraintConstants();
generateMemberAccessConstants();
generateMethodAccessConstants();
generateFieldAccessConstants();
generateStringPatternConstants();
generateTypePatternConstants();
generateClassNamePatternConstants();
generateInstanceOfPatternConstants();
generateAnnotationPatternConstants();
});
println("}");
}
void generateGroupConstants(ClassReference annoType, List<Group> groups) {
generateAnnotationConstants(annoType);
groups.forEach(g -> g.generateConstants(this));
}
private void generateAnnotationConstants(ClassReference clazz) {
String desc = clazz.getDescriptor();
println("public static final String DESCRIPTOR = " + quote(desc) + ";");
}
private void generateExtractedKeepAnnotationsConstants() {
println("public static final class ExtractedAnnotations {");
withIndent(
() -> {
generateAnnotationConstants(EXTRACTED_KEEP_ANNOTATIONS);
new GroupMember("value")
.setDocTitle("Extracted normalized keep edges.")
.requiredArrayValue(KEEP_EDGE)
.generateConstants(this);
});
println("}");
println();
println("public static final class ExtractedAnnotation {");
withIndent(
() -> {
generateAnnotationConstants(EXTRACTED_KEEP_ANNOTATION);
new GroupMember("version")
.setDocTitle("Extraction version used to generate this keep annotation.")
.requiredStringValue()
.generateConstants(this);
new GroupMember("context")
.setDocTitle("Extraction context from which this keep annotation is generated.")
.requiredStringValue()
.generateConstants(this);
new Group("keep-annotation")
.addMember(
new GroupMember("edge")
.setDocTitle("Extracted normalized keep edge.")
.requiredValue(KEEP_EDGE))
.addMember(
new GroupMember("checkRemoved")
.setDocTitle("Extracted check removed.")
.defaultBooleanValue(false))
.addMember(
new GroupMember("checkOptimizedOut")
.setDocTitle("Extracted check optimized out.")
.defaultBooleanValue(false))
.generateConstants(this);
});
println("}");
println();
}
List<Group> getKeepEdgeGroups() {
return ImmutableList.of(
createDescriptionGroup(),
createBindingsGroup(),
createPreconditionsGroup(),
createConsequencesGroup());
}
private void generateKeepEdgeConstants() {
println("public static final class Edge {");
withIndent(() -> generateGroupConstants(KEEP_EDGE, getKeepEdgeGroups()));
println("}");
println();
}
List<Group> getKeepForApiGroups() {
return ImmutableList.of(
createDescriptionGroup(), createAdditionalTargetsGroup("."), createMemberAccessGroup());
}
private void generateKeepForApiConstants() {
println("public static final class ForApi {");
withIndent(() -> generateGroupConstants(KEEP_FOR_API, getKeepForApiGroups()));
println("}");
println();
}
List<Group> getUsesReflectionGroups() {
return ImmutableList.of(
createDescriptionGroup(),
createConsequencesAsValueGroup(),
createAdditionalPreconditionsGroup());
}
private void generateUsesReflectionConstants() {
println("public static final class UsesReflection {");
withIndent(() -> generateGroupConstants(USES_REFLECTION, getUsesReflectionGroups()));
println("}");
println();
}
List<Group> getUsedByReflectionGroups() {
ImmutableList.Builder<Group> builder = ImmutableList.builder();
builder.addAll(getItemGroups());
builder.add(getKindGroup());
forEachExtraUsedByReflectionGroup(builder::add);
forEachKeepConstraintGroups(builder::add);
return builder.build();
}
private void forEachExtraUsedByReflectionGroup(Consumer<Group> fn) {
fn.accept(createDescriptionGroup());
fn.accept(createPreconditionsGroup());
fn.accept(createAdditionalTargetsGroup("."));
}
private void generateUsedByReflectionConstants() {
println("public static final class UsedByReflection {");
withIndent(
() -> {
generateAnnotationConstants(USED_BY_REFLECTION);
forEachExtraUsedByReflectionGroup(g -> g.generateConstants(this));
});
println("}");
println();
}
List<Group> getUsedByNativeGroups() {
return getUsedByReflectionGroups();
}
private void generateUsedByNativeConstants() {
println("public static final class UsedByNative {");
withIndent(
() -> {
generateAnnotationConstants(USED_BY_NATIVE);
println("// Content is the same as " + simpleName(USED_BY_REFLECTION) + ".");
});
println("}");
println();
}
private void generateCheckRemovedConstants() {
println("public static final class CheckRemoved {");
withIndent(
() -> {
generateAnnotationConstants(CHECK_REMOVED);
});
println("}");
println();
}
private void generateCheckOptimizedOutConstants() {
println("public static final class CheckOptimizedOut {");
withIndent(
() -> {
generateAnnotationConstants(CHECK_OPTIMIZED_OUT);
});
println("}");
println();
}
private List<Group> getItemGroups() {
return ImmutableList.of(
// Bindings.
createClassBindingGroup(),
createMemberBindingGroup(),
// Classes.
createClassNamePatternGroup(),
createClassInstanceOfPatternGroup(),
createClassAnnotatedByPatternGroup(),
// Members.
createMemberAnnotatedByGroup(),
createMemberAccessGroup(),
// Methods.
createMethodAnnotatedByGroup(),
createMethodAccessGroup(),
createMethodNameGroup(),
createMethodReturnTypeGroup(),
createMethodParametersGroup(),
// Fields.
createFieldAnnotatedByGroup(),
createFieldAccessGroup(),
createFieldNameGroup(),
createFieldTypeGroup());
}
private void generateItemConstants() {
DocPrinter.printer()
.setDocTitle("Item properties common to binding items, conditions and targets.")
.printDoc(this::println);
println("public static final class Item {");
withIndent(() -> getItemGroups().forEach(g -> g.generateConstants(this)));
println("}");
println();
}
List<Group> getBindingGroups() {
return ImmutableList.<Group>builder()
.addAll(getItemGroups())
.add(new Group("binding-name").addMember(bindingName()))
.build();
}
private void generateBindingConstants() {
println("public static final class Binding {");
withIndent(
() -> {
generateAnnotationConstants(KEEP_BINDING);
bindingName().generateConstants(this);
});
println("}");
println();
}
List<Group> getConditionGroups() {
return getItemGroups();
}
private void generateConditionConstants() {
println("public static final class Condition {");
withIndent(
() -> {
generateAnnotationConstants(KEEP_CONDITION);
});
println("}");
println();
}
List<Group> getTargetGroups() {
ImmutableList.Builder<Group> builder = ImmutableList.builder();
builder.addAll(getItemGroups());
forEachExtraTargetGroup(builder::add);
return builder.build();
}
private void forEachExtraTargetGroup(Consumer<Group> fn) {
fn.accept(getKindGroup());
forEachKeepConstraintGroups(fn);
}
private void generateTargetConstants() {
println("public static final class Target {");
withIndent(
() -> {
generateAnnotationConstants(KEEP_TARGET);
forEachExtraTargetGroup(g -> g.generateConstants(this));
});
println("}");
println();
}
private void generateKindConstants() {
println("public static final class Kind {");
withIndent(
() -> {
generateAnnotationConstants(KEEP_ITEM_KIND);
for (KeepItemKind value : KeepItemKind.values()) {
if (value != KeepItemKind.DEFAULT) {
println(
"public static final String "
+ value.name()
+ " = "
+ quote(value.name())
+ ";");
}
}
});
println("}");
println();
}
private void generateConstraintConstants() {
println("public static final class Constraints {");
withIndent(
() -> {
generateAnnotationConstants(KEEP_CONSTRAINT);
for (EnumReference constraint : KEEP_CONSTRAINT_VALUES) {
println(
"public static final String "
+ constraint.enumValue
+ " = "
+ quote(constraint.enumValue)
+ ";");
}
});
println("}");
println();
}
private boolean isAccessPropertyNegation(EnumReference enumReference) {
return enumReference.name().startsWith("NON_");
}
private boolean isMemberAccessProperty(EnumReference enumReference) {
for (EnumReference memberAccessValue : MEMBER_ACCESS_VALUES) {
if (memberAccessValue.enumValue.equals(enumReference.enumValue)) {
return true;
}
}
return false;
}
private void generateMemberAccessConstants() {
println("public static final class MemberAccess {");
withIndent(
() -> {
generateAnnotationConstants(MEMBER_ACCESS_FLAGS);
println("public static final String NEGATION_PREFIX = \"NON_\";");
for (EnumReference value : MEMBER_ACCESS_VALUES) {
assert !isAccessPropertyNegation(value);
assert isMemberAccessProperty(value);
println(
"public static final String " + value.name() + " = " + quote(value.name()) + ";");
}
});
println("}");
println();
}
private void generateMethodAccessConstants() {
println("public static final class MethodAccess {");
withIndent(
() -> {
generateAnnotationConstants(METHOD_ACCESS_FLAGS);
for (EnumReference value : METHOD_ACCESS_VALUES) {
assert !isAccessPropertyNegation(value);
assert !isMemberAccessProperty(value);
println(
"public static final String " + value.name() + " = " + quote(value.name()) + ";");
}
});
println("}");
println();
}
private void generateFieldAccessConstants() {
println("public static final class FieldAccess {");
withIndent(
() -> {
generateAnnotationConstants(FIELD_ACCESS_FLAGS);
for (EnumReference value : FIELD_ACCESS_VALUES) {
assert !isAccessPropertyNegation(value);
assert !isMemberAccessProperty(value);
println(
"public static final String " + value.name() + " = " + quote(value.name()) + ";");
}
});
println("}");
println();
}
List<Group> getStringPatternGroups() {
return ImmutableList.of(
stringPatternExactGroup(), stringPatternPrefixGroup(), stringPatternSuffixGroup());
}
private void generateStringPatternConstants() {
println("public static final class StringPattern {");
withIndent(
() -> {
generateAnnotationConstants(STRING_PATTERN);
getStringPatternGroups().forEach(g -> g.generateConstants(this));
});
println("}");
println();
}
List<Group> getTypePatternGroups() {
return ImmutableList.of(typePatternGroup());
}
private void generateTypePatternConstants() {
println("public static final class TypePattern {");
withIndent(
() -> {
generateAnnotationConstants(TYPE_PATTERN);
getTypePatternGroups().forEach(g -> g.generateConstants((this)));
});
println("}");
println();
}
private void generateClassNamePatternConstants() {
println("public static final class ClassNamePattern {");
withIndent(
() -> {
generateAnnotationConstants(CLASS_NAME_PATTERN);
classNamePatternFullNameGroup().generateConstants(this);
classNamePatternSimpleNameGroup().generateConstants(this);
classNamePatternPackageGroup().generateConstants(this);
});
println("}");
println();
}
private void generateInstanceOfPatternConstants() {
println("public static final class InstanceOfPattern {");
withIndent(
() -> {
generateAnnotationConstants(INSTANCE_OF_PATTERN);
instanceOfPatternInclusive().generateConstants(this);
instanceOfPatternClassNamePattern().generateConstants(this);
});
println("}");
println();
}
List<Group> getAnnotationPatternGroups() {
return ImmutableList.of(
annotationNameGroup(), new Group("retention").addMember(annotationRetention()));
}
private void generateAnnotationPatternConstants() {
println("public static final class AnnotationPattern {");
withIndent(
() -> {
generateAnnotationConstants(ANNOTATION_PATTERN);
getAnnotationPatternGroups().forEach(g -> g.generateConstants(this));
});
println("}");
println();
}
private static void writeFile(Path file, Consumer<Generator> fn, BiConsumer<Path, String> write)
throws IOException {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(byteStream);
Generator generator = new Generator(printStream);
fn.accept(generator);
String formatted = byteStream.toString();
if (file.toString().endsWith(".java")) {
formatted = CodeGenerationBase.formatRawOutput(formatted);
}
Path resolved = Paths.get(ToolHelper.getProjectRoot()).resolve(file);
write.accept(resolved, formatted);
}
public static Path source(ClassReference clazz) {
return Paths.get("src", "keepanno", "java").resolve(clazz.getBinaryName() + ".java");
}
public static void run(BiConsumer<Path, String> write) throws IOException {
Path projectRoot = Paths.get(ToolHelper.getProjectRoot());
writeFile(
Paths.get("doc/keepanno-guide.md"),
generator -> KeepAnnoMarkdownGenerator.generateMarkdownDoc(generator, projectRoot),
write);
writeFile(source(ANNOTATION_CONSTANTS), Generator::generateConstants, write);
writeFile(source(STRING_PATTERN), Generator::generateStringPattern, write);
writeFile(source(TYPE_PATTERN), Generator::generateTypePattern, write);
writeFile(source(CLASS_NAME_PATTERN), Generator::generateClassNamePattern, write);
writeFile(source(INSTANCE_OF_PATTERN), Generator::generateInstanceOfPattern, write);
writeFile(source(ANNOTATION_PATTERN), Generator::generateAnnotationPattern, write);
writeFile(source(KEEP_BINDING), Generator::generateKeepBinding, write);
writeFile(source(KEEP_TARGET), Generator::generateKeepTarget, write);
writeFile(source(KEEP_CONDITION), Generator::generateKeepCondition, write);
writeFile(source(KEEP_FOR_API), Generator::generateKeepForApi, write);
writeFile(source(USES_REFLECTION), Generator::generateUsesReflection, write);
writeFile(
source(USED_BY_REFLECTION),
g -> g.generateUsedByX("UsedByReflection", "accessed reflectively"),
write);
writeFile(
source(USED_BY_NATIVE),
g -> g.generateUsedByX("UsedByNative", "accessed from native code via JNI"),
write);
}
}
}