Skip to content

Commit 57ae9fb

Browse files
committed
8140442: Add getOutermostTypeElement to javax.lang.model utility class
Reviewed-by: jlahoda
1 parent 67869b4 commit 57ae9fb

File tree

6 files changed

+269
-55
lines changed

6 files changed

+269
-55
lines changed

src/java.compiler/share/classes/javax/lang/model/element/ElementKind.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -126,6 +126,10 @@ public enum ElementKind {
126126
*/
127127
BINDING_VARIABLE;
128128

129+
// Maintenance note: check if the default implementation of
130+
// Elements.getOutermostTypeElement needs updating when new kind
131+
// constants are added.
132+
129133
/**
130134
* Returns {@code true} if this is a kind of class:
131135
* either {@code CLASS} or {@code ENUM} or {@code RECORD}.

src/java.compiler/share/classes/javax/lang/model/util/Elements.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,67 @@ default ModuleElement getModuleOf(Element e) {
533533
*/
534534
List<? extends Element> getAllMembers(TypeElement type);
535535

536+
/**
537+
* {@return the outermost type element an element is contained in
538+
* if such a containing element exists; otherwise returns {@code
539+
* null}}
540+
*
541+
* {@linkplain ModuleElement Modules} and {@linkplain
542+
* PackageElement packages} do <em>not</em> have a containing type
543+
* element and therefore {@code null} is returned for those kinds
544+
* of elements.
545+
*
546+
* A {@link NestingKind#TOP_LEVEL top-level} class or
547+
* interface is its own outermost type element.
548+
*
549+
* @implSpec
550+
* The default implementation of this method first checks the kind
551+
* of the argument. For elements of kind {@code PACKAGE}, {@code
552+
* MODULE}, and {@code OTHER}, {@code null} is returned. For
553+
* elements of other kinds, the element is examined to see if it
554+
* is a top-level class or interface. If so, that element is
555+
* returned; otherwise, the {@linkplain
556+
* Element#getEnclosingElement enclosing element} chain is
557+
* followed until a top-level class or interface is found. The
558+
* element for the eventual top-level class or interface is
559+
* returned.
560+
*
561+
* @param e the element being examined
562+
* @see Element#getEnclosingElement
563+
* @since 18
564+
*/
565+
default TypeElement getOutermostTypeElement(Element e) {
566+
return switch (e.getKind()) {
567+
case PACKAGE,
568+
MODULE -> null; // Per the general spec above.
569+
case OTHER -> null; // Outside of base model of the javax.lang.model API
570+
571+
// Elements of all remaining kinds should be enclosed in some
572+
// sort of class or interface. Check to see if the element is
573+
// a top-level type; if so, return it. Otherwise, keep going
574+
// up the enclosing element chain until a top-level type is
575+
// found.
576+
default -> {
577+
Element enclosing = e;
578+
// This implementation is susceptible to infinite loops
579+
// for misbehaving element implementations.
580+
while (true) {
581+
// Conceptual instanceof TypeElement check. If the
582+
// argument is a type element, put it into a
583+
// one-element list, otherwise an empty list.
584+
List<TypeElement> possibleTypeElement = ElementFilter.typesIn(List.of(enclosing));
585+
if (!possibleTypeElement.isEmpty()) {
586+
TypeElement typeElement = possibleTypeElement.get(0);
587+
if (typeElement.getNestingKind() == NestingKind.TOP_LEVEL) {
588+
yield typeElement;
589+
}
590+
}
591+
enclosing = enclosing.getEnclosingElement();
592+
}
593+
}
594+
};
595+
}
596+
536597
/**
537598
* Returns all annotations <i>present</i> on an element, whether
538599
* directly present or present via inheritance.

src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,12 @@ private void addMembers(WriteableScope scope, Type type) {
571571
}
572572
}
573573

574+
@DefinedBy(Api.LANGUAGE_MODEL)
575+
public TypeElement getOutermostTypeElement(Element e) {
576+
Symbol sym = cast(Symbol.class, e);
577+
return sym.outermostClass();
578+
}
579+
574580
/**
575581
* Returns all annotations of an element, whether
576582
* inherited or directly present.

test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
* questions.
2222
*/
2323

24+
import java.io.Writer;
2425
import java.util.*;
2526
import javax.annotation.processing.*;
2627
import javax.lang.model.SourceVersion;
28+
import javax.lang.model.element.*;
2729
import javax.lang.model.util.*;
2830
import static javax.lang.model.SourceVersion.*;
2931

@@ -264,4 +266,60 @@ protected TypeKindVisitor(R defaultValue) {
264266
super(defaultValue);
265267
}
266268
}
269+
270+
/**
271+
* Vacuous implementation of javax.lang.model.util.Elements to aid
272+
* in test development. Methods with defaults in the interface are
273+
* *not* overridden to allow them to be tested.
274+
*/
275+
public static class VacuousElements implements Elements {
276+
public VacuousElements() {}
277+
278+
@Override
279+
public PackageElement getPackageElement(CharSequence name) {return null;}
280+
281+
@Override
282+
public TypeElement getTypeElement(CharSequence name) {return null;}
283+
284+
@Override
285+
public Map<? extends ExecutableElement, ? extends AnnotationValue>
286+
getElementValuesWithDefaults(AnnotationMirror a) {return null;}
287+
@Override
288+
public String getDocComment(Element e) {return null;}
289+
290+
@Override
291+
public boolean isDeprecated(Element e) {return false;}
292+
293+
@Override
294+
public Name getBinaryName(TypeElement type) {return null;}
295+
296+
@Override
297+
public PackageElement getPackageOf(Element e) {return null;}
298+
299+
@Override
300+
public List<? extends Element> getAllMembers(TypeElement type) {return null;}
301+
302+
@Override
303+
public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element e) {return null;}
304+
305+
@Override
306+
public boolean hides(Element hider, Element hidden) {return false;}
307+
308+
@Override
309+
public boolean overrides(ExecutableElement overrider,
310+
ExecutableElement overridden,
311+
TypeElement type) {return false;}
312+
313+
@Override
314+
public String getConstantExpression(Object value) {return null;}
315+
316+
@Override
317+
public void printElements(Writer w, Element... elements) {}
318+
319+
@Override
320+
public Name getName(CharSequence cs) {return null;}
321+
322+
@Override
323+
public boolean isFunctionalInterface(TypeElement type) {return false;}
324+
}
267325
}

