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
17package com.google.doclava;
18
19import com.google.doclava.apicheck.ApiInfo;
20import com.google.clearsilver.jsilver.data.Data;
21
22import com.sun.javadoc.*;
23import java.util.*;
24
25public 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      mInterfacesMap = new HashMap<String, ClassInfo>();
65      mOrdinaryClassesMap = new HashMap<String, ClassInfo>();
66      mEnumsMap = new HashMap<String, ClassInfo>();
67      mExceptionsMap = new HashMap<String, ClassInfo>();
68      mErrorsMap = new HashMap<String, ClassInfo>();
69  }
70
71  public String htmlPage() {
72    String s = mName;
73    s = s.replace('.', '/');
74    s += "/package-summary.html";
75    s = Doclava.javadocDir + s;
76    return s;
77  }
78
79  @Override
80  public ContainerInfo parent() {
81    return null;
82  }
83
84  @Override
85  public boolean isHidden() {
86    return comment().isHidden();
87  }
88
89  public boolean checkLevel() {
90    // TODO should return false if all classes are hidden but the package isn't.
91    // We don't have this so I'm not doing it now.
92    return !isHidden();
93  }
94
95  public String name() {
96    return mName;
97  }
98
99  public String qualifiedName() {
100    return mName;
101  }
102
103  public TagInfo[] inlineTags() {
104    return comment().tags();
105  }
106
107  public TagInfo[] firstSentenceTags() {
108    return comment().briefTags();
109  }
110
111  public static ClassInfo[] filterHidden(ClassInfo[] classes) {
112    ArrayList<ClassInfo> out = new ArrayList<ClassInfo>();
113
114    for (ClassInfo cl : classes) {
115      if (!cl.isHidden()) {
116        out.add(cl);
117      }
118    }
119
120    return out.toArray(new ClassInfo[0]);
121  }
122
123  public void makeLink(Data data, String base) {
124    if (checkLevel()) {
125      data.setValue(base + ".link", htmlPage());
126    }
127    data.setValue(base + ".name", name());
128    data.setValue(base + ".since", getSince());
129  }
130
131  public void makeClassLinkListHDF(Data data, String base) {
132    makeLink(data, base);
133    ClassInfo.makeLinkListHDF(data, base + ".interfaces", interfaces());
134    ClassInfo.makeLinkListHDF(data, base + ".classes", ordinaryClasses());
135    ClassInfo.makeLinkListHDF(data, base + ".enums", enums());
136    ClassInfo.makeLinkListHDF(data, base + ".exceptions", exceptions());
137    ClassInfo.makeLinkListHDF(data, base + ".errors", errors());
138    data.setValue(base + ".since", getSince());
139  }
140
141  public ClassInfo[] interfaces() {
142    if (mInterfaces == null) {
143      mInterfaces =
144          ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.interfaces())));
145    }
146    return mInterfaces;
147  }
148
149  public ClassInfo[] ordinaryClasses() {
150    if (mOrdinaryClasses == null) {
151      mOrdinaryClasses =
152          ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.ordinaryClasses())));
153    }
154    return mOrdinaryClasses;
155  }
156
157  public ClassInfo[] enums() {
158    if (mEnums == null) {
159      mEnums = ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.enums())));
160    }
161    return mEnums;
162  }
163
164  public ClassInfo[] exceptions() {
165    if (mExceptions == null) {
166      mExceptions =
167          ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.exceptions())));
168    }
169    return mExceptions;
170  }
171
172  public ClassInfo[] errors() {
173    if (mErrors == null) {
174      mErrors = ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.errors())));
175    }
176    return mErrors;
177  }
178
179  public ApiInfo containingApi() {
180    return mContainingApi;
181  }
182
183  public void setContainingApi(ApiInfo api) {
184    mContainingApi = api;
185  }
186
187  // in hashed containers, treat the name as the key
188  @Override
189  public int hashCode() {
190    return mName.hashCode();
191  }
192
193  private String mName;
194  private PackageDoc mPackage;
195  private ApiInfo mContainingApi;
196  private ClassInfo[] mInterfaces;
197  private ClassInfo[] mOrdinaryClasses;
198  private ClassInfo[] mEnums;
199  private ClassInfo[] mExceptions;
200  private ClassInfo[] mErrors;
201
202  private HashMap<String, ClassInfo> mInterfacesMap;
203  private HashMap<String, ClassInfo> mOrdinaryClassesMap;
204  private HashMap<String, ClassInfo> mEnumsMap;
205  private HashMap<String, ClassInfo> mExceptionsMap;
206  private HashMap<String, ClassInfo> mErrorsMap;
207
208
209  public ClassInfo getClass(String className) {
210      ClassInfo cls = mInterfacesMap.get(className);
211
212      if (cls != null) {
213          return cls;
214      }
215
216      cls = mOrdinaryClassesMap.get(className);
217
218      if (cls != null) {
219          return cls;
220      }
221
222      cls = mEnumsMap.get(className);
223
224      if (cls != null) {
225          return cls;
226      }
227
228      cls = mEnumsMap.get(className);
229
230      if (cls != null) {
231          return cls;
232      }
233
234      return mErrorsMap.get(className);
235  }
236
237  public void addInterface(ClassInfo cls) {
238      cls.setPackage(this);
239      mInterfacesMap.put(cls.name(), cls);
240  }
241
242  public ClassInfo getInterface(String interfaceName) {
243      return mInterfacesMap.get(interfaceName);
244  }
245
246  public ClassInfo getOrdinaryClass(String className) {
247      return mOrdinaryClassesMap.get(className);
248  }
249
250  public void addOrdinaryClass(ClassInfo cls) {
251      cls.setPackage(this);
252      mOrdinaryClassesMap.put(cls.name(), cls);
253  }
254
255  public ClassInfo getEnum(String enumName) {
256      return mEnumsMap.get(enumName);
257  }
258
259  public void addEnum(ClassInfo cls) {
260      cls.setPackage(this);
261      this.mEnumsMap.put(cls.name(), cls);
262  }
263
264  public ClassInfo getException(String exceptionName) {
265      return mExceptionsMap.get(exceptionName);
266  }
267
268  public ClassInfo getError(String errorName) {
269      return mErrorsMap.get(errorName);
270  }
271
272  // TODO: Leftovers from ApiCheck that should be better merged.
273  private HashMap<String, ClassInfo> mClasses = new HashMap<String, ClassInfo>();
274
275  public void addClass(ClassInfo cls) {
276    cls.setPackage(this);
277    mClasses.put(cls.name(), cls);
278  }
279
280  public HashMap<String, ClassInfo> allClasses() {
281    return mClasses;
282  }
283
284  public boolean isConsistent(PackageInfo pInfo) {
285    boolean consistent = true;
286    for (ClassInfo cInfo : mClasses.values()) {
287      if (pInfo.mClasses.containsKey(cInfo.name())) {
288        if (!cInfo.isConsistent(pInfo.mClasses.get(cInfo.name()))) {
289          consistent = false;
290        }
291      } else {
292        Errors.error(Errors.REMOVED_CLASS, cInfo.position(), "Removed public class "
293            + cInfo.qualifiedName());
294        consistent = false;
295      }
296    }
297    for (ClassInfo cInfo : pInfo.mClasses.values()) {
298      if (!mClasses.containsKey(cInfo.name())) {
299        Errors.error(Errors.ADDED_CLASS, cInfo.position(), "Added class " + cInfo.name()
300            + " to package " + pInfo.name());
301        consistent = false;
302      }
303    }
304    return consistent;
305  }
306}
307