8173945: Add methods for Elements.getAll{Type, Package, Module}Elements
authordarcy
Mon, 13 Feb 2017 10:29:04 -0800
changeset 43857 37614a3b7193
parent 43856 fcdebb803c62
child 43858 3f89ba6c36c0
8173945: Add methods for Elements.getAll{Type, Package, Module}Elements Reviewed-by: jlahoda, jjg
langtools/src/java.compiler/share/classes/javax/lang/model/util/Elements.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java
langtools/test/tools/javac/processing/model/util/elements/TestAllFoos.java
langtools/test/tools/javac/processing/model/util/elements/modules/m1/module-info.java
langtools/test/tools/javac/processing/model/util/elements/modules/m1/pkg/C.java
langtools/test/tools/javac/processing/model/util/elements/modules/m1/pkg/package-info.java
langtools/test/tools/javac/processing/model/util/elements/modules/m2/module-info.java
langtools/test/tools/javac/processing/model/util/elements/modules/m2/pkg/C.java
langtools/test/tools/javac/processing/model/util/elements/modules/m2/pkg/package-info.java
--- a/langtools/src/java.compiler/share/classes/javax/lang/model/util/Elements.java	Mon Feb 13 08:50:26 2017 -0800
+++ b/langtools/src/java.compiler/share/classes/javax/lang/model/util/Elements.java	Mon Feb 13 10:29:04 2017 -0800
@@ -25,9 +25,12 @@
 
 package javax.lang.model.util;
 
-
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.LinkedHashSet;
 
 import javax.lang.model.AnnotatedConstruct;
 import javax.lang.model.element.*;
@@ -65,6 +68,7 @@
      * @param name  fully qualified package name, or an empty string for an unnamed package
      * @param module module relative to which the lookup should happen
      * @return the specified package, or {@code null} if it cannot be found
+     * @see #getAllPackageElements
      * @since 9
      */
     default PackageElement getPackageElement(ModuleElement module, CharSequence name) {
@@ -72,6 +76,49 @@
     }
 
     /**
+     * Returns all package elements with the given canonical name.
+     *
+     * There may be more than one package element with the same canonical
+     * name if the package elements are in different modules.
+     *
+     * @implSpec The default implementation of this method calls
+     * {@link #getAllModuleElements() getAllModuleElements} and stores
+     * the result. If the set of modules is empty, {@link
+     * #getPackageElement(CharSequence) getPackageElement(name)} is
+     * called passing through the name argument. If {@code
+     * getPackageElement(name)} is {@code null}, an empty set of
+     * package elements is returned; otherwise, a single-element set
+     * with the found package element is returned. If the set of
+     * modules is nonempty, the modules are iterated over and any
+     * non-{@code null} results of {@link
+     * #getPackageElement(ModuleElement, CharSequence)
+     * getPackageElement(module, name)} are accumulated into a
+     * set. The set is then returned.
+     *
+     * @param name  the canonical name
+     * @return the package elements, or an empty set if no package with the name can be found
+     * @see #getPackageElement(ModuleElement, CharSequence)
+     * @since 9
+     */
+    default Set<? extends PackageElement> getAllPackageElements(CharSequence name) {
+        Set<? extends ModuleElement> modules = getAllModuleElements();
+        if (modules.isEmpty()) {
+            PackageElement packageElt = getPackageElement(name);
+            return (packageElt != null) ?
+                Collections.singleton(packageElt):
+                Collections.emptySet();
+        } else {
+            Set<PackageElement> result = new LinkedHashSet<>(1); // Usually expect at most 1 result
+            for (ModuleElement module: modules) {
+                PackageElement packageElt = getPackageElement(module, name);
+                if (packageElt != null)
+                    result.add(packageElt);
+            }
+            return Collections.unmodifiableSet(result);
+        }
+    }
+
+    /**
      * Returns a type element given its canonical name if the type element is unique in the environment.
      * If running with modules, all modules in the modules graph are searched for matching
      * type elements.
@@ -90,6 +137,7 @@
      * @param name  the canonical name
      * @param module module relative to which the lookup should happen
      * @return the named type element, or {@code null} if it cannot be found
+     * @see #getAllTypeElements
      * @since 9
      */
     default TypeElement getTypeElement(ModuleElement module, CharSequence name) {
@@ -97,11 +145,54 @@
     }
 
     /**
+     * Returns all type elements with the given canonical name.
+     *
+     * There may be more than one type element with the same canonical
+     * name if the type elements are in different modules.
+     *
+     * @implSpec The default implementation of this method calls
+     * {@link #getAllModuleElements() getAllModuleElements} and stores
+     * the result. If the set of modules is empty, {@link
+     * #getTypeElement(CharSequence) getTypeElement(name)} is called
+     * passing through the name argument. If {@code
+     * getTypeElement(name)} is {@code null}, an empty set of type
+     * elements is returned; otherwise, a single-element set with the
+     * found type element is returned. If the set of modules is
+     * nonempty, the modules are iterated over and any non-{@code null}
+     * results of {@link #getTypeElement(ModuleElement,
+     * CharSequence) getTypeElement(module, name)} are accumulated
+     * into a set. The set is then returned.
+     *
+     * @param name  the canonical name
+     * @return the type elements, or an empty set if no type with the name can be found
+     * @see #getTypeElement(ModuleElement, CharSequence)
+     * @since 9
+     */
+    default Set<? extends TypeElement> getAllTypeElements(CharSequence name) {
+        Set<? extends ModuleElement> modules = getAllModuleElements();
+        if (modules.isEmpty()) {
+            TypeElement typeElt = getTypeElement(name);
+            return (typeElt != null) ?
+                Collections.singleton(typeElt):
+                Collections.emptySet();
+        } else {
+            Set<TypeElement> result = new LinkedHashSet<>(1); // Usually expect at most 1 result
+            for (ModuleElement module: modules) {
+                TypeElement typeElt = getTypeElement(module, name);
+                if (typeElt != null)
+                    result.add(typeElt);
+            }
+            return Collections.unmodifiableSet(result);
+        }
+    }
+
+    /**
      * Returns a module element given its fully qualified name.
-     * If the named module cannot be found, null is returned. One situation where a module
-     * cannot be found is if the environment does not include modules, such as
-     * an annotation processing environment configured for
-     * a {@linkplain
+     *
+     * If the named module cannot be found, {@code null} is
+     * returned. One situation where a module cannot be found is if
+     * the environment does not include modules, such as an annotation
+     * processing environment configured for a {@linkplain
      * javax.annotation.processing.ProcessingEnvironment#getSourceVersion
      * source version} without modules.
      *
@@ -110,6 +201,7 @@
      *
      * @param name  the name
      * @return the named module element, or {@code null} if it cannot be found
+     * @see #getAllModuleElements
      * @since 9
      * @spec JPMS
      */
