This is an automated email from the ASF dual-hosted git repository. aw pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/yetus.git
commit ec3cdaadefc8ff99a371d033f5a59fd65f5ddef5 Author: Mike Drob <[email protected]> AuthorDate: Sat Jan 29 14:35:56 2022 -0600 YETUS-557 Update Docket Processors for JDK11 (#248) Co-authored-by: Mike Drob <[email protected]> Co-authored-by: Allen Wittenauer <[email protected]> --- .../audience-annotations-jdiff/pom.xml | 98 -------- .../main/assemblies/audience-annotations-jdiff.xml | 37 --- .../ExcludePrivateAnnotationsJDiffDoclet.java | 62 ----- .../audience-annotations/pom.xml | 15 ++ .../audience/tools/DocletEnvironmentProcessor.java | 148 ++++++++++++ .../ExcludePrivateAnnotationsStandardDoclet.java | 46 ++-- .../IncludePublicAnnotationsStandardDoclet.java | 41 ++-- .../yetus/audience/tools/RootDocProcessor.java | 251 --------------------- .../yetus/audience/tools/StabilityOptions.java | 96 ++++---- .../tools/DocletEnvironmentProcessorTest.java | 140 ++++++++++++ .../apache/yetus/audience/tools/MockElement.java | 111 +++++++++ audience-annotations-component/pom.xml | 9 - pom.xml | 3 +- .../src/main/shell/test-patch-docker/Dockerfile | 36 +-- precommit/src/main/shell/test-patch.sh | 2 +- 15 files changed, 522 insertions(+), 573 deletions(-) diff --git a/audience-annotations-component/audience-annotations-jdiff/pom.xml b/audience-annotations-component/audience-annotations-jdiff/pom.xml deleted file mode 100644 index c50d592..0000000 --- a/audience-annotations-component/audience-annotations-jdiff/pom.xml +++ /dev/null @@ -1,98 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. ---> -<project xmlns="https://maven.apache.org/POM/4.0.0" - xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 - https://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>org.apache.yetus</groupId> - <artifactId>yetus-project</artifactId> - <version>0.14.0-SNAPSHOT</version> - <relativePath>../..</relativePath> - </parent> - <artifactId>audience-annotations-jdiff</artifactId> - <description>Tools for managing JDiff generated javadocs</description> - <name>Apache Yetus - Audience Annotations JDiff Doclet</name> - <packaging>jar</packaging> - - <dependencies> - <dependency> - <groupId>org.apache.yetus</groupId> - <artifactId>audience-annotations</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>jdiff</groupId> - <artifactId>jdiff</artifactId> - <version>${jdiff.version}</version> - <scope>provided</scope> - <optional>true</optional> - </dependency> - </dependencies> - - <profiles> - <profile> - <id>jdk1.8</id> - <activation> - <jdk>(,1.8]</jdk> - </activation> - <dependencies> - <dependency> - <!-- Version and location set in project pom --> - <groupId>jdk.tools</groupId> - <artifactId>jdk.tools</artifactId> - <scope>system</scope> - <!-- Mark as optional so that it isn't taken transitively --> - <optional>true</optional> - </dependency> - </dependencies> - </profile> - </profiles> - - <build> - <plugins> - - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-assembly-plugin</artifactId> - <executions> - <execution> - <id>dist</id> - <phase>package</phase> - <goals> - <goal>single</goal> - </goals> - <configuration> - <finalName>apache-yetus-${project.version}</finalName> - <appendAssemblyId>false</appendAssemblyId> - <attach>false</attach> - <descriptors> - <descriptor>src/main/assemblies/${project.artifactId}.xml</descriptor> - </descriptors> - </configuration> - </execution> - </executions> - </plugin> - - </plugins> - </build> - -</project> diff --git a/audience-annotations-component/audience-annotations-jdiff/src/main/assemblies/audience-annotations-jdiff.xml b/audience-annotations-component/audience-annotations-jdiff/src/main/assemblies/audience-annotations-jdiff.xml deleted file mode 100644 index 780e386..0000000 --- a/audience-annotations-component/audience-annotations-jdiff/src/main/assemblies/audience-annotations-jdiff.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. ---> -<assembly xmlns="https://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3" - xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="https://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 https://maven.apache.org/xsd/assembly-1.1.3.xsd"> - <id>audience-annotations-jdiff</id> - <formats> - <format>dir</format> - </formats> - <includeBaseDirectory>false</includeBaseDirectory> - <fileSets> - <fileSet> - <directory>${basedir}/target</directory> - <outputDirectory>lib/audience-annotations</outputDirectory> - <includes> - <include>audience-annotations**-${project.version}.jar</include> - </includes> - </fileSet> - </fileSets> -</assembly> \ No newline at end of file diff --git a/audience-annotations-component/audience-annotations-jdiff/src/main/java/org/apache/yetus/audience/tools/ExcludePrivateAnnotationsJDiffDoclet.java b/audience-annotations-component/audience-annotations-jdiff/src/main/java/org/apache/yetus/audience/tools/ExcludePrivateAnnotationsJDiffDoclet.java deleted file mode 100644 index 1640c74..0000000 --- a/audience-annotations-component/audience-annotations-jdiff/src/main/java/org/apache/yetus/audience/tools/ExcludePrivateAnnotationsJDiffDoclet.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.yetus.audience.tools; - -import com.sun.javadoc.DocErrorReporter; -import com.sun.javadoc.LanguageVersion; -import com.sun.javadoc.RootDoc; - -import jdiff.JDiff; - -import org.apache.yetus.audience.InterfaceAudience; -import org.apache.yetus.audience.InterfaceStability; - -/** - * A <a href="https://docs.oracle.com/javase/8/docs/jdk/api/javadoc/doclet/">Doclet</a> - * for excluding elements that are annotated with - * {@link org.apache.yetus.audience.InterfaceAudience.Private} or - * {@link org.apache.yetus.audience.InterfaceAudience.LimitedPrivate}. - * It delegates to the JDiff Doclet, and takes the same options. - */ [email protected] [email protected] -public class ExcludePrivateAnnotationsJDiffDoclet { - - public static LanguageVersion languageVersion() { - return LanguageVersion.JAVA_1_5; - } - - public static boolean start(RootDoc root) { - return JDiff.start(RootDocProcessor.process(root)); - } - - public static int optionLength(String option) { - Integer length = StabilityOptions.optionLength(option); - if (length != null) { - return length; - } - return JDiff.optionLength(option); - } - - public static boolean validOptions(String[][] options, - DocErrorReporter reporter) { - StabilityOptions.validOptions(options, reporter); - String[][] filteredOptions = StabilityOptions.filterOptions(options); - return JDiff.validOptions(filteredOptions, reporter); - } -} diff --git a/audience-annotations-component/audience-annotations/pom.xml b/audience-annotations-component/audience-annotations/pom.xml index ecefa23..049c6c2 100644 --- a/audience-annotations-component/audience-annotations/pom.xml +++ b/audience-annotations-component/audience-annotations/pom.xml @@ -52,6 +52,21 @@ </profile> </profiles> + <dependencies> + <dependency> + <artifactId>junit-jupiter</artifactId> + <groupId>org.junit.jupiter</groupId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> <plugins> diff --git a/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/DocletEnvironmentProcessor.java b/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/DocletEnvironmentProcessor.java new file mode 100644 index 0000000..bc8deec --- /dev/null +++ b/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/DocletEnvironmentProcessor.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.yetus.audience.tools; + +import com.sun.source.util.DocTrees; +import jdk.javadoc.doclet.DocletEnvironment; +import org.apache.yetus.audience.InterfaceAudience; +import org.apache.yetus.audience.InterfaceStability; + +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileObject; +import java.util.Set; + +class DocletEnvironmentProcessor { + private boolean treatUnannotatedClassesAsPrivate = false; + private StabilityOption stability = StabilityOption.UNSTABLE; + + public void treatUnannotatedClassesAsPrivate() { + this.treatUnannotatedClassesAsPrivate = true; + } + + public void setStability(final StabilityOption stabilityOption) { + this.stability = stabilityOption; + } + + DocletEnvironment wrap(final DocletEnvironment environment) { + return new DocletEnvironment() { + @Override + public Set<? extends Element> getSpecifiedElements() { + return environment.getSpecifiedElements(); + } + + @Override + public Set<? extends Element> getIncludedElements() { + // TODO Do we need to handle exclusions here too? + return environment.getIncludedElements(); + } + + @Override + public DocTrees getDocTrees() { + return environment.getDocTrees(); + } + + @Override + public Elements getElementUtils() { + return environment.getElementUtils(); + } + + @Override + public Types getTypeUtils() { + return environment.getTypeUtils(); + } + + @Override + public boolean isIncluded(final Element e) { + return !excluded(e) && environment.isIncluded(e); + } + + @Override + public boolean isSelected(final Element e) { + return environment.isSelected(e); + } + + @Override + public JavaFileManager getJavaFileManager() { + return environment.getJavaFileManager(); + } + + @Override + public SourceVersion getSourceVersion() { + return environment.getSourceVersion(); + } + + @Override + public ModuleMode getModuleMode() { + return environment.getModuleMode(); + } + + @Override + public JavaFileObject.Kind getFileKind(final TypeElement type) { + return environment.getFileKind(type); + } + + /** + * Check if an element should be excluded by our annotation rules + * @param e the element to check + * @return true iff the element should be excluded + */ + private boolean excluded(final Element e) { + // Exclude private and limited private types + if (e.getAnnotation(InterfaceAudience.Private.class) != null) { + return true; + } + if (e.getAnnotation(InterfaceAudience.LimitedPrivate.class) != null) { + return true; + } + if (e.getAnnotation(InterfaceAudience.Public.class) == null) { + // No audience annotations + if (treatUnannotatedClassesAsPrivate) { + // Exclude classes and interfaces if they are not annotated + return e.getKind().isClass() || e.getKind().isInterface(); + } + } + + // At this point, everything is either public audience or unannotated + // and treat-as-public, which means they must have a stability + // annotation as well. + + // Filter types based on stability + if (e.getAnnotation(InterfaceStability.Unstable.class) != null) { + return stability == StabilityOption.STABLE + || stability == StabilityOption.EVOLVING; + } + if (e.getAnnotation(InterfaceStability.Evolving.class) != null) { + return stability == StabilityOption.STABLE; + } + if (e.getAnnotation(InterfaceStability.Stable.class) != null) { + // public or treat-as-public + return false; + } + + // Public, but no stability? This is an error, so we exclude + return true; + } + }; + } + +} diff --git a/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/ExcludePrivateAnnotationsStandardDoclet.java b/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/ExcludePrivateAnnotationsStandardDoclet.java index 0cb56dd..41f6be6 100644 --- a/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/ExcludePrivateAnnotationsStandardDoclet.java +++ b/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/ExcludePrivateAnnotationsStandardDoclet.java @@ -17,16 +17,17 @@ */ package org.apache.yetus.audience.tools; -import com.sun.javadoc.DocErrorReporter; -import com.sun.javadoc.LanguageVersion; -import com.sun.javadoc.RootDoc; -import com.sun.tools.doclets.standard.Standard; - +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.StandardDoclet; import org.apache.yetus.audience.InterfaceAudience; import org.apache.yetus.audience.InterfaceStability; +import java.util.EnumSet; +import java.util.Set; +import java.util.TreeSet; + /** - * A <a href="https://docs.oracle.com/javase/8/docs/jdk/api/javadoc/doclet/">Doclet</a> + * A {@link jdk.javadoc.doclet.Doclet} * for excluding elements that are annotated with * {@link org.apache.yetus.audience.InterfaceAudience.Private} or * {@link org.apache.yetus.audience.InterfaceAudience.LimitedPrivate}. @@ -34,28 +35,25 @@ import org.apache.yetus.audience.InterfaceStability; */ @InterfaceAudience.Public @InterfaceStability.Evolving -public class ExcludePrivateAnnotationsStandardDoclet { - - public static LanguageVersion languageVersion() { - return LanguageVersion.JAVA_1_5; - } +public class ExcludePrivateAnnotationsStandardDoclet extends StandardDoclet { + protected DocletEnvironmentProcessor processor = new DocletEnvironmentProcessor(); - public static boolean start(RootDoc root) { - return Standard.start(RootDocProcessor.process(root)); + @Override + public String getName() { + return "ExcludePrivateAnnotationsStandard"; } - public static int optionLength(String option) { - Integer length = StabilityOptions.optionLength(option); - if (length != null) { - return length; - } - return Standard.optionLength(option); + @Override + public Set<Option> getSupportedOptions() { + Set<Option> options = new TreeSet<>(super.getSupportedOptions()); + Set<StabilityOption> stabilityOptions = EnumSet.allOf(StabilityOption.class); + stabilityOptions.forEach(o -> o.setProcessor(processor)); + options.addAll(stabilityOptions); + return options; } - public static boolean validOptions(String[][] options, - DocErrorReporter reporter) { - StabilityOptions.validOptions(options, reporter); - String[][] filteredOptions = StabilityOptions.filterOptions(options); - return Standard.validOptions(filteredOptions, reporter); + @Override + public boolean run(final DocletEnvironment environment) { + return super.run(processor.wrap(environment)); } } diff --git a/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/IncludePublicAnnotationsStandardDoclet.java b/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/IncludePublicAnnotationsStandardDoclet.java index b9c6c05..3b308d1 100644 --- a/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/IncludePublicAnnotationsStandardDoclet.java +++ b/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/IncludePublicAnnotationsStandardDoclet.java @@ -17,16 +17,14 @@ */ package org.apache.yetus.audience.tools; -import com.sun.javadoc.DocErrorReporter; -import com.sun.javadoc.LanguageVersion; -import com.sun.javadoc.RootDoc; -import com.sun.tools.doclets.standard.Standard; - +import jdk.javadoc.doclet.Reporter; import org.apache.yetus.audience.InterfaceAudience; import org.apache.yetus.audience.InterfaceStability; +import java.util.Locale; + /** - * A <a href="https://docs.oracle.com/javase/8/docs/jdk/api/javadoc/doclet/">Doclet</a> + * A {@link jdk.javadoc.doclet.Doclet} * that only includes class-level elements that are annotated with * {@link org.apache.yetus.audience.InterfaceAudience.Public}. * Class-level elements with no annotation are excluded. @@ -38,29 +36,16 @@ import org.apache.yetus.audience.InterfaceStability; */ @InterfaceAudience.Public @InterfaceStability.Evolving -public class IncludePublicAnnotationsStandardDoclet { - - public static LanguageVersion languageVersion() { - return LanguageVersion.JAVA_1_5; - } - - public static boolean start(RootDoc root) { - RootDocProcessor.treatUnannotatedClassesAsPrivate = true; - return Standard.start(RootDocProcessor.process(root)); - } - - public static int optionLength(String option) { - Integer length = StabilityOptions.optionLength(option); - if (length != null) { - return length; - } - return Standard.optionLength(option); +public class IncludePublicAnnotationsStandardDoclet + extends ExcludePrivateAnnotationsStandardDoclet { + @Override + public void init(final Locale locale, final Reporter reporter) { + processor.treatUnannotatedClassesAsPrivate(); + super.init(locale, reporter); } - public static boolean validOptions(String[][] options, - DocErrorReporter reporter) { - StabilityOptions.validOptions(options, reporter); - String[][] filteredOptions = StabilityOptions.filterOptions(options); - return Standard.validOptions(filteredOptions, reporter); + @Override + public String getName() { + return "IncludePublicAnnotationsStandard"; } } diff --git a/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/RootDocProcessor.java b/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/RootDocProcessor.java deleted file mode 100644 index 02bbd16..0000000 --- a/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/RootDocProcessor.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.yetus.audience.tools; - -import com.sun.javadoc.AnnotationDesc; -import com.sun.javadoc.AnnotationTypeDoc; -import com.sun.javadoc.ClassDoc; -import com.sun.javadoc.ConstructorDoc; -import com.sun.javadoc.Doc; -import com.sun.javadoc.FieldDoc; -import com.sun.javadoc.MethodDoc; -import com.sun.javadoc.PackageDoc; -import com.sun.javadoc.ProgramElementDoc; -import com.sun.javadoc.RootDoc; - -import java.lang.reflect.Array; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; - -import org.apache.yetus.audience.InterfaceAudience; -import org.apache.yetus.audience.InterfaceStability; - -/** - * Process the {@link RootDoc} by substituting with (nested) proxy objects that - * exclude elements with Private or LimitedPrivate annotations. - * <p> - * Based on code from - * https://web.archive.org/web/20171114155534/http://sixlegs.com/blog/java/exclude-javadoc-tag.html. - */ -class RootDocProcessor { - - static String stability = StabilityOptions.UNSTABLE_OPTION; - static boolean treatUnannotatedClassesAsPrivate = false; - - public static RootDoc process(RootDoc root) { - return (RootDoc) process(root, RootDoc.class); - } - - private static Object process(Object obj, Class<?> type) { - if (obj == null) { - return null; - } - Class<?> cls = obj.getClass(); - if (cls.getName().startsWith("com.sun.")) { - return getProxy(obj); - } else if (obj instanceof Object[]) { - Class<?> componentType = type.isArray() ? type.getComponentType() - : cls.getComponentType(); - Object[] array = (Object[]) obj; - Object[] newArray = (Object[]) Array.newInstance(componentType, - array.length); - for (int i = 0; i < array.length; ++i) { - newArray[i] = process(array[i], componentType); - } - return newArray; - } - return obj; - } - - private static Map<Object, Object> proxies = - new WeakHashMap<Object, Object>(); - - private static Object getProxy(Object obj) { - Object proxy = proxies.get(obj); - if (proxy == null) { - proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), - obj.getClass().getInterfaces(), new ExcludeHandler(obj)); - proxies.put(obj, proxy); - } - return proxy; - } - - private static class ExcludeHandler implements InvocationHandler { - private Object target; - - public ExcludeHandler(Object target) { - this.target = target; - } - - @Override - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable { - String methodName = method.getName(); - if (target instanceof Doc) { - if (methodName.equals("isIncluded")) { - Doc doc = (Doc) target; - return !exclude(doc) && doc.isIncluded(); - } - if (target instanceof RootDoc) { - switch (methodName) { - case "classes": - return filter(((RootDoc) target).classes(), ClassDoc.class); - case "specifiedClasses": - return filter(((RootDoc) target).specifiedClasses(), ClassDoc.class); - case "specifiedPackages": - return filter(((RootDoc) target).specifiedPackages(), PackageDoc.class); - } - } else if (target instanceof ClassDoc) { - if (isFiltered(args)) { - switch (methodName) { - case "methods": - return filter(((ClassDoc) target).methods(true), MethodDoc.class); - case "fields": - return filter(((ClassDoc) target).fields(true), FieldDoc.class); - case "innerClasses": - return filter(((ClassDoc) target).innerClasses(true), - ClassDoc.class); - case "constructors": - return filter(((ClassDoc) target).constructors(true), - ConstructorDoc.class); - } - } - } else if (target instanceof PackageDoc) { - switch (methodName) { - case "allClasses": - if (isFiltered(args)) { - return filter(((PackageDoc) target).allClasses(true), - ClassDoc.class); - } else { - return filter(((PackageDoc) target).allClasses(), ClassDoc.class); - } - case "annotationTypes": - return filter(((PackageDoc) target).annotationTypes(), - AnnotationTypeDoc.class); - case "enums": - return filter(((PackageDoc) target).enums(), - ClassDoc.class); - case "errors": - return filter(((PackageDoc) target).errors(), - ClassDoc.class); - case "exceptions": - return filter(((PackageDoc) target).exceptions(), - ClassDoc.class); - case "interfaces": - return filter(((PackageDoc) target).interfaces(), - ClassDoc.class); - case "ordinaryClasses": - return filter(((PackageDoc) target).ordinaryClasses(), - ClassDoc.class); - } - } - } - - if (args != null) { - if (methodName.equals("compareTo") || methodName.equals("equals") - || methodName.equals("overrides") - || methodName.equals("subclassOf")) { - args[0] = unwrap(args[0]); - } - } - try { - return process(method.invoke(target, args), method.getReturnType()); - } catch (InvocationTargetException e) { - throw e.getTargetException(); - } - } - - private static boolean exclude(Doc doc) { - AnnotationDesc[] annotations = null; - if (doc instanceof ProgramElementDoc) { - annotations = ((ProgramElementDoc) doc).annotations(); - } else if (doc instanceof PackageDoc) { - annotations = ((PackageDoc) doc).annotations(); - } - if (annotations != null) { - for (AnnotationDesc annotation : annotations) { - String qualifiedTypeName = annotation.annotationType().qualifiedTypeName(); - if (qualifiedTypeName.equals( - InterfaceAudience.Private.class.getCanonicalName()) - || qualifiedTypeName.equals( - InterfaceAudience.LimitedPrivate.class.getCanonicalName())) { - return true; - } - if (stability.equals(StabilityOptions.EVOLVING_OPTION)) { - if (qualifiedTypeName.equals( - InterfaceStability.Unstable.class.getCanonicalName())) { - return true; - } - } - if (stability.equals(StabilityOptions.STABLE_OPTION)) { - if (qualifiedTypeName.equals( - InterfaceStability.Unstable.class.getCanonicalName()) - || qualifiedTypeName.equals( - InterfaceStability.Evolving.class.getCanonicalName())) { - return true; - } - } - } - for (AnnotationDesc annotation : annotations) { - String qualifiedTypeName = - annotation.annotationType().qualifiedTypeName(); - if (qualifiedTypeName.equals( - InterfaceAudience.Public.class.getCanonicalName())) { - return false; - } - } - } - if (treatUnannotatedClassesAsPrivate) { - return doc.isClass() || doc.isInterface() || doc.isAnnotationType(); - } - return false; - } - - private static Object[] filter(Doc[] array, Class<?> componentType) { - if (array == null || array.length == 0) { - return array; - } - List<Object> list = new ArrayList<Object>(array.length); - for (Doc entry : array) { - if (!exclude(entry)) { - list.add(process(entry, componentType)); - } - } - return list.toArray((Object[]) Array.newInstance(componentType, list - .size())); - } - - private Object unwrap(Object proxy) { - if (proxy instanceof Proxy) - return ((ExcludeHandler) Proxy.getInvocationHandler(proxy)).target; - return proxy; - } - - private boolean isFiltered(Object[] args) { - return args != null && Boolean.TRUE.equals(args[0]); - } - - } - -} diff --git a/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/StabilityOptions.java b/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/StabilityOptions.java index 26c1459..e0cc884 100644 --- a/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/StabilityOptions.java +++ b/audience-annotations-component/audience-annotations/src/main/java/org/apache/yetus/audience/tools/StabilityOptions.java @@ -17,60 +17,64 @@ */ package org.apache.yetus.audience.tools; -import com.sun.javadoc.DocErrorReporter; +import jdk.javadoc.doclet.Doclet; -import java.util.ArrayList; import java.util.List; -import java.util.Locale; -class StabilityOptions { - public static final String STABLE_OPTION = "-stable"; - public static final String EVOLVING_OPTION = "-evolving"; - public static final String UNSTABLE_OPTION = "-unstable"; +enum StabilityOption implements Doclet.Option { + /** Stability option corresponding to InterfaceAudience.Stable level. */ + STABLE("-stable"), + /** Stability option corresponding to InterfaceAudience.Evolving level. */ + EVOLVING("-evolving"), + /** Stability option corresponding to InterfaceAudience.Unstable level. */ + UNSTABLE("-unstable"); - public static Integer optionLength(String option) { - String opt = option.toLowerCase(Locale.ENGLISH); - if (opt.equals(UNSTABLE_OPTION)) { return 1;} - if (opt.equals(EVOLVING_OPTION)) { return 1; } - if (opt.equals(STABLE_OPTION)) { return 1; } - return null; + /** @see #getNames() */ + private final List<String> names; + /** The doclet processor that this option modifies. */ + private DocletEnvironmentProcessor processor; + + StabilityOption(final String name) { + this.names = List.of(name); + } + + public void setProcessor(final DocletEnvironmentProcessor dProcessor) { + this.processor = dProcessor; + } + + @Override + public int getArgumentCount() { + return 0; + } + + @Override + public String getDescription() { + return "Output only APIs annotated as " + getName().substring(1) + + (this == STABLE ? "" : " or stronger"); + } + + @Override + public Kind getKind() { + return Kind.STANDARD; } - public static void validOptions(String[][] options, - DocErrorReporter reporter) { - for (String[] option : options) { - String opt = option[0].toLowerCase(Locale.ENGLISH); - switch (opt) { - case UNSTABLE_OPTION: - RootDocProcessor.stability = UNSTABLE_OPTION; - break; - case EVOLVING_OPTION: - RootDocProcessor.stability = EVOLVING_OPTION; - break; - case STABLE_OPTION: - RootDocProcessor.stability = STABLE_OPTION; - break; - default: - break; - } - } + public String getName() { + return names.get(0); } - public static String[][] filterOptions(String[][] options) { - List<String[]> optionsList = new ArrayList<String[]>(options.length); - for (String[] option1 : options) { - if (!option1[0].equalsIgnoreCase(UNSTABLE_OPTION) - && !option1[0].equalsIgnoreCase(EVOLVING_OPTION) - && !option1[0].equalsIgnoreCase(STABLE_OPTION)) { - optionsList.add(option1); - } - } - String[][] filteredOptions = new String[optionsList.size()][]; - int i = 0; - for (String[] option : optionsList) { - filteredOptions[i++] = option; - } - return filteredOptions; + @Override + public List<String> getNames() { + return names; } + @Override + public String getParameters() { + return ""; + } + + @Override + public boolean process(final String option, final List<String> arguments) { + processor.setStability(this); + return true; + } } diff --git a/audience-annotations-component/audience-annotations/src/test/java/org/apache/yetus/audience/tools/DocletEnvironmentProcessorTest.java b/audience-annotations-component/audience-annotations/src/test/java/org/apache/yetus/audience/tools/DocletEnvironmentProcessorTest.java new file mode 100644 index 0000000..8447659 --- /dev/null +++ b/audience-annotations-component/audience-annotations/src/test/java/org/apache/yetus/audience/tools/DocletEnvironmentProcessorTest.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.yetus.audience.tools; + +import jdk.javadoc.doclet.DocletEnvironment; +import org.apache.yetus.audience.InterfaceAudience; +import org.apache.yetus.audience.InterfaceStability; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import javax.lang.model.element.Element; + +import java.lang.annotation.Annotation; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** Tests that will verify the annotation based javadoc exclusion logic */ +public class DocletEnvironmentProcessorTest { + /** The doclet processor that we test for proper configuration */ + private DocletEnvironmentProcessor processor; + + /** The environment that will host our annotated elements */ + private DocletEnvironment environment; + + @BeforeEach + public void setup() { + processor = new DocletEnvironmentProcessor(); + DocletEnvironment mockEnvironment = mock(DocletEnvironment.class); + when(mockEnvironment.isIncluded(any())).thenReturn(true); + environment = processor.wrap(mockEnvironment); + } + + /** + * Get an Element with no annotations. + * @return the element + */ + private Element get() { + return new MockElement(); + } + + /** + * Get an element with a single audience or stability annotations. + * @param annotation the annotation to apply + * @return the element + */ + private Element get(final Class<? extends Annotation> annotation) { + return new MockElement(annotation); + } + + /** + * Get an element with both audience and stability annotations. + * @param audience the interface audience + * @param stability the interface stability + * @return the element + */ + private Element get(final Class<? extends Annotation> audience, + final Class<? extends Annotation> stability) { + return new MockElement(audience, stability); + } + + @Test + public void testStable() { + processor.setStability(StabilityOption.STABLE); + + assertTrue(environment.isIncluded(get(InterfaceAudience.Public.class, InterfaceStability.Stable.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.Public.class, InterfaceStability.Evolving.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.Public.class, InterfaceStability.Unstable.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.Public.class))); + } + + @Test + public void testEvolving() { + processor.setStability(StabilityOption.EVOLVING); + + assertTrue(environment.isIncluded(get(InterfaceAudience.Public.class, InterfaceStability.Stable.class))); + assertTrue(environment.isIncluded(get(InterfaceAudience.Public.class, InterfaceStability.Evolving.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.Public.class, InterfaceStability.Unstable.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.Public.class))); + } + + @Test + public void testUnstable() { + processor.setStability(StabilityOption.UNSTABLE); + + assertTrue(environment.isIncluded(get(InterfaceAudience.Public.class, InterfaceStability.Stable.class))); + assertTrue(environment.isIncluded(get(InterfaceAudience.Public.class, InterfaceStability.Evolving.class))); + assertTrue(environment.isIncluded(get(InterfaceAudience.Public.class, InterfaceStability.Unstable.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.Public.class))); + } + + @Test + public void testUnannotatedIncluded() { + processor.setStability(StabilityOption.UNSTABLE); + + assertTrue(environment.isIncluded(get(InterfaceStability.Stable.class))); + assertTrue(environment.isIncluded(get(InterfaceStability.Evolving.class))); + assertTrue(environment.isIncluded(get(InterfaceStability.Unstable.class))); + assertFalse(environment.isIncluded(get())); + } + + @Test + public void testPrivateExcluded() { + processor.setStability(StabilityOption.UNSTABLE); + processor.treatUnannotatedClassesAsPrivate(); + + assertFalse(environment.isIncluded(get(InterfaceAudience.LimitedPrivate.class, InterfaceStability.Stable.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.LimitedPrivate.class, InterfaceStability.Evolving.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.LimitedPrivate.class, InterfaceStability.Unstable.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.LimitedPrivate.class))); + + assertFalse(environment.isIncluded(get(InterfaceAudience.Private.class, InterfaceStability.Stable.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.Private.class, InterfaceStability.Evolving.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.Private.class, InterfaceStability.Unstable.class))); + assertFalse(environment.isIncluded(get(InterfaceAudience.Private.class))); + + assertFalse(environment.isIncluded(get(InterfaceStability.Stable.class))); + assertFalse(environment.isIncluded(get(InterfaceStability.Evolving.class))); + assertFalse(environment.isIncluded(get(InterfaceStability.Unstable.class))); + assertFalse(environment.isIncluded(get())); + } +} diff --git a/audience-annotations-component/audience-annotations/src/test/java/org/apache/yetus/audience/tools/MockElement.java b/audience-annotations-component/audience-annotations/src/test/java/org/apache/yetus/audience/tools/MockElement.java new file mode 100644 index 0000000..d74c487 --- /dev/null +++ b/audience-annotations-component/audience-annotations/src/test/java/org/apache/yetus/audience/tools/MockElement.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.yetus.audience.tools; + +import org.apache.yetus.audience.InterfaceAudience; +import org.apache.yetus.audience.InterfaceStability; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ElementVisitor; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.type.TypeMirror; +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * A skeleton implementation of {@link Element} that acts like it is annotated + * by the set of annotations requested at initialization time. Most methods + * will return null or empty collections as appropriate, except + * {@link #getAnnotation(Class)}. + * <p>The annotations present on this type are so that we have a handy + * instance of each created for us by the runtime to return to callers. + * They do not represent the actual audience and stability of this class, + * which is private and unstable. + */ [email protected] [email protected]("") [email protected] [email protected] [email protected] [email protected] +class MockElement implements Element { + private List<Class<?>> savedAnnotations; + + public MockElement(Class<?>... annotations) { + savedAnnotations = List.of(annotations); + } + + @Override + public <A extends Annotation> A getAnnotation(Class<A> annotationType) { + if (annotationType != null + && savedAnnotations.contains(annotationType)) { + return MockElement.class.getAnnotation(annotationType); + } + return null; + } + + @Override + public TypeMirror asType() { + return null; + } + + @Override + public ElementKind getKind() { + return ElementKind.CLASS; + } + + @Override + public Set<Modifier> getModifiers() { + return Collections.emptySet(); + } + + @Override + public Name getSimpleName() { + return null; + } + + @Override + public Element getEnclosingElement() { + return null; + } + + @Override + public List<? extends Element> getEnclosedElements() { + return Collections.emptyList(); + } + + @Override + public List<? extends AnnotationMirror> getAnnotationMirrors() { + return Collections.emptyList(); + } + + @Override + public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) { + return null; + } + + @Override + public <R, P> R accept(ElementVisitor<R, P> v, P p) { + return null; + } +} diff --git a/audience-annotations-component/pom.xml b/audience-annotations-component/pom.xml index e9ac2cf..aaec25f 100644 --- a/audience-annotations-component/pom.xml +++ b/audience-annotations-component/pom.xml @@ -35,16 +35,7 @@ <modules> <module>audience-annotations</module> - <module>audience-annotations-jdiff</module> </modules> - <!-- profiles> - <profile> - <id>include-jdiff-module</id> - <modules> - <module>audience-annotations-jdiff</module> - </modules> - </profile> - </profiles --> <reporting> <plugins> diff --git a/pom.xml b/pom.xml index f1e75f8..7b4c3f7 100644 --- a/pom.xml +++ b/pom.xml @@ -67,9 +67,10 @@ <checkstyle.version>9.0</checkstyle.version> <commons.io.version>2.7</commons.io.version> <exec-maven-plugin.version>3.0.0</exec-maven-plugin.version> - <jdiff.version>1.0.9</jdiff.version> + <junit.version>5.8.2</junit.version> <maven-checkstyle-plugin.version>3.1.2</maven-checkstyle-plugin.version> <maven-project-info-reports-plugin.version>3.1.2</maven-project-info-reports-plugin.version> + <mockito.version>4.2.0</mockito.version> <spotbugs-maven-plugin.version>4.4.1</spotbugs-maven-plugin.version> <sourceReleaseAssemblyDescriptor>source-release-tar</sourceReleaseAssemblyDescriptor> diff --git a/precommit/src/main/shell/test-patch-docker/Dockerfile b/precommit/src/main/shell/test-patch-docker/Dockerfile index 8e7c61d..9194e3a 100644 --- a/precommit/src/main/shell/test-patch-docker/Dockerfile +++ b/precommit/src/main/shell/test-patch-docker/Dockerfile @@ -224,25 +224,14 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"] #### #### -# OpenJDK 8 -#### -# hadolint ignore=DL3008 -RUN apt-get -q update && apt-get -q install --no-install-recommends -y openjdk-8-jdk-headless \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -#### -# OpenJDK 11 (but keeps default to JDK8) +# OpenJDK 11 # NOTE: This default only works when Apache Yetus is launched # _in_ the container and not outside of it! #### # hadolint ignore=DL3008,DL3059 RUN apt-get -q update && apt-get -q install --no-install-recommends -y default-jre-headless openjdk-11-jdk-headless \ && apt-get clean \ - && rm -rf /var/lib/apt/lists/* \ - && update-java-alternatives -s "$(update-java-alternatives -l | grep 1.8.0 | awk '{print $1}')" \ - && rm -f /usr/lib/jvm/default-java \ - && ln -s "$(update-java-alternatives -l | grep 1.8.0 | awk '{print $NF}')" /usr/lib/jvm/default-java + && rm -rf /var/lib/apt/lists/* # this var will get yetus_abs'd when run under precommit so should be relatively safe ENV JAVA_HOME /usr/lib/jvm/default-java ENV SPOTBUGS_HOME /opt/spotbugs @@ -289,9 +278,24 @@ RUN add-apt-repository -y \ # Install maven ###### # hadolint ignore=DL3008,DL3059 -RUN apt-get -q update && apt-get -q install --no-install-recommends -y maven \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* +ARG MVN_VERSION=3.8.4 +ARG MVN_TGZ=apache-maven-$MVN_VERSION-bin.tar.gz +RUN curl -sSL \ + -o $MVN_TGZ \ + https://downloads.apache.org/maven/maven-3/$MVN_VERSION/binaries/$MVN_TGZ \ + && tar xzpf $MVN_TGZ \ + && mkdir -p /opt \ + && mv apache-maven-$MVN_VERSION /opt \ + && ln -s /opt/apache-maven-$MVN_VERSION/bin/mvn /bin \ + && curl -sSL \ + -o KEYS \ + https://downloads.apache.org/maven/KEYS \ + && gpg --import KEYS \ + && curl -sSL \ + -o $MVN_TGZ.asc \ + https://downloads.apache.org/maven/maven-3/$MVN_VERSION/binaries/$MVN_TGZ.asc \ + && gpg --verify $MVN_TGZ.asc $MVN_TGZ \ + && rm -rf $MVN_TGZ* /root/.gnupg KEYS ###### # Install perl diff --git a/precommit/src/main/shell/test-patch.sh b/precommit/src/main/shell/test-patch.sh index d316c29..8be4e83 100755 --- a/precommit/src/main/shell/test-patch.sh +++ b/precommit/src/main/shell/test-patch.sh @@ -95,7 +95,7 @@ function setup_defaults CONTINUOUS_IMPROVEMENT=false - PROC_LIMIT=1000 + PROC_LIMIT=2000 REEXECED=false RESETREPO=false REPORT_UNKNOWN_OPTIONS=true
