• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2009 Google Inc. All Rights Reserved.
2 
3 import com.android.apicheck.*;
4 
5 import java.util.*;
6 
7 import org.clearsilver.HDF;
8 
9 /**
10  * Applies version information to the DroidDoc class model from apicheck XML
11  * files. Sample usage:
12  * <pre>
13  *   ClassInfo[] classInfos = ...
14  *
15  *   SinceTagger sinceTagger = new SinceTagger()
16  *   sinceTagger.addVersion("frameworks/base/api/1.xml", "Android 1.0")
17  *   sinceTagger.addVersion("frameworks/base/api/2.xml", "Android 1.5")
18  *   sinceTagger.tagAll(...);
19  * </pre>
20  */
21 public class SinceTagger {
22 
23     private final Map<String, String> xmlToName
24             = new LinkedHashMap<String, String>();
25 
26     /**
27      * Specifies the apicheck XML file and the API version it holds. Calls to
28      * this method should be called in order from oldest version to newest.
29      */
addVersion(String file, String name)30     public void addVersion(String file, String name) {
31         xmlToName.put(file, name);
32     }
33 
tagAll(ClassInfo[] classDocs)34     public void tagAll(ClassInfo[] classDocs) {
35         // read through the XML files in order, applying their since information
36         // to the Javadoc models
37         for (Map.Entry<String, String> versionSpec : xmlToName.entrySet()) {
38             String xmlFile = versionSpec.getKey();
39             String versionName = versionSpec.getValue();
40             ApiInfo specApi = new ApiCheck().parseApi(xmlFile);
41 
42             applyVersionsFromSpec(versionName, specApi, classDocs);
43         }
44 
45         if (!xmlToName.isEmpty()) {
46             warnForMissingVersions(classDocs);
47         }
48     }
49 
50     /**
51      * Writes an index of the version names to {@code data}.
52      */
writeVersionNames(HDF data)53     public void writeVersionNames(HDF data) {
54         int index = 1;
55         for (String version : xmlToName.values()) {
56             data.setValue("since." + index + ".name", version);
57             index++;
58         }
59     }
60 
61     /**
62      * Applies the version information to {@code classDocs} where not already
63      * present.
64      *
65      * @param versionName the version name
66      * @param specApi the spec for this version. If a symbol is in this spec, it
67      *      was present in the named version
68      * @param classDocs the doc model to update
69      */
applyVersionsFromSpec(String versionName, ApiInfo specApi, ClassInfo[] classDocs)70     private void applyVersionsFromSpec(String versionName,
71             ApiInfo specApi, ClassInfo[] classDocs) {
72         for (ClassInfo classDoc : classDocs) {
73             com.android.apicheck.PackageInfo packageSpec
74                     = specApi.getPackages().get(classDoc.containingPackage().name());
75 
76             if (packageSpec == null) {
77                 continue;
78             }
79 
80             com.android.apicheck.ClassInfo classSpec
81                     = packageSpec.allClasses().get(classDoc.name());
82 
83             if (classSpec == null) {
84                 continue;
85             }
86 
87             versionPackage(versionName, classDoc.containingPackage());
88             versionClass(versionName, classDoc);
89             versionConstructors(versionName, classSpec, classDoc);
90             versionFields(versionName, classSpec, classDoc);
91             versionMethods(versionName, classSpec, classDoc);
92         }
93     }
94 
95     /**
96      * Applies version information to {@code doc} where not already present.
97      */
versionPackage(String versionName, PackageInfo doc)98     private void versionPackage(String versionName, PackageInfo doc) {
99         if (doc.getSince() == null) {
100             doc.setSince(versionName);
101         }
102     }
103 
104     /**
105      * Applies version information to {@code doc} where not already present.
106      */
versionClass(String versionName, ClassInfo doc)107     private void versionClass(String versionName, ClassInfo doc) {
108         if (doc.getSince() == null) {
109             doc.setSince(versionName);
110         }
111     }
112 
113     /**
114      * Applies version information from {@code spec} to {@code doc} where not
115      * already present.
116      */
versionConstructors(String versionName, com.android.apicheck.ClassInfo spec, ClassInfo doc)117     private void versionConstructors(String versionName,
118             com.android.apicheck.ClassInfo spec, ClassInfo doc) {
119         for (MethodInfo constructor : doc.constructors()) {
120             if (constructor.getSince() == null
121                     && spec.allConstructors().containsKey(constructor.getHashableName())) {
122                 constructor.setSince(versionName);
123             }
124         }
125     }
126 
127     /**
128      * Applies version information from {@code spec} to {@code doc} where not
129      * already present.
130      */
versionFields(String versionName, com.android.apicheck.ClassInfo spec, ClassInfo doc)131     private void versionFields(String versionName,
132             com.android.apicheck.ClassInfo spec, ClassInfo doc) {
133         for (FieldInfo field : doc.fields()) {
134             if (field.getSince() == null
135                     && spec.allFields().containsKey(field.name())) {
136                 field.setSince(versionName);
137             }
138         }
139     }
140 
141     /**
142      * Applies version information from {@code spec} to {@code doc} where not
143      * already present.
144      */
versionMethods(String versionName, com.android.apicheck.ClassInfo spec, ClassInfo doc)145     private void versionMethods(String versionName,
146             com.android.apicheck.ClassInfo spec, ClassInfo doc) {
147         for (MethodInfo method : doc.methods()) {
148             if (method.getSince() != null) {
149                 continue;
150             }
151 
152             for (com.android.apicheck.ClassInfo superclass : spec.hierarchy()) {
153                 if (superclass.allMethods().containsKey(method.getHashableName())) {
154                     method.setSince(versionName);
155                     break;
156                 }
157             }
158         }
159     }
160 
161     /**
162      * Warns if any symbols are missing version information. When configured
163      * properly, this will yield zero warnings because {@code apicheck}
164      * guarantees that all symbols are present in the most recent API.
165      */
warnForMissingVersions(ClassInfo[] classDocs)166     private void warnForMissingVersions(ClassInfo[] classDocs) {
167         for (ClassInfo claz : classDocs) {
168             if (claz.getSince() == null) {
169                 Errors.error(Errors.NO_SINCE_DATA, claz.position(),
170                         "XML missing class " + claz.qualifiedName());
171             }
172             for (FieldInfo field : claz.fields()) {
173                 if (field.getSince() == null) {
174                     Errors.error(Errors.NO_SINCE_DATA, field.position(),
175                             "XML missing field "
176                                     + claz.qualifiedName() + "#" + field .name());
177                 }
178             }
179             for (MethodInfo constructor : claz.constructors()) {
180                 if (constructor.getSince() == null) {
181                     Errors.error(Errors.NO_SINCE_DATA, constructor.position(),
182                             "XML missing constructor "
183                                     + claz.qualifiedName() + "#" + constructor.getHashableName());
184                 }
185             }
186             for (MethodInfo method : claz.methods()) {
187                 if (method.getSince() == null) {
188                     Errors.error(Errors.NO_SINCE_DATA, method.position(),
189                             "XML missing method "
190                                     + claz.qualifiedName() + "#" + method .getHashableName());
191                 }
192             }
193         }
194     }
195 }
196