test/langtools/tools/javac/processing/model/util/elements/TestIsAutomaticMod.java

Lines changed: 1 addition & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public boolean process(Set<? extends TypeElement> annotations,
5252
checkMod(enclosing, false);
5353
}
5454

55-
if ((new TestElements()).isAutomaticModule(null) != false) {
55+
if ((new VacuousElements()).isAutomaticModule(null) != false) {
5656
throw new RuntimeException("Bad behavior from default isAutomaticModule method");
5757
}
5858
}
@@ -68,57 +68,4 @@ private void checkMod(ModuleElement mod, boolean expectedIsAuto) {
6868
expectedIsAuto));
6969
}
7070
}
71-
72-
// Use default methods of javax.lang.model.util.Elements; define
73-
// vacuous methods to override the abstract methods.
74-
private static class TestElements implements Elements {
75-
public TestElements() {}
76-
77-
@Override
78-
public PackageElement getPackageElement(CharSequence name) {return null;}
79-
80-
@Override
81-
public TypeElement getTypeElement(CharSequence name) {return null;}
82-
83-
@Override
84-
public Map<? extends ExecutableElement, ? extends AnnotationValue>
85-
getElementValuesWithDefaults(AnnotationMirror a) {return null;}
86-
@Override
87-
public String getDocComment(Element e) {return null;}
88-
89-
@Override
90-
public boolean isDeprecated(Element e) {return false;}
91-
92-
@Override
93-
public Name getBinaryName(TypeElement type) {return null;}
94-
95-
@Override
96-
public PackageElement getPackageOf(Element e) {return null;}
97-
98-
@Override
99-
public List<? extends Element> getAllMembers(TypeElement type) {return null;}
100-
101-
@Override
102-
public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element e) {return null;}
103-
104-
@Override
105-
public boolean hides(Element hider, Element hidden) {return false;}
106-
107-
@Override
108-
public boolean overrides(ExecutableElement overrider,
109-
ExecutableElement overridden,
110-
TypeElement type) {return false;}
111-
112-
@Override
113-
public String getConstantExpression(Object value) {return null;}
114-
115-
@Override
116-
public void printElements(Writer w, Element... elements) {}
117-
118-
@Override
119-
public Name getName(CharSequence cs) {return null;}
120-
121-
@Override
122-
public boolean isFunctionalInterface(TypeElement type) {return false;}
123-
}
12471
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8140442
27+
* @summary Test Elements.getOutermostTypeElement
28+
* @library /tools/javac/lib
29+
* @build JavacTestingAbstractProcessor TestOutermostTypeElement
30+
* @compile -processor TestOutermostTypeElement -proc:only TestOutermostTypeElement.java
31+
*/
32+
33+
import java.io.Writer;
34+
import java.util.*;
35+
import javax.annotation.processing.*;
36+
import javax.lang.model.element.*;
37+
import javax.lang.model.util.*;
38+
39+
/**
40+
* Test basic workings of Elements.getOutermostTypeElement
41+
*/
42+
public class TestOutermostTypeElement extends JavacTestingAbstractProcessor {
43+
public boolean process(Set<? extends TypeElement> annotations,
44+
RoundEnvironment roundEnv) {
45+
if (!roundEnv.processingOver()) {
46+
Elements vacuousElts = new VacuousElements();
47+
48+
ModuleElement javaBaseMod = eltUtils.getModuleElement("java.base");
49+
checkOuter(javaBaseMod, null, vacuousElts);
50+
checkOuter(javaBaseMod, null, eltUtils);
51+
52+
PackageElement javaLangPkg = eltUtils.getPackageElement("java.lang");
53+
checkOuter(javaLangPkg, null, vacuousElts);
54+
checkOuter(javaLangPkg, null, eltUtils);
55+
56+
// Starting from the root elements, traverse over all
57+
// enclosed elements and type parameters. The outermost
58+
// enclosing type element should equal the root
59+
// element. This traversal does *not* hit elements
60+
// corresponding to structures inside of a method.
61+
for (TypeElement e : ElementFilter.typesIn(roundEnv.getRootElements()) ) {
62+
var outerScaner = new OuterScanner(e);
63+
outerScaner.scan(e, vacuousElts);
64+
outerScaner.scan(e, eltUtils);
65+
}
66+
}
67+
return true;
68+
}
69+
70+
private class OuterScanner extends ElementScanner<Void, Elements> {
71+
private TypeElement expectedOuter;
72+
public OuterScanner(TypeElement expectedOuter) {
73+
this.expectedOuter = expectedOuter;
74+
}
75+
76+
@Override
77+
public Void scan(Element e, Elements elts) {
78+
checkOuter(e, expectedOuter, elts);
79+
super.scan(e, elts);
80+
return null;
81+
}
82+
}
83+
84+
private void checkOuter(Element e, TypeElement expectedOuter, Elements elts) {
85+
var actualOuter = elts.getOutermostTypeElement(e);
86+
if (!Objects.equals(actualOuter, expectedOuter)) {
87+
throw new RuntimeException(String.format("Unexpected outermost ``%s''' for %s, expected ``%s.''%n",
88+
actualOuter,
89+
e,
90+
expectedOuter));
91+
}
92+
}
93+
}
94+
95+
/**
96+
* Outer class to host a variety of kinds of inner elements with Outer
97+
* as their outermost class.
98+
*/
99+
class Outer {
100+
private Outer() {}
101+
102+
public enum InnerEnum {
103+
VALUE1,
104+
VALUE2;
105+
106+
private int field;
107+
}
108+
109+
public static class InnerClass {
110+
private static int field;
111+
static {
112+
field = 5;
113+
}
114+
115+
public <C> InnerClass(C c) {}
116+
117+
void foo() {return;}
118+
static void bar() {return;}
119+
static <R> R baz(Class<? extends R> clazz) {return null;}
120+
121+
private class InnerInnerClass {
122+
public InnerInnerClass() {}
123+
}
124+
}
125+
126+
public interface InnerInterface {
127+
final int field = 42;
128+
void foo();
129+
}
130+
131+
public @interface InnerAnnotation {
132+
int value() default 1;
133+
}
134+
135+
public record InnerRecord(double rpm, double diameter) {
136+
void foo() {return;}
137+
}
138+
}

0 commit comments

Comments
 (0)