xref: /aosp_15_r20/external/doclava/src/com/google/doclava/AndroidAuxSource.java (revision feeed43c7c55e85932c547a3cefc559df175227c)
1*feeed43cSAndroid Build Coastguard Worker /*
2*feeed43cSAndroid Build Coastguard Worker  * Copyright (C) 2017 Google Inc.
3*feeed43cSAndroid Build Coastguard Worker  *
4*feeed43cSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*feeed43cSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*feeed43cSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*feeed43cSAndroid Build Coastguard Worker  *
8*feeed43cSAndroid Build Coastguard Worker  * http://www.apache.org/licenses/LICENSE-2.0
9*feeed43cSAndroid Build Coastguard Worker  *
10*feeed43cSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*feeed43cSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*feeed43cSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*feeed43cSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*feeed43cSAndroid Build Coastguard Worker  * limitations under the License.
15*feeed43cSAndroid Build Coastguard Worker  */
16*feeed43cSAndroid Build Coastguard Worker 
17*feeed43cSAndroid Build Coastguard Worker package com.google.doclava;
18*feeed43cSAndroid Build Coastguard Worker 
19*feeed43cSAndroid Build Coastguard Worker import java.util.ArrayList;
20*feeed43cSAndroid Build Coastguard Worker import java.util.HashMap;
21*feeed43cSAndroid Build Coastguard Worker import java.util.List;
22*feeed43cSAndroid Build Coastguard Worker import java.util.Map;
23*feeed43cSAndroid Build Coastguard Worker import java.util.regex.Pattern;
24*feeed43cSAndroid Build Coastguard Worker 
25*feeed43cSAndroid Build Coastguard Worker public class AndroidAuxSource implements AuxSource {
26*feeed43cSAndroid Build Coastguard Worker   private static final int TYPE_CLASS = 0;
27*feeed43cSAndroid Build Coastguard Worker   private static final int TYPE_FIELD = 1;
28*feeed43cSAndroid Build Coastguard Worker   private static final int TYPE_METHOD = 2;
29*feeed43cSAndroid Build Coastguard Worker   private static final int TYPE_PARAM = 3;
30*feeed43cSAndroid Build Coastguard Worker   private static final int TYPE_RETURN = 4;
31*feeed43cSAndroid Build Coastguard Worker 
32*feeed43cSAndroid Build Coastguard Worker   @Override
classAuxTags(ClassInfo clazz)33*feeed43cSAndroid Build Coastguard Worker   public TagInfo[] classAuxTags(ClassInfo clazz) {
34*feeed43cSAndroid Build Coastguard Worker     if (hasSuppress(clazz.annotations())) return TagInfo.EMPTY_ARRAY;
35*feeed43cSAndroid Build Coastguard Worker     ArrayList<TagInfo> tags = new ArrayList<>();
36*feeed43cSAndroid Build Coastguard Worker     for (AnnotationInstanceInfo annotation : clazz.annotations()) {
37*feeed43cSAndroid Build Coastguard Worker       // Document system services
38*feeed43cSAndroid Build Coastguard Worker       if (annotation.type().qualifiedNameMatches("android", "annotation.SystemService")) {
39*feeed43cSAndroid Build Coastguard Worker         ArrayList<TagInfo> valueTags = new ArrayList<>();
40*feeed43cSAndroid Build Coastguard Worker         valueTags
41*feeed43cSAndroid Build Coastguard Worker             .add(new ParsedTagInfo("", "",
42*feeed43cSAndroid Build Coastguard Worker                 "{@link android.content.Context#getSystemService(Class)"
43*feeed43cSAndroid Build Coastguard Worker                     + " Context.getSystemService(Class)}",
44*feeed43cSAndroid Build Coastguard Worker                 null, SourcePositionInfo.UNKNOWN));
45*feeed43cSAndroid Build Coastguard Worker         valueTags.add(new ParsedTagInfo("", "",
46*feeed43cSAndroid Build Coastguard Worker             "{@code " + clazz.name() + ".class}", null,
47*feeed43cSAndroid Build Coastguard Worker             SourcePositionInfo.UNKNOWN));
48*feeed43cSAndroid Build Coastguard Worker 
49*feeed43cSAndroid Build Coastguard Worker         ClassInfo contextClass = annotation.type().findClass("android.content.Context");
50*feeed43cSAndroid Build Coastguard Worker         for (AnnotationValueInfo val : annotation.elementValues()) {
51*feeed43cSAndroid Build Coastguard Worker           switch (val.element().name()) {
52*feeed43cSAndroid Build Coastguard Worker             case "value":
53*feeed43cSAndroid Build Coastguard Worker               final String expected = String.valueOf(val.value());
54*feeed43cSAndroid Build Coastguard Worker               for (FieldInfo field : contextClass.fields()) {
55*feeed43cSAndroid Build Coastguard Worker                 if (field.isHiddenOrRemoved()) continue;
56*feeed43cSAndroid Build Coastguard Worker                 if (String.valueOf(field.constantValue()).equals(expected)) {
57*feeed43cSAndroid Build Coastguard Worker                   valueTags.add(new ParsedTagInfo("", "",
58*feeed43cSAndroid Build Coastguard Worker                       "{@link android.content.Context#getSystemService(String)"
59*feeed43cSAndroid Build Coastguard Worker                           + " Context.getSystemService(String)}",
60*feeed43cSAndroid Build Coastguard Worker                       null, SourcePositionInfo.UNKNOWN));
61*feeed43cSAndroid Build Coastguard Worker                   valueTags.add(new ParsedTagInfo("", "",
62*feeed43cSAndroid Build Coastguard Worker                       "{@link android.content.Context#" + field.name()
63*feeed43cSAndroid Build Coastguard Worker                           + " Context." + field.name() + "}",
64*feeed43cSAndroid Build Coastguard Worker                       null, SourcePositionInfo.UNKNOWN));
65*feeed43cSAndroid Build Coastguard Worker                 }
66*feeed43cSAndroid Build Coastguard Worker               }
67*feeed43cSAndroid Build Coastguard Worker               break;
68*feeed43cSAndroid Build Coastguard Worker           }
69*feeed43cSAndroid Build Coastguard Worker         }
70*feeed43cSAndroid Build Coastguard Worker 
71*feeed43cSAndroid Build Coastguard Worker         Map<String, String> args = new HashMap<>();
72*feeed43cSAndroid Build Coastguard Worker         tags.add(new AuxTagInfo("@service", "@service", SourcePositionInfo.UNKNOWN, args,
73*feeed43cSAndroid Build Coastguard Worker             valueTags.toArray(TagInfo.getArray(valueTags.size()))));
74*feeed43cSAndroid Build Coastguard Worker       }
75*feeed43cSAndroid Build Coastguard Worker     }
76*feeed43cSAndroid Build Coastguard Worker     auxTags(TYPE_CLASS, clazz.annotations(), toString(clazz.inlineTags()), tags);
77*feeed43cSAndroid Build Coastguard Worker     return tags.toArray(TagInfo.getArray(tags.size()));
78*feeed43cSAndroid Build Coastguard Worker   }
79*feeed43cSAndroid Build Coastguard Worker 
80*feeed43cSAndroid Build Coastguard Worker   @Override
fieldAuxTags(FieldInfo field)81*feeed43cSAndroid Build Coastguard Worker   public TagInfo[] fieldAuxTags(FieldInfo field) {
82*feeed43cSAndroid Build Coastguard Worker     if (hasSuppress(field)) return TagInfo.EMPTY_ARRAY;
83*feeed43cSAndroid Build Coastguard Worker     return auxTags(TYPE_FIELD, field.annotations(), toString(field.inlineTags()));
84*feeed43cSAndroid Build Coastguard Worker   }
85*feeed43cSAndroid Build Coastguard Worker 
86*feeed43cSAndroid Build Coastguard Worker   @Override
methodAuxTags(MethodInfo method)87*feeed43cSAndroid Build Coastguard Worker   public TagInfo[] methodAuxTags(MethodInfo method) {
88*feeed43cSAndroid Build Coastguard Worker     if (hasSuppress(method)) return TagInfo.EMPTY_ARRAY;
89*feeed43cSAndroid Build Coastguard Worker     return auxTags(TYPE_METHOD, method.annotations(), toString(method.inlineTags().tags()));
90*feeed43cSAndroid Build Coastguard Worker   }
91*feeed43cSAndroid Build Coastguard Worker 
92*feeed43cSAndroid Build Coastguard Worker   @Override
paramAuxTags(MethodInfo method, ParameterInfo param, String comment)93*feeed43cSAndroid Build Coastguard Worker   public TagInfo[] paramAuxTags(MethodInfo method, ParameterInfo param, String comment) {
94*feeed43cSAndroid Build Coastguard Worker     if (hasSuppress(method)) return TagInfo.EMPTY_ARRAY;
95*feeed43cSAndroid Build Coastguard Worker     if (hasSuppress(param.annotations())) return TagInfo.EMPTY_ARRAY;
96*feeed43cSAndroid Build Coastguard Worker     return auxTags(TYPE_PARAM, param.annotations(), new String[] { comment });
97*feeed43cSAndroid Build Coastguard Worker   }
98*feeed43cSAndroid Build Coastguard Worker 
99*feeed43cSAndroid Build Coastguard Worker   @Override
returnAuxTags(MethodInfo method)100*feeed43cSAndroid Build Coastguard Worker   public TagInfo[] returnAuxTags(MethodInfo method) {
101*feeed43cSAndroid Build Coastguard Worker     if (hasSuppress(method)) return TagInfo.EMPTY_ARRAY;
102*feeed43cSAndroid Build Coastguard Worker     return auxTags(TYPE_RETURN, method.annotations(), toString(method.returnTags().tags()));
103*feeed43cSAndroid Build Coastguard Worker   }
104*feeed43cSAndroid Build Coastguard Worker 
auxTags(int type, List<AnnotationInstanceInfo> annotations, String[] comment)105*feeed43cSAndroid Build Coastguard Worker   private static TagInfo[] auxTags(int type, List<AnnotationInstanceInfo> annotations,
106*feeed43cSAndroid Build Coastguard Worker       String[] comment) {
107*feeed43cSAndroid Build Coastguard Worker     ArrayList<TagInfo> tags = new ArrayList<>();
108*feeed43cSAndroid Build Coastguard Worker     auxTags(type, annotations, comment, tags);
109*feeed43cSAndroid Build Coastguard Worker     return tags.toArray(TagInfo.getArray(tags.size()));
110*feeed43cSAndroid Build Coastguard Worker   }
111*feeed43cSAndroid Build Coastguard Worker 
auxTags(int type, List<AnnotationInstanceInfo> annotations, String[] comment, ArrayList<TagInfo> tags)112*feeed43cSAndroid Build Coastguard Worker   private static void auxTags(int type, List<AnnotationInstanceInfo> annotations,
113*feeed43cSAndroid Build Coastguard Worker       String[] comment, ArrayList<TagInfo> tags) {
114*feeed43cSAndroid Build Coastguard Worker     for (AnnotationInstanceInfo annotation : annotations) {
115*feeed43cSAndroid Build Coastguard Worker       // Ignore null-related annotations when docs already mention
116*feeed43cSAndroid Build Coastguard Worker       if (annotation.type().qualifiedNameMatches("android", "annotation.NonNull")
117*feeed43cSAndroid Build Coastguard Worker           || annotation.type().qualifiedNameMatches("android", "annotation.Nullable")) {
118*feeed43cSAndroid Build Coastguard Worker         boolean mentionsNull = false;
119*feeed43cSAndroid Build Coastguard Worker         for (String c : comment) {
120*feeed43cSAndroid Build Coastguard Worker           mentionsNull |= Pattern.compile("\\bnull\\b").matcher(c).find();
121*feeed43cSAndroid Build Coastguard Worker         }
122*feeed43cSAndroid Build Coastguard Worker         if (mentionsNull) {
123*feeed43cSAndroid Build Coastguard Worker           continue;
124*feeed43cSAndroid Build Coastguard Worker         }
125*feeed43cSAndroid Build Coastguard Worker       }
126*feeed43cSAndroid Build Coastguard Worker 
127*feeed43cSAndroid Build Coastguard Worker       // Blindly include docs requested by annotations
128*feeed43cSAndroid Build Coastguard Worker       ParsedTagInfo[] docTags = ParsedTagInfo.EMPTY_ARRAY;
129*feeed43cSAndroid Build Coastguard Worker       switch (type) {
130*feeed43cSAndroid Build Coastguard Worker         case TYPE_METHOD:
131*feeed43cSAndroid Build Coastguard Worker         case TYPE_FIELD:
132*feeed43cSAndroid Build Coastguard Worker         case TYPE_CLASS:
133*feeed43cSAndroid Build Coastguard Worker           docTags = annotation.type().comment().memberDocTags();
134*feeed43cSAndroid Build Coastguard Worker           break;
135*feeed43cSAndroid Build Coastguard Worker         case TYPE_PARAM:
136*feeed43cSAndroid Build Coastguard Worker           docTags = annotation.type().comment().paramDocTags();
137*feeed43cSAndroid Build Coastguard Worker           break;
138*feeed43cSAndroid Build Coastguard Worker         case TYPE_RETURN:
139*feeed43cSAndroid Build Coastguard Worker           docTags = annotation.type().comment().returnDocTags();
140*feeed43cSAndroid Build Coastguard Worker           break;
141*feeed43cSAndroid Build Coastguard Worker       }
142*feeed43cSAndroid Build Coastguard Worker       for (ParsedTagInfo docTag : docTags) {
143*feeed43cSAndroid Build Coastguard Worker         tags.add(docTag);
144*feeed43cSAndroid Build Coastguard Worker       }
145*feeed43cSAndroid Build Coastguard Worker 
146*feeed43cSAndroid Build Coastguard Worker       // Document required permissions
147*feeed43cSAndroid Build Coastguard Worker       if ((type == TYPE_CLASS || type == TYPE_METHOD || type == TYPE_FIELD)
148*feeed43cSAndroid Build Coastguard Worker           && annotation.type().qualifiedNameMatches("android", "annotation.RequiresPermission")) {
149*feeed43cSAndroid Build Coastguard Worker         ArrayList<AnnotationValueInfo> values = new ArrayList<>();
150*feeed43cSAndroid Build Coastguard Worker         boolean any = false;
151*feeed43cSAndroid Build Coastguard Worker         for (AnnotationValueInfo val : annotation.elementValues()) {
152*feeed43cSAndroid Build Coastguard Worker           switch (val.element().name()) {
153*feeed43cSAndroid Build Coastguard Worker             case "value":
154*feeed43cSAndroid Build Coastguard Worker               values.add(val);
155*feeed43cSAndroid Build Coastguard Worker               break;
156*feeed43cSAndroid Build Coastguard Worker             case "allOf":
157*feeed43cSAndroid Build Coastguard Worker               values = (ArrayList<AnnotationValueInfo>) val.value();
158*feeed43cSAndroid Build Coastguard Worker               break;
159*feeed43cSAndroid Build Coastguard Worker             case "anyOf":
160*feeed43cSAndroid Build Coastguard Worker               any = true;
161*feeed43cSAndroid Build Coastguard Worker               values = (ArrayList<AnnotationValueInfo>) val.value();
162*feeed43cSAndroid Build Coastguard Worker               break;
163*feeed43cSAndroid Build Coastguard Worker           }
164*feeed43cSAndroid Build Coastguard Worker         }
165*feeed43cSAndroid Build Coastguard Worker         if (values.isEmpty()) continue;
166*feeed43cSAndroid Build Coastguard Worker 
167*feeed43cSAndroid Build Coastguard Worker         ClassInfo permClass = annotation.type().findClass("android.Manifest.permission");
168*feeed43cSAndroid Build Coastguard Worker         ArrayList<TagInfo> valueTags = new ArrayList<>();
169*feeed43cSAndroid Build Coastguard Worker         for (AnnotationValueInfo value : values) {
170*feeed43cSAndroid Build Coastguard Worker           final String expected = String.valueOf(value.value());
171*feeed43cSAndroid Build Coastguard Worker           for (FieldInfo field : permClass.fields()) {
172*feeed43cSAndroid Build Coastguard Worker             if (field.isHiddenOrRemoved()) continue;
173*feeed43cSAndroid Build Coastguard Worker             if (String.valueOf(field.constantValue()).equals(expected)) {
174*feeed43cSAndroid Build Coastguard Worker               valueTags.add(new ParsedTagInfo("", "",
175*feeed43cSAndroid Build Coastguard Worker                   "{@link " + permClass.qualifiedName() + "#" + field.name() + "}", null,
176*feeed43cSAndroid Build Coastguard Worker                   SourcePositionInfo.UNKNOWN));
177*feeed43cSAndroid Build Coastguard Worker             }
178*feeed43cSAndroid Build Coastguard Worker           }
179*feeed43cSAndroid Build Coastguard Worker         }
180*feeed43cSAndroid Build Coastguard Worker 
181*feeed43cSAndroid Build Coastguard Worker         Map<String, String> args = new HashMap<>();
182*feeed43cSAndroid Build Coastguard Worker         if (any) args.put("any", "true");
183*feeed43cSAndroid Build Coastguard Worker         tags.add(new AuxTagInfo("@permission", "@permission", SourcePositionInfo.UNKNOWN, args,
184*feeed43cSAndroid Build Coastguard Worker             valueTags.toArray(TagInfo.getArray(valueTags.size()))));
185*feeed43cSAndroid Build Coastguard Worker       }
186*feeed43cSAndroid Build Coastguard Worker 
187*feeed43cSAndroid Build Coastguard Worker       // Document required features
188*feeed43cSAndroid Build Coastguard Worker       if ((type == TYPE_CLASS || type == TYPE_METHOD || type == TYPE_FIELD)
189*feeed43cSAndroid Build Coastguard Worker           && annotation.type().qualifiedNameMatches("android", "annotation.RequiresFeature")) {
190*feeed43cSAndroid Build Coastguard Worker         AnnotationValueInfo value = null;
191*feeed43cSAndroid Build Coastguard Worker         for (AnnotationValueInfo val : annotation.elementValues()) {
192*feeed43cSAndroid Build Coastguard Worker           switch (val.element().name()) {
193*feeed43cSAndroid Build Coastguard Worker             case "value":
194*feeed43cSAndroid Build Coastguard Worker               value = val;
195*feeed43cSAndroid Build Coastguard Worker               break;
196*feeed43cSAndroid Build Coastguard Worker           }
197*feeed43cSAndroid Build Coastguard Worker         }
198*feeed43cSAndroid Build Coastguard Worker         if (value == null) continue;
199*feeed43cSAndroid Build Coastguard Worker 
200*feeed43cSAndroid Build Coastguard Worker         ClassInfo pmClass = annotation.type().findClass("android.content.pm.PackageManager");
201*feeed43cSAndroid Build Coastguard Worker         ArrayList<TagInfo> valueTags = new ArrayList<>();
202*feeed43cSAndroid Build Coastguard Worker         final String expected = String.valueOf(value.value());
203*feeed43cSAndroid Build Coastguard Worker         for (FieldInfo field : pmClass.fields()) {
204*feeed43cSAndroid Build Coastguard Worker           if (field.isHiddenOrRemoved()) continue;
205*feeed43cSAndroid Build Coastguard Worker           if (String.valueOf(field.constantValue()).equals(expected)) {
206*feeed43cSAndroid Build Coastguard Worker             valueTags.add(new ParsedTagInfo("", "",
207*feeed43cSAndroid Build Coastguard Worker                 "{@link " + pmClass.qualifiedName() + "#" + field.name() + "}", null,
208*feeed43cSAndroid Build Coastguard Worker                 SourcePositionInfo.UNKNOWN));
209*feeed43cSAndroid Build Coastguard Worker           }
210*feeed43cSAndroid Build Coastguard Worker         }
211*feeed43cSAndroid Build Coastguard Worker 
212*feeed43cSAndroid Build Coastguard Worker         valueTags.add(new ParsedTagInfo("", "",
213*feeed43cSAndroid Build Coastguard Worker             "{@link android.content.pm.PackageManager#hasSystemFeature(String)"
214*feeed43cSAndroid Build Coastguard Worker                 + " PackageManager.hasSystemFeature(String)}",
215*feeed43cSAndroid Build Coastguard Worker             null, SourcePositionInfo.UNKNOWN));
216*feeed43cSAndroid Build Coastguard Worker 
217*feeed43cSAndroid Build Coastguard Worker         Map<String, String> args = new HashMap<>();
218*feeed43cSAndroid Build Coastguard Worker         tags.add(new AuxTagInfo("@feature", "@feature", SourcePositionInfo.UNKNOWN, args,
219*feeed43cSAndroid Build Coastguard Worker             valueTags.toArray(TagInfo.getArray(valueTags.size()))));
220*feeed43cSAndroid Build Coastguard Worker       }
221*feeed43cSAndroid Build Coastguard Worker 
222*feeed43cSAndroid Build Coastguard Worker       // Document provider columns
223*feeed43cSAndroid Build Coastguard Worker       if ((type == TYPE_FIELD) && annotation.type().qualifiedNameMatches("android", "Column")) {
224*feeed43cSAndroid Build Coastguard Worker         String value = null;
225*feeed43cSAndroid Build Coastguard Worker         boolean readOnly = false;
226*feeed43cSAndroid Build Coastguard Worker         for (AnnotationValueInfo val : annotation.elementValues()) {
227*feeed43cSAndroid Build Coastguard Worker           switch (val.element().name()) {
228*feeed43cSAndroid Build Coastguard Worker             case "value":
229*feeed43cSAndroid Build Coastguard Worker               value = String.valueOf(val.value());
230*feeed43cSAndroid Build Coastguard Worker               break;
231*feeed43cSAndroid Build Coastguard Worker             case "readOnly":
232*feeed43cSAndroid Build Coastguard Worker               readOnly = Boolean.parseBoolean(String.valueOf(val.value()));
233*feeed43cSAndroid Build Coastguard Worker               break;
234*feeed43cSAndroid Build Coastguard Worker           }
235*feeed43cSAndroid Build Coastguard Worker         }
236*feeed43cSAndroid Build Coastguard Worker 
237*feeed43cSAndroid Build Coastguard Worker         ArrayList<TagInfo> valueTags = new ArrayList<>();
238*feeed43cSAndroid Build Coastguard Worker         valueTags.add(new ParsedTagInfo("", "",
239*feeed43cSAndroid Build Coastguard Worker             "{@link android.content.ContentProvider}", null, SourcePositionInfo.UNKNOWN));
240*feeed43cSAndroid Build Coastguard Worker         valueTags.add(new ParsedTagInfo("", "",
241*feeed43cSAndroid Build Coastguard Worker             "{@link android.content.ContentValues}", null, SourcePositionInfo.UNKNOWN));
242*feeed43cSAndroid Build Coastguard Worker         valueTags.add(new ParsedTagInfo("", "",
243*feeed43cSAndroid Build Coastguard Worker             "{@link android.database.Cursor}", null, SourcePositionInfo.UNKNOWN));
244*feeed43cSAndroid Build Coastguard Worker 
245*feeed43cSAndroid Build Coastguard Worker         ClassInfo cursorClass = annotation.type().findClass("android.database.Cursor");
246*feeed43cSAndroid Build Coastguard Worker         for (FieldInfo field : cursorClass.fields()) {
247*feeed43cSAndroid Build Coastguard Worker           if (field.isHiddenOrRemoved()) continue;
248*feeed43cSAndroid Build Coastguard Worker           if (String.valueOf(field.constantValue()).equals(value)) {
249*feeed43cSAndroid Build Coastguard Worker             valueTags.add(new ParsedTagInfo("", "",
250*feeed43cSAndroid Build Coastguard Worker                 "{@link android.database.Cursor#" + field.name() + "}",
251*feeed43cSAndroid Build Coastguard Worker                 null, SourcePositionInfo.UNKNOWN));
252*feeed43cSAndroid Build Coastguard Worker           }
253*feeed43cSAndroid Build Coastguard Worker         }
254*feeed43cSAndroid Build Coastguard Worker         if (valueTags.size() < 4) continue;
255*feeed43cSAndroid Build Coastguard Worker 
256*feeed43cSAndroid Build Coastguard Worker         Map<String, String> args = new HashMap<>();
257*feeed43cSAndroid Build Coastguard Worker         if (readOnly) args.put("readOnly", "true");
258*feeed43cSAndroid Build Coastguard Worker         tags.add(new AuxTagInfo("@column", "@column", SourcePositionInfo.UNKNOWN, args,
259*feeed43cSAndroid Build Coastguard Worker             valueTags.toArray(TagInfo.getArray(valueTags.size()))));
260*feeed43cSAndroid Build Coastguard Worker       }
261*feeed43cSAndroid Build Coastguard Worker 
262*feeed43cSAndroid Build Coastguard Worker       // The remaining annotations below always appear on return docs, and
263*feeed43cSAndroid Build Coastguard Worker       // should not be included in the method body
264*feeed43cSAndroid Build Coastguard Worker       if (type == TYPE_METHOD) continue;
265*feeed43cSAndroid Build Coastguard Worker 
266*feeed43cSAndroid Build Coastguard Worker       // Document value ranges
267*feeed43cSAndroid Build Coastguard Worker       if (annotation.type().qualifiedNameMatches("android", "annotation.IntRange")
268*feeed43cSAndroid Build Coastguard Worker           || annotation.type().qualifiedNameMatches("android", "annotation.FloatRange")) {
269*feeed43cSAndroid Build Coastguard Worker         String from = null;
270*feeed43cSAndroid Build Coastguard Worker         String to = null;
271*feeed43cSAndroid Build Coastguard Worker         for (AnnotationValueInfo val : annotation.elementValues()) {
272*feeed43cSAndroid Build Coastguard Worker           switch (val.element().name()) {
273*feeed43cSAndroid Build Coastguard Worker             case "from": from = String.valueOf(val.value()); break;
274*feeed43cSAndroid Build Coastguard Worker             case "to": to = String.valueOf(val.value()); break;
275*feeed43cSAndroid Build Coastguard Worker           }
276*feeed43cSAndroid Build Coastguard Worker         }
277*feeed43cSAndroid Build Coastguard Worker         if (from != null || to != null) {
278*feeed43cSAndroid Build Coastguard Worker           Map<String, String> args = new HashMap<>();
279*feeed43cSAndroid Build Coastguard Worker           if (from != null) args.put("from", from);
280*feeed43cSAndroid Build Coastguard Worker           if (to != null) args.put("to", to);
281*feeed43cSAndroid Build Coastguard Worker           tags.add(new AuxTagInfo("@range", "@range", SourcePositionInfo.UNKNOWN, args,
282*feeed43cSAndroid Build Coastguard Worker               TagInfo.EMPTY_ARRAY));
283*feeed43cSAndroid Build Coastguard Worker         }
284*feeed43cSAndroid Build Coastguard Worker       }
285*feeed43cSAndroid Build Coastguard Worker 
286*feeed43cSAndroid Build Coastguard Worker       // Document integer values
287*feeed43cSAndroid Build Coastguard Worker       for (AnnotationInstanceInfo inner : annotation.type().annotations()) {
288*feeed43cSAndroid Build Coastguard Worker         boolean intDef = inner.type().qualifiedNameMatches("android", "annotation.IntDef");
289*feeed43cSAndroid Build Coastguard Worker         boolean stringDef = inner.type().qualifiedNameMatches("android", "annotation.StringDef");
290*feeed43cSAndroid Build Coastguard Worker         if (intDef || stringDef) {
291*feeed43cSAndroid Build Coastguard Worker           ArrayList<AnnotationValueInfo> prefixes = null;
292*feeed43cSAndroid Build Coastguard Worker           ArrayList<AnnotationValueInfo> suffixes = null;
293*feeed43cSAndroid Build Coastguard Worker           ArrayList<AnnotationValueInfo> values = null;
294*feeed43cSAndroid Build Coastguard Worker           final String kind = intDef ? "@intDef" : "@stringDef";
295*feeed43cSAndroid Build Coastguard Worker           boolean flag = false;
296*feeed43cSAndroid Build Coastguard Worker 
297*feeed43cSAndroid Build Coastguard Worker           for (AnnotationValueInfo val : inner.elementValues()) {
298*feeed43cSAndroid Build Coastguard Worker             switch (val.element().name()) {
299*feeed43cSAndroid Build Coastguard Worker               case "prefix": prefixes = (ArrayList<AnnotationValueInfo>) val.value(); break;
300*feeed43cSAndroid Build Coastguard Worker               case "suffix": suffixes = (ArrayList<AnnotationValueInfo>) val.value(); break;
301*feeed43cSAndroid Build Coastguard Worker               case "value": values = (ArrayList<AnnotationValueInfo>) val.value(); break;
302*feeed43cSAndroid Build Coastguard Worker               case "flag": flag = Boolean.parseBoolean(String.valueOf(val.value())); break;
303*feeed43cSAndroid Build Coastguard Worker             }
304*feeed43cSAndroid Build Coastguard Worker           }
305*feeed43cSAndroid Build Coastguard Worker 
306*feeed43cSAndroid Build Coastguard Worker           // Sadly we can only generate docs when told about a prefix/suffix
307*feeed43cSAndroid Build Coastguard Worker           if (prefixes == null) prefixes = new ArrayList<>();
308*feeed43cSAndroid Build Coastguard Worker           if (suffixes == null) suffixes = new ArrayList<>();
309*feeed43cSAndroid Build Coastguard Worker           if (prefixes.isEmpty() && suffixes.isEmpty()) continue;
310*feeed43cSAndroid Build Coastguard Worker 
311*feeed43cSAndroid Build Coastguard Worker           final ClassInfo clazz = annotation.type().containingClass();
312*feeed43cSAndroid Build Coastguard Worker           final HashMap<String, FieldInfo> candidates = new HashMap<>();
313*feeed43cSAndroid Build Coastguard Worker           for (FieldInfo field : clazz.fields()) {
314*feeed43cSAndroid Build Coastguard Worker             if (field.isHiddenOrRemoved()) continue;
315*feeed43cSAndroid Build Coastguard Worker             for (AnnotationValueInfo prefix : prefixes) {
316*feeed43cSAndroid Build Coastguard Worker               if (field.name().startsWith(String.valueOf(prefix.value()))) {
317*feeed43cSAndroid Build Coastguard Worker                 candidates.put(String.valueOf(field.constantValue()), field);
318*feeed43cSAndroid Build Coastguard Worker               }
319*feeed43cSAndroid Build Coastguard Worker             }
320*feeed43cSAndroid Build Coastguard Worker             for (AnnotationValueInfo suffix : suffixes) {
321*feeed43cSAndroid Build Coastguard Worker               if (field.name().endsWith(String.valueOf(suffix.value()))) {
322*feeed43cSAndroid Build Coastguard Worker                 candidates.put(String.valueOf(field.constantValue()), field);
323*feeed43cSAndroid Build Coastguard Worker               }
324*feeed43cSAndroid Build Coastguard Worker             }
325*feeed43cSAndroid Build Coastguard Worker           }
326*feeed43cSAndroid Build Coastguard Worker 
327*feeed43cSAndroid Build Coastguard Worker           ArrayList<TagInfo> valueTags = new ArrayList<>();
328*feeed43cSAndroid Build Coastguard Worker           for (AnnotationValueInfo value : values) {
329*feeed43cSAndroid Build Coastguard Worker             final String expected = String.valueOf(value.value());
330*feeed43cSAndroid Build Coastguard Worker             final FieldInfo field = candidates.remove(expected);
331*feeed43cSAndroid Build Coastguard Worker             if (field != null) {
332*feeed43cSAndroid Build Coastguard Worker               valueTags.add(new ParsedTagInfo("", "",
333*feeed43cSAndroid Build Coastguard Worker                   "{@link " + clazz.qualifiedName() + "#" + field.name() + "}", null,
334*feeed43cSAndroid Build Coastguard Worker                   SourcePositionInfo.UNKNOWN));
335*feeed43cSAndroid Build Coastguard Worker             }
336*feeed43cSAndroid Build Coastguard Worker           }
337*feeed43cSAndroid Build Coastguard Worker 
338*feeed43cSAndroid Build Coastguard Worker           if (!valueTags.isEmpty()) {
339*feeed43cSAndroid Build Coastguard Worker             Map<String, String> args = new HashMap<>();
340*feeed43cSAndroid Build Coastguard Worker             if (flag) args.put("flag", "true");
341*feeed43cSAndroid Build Coastguard Worker             tags.add(new AuxTagInfo(kind, kind, SourcePositionInfo.UNKNOWN, args,
342*feeed43cSAndroid Build Coastguard Worker                 valueTags.toArray(TagInfo.getArray(valueTags.size()))));
343*feeed43cSAndroid Build Coastguard Worker           }
344*feeed43cSAndroid Build Coastguard Worker         }
345*feeed43cSAndroid Build Coastguard Worker       }
346*feeed43cSAndroid Build Coastguard Worker     }
347*feeed43cSAndroid Build Coastguard Worker   }
348*feeed43cSAndroid Build Coastguard Worker 
toString(TagInfo[] tags)349*feeed43cSAndroid Build Coastguard Worker   private static String[] toString(TagInfo[] tags) {
350*feeed43cSAndroid Build Coastguard Worker     final String[] res = new String[tags.length];
351*feeed43cSAndroid Build Coastguard Worker     for (int i = 0; i < res.length; i++) {
352*feeed43cSAndroid Build Coastguard Worker       res[i] = tags[i].text();
353*feeed43cSAndroid Build Coastguard Worker     }
354*feeed43cSAndroid Build Coastguard Worker     return res;
355*feeed43cSAndroid Build Coastguard Worker   }
356*feeed43cSAndroid Build Coastguard Worker 
hasSuppress(MemberInfo member)357*feeed43cSAndroid Build Coastguard Worker   private static boolean hasSuppress(MemberInfo member) {
358*feeed43cSAndroid Build Coastguard Worker     return hasSuppress(member.annotations())
359*feeed43cSAndroid Build Coastguard Worker         || hasSuppress(member.containingClass().annotations());
360*feeed43cSAndroid Build Coastguard Worker   }
361*feeed43cSAndroid Build Coastguard Worker 
hasSuppress(List<AnnotationInstanceInfo> annotations)362*feeed43cSAndroid Build Coastguard Worker   private static boolean hasSuppress(List<AnnotationInstanceInfo> annotations) {
363*feeed43cSAndroid Build Coastguard Worker     for (AnnotationInstanceInfo annotation : annotations) {
364*feeed43cSAndroid Build Coastguard Worker       if (annotation.type().qualifiedNameMatches("android", "annotation.SuppressAutoDoc")) {
365*feeed43cSAndroid Build Coastguard Worker         return true;
366*feeed43cSAndroid Build Coastguard Worker       }
367*feeed43cSAndroid Build Coastguard Worker     }
368*feeed43cSAndroid Build Coastguard Worker     return false;
369*feeed43cSAndroid Build Coastguard Worker   }
370*feeed43cSAndroid Build Coastguard Worker }
371