Merge 1714fa197bd351843ab8469a79f8cecbe03d8c4c on remote branch
Change-Id: I956012c08eb2f08040b2d41634319282ba2da7a6
diff --git a/Android.mk b/Android.mk
index 7f6aa4b..5955b0f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,3 +17,10 @@
ifneq ($INTERNAL_PLATFORM_MERGED_COMPAT_CONFIG,)
$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_MERGED_COMPAT_CONFIG))
endif
+
+# ==== hiddenapi lists =======================================
+ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
+$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_INDEX))
+endif # UNSAFE_DISABLE_HIDDENAPI_FLAGS
diff --git a/java/android/compat/annotation/Android.bp b/java/android/compat/annotation/Android.bp
index fb88748..c607e5d 100644
--- a/java/android/compat/annotation/Android.bp
+++ b/java/android/compat/annotation/Android.bp
@@ -23,10 +23,11 @@
"EnabledAfter.java",
"LoggingOnly.java",
],
- // Allow core_current to use the annotations.
- sdk_version: "current",
+ sdk_version: "core_current",
+ exported_plugins: ["compat-changeid-annotation-processor"],
}
+// Don't forget to depend on exported_plugins, if using sources directly
filegroup {
name: "app-compat-annotations-source",
srcs: [
@@ -36,6 +37,7 @@
"LoggingOnly.java",
"UnsupportedAppUsage.java",
],
+ visibility: ["//libcore:__pkg__"],
}
java_library {
@@ -45,4 +47,5 @@
"UnsupportedAppUsage.java",
],
sdk_version: "core_current",
+ exported_plugins: ["unsupportedappusage-annotation-processor"],
}
diff --git a/java/android/processor/compat/Android.bp b/java/android/processor/compat/Android.bp
new file mode 100644
index 0000000..09721f7
--- /dev/null
+++ b/java/android/processor/compat/Android.bp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * 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.
+ */
+
+java_library_host {
+ name: "compat-processor",
+ srcs: [
+ "*.java",
+ ],
+ static_libs: [
+ "guava",
+ ],
+ openjdk9: {
+ javacflags: [
+ "--add-modules=jdk.compiler",
+ "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
+ },
+
+ visibility: ["//tools/platform-compat/java/android/processor/compat:__subpackages__"],
+}
diff --git a/java/android/processor/compat/SingleAnnotationProcessor.java b/java/android/processor/compat/SingleAnnotationProcessor.java
new file mode 100644
index 0000000..df5afe1
--- /dev/null
+++ b/java/android/processor/compat/SingleAnnotationProcessor.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.compat;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Table;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.LineMap;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.SourcePositions;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.Trees;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.QualifiedNameable;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+/**
+ * Abstract annotation processor that goes over annotated elements in bulk (per .class file).
+ *
+ * <p>It expects only one supported annotation, i.e. {@link #getSupportedAnnotationTypes()} must
+ * return one annotation type only.
+ *
+ * <p>Annotated elements are pre-filtered by {@link #ignoreAnnotatedElement(Element,
+ * AnnotationMirror)}. Afterwards, the table with package and enclosing element name into list of
+ * elements is generated and passed to {@link #process(TypeElement, Table)}.
+ */
+public abstract class SingleAnnotationProcessor extends AbstractProcessor {
+
+ protected Elements elements;
+ protected Messager messager;
+ protected SourcePositions sourcePositions;
+ protected Trees trees;
+ protected Types types;
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+
+ this.elements = processingEnv.getElementUtils();
+ this.messager = processingEnv.getMessager();
+ this.trees = Trees.instance(processingEnv);
+ this.types = processingEnv.getTypeUtils();
+
+ this.sourcePositions = trees.getSourcePositions();
+ }
+
+ @Override
+ public boolean process(
+ Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
+ if (annotations.size() == 0) {
+ // no annotations to process, doesn't really matter what we return here.
+ return true;
+ }
+
+ TypeElement annotation = Iterables.getOnlyElement(annotations);
+ String supportedAnnotation = Iterables.getOnlyElement(getSupportedAnnotationTypes());
+ Preconditions.checkState(supportedAnnotation.equals(annotation.toString()));
+
+ Table<PackageElement, String, List<Element>> annotatedElements = HashBasedTable.create();
+ for (Element annotatedElement : roundEnvironment.getElementsAnnotatedWith(annotation)) {
+ AnnotationMirror annotationMirror =
+ getSupportedAnnotationMirror(annotation, annotatedElement);
+ if (ignoreAnnotatedElement(annotatedElement, annotationMirror)) {
+ continue;
+ }
+
+ PackageElement packageElement = elements.getPackageOf(annotatedElement);
+ String enclosingElementName = getEnclosingElementName(annotatedElement);
+ Preconditions.checkNotNull(packageElement);
+ Preconditions.checkNotNull(enclosingElementName);
+
+ if (!annotatedElements.contains(packageElement, enclosingElementName)) {
+ annotatedElements.put(packageElement, enclosingElementName, new ArrayList<>());
+ }
+ annotatedElements.get(packageElement, enclosingElementName).add(annotatedElement);
+ }
+
+ process(annotation, annotatedElements);
+ return true;
+ }
+
+ /**
+ * Processes a set of elements annotated with supported annotation and not ignored via {@link
+ * #ignoreAnnotatedElement(Element, AnnotationMirror)}.
+ *
+ * @param annotation {@link TypeElement} of the supported annotation
+ * @param annotatedElements table with {@code package}, {@code enclosing elements name}, and the
+ * list of elements
+ */
+ protected abstract void process(TypeElement annotation,
+ Table<PackageElement, String, List<Element>> annotatedElements);
+
+ /** Whether to process given element with the annotation mirror. */
+ protected boolean ignoreAnnotatedElement(Element element, AnnotationMirror mirror) {
+ return false;
+ }
+
+ /**
+ * Returns the annotation mirror for the supported annotation on the given element.
+ *
+ * <p>We are not using a class to avoid choosing which Java 9 Module to select from, in case
+ * the annotation is present in base module and unnamed module.
+ */
+ protected final AnnotationMirror getSupportedAnnotationMirror(TypeElement annotation,
+ Element element) {
+ for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
+ if (types.isSameType(annotation.asType(), mirror.getAnnotationType())) {
+ return mirror;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns {@link SourcePosition} of an annotation on the given element or null if position is
+ * not found.
+ */
+ @Nullable
+ protected final SourcePosition getSourcePosition(Element element,
+ AnnotationMirror annotationMirror) {
+ TreePath path = trees.getPath(element, annotationMirror);
+ if (path == null) {
+ return null;
+ }
+ CompilationUnitTree compilationUnit = path.getCompilationUnit();
+ Tree tree = path.getLeaf();
+ long startPosition = sourcePositions.getStartPosition(compilationUnit, tree);
+ long endPosition = sourcePositions.getEndPosition(compilationUnit, tree);
+
+ LineMap lineMap = path.getCompilationUnit().getLineMap();
+ return new SourcePosition(
+ compilationUnit.getSourceFile().getName(),
+ lineMap.getLineNumber(startPosition),
+ lineMap.getColumnNumber(startPosition),
+ lineMap.getLineNumber(endPosition),
+ lineMap.getColumnNumber(endPosition));
+ }
+
+ @Nullable
+ protected final AnnotationValue getAnnotationValue(
+ Element element, AnnotationMirror annotation, String propertyName) {
+ return annotation.getElementValues().keySet().stream()
+ .filter(key -> propertyName.equals(key.getSimpleName().toString()))
+ .map(key -> annotation.getElementValues().get(key))
+ .reduce((a, b) -> {
+ throw new IllegalStateException(
+ String.format("Only one %s expected, found %s in %s",
+ propertyName, annotation, element));
+ })
+ .orElse(null);
+ }
+
+ /**
+ * Returns a name of an enclosing element without the package name.
+ *
+ * <p>This would return names of all enclosing classes, e.g. <code>Outer.Inner.Foo</code>.
+ */
+ private String getEnclosingElementName(Element element) {
+ String fullQualifiedName =
+ ((QualifiedNameable) element.getEnclosingElement()).getQualifiedName().toString();
+ String packageName = elements.getPackageOf(element).toString();
+ return fullQualifiedName.substring(packageName.length() + 1);
+ }
+
+}
diff --git a/java/android/processor/compat/SourcePosition.java b/java/android/processor/compat/SourcePosition.java
new file mode 100644
index 0000000..0a27899
--- /dev/null
+++ b/java/android/processor/compat/SourcePosition.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.compat;
+
+import java.util.Objects;
+
+/** POJO to represent source position of a tree in Java source file. */
+public final class SourcePosition {
+
+ private final String filename;
+ private final long startLineNumber;
+ private final long startColumnNumber;
+ private final long endLineNumber;
+ private final long endColumnNumber;
+
+ public SourcePosition(String filename, long startLineNumber, long startColumnNumber,
+ long endLineNumber, long endColumnNumber) {
+ this.filename = filename;
+ this.startLineNumber = startLineNumber;
+ this.startColumnNumber = startColumnNumber;
+ this.endLineNumber = endLineNumber;
+ this.endColumnNumber = endColumnNumber;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public long getStartLineNumber() {
+ return startLineNumber;
+ }
+
+ public long getStartColumnNumber() {
+ return startColumnNumber;
+ }
+
+ public long getEndLineNumber() {
+ return endLineNumber;
+ }
+
+ public long getEndColumnNumber() {
+ return endColumnNumber;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SourcePosition that = (SourcePosition) o;
+ return startLineNumber == that.startLineNumber
+ && startColumnNumber == that.startColumnNumber
+ && endLineNumber == that.endLineNumber
+ && endColumnNumber == that.endColumnNumber
+ && Objects.equals(filename, that.filename);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ filename,
+ startLineNumber,
+ startColumnNumber,
+ endLineNumber,
+ endColumnNumber);
+ }
+}
diff --git a/java/android/processor/compat/TEST_MAPPING b/java/android/processor/compat/TEST_MAPPING
new file mode 100644
index 0000000..adba4ca
--- /dev/null
+++ b/java/android/processor/compat/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "unsupportedappusage-processor-test",
+ "host" : true
+ },
+ {
+ "name": "compat-changeid-annotation-processor-test",
+ "host" : true
+ }
+ ]
+}
diff --git a/java/android/processor/compat/changeid/Android.bp b/java/android/processor/compat/changeid/Android.bp
index 2cfe509..3bd831a 100644
--- a/java/android/processor/compat/changeid/Android.bp
+++ b/java/android/processor/compat/changeid/Android.bp
@@ -25,6 +25,7 @@
static_libs: [
"guava",
+ "//tools/platform-compat/java/android/processor/compat:compat-processor",
],
openjdk9: {
@@ -44,8 +45,6 @@
name: "compat-changeid-annotation-processor",
processor_class: "android.processor.compat.changeid.ChangeIdProcessor",
- visibility: ["//visibility:public"],
-
java_resources: [
"META-INF/**/*",
],
@@ -56,4 +55,9 @@
installable: false,
use_tools_jar: true,
+
+ visibility: [
+ "//libcore:__pkg__",
+ "//tools/platform-compat/java/android/compat/annotation:__subpackages__",
+ ],
}
diff --git a/java/android/processor/compat/changeid/Change.java b/java/android/processor/compat/changeid/Change.java
index a3ea7b9..3054a67 100644
--- a/java/android/processor/compat/changeid/Change.java
+++ b/java/android/processor/compat/changeid/Change.java
@@ -16,13 +16,10 @@
package android.processor.compat.changeid;
-import com.google.common.annotations.VisibleForTesting;
-
/**
* Simple data class that represents a change, built from the code annotations.
*/
-@VisibleForTesting
-public class Change {
+final class Change {
final Long id;
final String name;
final boolean disabled;
@@ -46,8 +43,7 @@
*/
final String sourcePosition;
- @VisibleForTesting
- public Change(Long id, String name, boolean disabled, boolean loggingOnly, Integer enabledAfter,
+ Change(Long id, String name, boolean disabled, boolean loggingOnly, Integer enabledAfter,
String description, String javaPackage, String className, String qualifiedClass,
String sourcePosition) {
this.id = id;
@@ -74,7 +70,7 @@
String qualifiedClass;
String sourcePosition;
- public Builder() {
+ Builder() {
}
public Builder id(long id) {
@@ -117,7 +113,7 @@
return this;
}
- public Builder qualifedClass(String className) {
+ public Builder qualifiedClass(String className) {
this.qualifiedClass = className;
return this;
}
@@ -129,9 +125,7 @@
public Change build() {
return new Change(id, name, disabled, loggingOnly, enabledAfter, description,
- javaPackage, javaClass,
- qualifiedClass, sourcePosition);
+ javaPackage, javaClass, qualifiedClass, sourcePosition);
}
-
}
}
diff --git a/java/android/processor/compat/changeid/ChangeIdProcessor.java b/java/android/processor/compat/changeid/ChangeIdProcessor.java
index fd43183..6a7d1e8 100644
--- a/java/android/processor/compat/changeid/ChangeIdProcessor.java
+++ b/java/android/processor/compat/changeid/ChangeIdProcessor.java
@@ -16,43 +16,36 @@
package android.processor.compat.changeid;
+import static javax.lang.model.element.ElementKind.CLASS;
+import static javax.lang.model.element.ElementKind.PARAMETER;
import static javax.tools.Diagnostic.Kind.ERROR;
import static javax.tools.StandardLocation.CLASS_OUTPUT;
-import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
+import android.processor.compat.SingleAnnotationProcessor;
+import android.processor.compat.SourcePosition;
+
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.sun.tools.javac.model.JavacElements;
-import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.util.Pair;
-import com.sun.tools.javac.util.Position;
+import com.google.common.collect.Table;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.Objects;
import java.util.regex.Pattern;
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.Messager;
-import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.PackageElement;
-import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
+import javax.tools.FileObject;
/**
* Annotation processor for ChangeId annotations.
@@ -62,245 +55,181 @@
* Design doc: go/gating-and-logging.
*/
@SupportedAnnotationTypes({"android.compat.annotation.ChangeId"})
-public class ChangeIdProcessor extends AbstractProcessor {
+@SupportedSourceVersion(SourceVersion.RELEASE_9)
+public class ChangeIdProcessor extends SingleAnnotationProcessor {
private static final String CONFIG_XML = "compat_config.xml";
+ private static final String IGNORED_CLASS = "android.compat.Compatibility";
private static final ImmutableSet<String> IGNORED_METHOD_NAMES =
ImmutableSet.of("reportChange", "isChangeEnabled");
- private static final String IGNORED_CLASS = "android.compat.Compatibility";
- private static final String SUPPORTED_ANNOTATION =
+ private static final String CHANGE_ID_QUALIFIED_CLASS_NAME =
"android.compat.annotation.ChangeId";
private static final String DISABLED_CLASS_NAME = "android.compat.annotation.Disabled";
- private static final String LOGGING_CLASS_NAME = "android.compat.annotation.LoggingOnly";
private static final String ENABLED_AFTER_CLASS_NAME = "android.compat.annotation.EnabledAfter";
+ private static final String LOGGING_CLASS_NAME = "android.compat.annotation.LoggingOnly";
private static final String TARGET_SDK_VERSION = "targetSdkVersion";
private static final Pattern JAVADOC_SANITIZER = Pattern.compile("^\\s", Pattern.MULTILINE);
private static final Pattern HIDE_TAG_MATCHER = Pattern.compile("(\\s|^)@hide(\\s|$)");
- /**
- * Used as a map key when sharding by classname.
- */
- class PackageClass {
- final String javaPackage;
- final String javaClass;
+ @Override
+ protected void process(TypeElement annotation,
+ Table<PackageElement, String, List<Element>> annotatedElements) {
+ for (PackageElement packageElement : annotatedElements.rowKeySet()) {
+ for (String enclosingElementName : annotatedElements.row(packageElement).keySet()) {
+ XmlWriter writer = new XmlWriter();
+ for (Element element : annotatedElements.get(packageElement,
+ enclosingElementName)) {
+ Change change =
+ createChange(packageElement.toString(), enclosingElementName, element);
+ writer.addChange(change);
+ }
- PackageClass(String pkg, String cls) {
- this.javaPackage = pkg;
- this.javaClass = cls;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof PackageClass) {
- PackageClass that = (PackageClass) obj;
- return Objects.equal(this.javaPackage, that.javaPackage) &&
- Objects.equal(this.javaClass, that.javaClass);
+ try {
+ FileObject resource = processingEnv.getFiler().createResource(
+ CLASS_OUTPUT, packageElement.toString(),
+ enclosingElementName + "_" + CONFIG_XML);
+ try (OutputStream outputStream = resource.openOutputStream()) {
+ writer.write(outputStream);
+ }
+ } catch (IOException e) {
+ messager.printMessage(ERROR, "Failed to write output: " + e);
+ }
}
- return false;
- }
-
- public int hashCode() {
- return Objects.hashCode(javaPackage, javaClass);
}
}
@Override
- public SourceVersion getSupportedSourceVersion() {
- return SourceVersion.latest();
- }
-
- /**
- * This is the main entry point in the processor, called by the compiler.
- */
- @Override
- public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(
- processingEnv.getElementUtils().getTypeElement(
- SUPPORTED_ANNOTATION));
- if (annotatedElements.isEmpty()) {
- return true;
- }
-
- Map<PackageClass, XmlWriter> writersByClass = new HashMap<>();
-
- for (Element e : annotatedElements) {
- if (!isValidChangeId(e, processingEnv.getMessager())) {
- continue;
- }
- Change change = createChange(e, processingEnv.getMessager(),
- processingEnv.getElementUtils().getDocComment(e));
- PackageClass key = new PackageClass(change.javaPackage, change.className);
- XmlWriter writer = writersByClass.get(key);
- if (writer == null) {
- writer = new XmlWriter();
- writersByClass.put(key, writer);
- }
- writer.addChange(change);
- }
-
- for (Map.Entry<PackageClass, XmlWriter> entry : writersByClass.entrySet()) {
- PackageClass key = entry.getKey();
- try (OutputStream output = processingEnv.getFiler().createResource(
- CLASS_OUTPUT,
- key.javaPackage,
- key.javaClass + "_" + CONFIG_XML)
- .openOutputStream()) {
- entry.getValue().write(output);
- } catch (IOException e) {
- throw new RuntimeException("Failed to write output for " + entry.getKey(), e);
- }
- }
-
-
- return true;
- }
-
-
- private boolean shouldIgnoreAnnotation(Element e) {
- // Just ignore the annotations on function known methods in package android.compat
+ protected boolean ignoreAnnotatedElement(Element element, AnnotationMirror mirror) {
+ // Ignore the annotations on method parameters in known methods in package android.compat
// (libcore/luni/src/main/java/android/compat/Compatibility.java)
// without generating an error.
- return (e.getKind() == ElementKind.PARAMETER
- && e.getEnclosingElement().getKind() == ElementKind.METHOD
- && IGNORED_METHOD_NAMES.contains(e.getEnclosingElement().getSimpleName().toString())
- && e.getEnclosingElement().getEnclosingElement().getKind() == ElementKind.CLASS
- && ((TypeElement) e.getEnclosingElement().getEnclosingElement()).getQualifiedName()
- .toString().equals(IGNORED_CLASS));
+ if (element.getKind() == PARAMETER) {
+ Element enclosingMethod = element.getEnclosingElement();
+ Element enclosingElement = enclosingMethod.getEnclosingElement();
+ if (enclosingElement.getKind() == CLASS) {
+ if (enclosingElement.toString().equals(IGNORED_CLASS) &&
+ IGNORED_METHOD_NAMES.contains(enclosingMethod.getSimpleName().toString())) {
+ return true;
+ }
+ }
+ }
+ return !isValidChangeId(element);
}
/**
* Checks if the provided java element is a valid change id (i.e. a long parameter with a
* constant value).
*
- * @param e java element to check.
- * @param messager updated with compilation errors if the annotated element is not valid.
+ * @param element java element to check.
* @return true if the provided element is a legal change id that should be added to the
- * produced XML file. If true is returned it's guaranteed that the following
- * operations are safe.
+ * produced XML file. If true is returned it's guaranteed that the following operations are
+ * safe.
*/
- private boolean isValidChangeId(Element e, Messager messager) {
- if (shouldIgnoreAnnotation(e)) {
- return false;
- }
- if (e.getKind() != ElementKind.FIELD) {
+ private boolean isValidChangeId(Element element) {
+ if (element.getKind() != ElementKind.FIELD) {
messager.printMessage(
ERROR,
- String.format(
- "Non field element %s annotated with @ChangeId. Got type "
- + "%s, expected FIELD.",
- e.getSimpleName().toString(), e.getKind().toString()),
- e);
+ "Non FIELD element annotated with @ChangeId.",
+ element);
return false;
}
- if (!(e instanceof VariableElement)) {
+ if (!(element instanceof VariableElement)) {
messager.printMessage(
ERROR,
- String.format(
- "Non variable %s annotated with @ChangeId.",
- e.getSimpleName().toString()),
- e);
+ "Non variable annotated with @ChangeId.",
+ element);
return false;
}
- if (((VariableElement) e).getConstantValue() == null) {
+ if (((VariableElement) element).getConstantValue() == null) {
messager.printMessage(
ERROR,
- String.format(
- "Non constant/final variable %s (or non constant value) "
- + "annotated with @ChangeId.",
- e.getSimpleName().toString()),
- e);
+ "Non constant/final variable annotated with @ChangeId.",
+ element);
return false;
}
- if (e.asType().getKind() != TypeKind.LONG) {
+ if (element.asType().getKind() != TypeKind.LONG) {
messager.printMessage(
ERROR,
- "Variables annotated with @ChangeId should be of type long.",
- e);
+ "Variables annotated with @ChangeId must be of type long.",
+ element);
return false;
}
- if (!e.getModifiers().contains(Modifier.STATIC)) {
+ if (!element.getModifiers().contains(Modifier.STATIC)) {
messager.printMessage(
ERROR,
- String.format(
- "Non static variable %s annotated with @ChangeId.",
- e.getSimpleName().toString()),
- e);
+ "Non static variable annotated with @ChangeId.",
+ element);
return false;
}
return true;
}
- private String getSourcePosition(Element e, AnnotationMirror a) {
- JavacElements javacElem = (JavacElements) processingEnv.getElementUtils();
- Pair<JCTree, JCTree.JCCompilationUnit> pair = javacElem.getTreeAndTopLevel(e, a, null);
- Position.LineMap lines = pair.snd.lineMap;
- return String.format("%s:%d", pair.snd.getSourceFile().getName(),
- lines.getLineNumber(pair.fst.pos().getStartPosition()));
- }
-
- private Change createChange(Element e, Messager messager, String comment) {
+ private Change createChange(String packageName, String enclosingElementName, Element element) {
Change.Builder builder = new Change.Builder()
- .id((Long) ((VariableElement) e).getConstantValue())
- .name(e.getSimpleName().toString());
+ .id((Long) ((VariableElement) element).getConstantValue())
+ .name(element.getSimpleName().toString());
AnnotationMirror changeId = null;
- for (AnnotationMirror m : e.getAnnotationMirrors()) {
+ for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
String type =
- ((TypeElement) m.getAnnotationType().asElement()).getQualifiedName().toString();
- if (type.equals(DISABLED_CLASS_NAME)) {
- builder.disabled();
- } else if (type.equals(LOGGING_CLASS_NAME)) {
- builder.loggingOnly();
- } else if (type.equals(ENABLED_AFTER_CLASS_NAME)) {
- for (Map.Entry<?, ?> entry : m.getElementValues().entrySet()) {
- String key = ((ExecutableElement) entry.getKey()).getSimpleName().toString();
- if (key.equals(TARGET_SDK_VERSION)) {
- builder.enabledAfter(
- (Integer) ((AnnotationValue) entry.getValue()).getValue());
- }
- }
- } else if (type.equals(SUPPORTED_ANNOTATION)) {
- changeId = m;
+ ((TypeElement) mirror.getAnnotationType().asElement()).getQualifiedName().toString();
+ switch (type) {
+ case DISABLED_CLASS_NAME:
+ builder.disabled();
+ break;
+ case LOGGING_CLASS_NAME:
+ builder.loggingOnly();
+ break;
+ case ENABLED_AFTER_CLASS_NAME:
+ AnnotationValue value = getAnnotationValue(element, mirror, TARGET_SDK_VERSION);
+ builder.enabledAfter((Integer)(Objects.requireNonNull(value).getValue()));
+ break;
+ case CHANGE_ID_QUALIFIED_CLASS_NAME:
+ changeId = mirror;
+ break;
}
}
+ String comment =
+ elements.getDocComment(element);
if (comment != null) {
comment = HIDE_TAG_MATCHER.matcher(comment).replaceAll("");
comment = JAVADOC_SANITIZER.matcher(comment).replaceAll("");
- builder.description(comment.replaceAll("\\n"," ").trim());
+ comment = comment.replaceAll("\\n", " ");
+ builder.description(comment.trim());
}
- // TODO(satayev): move common processors code to android.processor.compat.
- String packageName = processingEnv.getElementUtils().getPackageOf(e).toString();
- String enclosingElementName = ((QualifiedNameable) e.getEnclosingElement()).getQualifiedName().toString();
- String className = enclosingElementName.substring(packageName.length() + 1);
+ return verifyChange(element,
+ builder.javaClass(enclosingElementName)
+ .javaPackage(packageName)
+ .qualifiedClass(packageName + "." + enclosingElementName)
+ .sourcePosition(getLineNumber(element, changeId))
+ .build());
+ }
- Change change = builder.javaClass(className)
- .javaPackage(packageName)
- .qualifedClass(enclosingElementName)
- .sourcePosition(getSourcePosition(e, changeId))
- .build();
+ private String getLineNumber(Element element, AnnotationMirror mirror) {
+ SourcePosition position = Objects.requireNonNull(getSourcePosition(element, mirror));
+ return String.format("%s:%d", position.getFilename(), position.getStartLineNumber());
+ }
+ private Change verifyChange(Element element, Change change) {
if (change.disabled && change.enabledAfter != null) {
messager.printMessage(
ERROR,
"ChangeId cannot be annotated with both @Disabled and @EnabledAfter.",
- e);
+ element);
}
-
if (change.loggingOnly && (change.disabled || change.enabledAfter != null)) {
messager.printMessage(
ERROR,
- "ChangeId cannot be annotated with both @LoggingOnly and @EnabledAfter or "
- + "@Disabled.",
- e);
+ "ChangeId cannot be annotated with both @LoggingOnly and "
+ + "(@EnabledAfter | @Disabled).",
+ element);
}
-
return change;
}
-
}
diff --git a/java/android/processor/compat/changeid/TEST_MAPPING b/java/android/processor/compat/changeid/TEST_MAPPING
deleted file mode 100644
index 06a8fe6..0000000
--- a/java/android/processor/compat/changeid/TEST_MAPPING
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "presubmit": [
- {
- "name": "compat-changeid-annotation-processor-test",
- "host" : true
- }
- ]
-}
diff --git a/java/android/processor/compat/changeid/XmlWriter.java b/java/android/processor/compat/changeid/XmlWriter.java
index 484c0ba..eabacdb 100644
--- a/java/android/processor/compat/changeid/XmlWriter.java
+++ b/java/android/processor/compat/changeid/XmlWriter.java
@@ -16,11 +16,10 @@
package android.processor.compat.changeid;
-import com.google.common.annotations.VisibleForTesting;
-
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+import java.io.IOException;
import java.io.OutputStream;
import javax.xml.parsers.DocumentBuilder;
@@ -39,9 +38,11 @@
* {@code
* <config>
* <compat-change id="111" name="change-name1">
- * <meta-data definedIn="java.package.ClassName" sourcePosition="java/package/ClassName.java:10" />
+ * <meta-data definedIn="java.package.ClassName" sourcePosition="java/package/ClassName
+ * .java:10" />
* </compat-change>
- * <compat-change disabled="true" id="222" loggingOnly= "true" name="change-name2" description="my change">
+ * <compat-change disabled="true" id="222" loggingOnly= "true" name="change-name2"
+ * description="my change">
* <meta-data .../>
* </compat-change>
* <compat-change enableAfterTargetSdk="28" id="333" name="change-name3">
@@ -54,10 +55,8 @@
*
* The inner {@code meta-data} tags are intended to be stripped before embedding the config on a
* device. They are intended for use by intermediate build tools only.
- *
*/
-@VisibleForTesting
-public final class XmlWriter {
+final class XmlWriter {
//XML tags
private static final String XML_ROOT = "config";
private static final String XML_CHANGE_ELEMENT = "compat-change";
@@ -74,15 +73,13 @@
private Document mDocument;
private Element mRoot;
- @VisibleForTesting
- public XmlWriter() {
+ XmlWriter() {
mDocument = createDocument();
mRoot = mDocument.createElement(XML_ROOT);
mDocument.appendChild(mRoot);
}
- @VisibleForTesting
- public void addChange(Change change) {
+ void addChange(Change change) {
Element newElement = mDocument.createElement(XML_CHANGE_ELEMENT);
newElement.setAttribute(XML_NAME_ATTR, change.name);
newElement.setAttribute(XML_ID_ATTR, change.id.toString());
@@ -111,8 +108,7 @@
mRoot.appendChild(newElement);
}
- @VisibleForTesting
- public void write(OutputStream output) {
+ void write(OutputStream output) throws IOException {
try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
@@ -122,7 +118,7 @@
transformer.transform(domSource, result);
} catch (TransformerException e) {
- throw new RuntimeException("Failed to write output", e);
+ throw new IOException("Failed to write output", e);
}
}
diff --git a/java/android/processor/compat/unsupportedappusage/Android.bp b/java/android/processor/compat/unsupportedappusage/Android.bp
index dead2b2..38d35eb 100644
--- a/java/android/processor/compat/unsupportedappusage/Android.bp
+++ b/java/android/processor/compat/unsupportedappusage/Android.bp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
java_library_host {
name: "unsupportedappusage-annotation-processor-lib",
srcs: [
@@ -22,6 +21,7 @@
],
static_libs: [
"guava",
+ "//tools/platform-compat/java/android/processor/compat:compat-processor",
],
openjdk9: {
javacflags: [
@@ -48,4 +48,9 @@
],
use_tools_jar: true,
+
+ visibility: [
+ "//libcore:__pkg__",
+ "//tools/platform-compat/java/android/compat/annotation:__subpackages__",
+ ],
}
diff --git a/java/android/processor/compat/unsupportedappusage/TEST_MAPPING b/java/android/processor/compat/unsupportedappusage/TEST_MAPPING
deleted file mode 100644
index 6942c03..0000000
--- a/java/android/processor/compat/unsupportedappusage/TEST_MAPPING
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "presubmit": [
- {
- "name": "unsupportedappusage-processor-test",
- "host" : true
- }
- ]
-}
diff --git a/java/android/processor/compat/unsupportedappusage/UnsupportedAppUsageProcessor.java b/java/android/processor/compat/unsupportedappusage/UnsupportedAppUsageProcessor.java
index 771a34d..3773b6b 100644
--- a/java/android/processor/compat/unsupportedappusage/UnsupportedAppUsageProcessor.java
+++ b/java/android/processor/compat/unsupportedappusage/UnsupportedAppUsageProcessor.java
@@ -15,33 +15,26 @@
*/
package android.processor.compat.unsupportedappusage;
+import static javax.tools.Diagnostic.Kind.ERROR;
import static javax.tools.StandardLocation.CLASS_OUTPUT;
+import android.processor.compat.SingleAnnotationProcessor;
+import android.processor.compat.SourcePosition;
+
import com.google.common.base.Joiner;
-import com.google.common.collect.Iterables;
-import com.sun.source.tree.CompilationUnitTree;
-import com.sun.source.tree.LineMap;
-import com.sun.source.tree.Tree;
-import com.sun.source.util.SourcePositions;
-import com.sun.source.util.TreePath;
-import com.sun.source.util.Trees;
+import com.google.common.collect.Table;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.TreeMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.Messager;
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.Nullable;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
@@ -49,9 +42,9 @@
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.Types;
-import javax.tools.Diagnostic;
+import javax.tools.FileObject;
/**
* Annotation processor for {@code UnsupportedAppUsage} annotation.
@@ -62,107 +55,73 @@
*/
@SupportedAnnotationTypes({"android.compat.annotation.UnsupportedAppUsage"})
@SupportedSourceVersion(SourceVersion.RELEASE_9)
-public class UnsupportedAppUsageProcessor extends AbstractProcessor {
+public final class UnsupportedAppUsageProcessor extends SingleAnnotationProcessor {
- // Package name for writing output. Output will be written to the "class output" location within
- // this package.
- private static final String PACKAGE = "unsupportedappusage";
- private static final String INDEX_CSV = "unsupportedappusage_index.csv";
+ private static final String GENERATED_INDEX_FILE_EXTENSION = ".uau";
private static final String OVERRIDE_SOURCE_POSITION_PROPERTY = "overrideSourcePosition";
private static final Pattern OVERRIDE_SOURCE_POSITION_PROPERTY_PATTERN = Pattern.compile(
"^[^:]+:\\d+:\\d+:\\d+:\\d+$");
- private Messager messager;
- private SourcePositions sourcePositions;
- private Trees trees;
- private Types types;
-
- public synchronized void init(ProcessingEnvironment processingEnv) {
- super.init(processingEnv);
-
- this.messager = processingEnv.getMessager();
- this.trees = Trees.instance(processingEnv);
- this.types = processingEnv.getTypeUtils();
-
- this.sourcePositions = trees.getSourcePositions();
- }
-
/**
- * This is the main entry point in the processor, called by the compiler.
+ * CSV header line for the columns returned by {@link #getAnnotationIndex(String, TypeElement,
+ * Element)}.
*/
+ private static final String CSV_HEADER = Joiner.on(',').join(
+ "signature",
+ "file",
+ "startline",
+ "startcol",
+ "endline",
+ "endcol",
+ "properties"
+ );
+
@Override
- public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- if (annotations.size() == 0) {
- return true;
- }
-
- Map<String, Element> signatureMap = new TreeMap<>();
+ protected void process(TypeElement annotation,
+ Table<PackageElement, String, List<Element>> annotatedElements) {
SignatureConverter signatureConverter = new SignatureConverter(messager);
- TypeElement annotation = Iterables.getOnlyElement(annotations);
- for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(annotation)) {
- AnnotationMirror annotationMirror =
- getUnsupportedAppUsageAnnotationMirror(annotation, annotatedElement);
- if (hasElement(annotationMirror, "implicitMember")) {
- // Implicit member refers to member not present in code, ignore.
- continue;
- }
- String signature = signatureConverter.getSignature(
- types, annotation, annotatedElement);
- if (signature != null) {
- signatureMap.put(signature, annotatedElement);
+ for (PackageElement packageElement : annotatedElements.rowKeySet()) {
+ Map<String, List<Element>> row = annotatedElements.row(packageElement);
+ for (String enclosingElementName : row.keySet()) {
+ List<String> content = new ArrayList<>();
+ for (Element annotatedElement : row.get(enclosingElementName)) {
+ String signature = signatureConverter.getSignature(
+ types, annotation, annotatedElement);
+ if (signature != null) {
+ String annotationIndex = getAnnotationIndex(signature, annotation,
+ annotatedElement);
+ if (annotationIndex != null) {
+ content.add(annotationIndex);
+ }
+ }
+ }
+
+ if (content.isEmpty()) {
+ continue;
+ }
+
+ try {
+ FileObject resource = processingEnv.getFiler().createResource(
+ CLASS_OUTPUT,
+ packageElement.toString(),
+ enclosingElementName + GENERATED_INDEX_FILE_EXTENSION);
+ try (PrintStream outputStream = new PrintStream(resource.openOutputStream())) {
+ outputStream.println(CSV_HEADER);
+ content.forEach(outputStream::println);
+ }
+ } catch (IOException exception) {
+ messager.printMessage(ERROR, "Could not write CSV file: " + exception);
+ }
}
}
-
- if (!signatureMap.isEmpty()) {
- try {
- writeToFile(INDEX_CSV,
- getCsvHeaders(),
- signatureMap.entrySet()
- .stream()
- .map(e -> getAnnotationIndex(e.getKey(), annotation,
- e.getValue())));
- } catch (IOException e) {
- throw new RuntimeException("Failed to write output", e);
- }
- }
- return true;
}
- /**
- * Write the contents of a stream to a text file, with one line per item.
- */
- private void writeToFile(String name,
- String headerLine,
- Stream<?> contents) throws IOException {
- PrintStream out = new PrintStream(processingEnv.getFiler().createResource(
- CLASS_OUTPUT,
- PACKAGE,
- name)
- .openOutputStream());
- out.println(headerLine);
- contents.forEach(o -> out.println(o));
- if (out.checkError()) {
- throw new IOException("Error when writing to " + name);
- }
- out.close();
- }
-
- /**
- * Returns a CSV header line for the columns returned by
- * {@link #getAnnotationIndex(String, TypeElement, Element)}.
- */
- private String getCsvHeaders() {
- return Joiner.on(',').join(
- "signature",
- "file",
- "startline",
- "startcol",
- "endline",
- "endcol",
- "properties"
- );
+ @Override
+ protected boolean ignoreAnnotatedElement(Element element, AnnotationMirror mirror) {
+ // Implicit member refers to member not present in code, ignore.
+ return hasElement(mirror, "implicitMember");
}
/**
@@ -173,79 +132,46 @@
* dex-signature,filename,start-line,start-col,end-line,end-col,properties
*
* <p>The positions refer to the annotation itself, *not* the annotated member. This can
- * therefore be used to read just the annotation from the file, and to perform in-place edits on
- * it.
+ * therefore be used to read just the annotation from the file, and to perform in-place
+ * edits on it.
*
* @return A single line of CSV text
*/
+ @Nullable
private String getAnnotationIndex(String signature, TypeElement annotation, Element element) {
- AnnotationMirror annotationMirror =
- getUnsupportedAppUsageAnnotationMirror(annotation, element);
-
+ AnnotationMirror annotationMirror = getSupportedAnnotationMirror(annotation, element);
String position = getSourcePositionOverride(element, annotationMirror);
if (position == null) {
- position = getSourcePosition(element, annotationMirror);
+ SourcePosition sourcePosition = getSourcePosition(element, annotationMirror);
+ if (sourcePosition == null) {
+ return null;
+ }
+ position = Joiner.on(",").join(
+ sourcePosition.getFilename(),
+ sourcePosition.getStartLineNumber(),
+ sourcePosition.getStartColumnNumber(),
+ sourcePosition.getEndLineNumber(),
+ sourcePosition.getEndColumnNumber());
}
return Joiner.on(",").join(
signature,
position,
- getProperties(annotationMirror));
+ getAllProperties(annotationMirror));
}
- /**
- * Find the annotation mirror for the @UnsupportedAppUsage annotation on the given element.
- */
- private AnnotationMirror getUnsupportedAppUsageAnnotationMirror(TypeElement annotation,
- Element element) {
- for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
- TypeElement type = (TypeElement) mirror.getAnnotationType().asElement();
- if (types.isSameType(annotation.asType(), mirror.getAnnotationType())) {
- return mirror;
- }
- }
- return null;
- }
-
- private String getSourcePosition(Element element, AnnotationMirror annotationMirror) {
- TreePath path = trees.getPath(element, annotationMirror);
- CompilationUnitTree compilationUnit = path.getCompilationUnit();
- Tree tree = path.getLeaf();
- long startPosition = sourcePositions.getStartPosition(compilationUnit, tree);
- long endPosition = sourcePositions.getEndPosition(compilationUnit, tree);
-
- LineMap lineMap = path.getCompilationUnit().getLineMap();
- return Joiner.on(",").join(
- compilationUnit.getSourceFile().getName(),
- lineMap.getLineNumber(startPosition),
- lineMap.getColumnNumber(startPosition),
- lineMap.getLineNumber(endPosition),
- lineMap.getColumnNumber(endPosition));
- }
-
- private String getSourcePositionOverride(Element annotatedElement,
- AnnotationMirror annotation) {
- Optional<? extends AnnotationValue> annotationValue =
- annotation.getElementValues().keySet().stream()
- .filter(key -> key.getSimpleName().toString().equals(
- OVERRIDE_SOURCE_POSITION_PROPERTY))
- .map(key -> annotation.getElementValues().get(key))
- .reduce((a, b) -> {
- throw new IllegalStateException(
- String.format("Only one %s expected, found %s in %s",
- OVERRIDE_SOURCE_POSITION_PROPERTY, annotation,
- annotatedElement));
- });
-
- if (!annotationValue.isPresent()) {
+ @Nullable
+ private String getSourcePositionOverride(Element element, AnnotationMirror annotation) {
+ AnnotationValue annotationValue =
+ getAnnotationValue(element, annotation, OVERRIDE_SOURCE_POSITION_PROPERTY);
+ if (annotationValue == null) {
return null;
}
- String parameterValue = annotationValue.get().getValue().toString();
-
+ String parameterValue = annotationValue.getValue().toString();
if (!OVERRIDE_SOURCE_POSITION_PROPERTY_PATTERN.matcher(parameterValue).matches()) {
- messager.printMessage(Diagnostic.Kind.ERROR, String.format(
+ messager.printMessage(ERROR, String.format(
"Expected %s to have format string:int:int:int:int",
- OVERRIDE_SOURCE_POSITION_PROPERTY), annotatedElement, annotation);
+ OVERRIDE_SOURCE_POSITION_PROPERTY), element, annotation);
return null;
}
@@ -257,7 +183,7 @@
key -> elementName.equals(key.getSimpleName().toString()));
}
- private String getProperties(AnnotationMirror annotation) {
+ private String getAllProperties(AnnotationMirror annotation) {
return annotation.getElementValues().keySet().stream()
.filter(key -> !key.getSimpleName().toString().equals(
OVERRIDE_SOURCE_POSITION_PROPERTY))
@@ -278,5 +204,4 @@
}
}
-
}
diff --git a/javatest/android/processor/compat/changeid/ChangeIdProcessorTest.java b/javatest/android/processor/compat/changeid/ChangeIdProcessorTest.java
index a94a874..dcb47d4 100644
--- a/javatest/android/processor/compat/changeid/ChangeIdProcessorTest.java
+++ b/javatest/android/processor/compat/changeid/ChangeIdProcessorTest.java
@@ -20,18 +20,16 @@
import static javax.tools.StandardLocation.CLASS_OUTPUT;
-import com.google.testing.compile.JavaFileObjects;
-import com.google.testing.compile.Compilation;
-import com.google.testing.compile.Compiler;
-import com.google.testing.compile.CompilationSubject;
import com.google.common.collect.ObjectArrays;
-
-import com.google.common.io.ByteSource;
-
-import javax.tools.JavaFileObject;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.CompilationSubject;
+import com.google.testing.compile.Compiler;
+import com.google.testing.compile.JavaFileObjects;
import org.junit.Test;
+import javax.tools.JavaFileObject;
+
public class ChangeIdProcessorTest {
@@ -120,7 +118,7 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).succeeded();
CompilationSubject.assertThat(compilation).generatedFile(CLASS_OUTPUT, "libcore.util",
"Compat_compat_config.xml").contentsAsString(UTF_8).isEqualTo(expectedFile);
@@ -168,12 +166,13 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).succeeded();
CompilationSubject.assertThat(compilation).generatedFile(CLASS_OUTPUT, "libcore.util",
"Compat_compat_config.xml").contentsAsString(UTF_8).isEqualTo(libcoreExpectedFile);
CompilationSubject.assertThat(compilation).generatedFile(CLASS_OUTPUT, "android.util",
- "SomeClass_compat_config.xml").contentsAsString(UTF_8).isEqualTo(androidExpectedFile);
+ "SomeClass_compat_config.xml").contentsAsString(UTF_8).isEqualTo(
+ androidExpectedFile);
}
@Test
@@ -204,7 +203,7 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).succeeded();
CompilationSubject.assertThat(compilation).generatedFile(CLASS_OUTPUT, "libcore.util",
"Compat.Inner_compat_config.xml").contentsAsString(UTF_8).isEqualTo(expectedFile);
@@ -236,7 +235,7 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).succeeded();
CompilationSubject.assertThat(compilation).generatedFile(CLASS_OUTPUT, "libcore.util",
"Compat_compat_config.xml").contentsAsString(UTF_8).isEqualTo(expectedFile);
@@ -269,7 +268,7 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).succeeded();
CompilationSubject.assertThat(compilation).generatedFile(CLASS_OUTPUT, "libcore.util",
"Compat_compat_config.xml").contentsAsString(UTF_8).isEqualTo(expectedFile);
@@ -321,8 +320,8 @@
.withProcessors(new ChangeIdProcessor())
.compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
- "ChangeId cannot be annotated with both @LoggingOnly and @EnabledAfter or "
- + "@Disabled.");
+ "ChangeId cannot be annotated with both @LoggingOnly and "
+ + "(@EnabledAfter | @Disabled).");
}
@Test
@@ -346,8 +345,8 @@
.withProcessors(new ChangeIdProcessor())
.compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
- "ChangeId cannot be annotated with both @LoggingOnly and @EnabledAfter or "
- + "@Disabled.");
+ "ChangeId cannot be annotated with both @LoggingOnly and "
+ + "(@EnabledAfter | @Disabled).");
}
@Test
@@ -377,6 +376,7 @@
CompilationSubject.assertThat(compilation).generatedFile(CLASS_OUTPUT, "libcore.util",
"Compat_compat_config.xml").contentsAsString(UTF_8).isEqualTo(expectedFile);
}
+
@Test
public void testIgnoredParams() {
JavaFileObject[] source = {
@@ -394,7 +394,7 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).succeeded();
}
@@ -412,10 +412,9 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
- "Non field element changeId annotated with @ChangeId. Got type PARAMETER, "
- + "expected FIELD.");
+ "Non FIELD element annotated with @ChangeId.");
}
@Test
@@ -432,10 +431,9 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
- "Non field element changeId annotated with @ChangeId. Got type PARAMETER, "
- + "expected FIELD.");
+ "Non FIELD element annotated with @ChangeId.");
}
@Test
@@ -455,10 +453,9 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
- "Non constant/final variable MY_CHANGE_ID (or non constant value) annotated with "
- + "@ChangeId.");
+ "Non constant/final variable annotated with @ChangeId.");
}
@Test
@@ -478,9 +475,9 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
- "Variables annotated with @ChangeId should be of type long.");
+ "Variables annotated with @ChangeId must be of type long.");
}
@Test
@@ -500,9 +497,9 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).hadErrorContaining(
- "Non static variable MY_CHANGE_ID annotated with @ChangeId.");
+ "Non static variable annotated with @ChangeId.");
}
@Test
@@ -533,7 +530,7 @@
Compilation compilation =
Compiler.javac()
.withProcessors(new ChangeIdProcessor())
- .compile(ObjectArrays.concat(mAnnotations,source, JavaFileObject.class));
+ .compile(ObjectArrays.concat(mAnnotations, source, JavaFileObject.class));
CompilationSubject.assertThat(compilation).succeeded();
CompilationSubject.assertThat(compilation).generatedFile(CLASS_OUTPUT, "libcore.util",
"Compat.Inner_compat_config.xml").contentsAsString(UTF_8).isEqualTo(expectedFile);
diff --git a/javatest/android/processor/compat/changeid/TEST_MAPPING b/javatest/android/processor/compat/changeid/TEST_MAPPING
index 27e7926..8b15351 100644
--- a/javatest/android/processor/compat/changeid/TEST_MAPPING
+++ b/javatest/android/processor/compat/changeid/TEST_MAPPING
@@ -1,7 +1,7 @@
{
"imports": [
{
- "path": "java/android/processor/compat/changeid"
+ "path": "java/android/processor/compat"
}
]
}
diff --git a/javatest/android/processor/compat/changeid/XmlWriterTest.java b/javatest/android/processor/compat/changeid/XmlWriterTest.java
index a6843d9..f401fc6 100644
--- a/javatest/android/processor/compat/changeid/XmlWriterTest.java
+++ b/javatest/android/processor/compat/changeid/XmlWriterTest.java
@@ -24,7 +24,6 @@
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
-
public class XmlWriterTest {
private static final String HEADER =
@@ -33,7 +32,7 @@
private OutputStream mOutputStream = new ByteArrayOutputStream();
@Test
- public void testNoChanges() {
+ public void testNoChanges() throws Exception {
XmlWriter writer = new XmlWriter();
writer.write(mOutputStream);
@@ -43,7 +42,7 @@
}
@Test
- public void testOneChange() {
+ public void testOneChange() throws Exception {
XmlWriter writer = new XmlWriter();
Change c = new Change.Builder()
.id(123456789L)
@@ -61,7 +60,7 @@
}
@Test
- public void testSomeChanges() {
+ public void testSomeChanges() throws Exception {
XmlWriter writer = new XmlWriter();
Change c = new Change.Builder()
.id(111L)
diff --git a/javatest/android/processor/compat/unsupportedappusage/TEST_MAPPING b/javatest/android/processor/compat/unsupportedappusage/TEST_MAPPING
index 6e4d471..8b15351 100644
--- a/javatest/android/processor/compat/unsupportedappusage/TEST_MAPPING
+++ b/javatest/android/processor/compat/unsupportedappusage/TEST_MAPPING
@@ -1,7 +1,7 @@
{
"imports": [
{
- "path": "java/android/processor/compat/unsupportedappusage"
+ "path": "java/android/processor/compat"
}
]
}
diff --git a/javatest/android/processor/compat/unsupportedappusage/UnsupportedAppUsageProcessorTest.java b/javatest/android/processor/compat/unsupportedappusage/UnsupportedAppUsageProcessorTest.java
index 51a0822..65b1a2d 100644
--- a/javatest/android/processor/compat/unsupportedappusage/UnsupportedAppUsageProcessorTest.java
+++ b/javatest/android/processor/compat/unsupportedappusage/UnsupportedAppUsageProcessorTest.java
@@ -26,8 +26,10 @@
import org.junit.Test;
import java.io.IOException;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.stream.Collectors;
import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;
@@ -41,15 +43,21 @@
" String expectedSignature() default \"\";\n",
" String someProperty() default \"\";",
" String overrideSourcePosition() default \"\";",
+ " String implicitMember() default \"\";",
"}");
- private CsvReader compileAndReadCsv(JavaFileObject source) throws IOException {
+ private Compilation compile(JavaFileObject source) {
Compilation compilation =
Compiler.javac().withProcessors(new UnsupportedAppUsageProcessor())
.compile(ANNOTATION, source);
CompilationSubject.assertThat(compilation).succeeded();
- Optional<JavaFileObject> csv = compilation.generatedFile(StandardLocation.CLASS_OUTPUT,
- "unsupportedappusage/unsupportedappusage_index.csv");
+ return compilation;
+ }
+
+ private CsvReader compileAndReadCsv(JavaFileObject source, String filename) throws IOException {
+ Compilation compilation = compile(source);
+ Optional<JavaFileObject> csv = compilation.generatedFile(
+ StandardLocation.CLASS_OUTPUT, filename);
assertThat(csv.isPresent()).isTrue();
return new CsvReader(csv.get().openInputStream());
@@ -64,7 +72,7 @@
" @UnsupportedAppUsage",
" public void method() {}",
"}");
- assertThat(compileAndReadCsv(src).getContents().get(0)).containsEntry(
+ assertThat(compileAndReadCsv(src, "a/b/Class.uau").getContents().get(0)).containsEntry(
"signature", "La/b/Class;->method()V"
);
}
@@ -78,7 +86,7 @@
" @UnsupportedAppUsage", // 4
" public void method() {}", // 5
"}");
- Map<String, String> row = compileAndReadCsv(src).getContents().get(0);
+ Map<String, String> row = compileAndReadCsv(src, "a/b/Class.uau").getContents().get(0);
assertThat(row).containsEntry("startline", "4");
assertThat(row).containsEntry("startcol", "3");
assertThat(row).containsEntry("endline", "4");
@@ -94,7 +102,7 @@
" @UnsupportedAppUsage(someProperty=\"value\")", // 4
" public void method() {}", // 5
"}");
- assertThat(compileAndReadCsv(src).getContents().get(0)).containsEntry(
+ assertThat(compileAndReadCsv(src, "a/b/Class.uau").getContents().get(0)).containsEntry(
"properties", "someProperty=%22value%22");
}
@@ -107,7 +115,7 @@
" @UnsupportedAppUsage(overrideSourcePosition=\"otherfile.aidl:30:10:31:20\")",
" public void method() {}", // 5
"}");
- Map<String, String> row = compileAndReadCsv(src).getContents().get(0);
+ Map<String, String> row = compileAndReadCsv(src, "a/b/Class.uau").getContents().get(0);
assertThat(row).containsEntry("file", "otherfile.aidl");
assertThat(row).containsEntry("startline", "30");
assertThat(row).containsEntry("startcol", "10");
@@ -153,6 +161,24 @@
}
@Test
+ public void testImplicitMemberSkipped() throws Exception {
+ JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class",
+ "package a.b;", // 1
+ "import android.compat.annotation.UnsupportedAppUsage;", // 2
+ "public class Class {", // 3
+ " @UnsupportedAppUsage(implicitMember=\"foo\")", // 4
+ " public void method() {}", // 5
+ "}");
+ List<JavaFileObject> generatedNonClassFiles =
+ compile(src)
+ .generatedFiles()
+ .stream()
+ .filter(file -> !file.getName().endsWith(".class"))
+ .collect(Collectors.toList());
+ assertThat(generatedNonClassFiles).hasSize(0);
+ }
+
+ @Test
public void testExpectedSignatureSucceedsIfMatching() throws Exception {
JavaFileObject src = JavaFileObjects.forSourceLines("a.b.Class",
"package a.b;", // 1