• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.apicheck;
18 
19 import com.google.doclava.ClassInfo;
20 import com.google.doclava.Errors;
21 import com.google.doclava.PackageInfo;
22 
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 
30 public class ApiInfo {
31 
32   private HashMap<String, PackageInfo> mPackages
33       = new HashMap<String, PackageInfo>();
34   private HashMap<String, ClassInfo> mAllClasses
35       = new HashMap<String, ClassInfo>();
36   private Map<ClassInfo,String> mClassToSuper
37       = new HashMap<ClassInfo, String>();
38   private Map<ClassInfo, ArrayList<String>> mClassToInterface
39       = new HashMap<ClassInfo, ArrayList<String>>();
40 
41 
findClass(String name)42   public ClassInfo findClass(String name) {
43     return mAllClasses.get(name);
44   }
45 
resolveInterfaces()46   protected void resolveInterfaces() {
47     for (ClassInfo cl : mAllClasses.values()) {
48       ArrayList<String> ifaces = mClassToInterface.get(cl);
49       if (ifaces == null) {
50         continue;
51       }
52       for (String iface : ifaces) {
53         ClassInfo ci = mAllClasses.get(iface);
54         if (ci == null) {
55           // Interface not provided by this codebase. Inject a stub.
56           ci = new ClassInfo(iface);
57         }
58         cl.addInterface(ci);
59       }
60     }
61   }
62 
63   /**
64    * Checks to see if this api is consistent with a newer version.
65    */
isConsistent(ApiInfo otherApi)66   public boolean isConsistent(ApiInfo otherApi) {
67     return isConsistent(otherApi, null);
68   }
69 
isConsistent(ApiInfo otherApi, List<PackageInfo> pkgInfoDiff)70   public boolean isConsistent(ApiInfo otherApi, List<PackageInfo> pkgInfoDiff) {
71       return isConsistent(otherApi, pkgInfoDiff, null, null);
72   }
73 
74   /**
75    * Checks to see if this api is consistent with a newer version.
76    *
77    * @param otherApi the other api to test consistency against
78    * @param pkgInfoDiff
79    * @param ignoredPackages packages to skip consistency checks (will match by exact name)
80    * @param ignoredClasses classes to skip consistency checks (will match by exact fully qualified
81    * name)
82    */
isConsistent(ApiInfo otherApi, List<PackageInfo> pkgInfoDiff, Collection<String> ignoredPackages, Collection<String> ignoredClasses)83   public boolean isConsistent(ApiInfo otherApi, List<PackageInfo> pkgInfoDiff,
84       Collection<String> ignoredPackages, Collection<String> ignoredClasses) {
85     boolean consistent = true;
86     boolean diffMode = pkgInfoDiff != null;
87     for (PackageInfo pInfo : mPackages.values()) {
88       List<ClassInfo> newClsApis = null;
89 
90       // TODO: Add support for matching subpackages (e.g, something like
91       // test.example.* should match test.example.subpackage, and
92       // test.example.** should match the above AND test.example.subpackage.more)
93       if (ignoredPackages != null && ignoredPackages.contains(pInfo.name())) {
94           // TODO: Log skipping this?
95           continue;
96       }
97       if (otherApi.getPackages().containsKey(pInfo.name())) {
98         if (diffMode) {
99           newClsApis = new ArrayList<>();
100         }
101         if (!pInfo.isConsistent(otherApi.getPackages().get(pInfo.name()), newClsApis, ignoredClasses)) {
102           consistent = false;
103         }
104         if (diffMode && !newClsApis.isEmpty()) {
105           PackageInfo info = new PackageInfo(pInfo.name(), pInfo.position());
106           for (ClassInfo cInfo : newClsApis) {
107             if (ignoredClasses == null || !ignoredClasses.contains(cInfo.qualifiedName())) {
108               info.addClass(cInfo);
109             }
110           }
111           pkgInfoDiff.add(info);
112         }
113       } else {
114         Errors.error(Errors.REMOVED_PACKAGE, pInfo.position(), "Removed package " + pInfo.name());
115         consistent = false;
116       }
117     }
118     for (PackageInfo pInfo : otherApi.mPackages.values()) {
119       if (ignoredPackages != null && ignoredPackages.contains(pInfo.name())) {
120           // TODO: Log skipping this?
121           continue;
122       }
123       if (!mPackages.containsKey(pInfo.name())) {
124         Errors.error(Errors.ADDED_PACKAGE, pInfo.position(), "Added package " + pInfo.name());
125         consistent = false;
126         if (diffMode) {
127           pkgInfoDiff.add(pInfo);
128         }
129       }
130     }
131     if (diffMode) {
132       Collections.sort(pkgInfoDiff, PackageInfo.comparator);
133     }
134     return consistent;
135   }
136 
getPackages()137   public HashMap<String, PackageInfo> getPackages() {
138     return mPackages;
139   }
140 
mapClassToSuper(ClassInfo classInfo, String superclass)141   protected void mapClassToSuper(ClassInfo classInfo, String superclass) {
142     mClassToSuper.put(classInfo, superclass);
143   }
144 
mapClassToInterface(ClassInfo classInfo, String iface)145   protected void mapClassToInterface(ClassInfo classInfo, String iface) {
146     if (!mClassToInterface.containsKey(classInfo)) {
147       mClassToInterface.put(classInfo, new ArrayList<String>());
148     }
149     mClassToInterface.get(classInfo).add(iface);
150   }
151 
addPackage(PackageInfo pInfo)152   protected void addPackage(PackageInfo pInfo) {
153     // track the set of organized packages in the API
154     pInfo.setContainingApi(this);
155     mPackages.put(pInfo.name(), pInfo);
156 
157     // accumulate a direct map of all the classes in the API
158     for (ClassInfo cl : pInfo.allClasses().values()) {
159       mAllClasses.put(cl.qualifiedName(), cl);
160     }
161   }
162 
resolveSuperclasses()163   protected void resolveSuperclasses() {
164     for (ClassInfo cl : mAllClasses.values()) {
165       // java.lang.Object has no superclass
166       if (!cl.qualifiedName().equals("java.lang.Object")) {
167         String scName = mClassToSuper.get(cl);
168         if (scName == null) {
169           scName = "java.lang.Object";
170         }
171         ClassInfo superclass = mAllClasses.get(scName);
172         if (superclass == null) {
173           // Superclass not provided by this codebase. Inject a stub.
174           superclass = new ClassInfo(scName);
175         }
176         cl.setSuperClass(superclass);
177       }
178     }
179   }
180 }
181