1 /* 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 17 package com.google.doclava; 18 19 import com.google.doclava.apicheck.ApiInfo; 20 import com.google.clearsilver.jsilver.data.Data; 21 22 import com.sun.javadoc.*; 23 import java.util.*; 24 25 public class PackageInfo extends DocInfo implements ContainerInfo { 26 public static final String DEFAULT_PACKAGE = "default package"; 27 28 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; 43 initializeMaps(); 44 } 45 46 public PackageInfo(String name) { 47 super("", null); 48 mName = name; 49 initializeMaps(); 50 } 51 52 public PackageInfo(String name, SourcePositionInfo position) { 53 super("", position); 54 55 if (name.isEmpty()) { 56 mName = "default package"; 57 } else { 58 mName = name; 59 } 60 initializeMaps(); 61 } 62 63 private void initializeMaps() { 64 mAnnotationsMap = new HashMap<String, ClassInfo>(); 65 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>(); 70 } 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 80 @Override 81 public ContainerInfo parent() { 82 return null; 83 } 84 85 @Override 86 public boolean isHidden() { 87 if (mHidden == null) { 88 if (hasHideComment()) { 89 // We change the hidden value of the package if a class wants to be not hidden. 90 ClassInfo[][] types = new ClassInfo[][] { annotations(), interfaces(), ordinaryClasses(), 91 enums(), exceptions() }; 92 for (ClassInfo[] type : types) { 93 if (type != null) { 94 for (ClassInfo c : type) { 95 if (c.hasShowAnnotation()) { 96 mHidden = false; 97 return false; 98 } 99 } 100 } 101 } 102 mHidden = true; 103 } else { 104 mHidden = false; 105 } 106 } 107 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(); 139 } 140 141 /** 142 * Used by ClassInfo to determine packages default visability before annoations. 143 */ 144 public boolean hasHideComment() { 145 if (mHiddenByComment == null) { 146 if (Doclava.hiddenPackages.contains(mName)) { 147 mHiddenByComment = true; 148 } else { 149 mHiddenByComment = comment().isHidden(); 150 } 151 } 152 return mHiddenByComment; 153 } 154 155 public boolean hasRemovedComment() { 156 if (mRemovedByComment == null) { 157 mRemovedByComment = comment().isRemoved(); 158 } 159 160 return mRemovedByComment; 161 } 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. 166 return !isHiddenOrRemoved(); 167 } 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 185 /** 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) { 190 ArrayList<ClassInfo> out = new ArrayList<ClassInfo>(); 191 192 for (ClassInfo cl : classes) { 193 if (!cl.isHiddenOrRemoved()) { 194 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); 211 ClassInfo.makeLinkListHDF(data, base + ".annotations", annotations()); 212 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 220 public ClassInfo[] annotations() { 221 if (mAnnotations == null) { 222 mAnnotations = 223 ClassInfo.sortByName(filterHiddenAndRemoved( 224 Converter.convertClasses(mPackage.annotationTypes()))); 225 } 226 return mAnnotations; 227 } 228 229 public ClassInfo[] interfaces() { 230 if (mInterfaces == null) { 231 mInterfaces = 232 ClassInfo.sortByName(filterHiddenAndRemoved( 233 Converter.convertClasses(mPackage.interfaces()))); 234 } 235 return mInterfaces; 236 } 237 238 public ClassInfo[] ordinaryClasses() { 239 if (mOrdinaryClasses == null) { 240 mOrdinaryClasses = 241 ClassInfo.sortByName(filterHiddenAndRemoved( 242 Converter.convertClasses(mPackage.ordinaryClasses()))); 243 } 244 return mOrdinaryClasses; 245 } 246 247 public ClassInfo[] enums() { 248 if (mEnums == null) { 249 mEnums = ClassInfo.sortByName(filterHiddenAndRemoved( 250 Converter.convertClasses(mPackage.enums()))); 251 } 252 return mEnums; 253 } 254 255 public ClassInfo[] exceptions() { 256 if (mExceptions == null) { 257 mExceptions = 258 ClassInfo.sortByName(filterHiddenAndRemoved( 259 Converter.convertClasses(mPackage.exceptions()))); 260 } 261 return mExceptions; 262 } 263 264 public ClassInfo[] errors() { 265 if (mErrors == null) { 266 mErrors = ClassInfo.sortByName(filterHiddenAndRemoved( 267 Converter.convertClasses(mPackage.errors()))); 268 } 269 return mErrors; 270 } 271 272 public ApiInfo containingApi() { 273 return mContainingApi; 274 } 275 276 public void setContainingApi(ApiInfo api) { 277 mContainingApi = api; 278 } 279 280 // in hashed containers, treat the name as the key 281 @Override 282 public int hashCode() { 283 return mName.hashCode(); 284 } 285 286 private Boolean mHidden = null; 287 private Boolean mHiddenByComment = null; 288 private Boolean mRemoved = null; 289 private Boolean mRemovedByComment = null; 290 private String mName; 291 private PackageDoc mPackage; 292 private ApiInfo mContainingApi; 293 private ClassInfo[] mAnnotations; 294 private ClassInfo[] mInterfaces; 295 private ClassInfo[] mOrdinaryClasses; 296 private ClassInfo[] mEnums; 297 private ClassInfo[] mExceptions; 298 private ClassInfo[] mErrors; 299 300 private HashMap<String, ClassInfo> mAnnotationsMap; 301 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 } 332 cls = mAnnotationsMap.get(className); 333 334 if (cls != null) { 335 return cls; 336 } 337 338 return mErrorsMap.get(className); 339 } 340 341 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 350 public void addInterface(ClassInfo cls) { 351 cls.setPackage(this); 352 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) { 364 cls.setPackage(this); 365 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) { 373 cls.setPackage(this); 374 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 385 // TODO: Leftovers from ApiCheck that should be better merged. 386 private HashMap<String, ClassInfo> mClasses = new HashMap<String, ClassInfo>(); 387 388 public void addClass(ClassInfo cls) { 389 cls.setPackage(this); 390 mClasses.put(cls.name(), cls); 391 } 392 393 public HashMap<String, ClassInfo> allClasses() { 394 return mClasses; 395 } 396 397 public boolean isConsistent(PackageInfo pInfo) { 398 boolean consistent = true; 399 for (ClassInfo cInfo : mClasses.values()) { 400 if (pInfo.mClasses.containsKey(cInfo.name())) { 401 if (!cInfo.isConsistent(pInfo.mClasses.get(cInfo.name()))) { 402 consistent = false; 403 } 404 } else { 405 Errors.error(Errors.REMOVED_CLASS, cInfo.position(), "Removed public class " 406 + cInfo.qualifiedName()); 407 consistent = false; 408 } 409 } 410 for (ClassInfo cInfo : pInfo.mClasses.values()) { 411 if (!mClasses.containsKey(cInfo.name())) { 412 Errors.error(Errors.ADDED_CLASS, cInfo.position(), "Added class " + cInfo.name() 413 + " to package " + pInfo.name()); 414 consistent = false; 415 } 416 } 417 return consistent; 418 } 419 } 420