blob: 46b5b8fa6269186998cdee616a3c7c9728df47e3 [file] [log] [blame]
Ben Dodson920dbbb2010-08-04 15:21:06 -07001/*
2 * Copyright (C) 2010 Google Inc.
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
17package com.google.doclava;
18
Mathieu11b17962013-06-07 17:24:18 -050019import com.google.doclava.apicheck.ApiInfo;
Ben Dodson920dbbb2010-08-04 15:21:06 -070020import com.google.clearsilver.jsilver.data.Data;
Ben Dodson920dbbb2010-08-04 15:21:06 -070021import com.sun.javadoc.*;
Guang Zhuf9b7c1b2015-04-24 16:45:42 -070022
Ben Dodson920dbbb2010-08-04 15:21:06 -070023import java.util.*;
24
25public class PackageInfo extends DocInfo implements ContainerInfo {
26 public static final String DEFAULT_PACKAGE = "default package";
Scott Main3c1a6b22010-10-15 17:34:04 -070027
Ben Dodson920dbbb2010-08-04 15:21:06 -070028 public static final Comparator<PackageInfo> comparator = new Comparator<PackageInfo>() {
29 public int compare(PackageInfo a, PackageInfo b) {
30 return a.name().compareTo(b.name());
31 }
32 };
33
34 public PackageInfo(PackageDoc pkg, String name, SourcePositionInfo position) {
35 super(pkg.getRawCommentText(), position);
36 if (name.isEmpty()) {
37 mName = DEFAULT_PACKAGE;
38 } else {
39 mName = name;
40 }
41
42 mPackage = pkg;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070043 initializeMaps();
Ben Dodson920dbbb2010-08-04 15:21:06 -070044 }
Scott Main3c1a6b22010-10-15 17:34:04 -070045
Ben Dodson920dbbb2010-08-04 15:21:06 -070046 public PackageInfo(String name) {
47 super("", null);
48 mName = name;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070049 initializeMaps();
Ben Dodson920dbbb2010-08-04 15:21:06 -070050 }
Scott Main3c1a6b22010-10-15 17:34:04 -070051
Ben Dodson920dbbb2010-08-04 15:21:06 -070052 public PackageInfo(String name, SourcePositionInfo position) {
53 super("", position);
Scott Main3c1a6b22010-10-15 17:34:04 -070054
Ben Dodson920dbbb2010-08-04 15:21:06 -070055 if (name.isEmpty()) {
56 mName = "default package";
57 } else {
58 mName = name;
59 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070060 initializeMaps();
61 }
62
63 private void initializeMaps() {
Brett Chabot700b9f22013-10-04 16:57:39 -070064 mAnnotationsMap = new HashMap<String, ClassInfo>();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070065 mInterfacesMap = new HashMap<String, ClassInfo>();
66 mOrdinaryClassesMap = new HashMap<String, ClassInfo>();
67 mEnumsMap = new HashMap<String, ClassInfo>();
68 mExceptionsMap = new HashMap<String, ClassInfo>();
69 mErrorsMap = new HashMap<String, ClassInfo>();
Ben Dodson920dbbb2010-08-04 15:21:06 -070070 }
71
72 public String htmlPage() {
73 String s = mName;
74 s = s.replace('.', '/');
75 s += "/package-summary.html";
76 s = Doclava.javadocDir + s;
77 return s;
78 }
79
Ben Dodson920dbbb2010-08-04 15:21:06 -070080 @Override
81 public ContainerInfo parent() {
82 return null;
83 }
84
85 @Override
86 public boolean isHidden() {
Hui Shu5118ffe2014-02-18 14:06:42 -080087 if (mHidden == null) {
Adam Metcalff2d2e932013-08-19 17:12:24 -070088 if (hasHideComment()) {
89 // We change the hidden value of the package if a class wants to be not hidden.
Hui Shu5118ffe2014-02-18 14:06:42 -080090 ClassInfo[][] types = new ClassInfo[][] { annotations(), interfaces(), ordinaryClasses(),
91 enums(), exceptions() };
Adam Metcalff2d2e932013-08-19 17:12:24 -070092 for (ClassInfo[] type : types) {
93 if (type != null) {
94 for (ClassInfo c : type) {
95 if (c.hasShowAnnotation()) {
Hui Shu5118ffe2014-02-18 14:06:42 -080096 mHidden = false;
Adam Metcalff2d2e932013-08-19 17:12:24 -070097 return false;
98 }
99 }
100 }
101 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800102 mHidden = true;
Adam Metcalff2d2e932013-08-19 17:12:24 -0700103 } else {
Hui Shu5118ffe2014-02-18 14:06:42 -0800104 mHidden = false;
Adam Metcalff2d2e932013-08-19 17:12:24 -0700105 }
106 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800107 return mHidden;
108 }
109
110 @Override
111 public boolean isRemoved() {
112 if (mRemoved == null) {
113 if (hasRemovedComment()) {
114 // We change the removed value of the package if a class wants to be not hidden.
115 ClassInfo[][] types = new ClassInfo[][] { annotations(), interfaces(), ordinaryClasses(),
116 enums(), exceptions() };
117 for (ClassInfo[] type : types) {
118 if (type != null) {
119 for (ClassInfo c : type) {
120 if (c.hasShowAnnotation()) {
121 mRemoved = false;
122 return false;
123 }
124 }
125 }
126 }
127 mRemoved = true;
128 } else {
129 mRemoved = false;
130 }
131 }
132
133 return mRemoved;
134 }
135
136 @Override
137 public boolean isHiddenOrRemoved() {
138 return isHidden() || isRemoved();
Adam Metcalff2d2e932013-08-19 17:12:24 -0700139 }
140
141 /**
142 * Used by ClassInfo to determine packages default visability before annoations.
143 */
144 public boolean hasHideComment() {
Hui Shu5118ffe2014-02-18 14:06:42 -0800145 if (mHiddenByComment == null) {
Alex Klyubincbc60de2014-06-03 16:53:17 -0700146 if (Doclava.hiddenPackages.contains(mName)) {
147 mHiddenByComment = true;
148 } else {
149 mHiddenByComment = comment().isHidden();
150 }
Adam Metcalff2d2e932013-08-19 17:12:24 -0700151 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800152 return mHiddenByComment;
153 }
154
155 public boolean hasRemovedComment() {
156 if (mRemovedByComment == null) {
157 mRemovedByComment = comment().isRemoved();
158 }
159
160 return mRemovedByComment;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700161 }
162
163 public boolean checkLevel() {
164 // TODO should return false if all classes are hidden but the package isn't.
165 // We don't have this so I'm not doing it now.
Hui Shu5118ffe2014-02-18 14:06:42 -0800166 return !isHiddenOrRemoved();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700167 }
168
169 public String name() {
170 return mName;
171 }
172
173 public String qualifiedName() {
174 return mName;
175 }
176
177 public TagInfo[] inlineTags() {
178 return comment().tags();
179 }
180
181 public TagInfo[] firstSentenceTags() {
182 return comment().briefTags();
183 }
184
Hui Shu5118ffe2014-02-18 14:06:42 -0800185 /**
186 * @param classes the Array of ClassInfo to be filtered
187 * @return an Array of ClassInfo without any hidden or removed classes
188 */
189 public static ClassInfo[] filterHiddenAndRemoved(ClassInfo[] classes) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700190 ArrayList<ClassInfo> out = new ArrayList<ClassInfo>();
191
192 for (ClassInfo cl : classes) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800193 if (!cl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700194 out.add(cl);
195 }
196 }
197
198 return out.toArray(new ClassInfo[0]);
199 }
200
201 public void makeLink(Data data, String base) {
202 if (checkLevel()) {
203 data.setValue(base + ".link", htmlPage());
204 }
205 data.setValue(base + ".name", name());
206 data.setValue(base + ".since", getSince());
207 }
208
209 public void makeClassLinkListHDF(Data data, String base) {
210 makeLink(data, base);
Brett Chabot700b9f22013-10-04 16:57:39 -0700211 ClassInfo.makeLinkListHDF(data, base + ".annotations", annotations());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700212 ClassInfo.makeLinkListHDF(data, base + ".interfaces", interfaces());
213 ClassInfo.makeLinkListHDF(data, base + ".classes", ordinaryClasses());
214 ClassInfo.makeLinkListHDF(data, base + ".enums", enums());
215 ClassInfo.makeLinkListHDF(data, base + ".exceptions", exceptions());
216 ClassInfo.makeLinkListHDF(data, base + ".errors", errors());
217 data.setValue(base + ".since", getSince());
218 }
219
Brett Chabot700b9f22013-10-04 16:57:39 -0700220 public ClassInfo[] annotations() {
221 if (mAnnotations == null) {
222 mAnnotations =
Hui Shu5118ffe2014-02-18 14:06:42 -0800223 ClassInfo.sortByName(filterHiddenAndRemoved(
224 Converter.convertClasses(mPackage.annotationTypes())));
Brett Chabot700b9f22013-10-04 16:57:39 -0700225 }
226 return mAnnotations;
227 }
228
Ben Dodson920dbbb2010-08-04 15:21:06 -0700229 public ClassInfo[] interfaces() {
230 if (mInterfaces == null) {
231 mInterfaces =
Hui Shu5118ffe2014-02-18 14:06:42 -0800232 ClassInfo.sortByName(filterHiddenAndRemoved(
233 Converter.convertClasses(mPackage.interfaces())));
Ben Dodson920dbbb2010-08-04 15:21:06 -0700234 }
235 return mInterfaces;
236 }
237
238 public ClassInfo[] ordinaryClasses() {
239 if (mOrdinaryClasses == null) {
240 mOrdinaryClasses =
Hui Shu5118ffe2014-02-18 14:06:42 -0800241 ClassInfo.sortByName(filterHiddenAndRemoved(
242 Converter.convertClasses(mPackage.ordinaryClasses())));
Ben Dodson920dbbb2010-08-04 15:21:06 -0700243 }
244 return mOrdinaryClasses;
245 }
246
247 public ClassInfo[] enums() {
248 if (mEnums == null) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800249 mEnums = ClassInfo.sortByName(filterHiddenAndRemoved(
250 Converter.convertClasses(mPackage.enums())));
Ben Dodson920dbbb2010-08-04 15:21:06 -0700251 }
252 return mEnums;
253 }
254
255 public ClassInfo[] exceptions() {
256 if (mExceptions == null) {
257 mExceptions =
Hui Shu5118ffe2014-02-18 14:06:42 -0800258 ClassInfo.sortByName(filterHiddenAndRemoved(
259 Converter.convertClasses(mPackage.exceptions())));
Ben Dodson920dbbb2010-08-04 15:21:06 -0700260 }
261 return mExceptions;
262 }
263
264 public ClassInfo[] errors() {
265 if (mErrors == null) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800266 mErrors = ClassInfo.sortByName(filterHiddenAndRemoved(
267 Converter.convertClasses(mPackage.errors())));
Ben Dodson920dbbb2010-08-04 15:21:06 -0700268 }
269 return mErrors;
270 }
271
Mathieu11b17962013-06-07 17:24:18 -0500272 public ApiInfo containingApi() {
273 return mContainingApi;
274 }
275
276 public void setContainingApi(ApiInfo api) {
277 mContainingApi = api;
278 }
279
Ben Dodson920dbbb2010-08-04 15:21:06 -0700280 // in hashed containers, treat the name as the key
281 @Override
282 public int hashCode() {
283 return mName.hashCode();
284 }
285
Hui Shu5118ffe2014-02-18 14:06:42 -0800286 private Boolean mHidden = null;
287 private Boolean mHiddenByComment = null;
288 private Boolean mRemoved = null;
289 private Boolean mRemovedByComment = null;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700290 private String mName;
291 private PackageDoc mPackage;
Mathieu11b17962013-06-07 17:24:18 -0500292 private ApiInfo mContainingApi;
Brett Chabot700b9f22013-10-04 16:57:39 -0700293 private ClassInfo[] mAnnotations;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700294 private ClassInfo[] mInterfaces;
295 private ClassInfo[] mOrdinaryClasses;
296 private ClassInfo[] mEnums;
297 private ClassInfo[] mExceptions;
298 private ClassInfo[] mErrors;
Scott Main3c1a6b22010-10-15 17:34:04 -0700299
Brett Chabot700b9f22013-10-04 16:57:39 -0700300 private HashMap<String, ClassInfo> mAnnotationsMap;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700301 private HashMap<String, ClassInfo> mInterfacesMap;
302 private HashMap<String, ClassInfo> mOrdinaryClassesMap;
303 private HashMap<String, ClassInfo> mEnumsMap;
304 private HashMap<String, ClassInfo> mExceptionsMap;
305 private HashMap<String, ClassInfo> mErrorsMap;
306
307
308 public ClassInfo getClass(String className) {
309 ClassInfo cls = mInterfacesMap.get(className);
310
311 if (cls != null) {
312 return cls;
313 }
314
315 cls = mOrdinaryClassesMap.get(className);
316
317 if (cls != null) {
318 return cls;
319 }
320
321 cls = mEnumsMap.get(className);
322
323 if (cls != null) {
324 return cls;
325 }
326
327 cls = mEnumsMap.get(className);
328
329 if (cls != null) {
330 return cls;
331 }
Brett Chabot700b9f22013-10-04 16:57:39 -0700332 cls = mAnnotationsMap.get(className);
333
334 if (cls != null) {
335 return cls;
336 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700337
338 return mErrorsMap.get(className);
339 }
340
Brett Chabot700b9f22013-10-04 16:57:39 -0700341 public void addAnnotation(ClassInfo cls) {
342 cls.setPackage(this);
343 mAnnotationsMap.put(cls.name(), cls);
344 }
345
346 public ClassInfo getAnnotation(String annotationName) {
347 return mAnnotationsMap.get(annotationName);
348 }
349
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700350 public void addInterface(ClassInfo cls) {
Mathieu11b17962013-06-07 17:24:18 -0500351 cls.setPackage(this);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700352 mInterfacesMap.put(cls.name(), cls);
353 }
354
355 public ClassInfo getInterface(String interfaceName) {
356 return mInterfacesMap.get(interfaceName);
357 }
358
359 public ClassInfo getOrdinaryClass(String className) {
360 return mOrdinaryClassesMap.get(className);
361 }
362
363 public void addOrdinaryClass(ClassInfo cls) {
Mathieu11b17962013-06-07 17:24:18 -0500364 cls.setPackage(this);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700365 mOrdinaryClassesMap.put(cls.name(), cls);
366 }
367
368 public ClassInfo getEnum(String enumName) {
369 return mEnumsMap.get(enumName);
370 }
371
372 public void addEnum(ClassInfo cls) {
Mathieu11b17962013-06-07 17:24:18 -0500373 cls.setPackage(this);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700374 this.mEnumsMap.put(cls.name(), cls);
375 }
376
377 public ClassInfo getException(String exceptionName) {
378 return mExceptionsMap.get(exceptionName);
379 }
380
381 public ClassInfo getError(String errorName) {
382 return mErrorsMap.get(errorName);
383 }
384
Ben Dodson920dbbb2010-08-04 15:21:06 -0700385 // TODO: Leftovers from ApiCheck that should be better merged.
386 private HashMap<String, ClassInfo> mClasses = new HashMap<String, ClassInfo>();
Scott Main3c1a6b22010-10-15 17:34:04 -0700387
Mathieu11b17962013-06-07 17:24:18 -0500388 public void addClass(ClassInfo cls) {
389 cls.setPackage(this);
390 mClasses.put(cls.name(), cls);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700391 }
392
393 public HashMap<String, ClassInfo> allClasses() {
394 return mClasses;
395 }
Scott Main3c1a6b22010-10-15 17:34:04 -0700396
Ben Dodson920dbbb2010-08-04 15:21:06 -0700397 public boolean isConsistent(PackageInfo pInfo) {
Guang Zhuf9b7c1b2015-04-24 16:45:42 -0700398 return isConsistent(pInfo, null);
399 }
400
401 /**
402 * Creates the delta class by copying class signatures from original, but use provided list of
403 * constructors and methods.
404 */
405 private ClassInfo createDeltaClass(ClassInfo original,
406 ArrayList<MethodInfo> constructors, ArrayList<MethodInfo> methods) {
407 ArrayList<FieldInfo> emptyFields = new ArrayList<>();
408 ArrayList<ClassInfo> emptyClasses = new ArrayList<>();
409 ArrayList<TypeInfo> emptyTypes = new ArrayList<>();
410 ArrayList<MethodInfo> emptyMethods = new ArrayList<>();
411 ClassInfo ret = new ClassInfo(null, original.getRawCommentText(), original.position(),
412 original.isPublic(), original.isProtected(), original.isPackagePrivate(),
413 original.isPrivate(), original.isStatic(), original.isInterface(),
414 original.isAbstract(), original.isOrdinaryClass(),
415 original.isException(), original.isError(), original.isEnum(), original.isAnnotation(),
416 original.isFinal(), original.isIncluded(), original.name(), original.qualifiedName(),
417 original.qualifiedTypeName(), original.isPrimitive());
418 ArrayList<ClassInfo> interfaces = original.interfaces();
419 // avoid providing null to init method, replace with empty array list when needed
420 if (interfaces == null) {
421 interfaces = emptyClasses;
422 }
423 ArrayList<TypeInfo> interfaceTypes = original.interfaceTypes();
424 if (interfaceTypes == null) {
425 interfaceTypes = emptyTypes;
426 }
427 ArrayList<ClassInfo> innerClasses = original.innerClasses();
428 if (innerClasses == null) {
429 innerClasses = emptyClasses;
430 }
431 ArrayList<MethodInfo> annotationElements = original.annotationElements();
432 if (annotationElements == null) {
433 annotationElements = emptyMethods;
434 }
435 ArrayList<AnnotationInstanceInfo> annotations = original.annotations();
436 if (annotations == null) {
437 annotations = new ArrayList<>();
438 }
439 ret.init(original.type(), interfaces, interfaceTypes, innerClasses,
440 constructors, methods, annotationElements,
441 emptyFields /* fields */, emptyFields /* enum */,
442 original.containingPackage(), original.containingClass(), original.superclass(),
443 original.superclassType(), annotations);
444 return ret;
445 }
446
447 /**
448 * Check if packages are consistent, also record class deltas.
449 * <p>
450 * <ul>class deltas are:
451 * <li>brand new classes that are not present in current package
452 * <li>stripped existing classes stripped where only newly added methods are kept
453 * @param pInfo
454 * @param clsInfoDiff
455 * @return
456 */
457 public boolean isConsistent(PackageInfo pInfo, List<ClassInfo> clsInfoDiff) {
C. Sean Youngc47ef092015-05-20 12:50:00 -0500458 return isConsistent(pInfo, clsInfoDiff, null);
459 }
460
461 /**
462 * Check if packages are consistent, also record class deltas.
463 * <p>
464 * <ul>class deltas are:
465 * <li>brand new classes that are not present in current package
466 * <li>stripped existing classes stripped where only newly added methods are kept
467 * @param pInfo
468 * @param clsInfoDiff
469 * @param ignoredClasses
470 * @return
471 */
472 public boolean isConsistent(PackageInfo pInfo, List<ClassInfo> clsInfoDiff,
473 Collection<String> ignoredClasses) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700474 boolean consistent = true;
Guang Zhuf9b7c1b2015-04-24 16:45:42 -0700475 boolean diffMode = clsInfoDiff != null;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700476 for (ClassInfo cInfo : mClasses.values()) {
Guang Zhuf9b7c1b2015-04-24 16:45:42 -0700477 ArrayList<MethodInfo> newClsApis = null;
478 ArrayList<MethodInfo> newClsCtors = null;
C. Sean Youngc47ef092015-05-20 12:50:00 -0500479
C. Sean Young91c627c2015-05-11 15:36:03 -0500480 // TODO: Add support for matching inner classes (e.g, something like
481 // example.Type.* should match example.Type.InnerType)
482 if (ignoredClasses != null && ignoredClasses.contains(cInfo.qualifiedName())) {
483 // TODO: Log skipping this?
484 continue;
485 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700486 if (pInfo.mClasses.containsKey(cInfo.name())) {
Guang Zhuf9b7c1b2015-04-24 16:45:42 -0700487 if (diffMode) {
488 newClsApis = new ArrayList<>();
489 newClsCtors = new ArrayList<>();
490 }
491 if (!cInfo.isConsistent(pInfo.mClasses.get(cInfo.name()), newClsCtors, newClsApis)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700492 consistent = false;
493 }
Guang Zhuf9b7c1b2015-04-24 16:45:42 -0700494 // if we are in diff mode, add class to list if there's new ctor or new apis
495 if (diffMode && !(newClsCtors.isEmpty() && newClsApis.isEmpty())) {
496 // generate a "delta" class with only added methods and constructors, but no fields etc
497 ClassInfo deltaClsInfo = createDeltaClass(cInfo, newClsCtors, newClsApis);
498 clsInfoDiff.add(deltaClsInfo);
499 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700500 } else {
501 Errors.error(Errors.REMOVED_CLASS, cInfo.position(), "Removed public class "
502 + cInfo.qualifiedName());
503 consistent = false;
504 }
505 }
506 for (ClassInfo cInfo : pInfo.mClasses.values()) {
C. Sean Young91c627c2015-05-11 15:36:03 -0500507 if (ignoredClasses != null && ignoredClasses.contains(cInfo.qualifiedName())) {
508 // TODO: Log skipping this?
509 continue;
510 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700511 if (!mClasses.containsKey(cInfo.name())) {
512 Errors.error(Errors.ADDED_CLASS, cInfo.position(), "Added class " + cInfo.name()
513 + " to package " + pInfo.name());
514 consistent = false;
Guang Zhuf9b7c1b2015-04-24 16:45:42 -0700515 // brand new class, add everything as is
516 if (diffMode) {
517 clsInfoDiff.add(cInfo);
518 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700519 }
520 }
Guang Zhuf9b7c1b2015-04-24 16:45:42 -0700521 if (diffMode) {
522 Collections.sort(clsInfoDiff, ClassInfo.comparator);
523 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700524 return consistent;
525 }
526}