@@ -118,6 +210,27 @@
     }
 
     /**
+     * Returns all module elements in the current environment.
+     *
+     * If no modules are present, an empty set is returned. One
+     * situation where no modules are present occurs when the
+     * environment does not include modules, such as an annotation
+     * processing environment configured for a {@linkplain
+     * javax.annotation.processing.ProcessingEnvironment#getSourceVersion
+     * source version} without modules.
+     *
+     * @implSpec The default implementation of this method returns
+     * an empty set.
+     *
+     * @return the known module elements, or an empty set if there are no modules
+     * @see #getModuleElement(CharSequence)
+     * @since 9
+     */
+    default Set<? extends ModuleElement> getAllModuleElements() {
+        return Collections.emptySet();
+    }
+
+    /**
      * Returns the values of an annotation's elements, including defaults.
      *
      * @see AnnotationMirror#getElementValues()
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java	Mon Feb 13 08:50:26 2017 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java	Mon Feb 13 10:29:04 2017 -0800
@@ -25,6 +25,7 @@
 
 package com.sun.tools.javac.model;
 
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.Map;
@@ -117,6 +118,14 @@
     }
 
     @Override @DefinedBy(Api.LANGUAGE_MODEL)
+    public Set<? extends ModuleElement> getAllModuleElements() {
+        if (allowModules)
+            return Collections.unmodifiableSet(modules.allModules());
+        else
+            return Collections.emptySet();
+    }
+
+    @Override @DefinedBy(Api.LANGUAGE_MODEL)
     public ModuleSymbol getModuleElement(CharSequence name) {
         ensureEntered("getModuleElement");
         if (modules.getDefaultModule() == syms.noModule)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/model/util/elements/TestAllFoos.java	Mon Feb 13 10:29:04 2017 -0800
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8173945
+ * @summary Test Elements.getAll{Type, Package, Module}Elements
+ * @library /tools/javac/lib
+ * @modules java.compiler
+ *          jdk.compiler
+ * @build   JavacTestingAbstractProcessor TestAllFoos
+ * @compile -processor TestAllFoos -proc:only --release 8 --source-path modules/m1/pkg  modules/m1/pkg/C.java
+ * @compile -processor TestAllFoos -proc:only --release 8 --source-path modules/m2/pkg  modules/m2/pkg/C.java
+ */
+// @compile -processor TestAllFoos -proc:only             --module-source-path  modules -m m1,m2
+
+import java.util.Set;
+import static java.util.Objects.*;
+import javax.annotation.processing.*;
+import static javax.lang.model.SourceVersion.*;
+import javax.lang.model.element.*;
+import javax.lang.model.util.*;
+
+/**
+ * Test basic workings of Elements.getAll{Type, Package, Module}Elements under
+ * pre- and post-modules.
+ */
+public class TestAllFoos extends JavacTestingAbstractProcessor {
+    public boolean process(Set<? extends TypeElement> annotations,
+                           RoundEnvironment roundEnv) {
+        if (!roundEnv.processingOver()) {
+            boolean expectModules =
+                (processingEnv.getSourceVersion().compareTo(RELEASE_9) >= 0);
+
+            testSetSize(eltUtils.getAllTypeElements("java.lang.String"), 1);
+            testSetSize(eltUtils.getAllTypeElements("example.com"), 0);
+
+            if (!expectModules) {
+                // Expect empty modules set, single package named "pkg" with one type "pkg.C".
+                testSetSize(eltUtils.getAllModuleElements(), 0);
+                testSetSize(eltUtils.getAllPackageElements("pkg"), 1);
+                testSetSize(eltUtils.getAllTypeElements("pkg.C"),  1);
+            } else {
+                Set<? extends ModuleElement> modules =
+                    requireNonNull(eltUtils.getAllModuleElements());
+
+                ModuleElement m1 = requireNonNull(eltUtils.getModuleElement("m1"));
+                ModuleElement m2 = requireNonNull(eltUtils.getModuleElement("m2"));
+
+                if (!modules.contains(m1) ||
+                    !modules.contains(m2) ||
+                    !modules.contains(requireNonNull(eltUtils.getModuleElement("java.base"))))
+                    throw new RuntimeException("Missing modules " + modules);
+
+                // Expect two packages named "pkg" and two types named "pkg.C".
+                testSetSize(eltUtils.getAllPackageElements("pkg"), 2);
+                testSetSize(eltUtils.getAllTypeElements("pkg.C"),  2);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Check the set argument against null and throw an exception if
+     * the set is not of the expected size.
+     */
+    private static <E> Set<E> testSetSize(Set<E> set, int expectedSize) {
+        requireNonNull(set);
+        if (set.size() != expectedSize)
+            throw new RuntimeException("Unexpected size of set " + set);
+        return set;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/model/util/elements/modules/m1/module-info.java	Mon Feb 13 10:29:04 2017 -0800
@@ -0,0 +1,4 @@
+/* /nodynamiccopyright/ */
+
+module m1 {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/model/util/elements/modules/m1/pkg/C.java	Mon Feb 13 10:29:04 2017 -0800
@@ -0,0 +1,11 @@
+/* /nodynamiccopyright/ */
+
+package pkg;
+
+/**
+ * A lovely description of class C of package pkg in module m1.
+ */
+public class C {
+    public C() {}
+    public static String foo() {return "foo";}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/model/util/elements/modules/m1/pkg/package-info.java	Mon Feb 13 10:29:04 2017 -0800
@@ -0,0 +1,6 @@
+/* /nodynamiccopyright/ */
+
+/**
+ * A lovely description of package pkg in module m1.
+ */
+package pkg;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/model/util/elements/modules/m2/module-info.java	Mon Feb 13 10:29:04 2017 -0800
@@ -0,0 +1,4 @@
+/* /nodynamiccopyright/ */
+
+module m2 {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/model/util/elements/modules/m2/pkg/C.java	Mon Feb 13 10:29:04 2017 -0800
@@ -0,0 +1,11 @@
+/* /nodynamiccopyright/ */
+
+package pkg;
+
+/**
+ * A lovely description of class C of package pkg in module m2.
+ */
+public class C {
+    public C() {}
+    public static String bar() {return "bar";}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/processing/model/util/elements/modules/m2/pkg/package-info.java	Mon Feb 13 10:29:04 2017 -0800
@@ -0,0 +1,6 @@
+/* /nodynamiccopyright/ */
+
+/**
+ * A lovely description of package pkg in module m2.
+ */
+package pkg;