• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.doclava.javadoc;
18 
19 import java.util.ArrayDeque;
20 import java.util.Deque;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.NoSuchElementException;
24 import javax.lang.model.element.Element;
25 import javax.lang.model.element.ElementKind;
26 import javax.lang.model.element.TypeElement;
27 import javax.lang.model.type.TypeMirror;
28 import javax.lang.model.util.Elements;
29 import javax.lang.model.util.Types;
30 import jdk.javadoc.doclet.DocletEnvironment;
31 
32 class DocletElementUtils {
33 
34     private final DocletEnvironment env;
35     private final Types typeUtils;
36     private final Elements elementUtils;
37 
38     private final TypeMirror exceptionType;
39     private final TypeMirror errorType;
40     private final TypeMirror throwableType;
41 
DocletElementUtils()42     private DocletElementUtils() {
43         throw new UnsupportedOperationException("Hide default constructor.");
44     }
45 
DocletElementUtils(DocletEnvironment e)46     public DocletElementUtils(DocletEnvironment e) {
47         this.env = e;
48         this.typeUtils = e.getTypeUtils();
49         this.elementUtils = e.getElementUtils();
50 
51         this.exceptionType = getTypeByName("java.lang.Exception");
52         this.errorType = getTypeByName("java.lang.Error");
53         this.throwableType = getTypeByName("java.lang.Throwable");
54     }
55 
isException(Element e)56     public boolean isException(Element e) {
57         if (!isClass(e)) {
58             return false;
59         }
60         return typeUtils.isSubtype(e.asType(), exceptionType);
61     }
62 
isError(Element e)63     public boolean isError(Element e) {
64         if (!isClass(e)) {
65             return false;
66         }
67         return typeUtils.isSubtype(e.asType(), errorType);
68     }
69 
isThrowable(Element e)70     public boolean isThrowable(Element e) {
71         if (!isClass(e)) {
72             return false;
73         }
74         return typeUtils.isSubtype(e.asType(), throwableType);
75     }
76 
isAnnotation(Element e)77     public boolean isAnnotation(Element e) {
78         return e.getKind() == ElementKind.ANNOTATION_TYPE;
79     }
80 
isEnum(Element e)81     public boolean isEnum(Element e) {
82         return e.getKind() == ElementKind.ENUM;
83     }
84 
isInterface(Element e)85     public boolean isInterface(Element e) {
86         return e.getKind() == ElementKind.INTERFACE;
87     }
88 
isClass(Element e)89     public boolean isClass(Element e) {
90         return e.getKind() == ElementKind.CLASS;
91     }
92 
isPackage(Element e)93     public boolean isPackage(Element e) {
94         return e.getKind() == ElementKind.PACKAGE;
95     }
96 
97     private final HashMap<String, TypeMirror> typeMirrorsCache = new HashMap<>();
98 
getTypeByName(String name)99     public TypeMirror getTypeByName(String name) {
100         TypeMirror mirror = typeMirrorsCache.get(name);
101         if (mirror != null) {
102             return mirror;
103         }
104         TypeElement e = elementUtils.getTypeElement(name);
105         if (e == null || (mirror = e.asType()) == null) {
106             return null;
107         }
108         typeMirrorsCache.put(name, mirror);
109         return mirror;
110     }
111 
112     /**
113      * Return the class name without package qualifier but with enclosing class qualifier, e.g. for
114      * {@code java.lang.String} return {@code String} (public class); and for {@code
115      * java.util.Map.Entry} return {@code Map.Entry} (nested class).
116      *
117      * @param e class
118      * @return class name without package qualifier but with enclosing class qualifier
119      */
getClassNameUntilNotNested(TypeElement e)120     public String getClassNameUntilNotNested(TypeElement e) {
121         Deque<String> acc = new ArrayDeque<>();
122         var it = new EnclosingUntilNotNestedIterator(e, this);
123         it.forEachRemaining(nestedElement -> acc.addFirst(nestedElement.getSimpleName().toString()));
124         return String.join(".", acc);
125     }
126 
getEnclosingTypeElement(Element e)127     public TypeElement getEnclosingTypeElement(Element e) {
128         if (isPackage(e)) {
129             return null;
130 
131         }
132         Element encl = e.getEnclosingElement();
133         if (isPackage(encl)) {
134             return null;
135         }
136         while (!(isClass(encl) || isEnum(encl) || isInterface(encl) || isAnnotation(encl))) {
137             encl = encl.getEnclosingElement();
138             if (encl == null) {
139                 return null;
140             }
141         }
142         return (TypeElement) encl;
143     }
144 
145     /**
146      * Iterates over the immediately lexically enclosing elements for a nested type up until type
147      * is no longer nested.
148      */
149     private static class EnclosingUntilNotNestedIterator implements Iterator<TypeElement> {
150 
151         private TypeElement current;
152         private final DocletElementUtils utils;
153 
EnclosingUntilNotNestedIterator(TypeElement typeElement, DocletElementUtils utils)154         public EnclosingUntilNotNestedIterator(TypeElement typeElement, DocletElementUtils utils) {
155             this.current = typeElement;
156             this.utils = utils;
157         }
158 
159         @Override
hasNext()160         public boolean hasNext() {
161             return (current != null);
162         }
163 
164         @Override
next()165         public TypeElement next() {
166             if (!hasNext()) {
167                 throw new NoSuchElementException();
168             }
169             TypeElement ret = current;
170             current = utils.getEnclosingTypeElement(current);
171             return ret;
172         }
173     }
174 }
175