blob: fa82802a063819de08fecf82fa87048e56fc05dc [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
Brian Carlstromed8f7972011-06-10 11:10:48 -070019import java.io.BufferedOutputStream;
Ben Dodson920dbbb2010-08-04 15:21:06 -070020import java.io.File;
21import java.io.FileNotFoundException;
Brian Carlstromed8f7972011-06-10 11:10:48 -070022import java.io.FileOutputStream;
Ben Dodson920dbbb2010-08-04 15:21:06 -070023import java.io.PrintStream;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070024import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.Collection;
27import java.util.Collections;
28import java.util.HashMap;
29import java.util.HashSet;
Chris Banes372e36e2015-04-30 10:31:25 +010030import java.util.Iterator;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070031import java.util.List;
32import java.util.Set;
Chris Banes372e36e2015-04-30 10:31:25 +010033import java.util.regex.Pattern;
Ben Dodson920dbbb2010-08-04 15:21:06 -070034
35public class Stubs {
Jeff Hamilton970f13f2012-06-22 00:21:56 -050036 public static void writeStubsAndApi(String stubsDir, String apiFile, String keepListFile,
Hui Shu5118ffe2014-02-18 14:06:42 -080037 String removedApiFile, HashSet<String> stubPackages) {
Ben Dodson920dbbb2010-08-04 15:21:06 -070038 // figure out which classes we need
Joe Onorato04099252011-03-09 13:34:18 -080039 final HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
Ben Dodson920dbbb2010-08-04 15:21:06 -070040 ClassInfo[] all = Converter.allClasses();
Joe Onorato04099252011-03-09 13:34:18 -080041 PrintStream apiWriter = null;
Jeff Hamilton970f13f2012-06-22 00:21:56 -050042 PrintStream keepListWriter = null;
Hui Shu5118ffe2014-02-18 14:06:42 -080043 PrintStream removedApiWriter = null;
44
Joe Onorato04099252011-03-09 13:34:18 -080045 if (apiFile != null) {
Ben Dodson920dbbb2010-08-04 15:21:06 -070046 try {
Joe Onorato04099252011-03-09 13:34:18 -080047 File xml = new File(apiFile);
Ben Dodson920dbbb2010-08-04 15:21:06 -070048 xml.getParentFile().mkdirs();
Brian Carlstromed8f7972011-06-10 11:10:48 -070049 apiWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(xml)));
Ben Dodson920dbbb2010-08-04 15:21:06 -070050 } catch (FileNotFoundException e) {
Joe Onorato04099252011-03-09 13:34:18 -080051 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(apiFile, 0, 0),
Ben Dodson920dbbb2010-08-04 15:21:06 -070052 "Cannot open file for write.");
53 }
54 }
Jeff Hamilton970f13f2012-06-22 00:21:56 -050055 if (keepListFile != null) {
56 try {
57 File keepList = new File(keepListFile);
58 keepList.getParentFile().mkdirs();
59 keepListWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(keepList)));
60 } catch (FileNotFoundException e) {
61 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(keepListFile, 0, 0),
62 "Cannot open file for write.");
63 }
64 }
Hui Shu5118ffe2014-02-18 14:06:42 -080065 if (removedApiFile != null) {
66 try {
67 File removedApi = new File(removedApiFile);
68 removedApi.getParentFile().mkdirs();
69 removedApiWriter = new PrintStream(
70 new BufferedOutputStream(new FileOutputStream(removedApi)));
71 } catch (FileNotFoundException e) {
72 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(removedApiFile, 0, 0),
73 "Cannot open file for write");
74 }
75 }
Ben Dodson920dbbb2010-08-04 15:21:06 -070076 // If a class is public or protected, not hidden, and marked as included,
77 // then we can't strip it
78 for (ClassInfo cl : all) {
79 if (cl.checkLevel() && cl.isIncluded()) {
80 cantStripThis(cl, notStrippable, "0:0");
81 }
82 }
83
84 // complain about anything that looks includeable but is not supposed to
85 // be written, e.g. hidden things
86 for (ClassInfo cl : notStrippable) {
Hui Shu5118ffe2014-02-18 14:06:42 -080087 if (!cl.isHiddenOrRemoved()) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070088 for (MethodInfo m : cl.selfMethods()) {
Hui Shu5118ffe2014-02-18 14:06:42 -080089 if (m.isHiddenOrRemoved()) {
90 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to unavailable method "
Ben Dodson920dbbb2010-08-04 15:21:06 -070091 + m.name());
92 } else if (m.isDeprecated()) {
93 // don't bother reporting deprecated methods
94 // unless they are public
95 Errors.error(Errors.DEPRECATED, m.position(), "Method " + cl.qualifiedName() + "."
96 + m.name() + " is deprecated");
97 }
98
Jeff Arneson74512be2015-03-10 14:09:08 -070099 ClassInfo hiddenClass = findHiddenClasses(m.returnType());
100 if (null != hiddenClass) {
101 if (hiddenClass.qualifiedName() == m.returnType().asClassInfo().qualifiedName()) {
Jeff Arneson22ddcbd2015-01-27 15:22:21 -0800102 // Return type is hidden
103 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Method " + cl.qualifiedName()
Jeff Arneson74512be2015-03-10 14:09:08 -0700104 + "." + m.name() + " returns unavailable type " + hiddenClass.name());
Jeff Arneson22ddcbd2015-01-27 15:22:21 -0800105 } else {
106 // Return type contains a generic parameter
107 Errors.error(Errors.HIDDEN_TYPE_PARAMETER, m.position(), "Method " + cl.qualifiedName()
Jeff Arneson74512be2015-03-10 14:09:08 -0700108 + "." + m.name() + " returns unavailable type " + hiddenClass.name()
Jeff Arneson22ddcbd2015-01-27 15:22:21 -0800109 + " as a type parameter");
110 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700111 }
112
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700113 for (ParameterInfo p : m.parameters()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700114 TypeInfo t = p.type();
115 if (!t.isPrimitive()) {
Jeff Arneson74512be2015-03-10 14:09:08 -0700116 hiddenClass = findHiddenClasses(t);
117 if (null != hiddenClass) {
118 if (hiddenClass.qualifiedName() == t.asClassInfo().qualifiedName()) {
Jeff Arneson22ddcbd2015-01-27 15:22:21 -0800119 // Parameter type is hidden
120 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(),
121 "Parameter of unavailable type " + t.fullName() + " in " + cl.qualifiedName()
122 + "." + m.name() + "()");
123 } else {
124 // Parameter type contains a generic parameter
125 Errors.error(Errors.HIDDEN_TYPE_PARAMETER, m.position(),
126 "Parameter uses type parameter of unavailable type " + t.fullName() + " in "
127 + cl.qualifiedName() + "." + m.name() + "()");
128 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700129 }
130 }
131 }
132 }
133
134 // annotations are handled like methods
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700135 for (MethodInfo m : cl.annotationElements()) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800136 if (m.isHiddenOrRemoved()) {
137 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to unavailable annotation "
Ben Dodson920dbbb2010-08-04 15:21:06 -0700138 + m.name());
139 }
140
141 ClassInfo returnClass = m.returnType().asClassInfo();
Hui Shu5118ffe2014-02-18 14:06:42 -0800142 if (returnClass != null && returnClass.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700143 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Annotation '" + m.name()
144 + "' returns unavailable type " + returnClass.name());
145 }
146
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700147 for (ParameterInfo p : m.parameters()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700148 TypeInfo t = p.type();
149 if (!t.isPrimitive()) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800150 if (t.asClassInfo().isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700151 Errors.error(Errors.UNAVAILABLE_SYMBOL, p.position(),
152 "Reference to unavailable annotation class " + t.fullName());
153 }
154 }
155 }
156 }
157 } else if (cl.isDeprecated()) {
158 // not hidden, but deprecated
159 Errors.error(Errors.DEPRECATED, cl.position(), "Class " + cl.qualifiedName()
160 + " is deprecated");
161 }
162 }
163
Hui Shu5118ffe2014-02-18 14:06:42 -0800164 // packages contains all the notStrippable classes mapped by their containing packages
Ben Dodson920dbbb2010-08-04 15:21:06 -0700165 HashMap<PackageInfo, List<ClassInfo>> packages = new HashMap<PackageInfo, List<ClassInfo>>();
Chris Banes372e36e2015-04-30 10:31:25 +0100166 final HashSet<Pattern> stubPackageWildcards = extractWildcards(stubPackages);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700167 for (ClassInfo cl : notStrippable) {
168 if (!cl.isDocOnly()) {
Chris Banes372e36e2015-04-30 10:31:25 +0100169 if (shouldWriteStub(cl.containingPackage().name(), stubPackages, stubPackageWildcards)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700170 // write out the stubs
171 if (stubsDir != null) {
Joe Onorato04099252011-03-09 13:34:18 -0800172 writeClassFile(stubsDir, notStrippable, cl);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700173 }
Jeff Hamilton970f13f2012-06-22 00:21:56 -0500174 // build class list for api file or keep list file
175 if (apiWriter != null || keepListWriter != null) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700176 if (packages.containsKey(cl.containingPackage())) {
177 packages.get(cl.containingPackage()).add(cl);
178 } else {
179 ArrayList<ClassInfo> classes = new ArrayList<ClassInfo>();
180 classes.add(cl);
181 packages.put(cl.containingPackage(), classes);
182 }
183 }
184 }
185 }
186 }
Joe Onorato04099252011-03-09 13:34:18 -0800187 // write out the Api
188 if (apiWriter != null) {
189 writeApi(apiWriter, packages, notStrippable);
190 apiWriter.close();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700191 }
Jeff Hamilton970f13f2012-06-22 00:21:56 -0500192
193 // write out the keep list
194 if (keepListWriter != null) {
195 writeKeepList(keepListWriter, packages, notStrippable);
196 keepListWriter.close();
197 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800198
199 HashMap<PackageInfo, List<ClassInfo>> allPackageClassMap =
200 new HashMap<PackageInfo, List<ClassInfo>>();
201 for (ClassInfo cl : Converter.allClasses()) {
202 if (allPackageClassMap.containsKey(cl.containingPackage())) {
203 allPackageClassMap.get(cl.containingPackage()).add(cl);
204 } else {
205 ArrayList<ClassInfo> classes = new ArrayList<ClassInfo>();
206 classes.add(cl);
207 allPackageClassMap.put(cl.containingPackage(), classes);
208 }
209 }
210 // write out the removed Api
211 if (removedApiWriter != null) {
212 writeRemovedApi(removedApiWriter, allPackageClassMap, notStrippable);
213 removedApiWriter.close();
214 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700215 }
216
Chris Banes372e36e2015-04-30 10:31:25 +0100217 private static boolean shouldWriteStub(final String packageName,
218 final HashSet<String> stubPackages, final HashSet<Pattern> stubPackageWildcards) {
219 if (stubPackages == null) {
220 // There aren't any stub packages set, write all stubs
221 return true;
222 }
223 if (stubPackages.contains(packageName)) {
224 // Stub packages contains package, return true
225 return true;
226 }
227 if (stubPackageWildcards != null) {
228 // Else, we will iterate through the wildcards to see if there's a match
229 for (Pattern wildcard : stubPackageWildcards) {
230 if (wildcard.matcher(packageName).matches()) {
231 return true;
232 }
233 }
234 }
235 return false;
236 }
237
238 private static HashSet<Pattern> extractWildcards(HashSet<String> stubPackages) {
239 HashSet<Pattern> wildcards = null;
240 if (stubPackages != null) {
241 for (Iterator<String> i = stubPackages.iterator(); i.hasNext();) {
242 final String pkg = i.next();
243 if (pkg.indexOf('*') != -1) {
244 if (wildcards == null) {
245 wildcards = new HashSet<Pattern>();
246 }
247 // Add the compiled wildcard, replacing * with the regex equivalent
248 wildcards.add(Pattern.compile(pkg.replace("*", ".*?")));
249 // And remove the raw wildcard from the packages
250 i.remove();
251 }
252 }
253 }
254 return wildcards;
255 }
256
Jeff Arneson22ddcbd2015-01-27 15:22:21 -0800257 private static ClassInfo findHiddenClasses(TypeInfo ti) {
258 ClassInfo ci = ti.asClassInfo();
259 if (ci == null) return null;
260 if (ci.isHiddenOrRemoved()) return ci;
261 if (ti.typeArguments() != null) {
262 for (TypeInfo tii : ti.typeArguments()) {
263 // Avoid infinite recursion in the case of Foo<T extends Foo>
264 if (tii.qualifiedTypeName() != ti.qualifiedTypeName()) {
265 ClassInfo hiddenClass = findHiddenClasses(tii);
266 if (hiddenClass != null) return hiddenClass;
267 }
268 }
269 }
270 return null;
271 }
272
Ben Dodson920dbbb2010-08-04 15:21:06 -0700273 public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why) {
274
275 if (!notStrippable.add(cl)) {
276 // slight optimization: if it already contains cl, it already contains
277 // all of cl's parents
278 return;
279 }
280 cl.setReasonIncluded(why);
281
282 // cant strip annotations
283 /*
284 * if (cl.annotations() != null){ for (AnnotationInstanceInfo ai : cl.annotations()){ if
285 * (ai.type() != null){ cantStripThis(ai.type(), notStrippable, "1:" + cl.qualifiedName()); } }
286 * }
287 */
288 // cant strip any public fields or their generics
Ying Wangae8cb832014-04-03 21:16:48 -0700289 if (cl.selfFields() != null) {
290 for (FieldInfo fInfo : cl.selfFields()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700291 if (fInfo.type() != null) {
292 if (fInfo.type().asClassInfo() != null) {
293 cantStripThis(fInfo.type().asClassInfo(), notStrippable, "2:" + cl.qualifiedName());
294 }
295 if (fInfo.type().typeArguments() != null) {
296 for (TypeInfo tTypeInfo : fInfo.type().typeArguments()) {
297 if (tTypeInfo.asClassInfo() != null) {
298 cantStripThis(tTypeInfo.asClassInfo(), notStrippable, "3:" + cl.qualifiedName());
299 }
300 }
301 }
302 }
303 }
304 }
305 // cant strip any of the type's generics
306 if (cl.asTypeInfo() != null) {
307 if (cl.asTypeInfo().typeArguments() != null) {
308 for (TypeInfo tInfo : cl.asTypeInfo().typeArguments()) {
309 if (tInfo.asClassInfo() != null) {
310 cantStripThis(tInfo.asClassInfo(), notStrippable, "4:" + cl.qualifiedName());
311 }
312 }
313 }
314 }
315 // cant strip any of the annotation elements
316 // cantStripThis(cl.annotationElements(), notStrippable);
317 // take care of methods
318 cantStripThis(cl.allSelfMethods(), notStrippable);
319 cantStripThis(cl.allConstructors(), notStrippable);
320 // blow the outer class open if this is an inner class
321 if (cl.containingClass() != null) {
322 cantStripThis(cl.containingClass(), notStrippable, "5:" + cl.qualifiedName());
323 }
324 // blow open super class and interfaces
325 ClassInfo supr = cl.realSuperclass();
326 if (supr != null) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800327 if (supr.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700328 // cl is a public class declared as extending a hidden superclass.
329 // this is not a desired practice but it's happened, so we deal
Mathieu Chartier2da2d642012-09-21 15:42:59 -0700330 // with it by finding the first super class which passes checklevel for purposes of
Ben Dodson920dbbb2010-08-04 15:21:06 -0700331 // generating the doc & stub information, and proceeding normally.
332 cl.init(cl.asTypeInfo(), cl.realInterfaces(), cl.realInterfaceTypes(), cl.innerClasses(),
333 cl.allConstructors(), cl.allSelfMethods(), cl.annotationElements(), cl.allSelfFields(),
Mathieu Chartier2da2d642012-09-21 15:42:59 -0700334 cl.enumConstants(), cl.containingPackage(), cl.containingClass(),
Elliott Hughesbf322c12013-06-05 15:47:13 -0700335 supr.superclass(), supr.superclassType(), cl.annotations());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700336 Errors.error(Errors.HIDDEN_SUPERCLASS, cl.position(), "Public class " + cl.qualifiedName()
337 + " stripped of unavailable superclass " + supr.qualifiedName());
338 } else {
339 cantStripThis(supr, notStrippable, "6:" + cl.realSuperclass().name() + cl.qualifiedName());
Ying Wang48639612015-03-31 18:14:26 -0700340 if (supr.isPrivate()) {
341 Errors.error(Errors.PRIVATE_SUPERCLASS, cl.position(), "Public class "
342 + cl.qualifiedName() + " extends private class " + supr.qualifiedName());
343 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700344 }
345 }
346 }
347
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700348 private static void cantStripThis(ArrayList<MethodInfo> mInfos, HashSet<ClassInfo> notStrippable) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700349 // for each method, blow open the parameters, throws and return types. also blow open their
350 // generics
351 if (mInfos != null) {
352 for (MethodInfo mInfo : mInfos) {
353 if (mInfo.getTypeParameters() != null) {
354 for (TypeInfo tInfo : mInfo.getTypeParameters()) {
355 if (tInfo.asClassInfo() != null) {
356 cantStripThis(tInfo.asClassInfo(), notStrippable, "8:"
357 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
358 }
359 }
360 }
361 if (mInfo.parameters() != null) {
362 for (ParameterInfo pInfo : mInfo.parameters()) {
363 if (pInfo.type() != null && pInfo.type().asClassInfo() != null) {
364 cantStripThis(pInfo.type().asClassInfo(), notStrippable, "9:"
365 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
366 if (pInfo.type().typeArguments() != null) {
367 for (TypeInfo tInfoType : pInfo.type().typeArguments()) {
368 if (tInfoType.asClassInfo() != null) {
369 ClassInfo tcl = tInfoType.asClassInfo();
Hui Shu5118ffe2014-02-18 14:06:42 -0800370 if (tcl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700371 Errors
372 .error(Errors.UNAVAILABLE_SYMBOL, mInfo.position(),
373 "Parameter of hidden type " + tInfoType.fullName() + " in "
374 + mInfo.containingClass().qualifiedName() + '.' + mInfo.name()
375 + "()");
376 } else {
377 cantStripThis(tcl, notStrippable, "10:"
378 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
379 }
380 }
381 }
382 }
383 }
384 }
385 }
386 for (ClassInfo thrown : mInfo.thrownExceptions()) {
387 cantStripThis(thrown, notStrippable, "11:" + mInfo.realContainingClass().qualifiedName()
388 + ":" + mInfo.name());
389 }
390 if (mInfo.returnType() != null && mInfo.returnType().asClassInfo() != null) {
391 cantStripThis(mInfo.returnType().asClassInfo(), notStrippable, "12:"
392 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
393 if (mInfo.returnType().typeArguments() != null) {
394 for (TypeInfo tyInfo : mInfo.returnType().typeArguments()) {
395 if (tyInfo.asClassInfo() != null) {
396 cantStripThis(tyInfo.asClassInfo(), notStrippable, "13:"
397 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
398 }
399 }
400 }
401 }
402 }
403 }
404 }
405
406 static String javaFileName(ClassInfo cl) {
407 String dir = "";
408 PackageInfo pkg = cl.containingPackage();
409 if (pkg != null) {
410 dir = pkg.name();
411 dir = dir.replace('.', '/') + '/';
412 }
413 return dir + cl.name() + ".java";
414 }
415
Joe Onorato04099252011-03-09 13:34:18 -0800416 static void writeClassFile(String stubsDir, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700417 // inner classes are written by their containing class
418 if (cl.containingClass() != null) {
419 return;
420 }
421
422 // Work around the bogus "Array" class we invent for
423 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
424 if (cl.containingPackage() != null
425 && cl.containingPackage().name().equals(PackageInfo.DEFAULT_PACKAGE)) {
426 return;
427 }
428
429 String filename = stubsDir + '/' + javaFileName(cl);
430 File file = new File(filename);
431 ClearPage.ensureDirectory(file);
432
433 PrintStream stream = null;
434 try {
Brian Carlstromed8f7972011-06-10 11:10:48 -0700435 stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(file)));
Joe Onorato04099252011-03-09 13:34:18 -0800436 writeClassFile(stream, notStrippable, cl);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700437 } catch (FileNotFoundException e) {
438 System.err.println("error writing file: " + filename);
439 } finally {
440 if (stream != null) {
441 stream.close();
442 }
443 }
444 }
445
Joe Onorato04099252011-03-09 13:34:18 -0800446 static void writeClassFile(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700447 PackageInfo pkg = cl.containingPackage();
448 if (pkg != null) {
449 stream.println("package " + pkg.name() + ";");
450 }
Joe Onorato04099252011-03-09 13:34:18 -0800451 writeClass(stream, notStrippable, cl);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700452 }
453
Joe Onorato04099252011-03-09 13:34:18 -0800454 static void writeClass(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
Tor Norbye73934882012-02-29 07:50:52 -0800455 writeAnnotations(stream, cl.annotations(), cl.isDeprecated());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700456
457 stream.print(cl.scope() + " ");
458 if (cl.isAbstract() && !cl.isAnnotation() && !cl.isInterface()) {
459 stream.print("abstract ");
460 }
461 if (cl.isStatic()) {
462 stream.print("static ");
463 }
464 if (cl.isFinal() && !cl.isEnum()) {
465 stream.print("final ");
466 }
467 if (false) {
468 stream.print("strictfp ");
469 }
470
471 HashSet<String> classDeclTypeVars = new HashSet();
472 String leafName = cl.asTypeInfo().fullName(classDeclTypeVars);
473 int bracket = leafName.indexOf('<');
474 if (bracket < 0) bracket = leafName.length() - 1;
475 int period = leafName.lastIndexOf('.', bracket);
476 if (period < 0) period = -1;
477 leafName = leafName.substring(period + 1);
478
479 String kind = cl.kind();
480 stream.println(kind + " " + leafName);
481
482 TypeInfo base = cl.superclassType();
483
484 if (!"enum".equals(kind)) {
485 if (base != null && !"java.lang.Object".equals(base.qualifiedTypeName())) {
486 stream.println(" extends " + base.fullName(classDeclTypeVars));
487 }
488 }
489
Ben Dodson920dbbb2010-08-04 15:21:06 -0700490 List<TypeInfo> usedInterfaces = new ArrayList<TypeInfo>();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700491 for (TypeInfo iface : cl.realInterfaceTypes()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700492 if (notStrippable.contains(iface.asClassInfo()) && !iface.asClassInfo().isDocOnly()) {
493 usedInterfaces.add(iface);
494 }
495 }
496 if (usedInterfaces.size() > 0 && !cl.isAnnotation()) {
497 // can java annotations extend other ones?
498 if (cl.isInterface() || cl.isAnnotation()) {
499 stream.print(" extends ");
500 } else {
501 stream.print(" implements ");
502 }
503 String comma = "";
504 for (TypeInfo iface : usedInterfaces) {
505 stream.print(comma + iface.fullName(classDeclTypeVars));
506 comma = ", ";
507 }
508 stream.println();
509 }
510
511 stream.println("{");
512
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700513 ArrayList<FieldInfo> enumConstants = cl.enumConstants();
514 int N = enumConstants.size();
515 int i = 0;
516 for (FieldInfo field : enumConstants) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700517 if (!field.constantLiteralValue().equals("null")) {
518 stream.println(field.name() + "(" + field.constantLiteralValue()
519 + (i == N - 1 ? ");" : "),"));
520 } else {
521 stream.println(field.name() + "(" + (i == N - 1 ? ");" : "),"));
522 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700523 i++;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700524 }
525
526 for (ClassInfo inner : cl.getRealInnerClasses()) {
527 if (notStrippable.contains(inner) && !inner.isDocOnly()) {
Joe Onorato04099252011-03-09 13:34:18 -0800528 writeClass(stream, notStrippable, inner);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700529 }
530 }
531
532
533 for (MethodInfo method : cl.constructors()) {
534 if (!method.isDocOnly()) {
535 writeMethod(stream, method, true);
536 }
537 }
538
539 boolean fieldNeedsInitialization = false;
540 boolean staticFieldNeedsInitialization = false;
Ying Wangae8cb832014-04-03 21:16:48 -0700541 for (FieldInfo field : cl.selfFields()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700542 if (!field.isDocOnly()) {
543 if (!field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
544 fieldNeedsInitialization = true;
545 }
546 if (field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
547 staticFieldNeedsInitialization = true;
548 }
549 }
550 }
551
552 // The compiler includes a default public constructor that calls the super classes
553 // default constructor in the case where there are no written constructors.
554 // So, if we hide all the constructors, java may put in a constructor
555 // that calls a nonexistent super class constructor. So, if there are no constructors,
556 // and the super class doesn't have a default constructor, write in a private constructor
557 // that works. TODO -- we generate this as protected, but we really should generate
558 // it as private unless it also exists in the real code.
Hui Shu5118ffe2014-02-18 14:06:42 -0800559 if ((cl.constructors().isEmpty() && (!cl.getNonWrittenConstructors().isEmpty() ||
560 fieldNeedsInitialization)) && !cl.isAnnotation() && !cl.isInterface() && !cl.isEnum()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700561 // Errors.error(Errors.HIDDEN_CONSTRUCTOR,
562 // cl.position(), "No constructors " +
563 // "found and superclass has no parameterless constructor. A constructor " +
564 // "that calls an appropriate superclass constructor " +
565 // "was automatically written to stubs.\n");
566 stream.println(cl.leafName() + "() { " + superCtorCall(cl, null) + "throw new"
567 + " RuntimeException(\"Stub!\"); }");
568 }
569
570 for (MethodInfo method : cl.allSelfMethods()) {
571 if (cl.isEnum()) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800572 if (("values".equals(method.name()) && "()".equals(method.signature())) ||
573 ("valueOf".equals(method.name()) &&
574 "(java.lang.String)".equals(method.signature()))) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700575 // skip these two methods on enums, because they're synthetic,
576 // although for some reason javadoc doesn't mark them as synthetic,
577 // maybe because they still want them documented
578 continue;
579 }
580 }
581 if (!method.isDocOnly()) {
582 writeMethod(stream, method, false);
583 }
584 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800585 // Write all methods that are hidden or removed, but override abstract methods or interface methods.
Ben Dodson920dbbb2010-08-04 15:21:06 -0700586 // These can't be hidden.
Hui Shu5118ffe2014-02-18 14:06:42 -0800587 List<MethodInfo> hiddenAndRemovedMethods = cl.getHiddenMethods();
588 hiddenAndRemovedMethods.addAll(cl.getRemovedMethods());
589 for (MethodInfo method : hiddenAndRemovedMethods) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700590 MethodInfo overriddenMethod =
591 method.findRealOverriddenMethod(method.name(), method.signature(), notStrippable);
592 ClassInfo classContainingMethod =
593 method.findRealOverriddenClass(method.name(), method.signature());
Hui Shu5118ffe2014-02-18 14:06:42 -0800594 if (overriddenMethod != null && !overriddenMethod.isHiddenOrRemoved() &&
595 !overriddenMethod.isDocOnly() &&
596 (overriddenMethod.isAbstract() || overriddenMethod.containingClass().isInterface())) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700597 method.setReason("1:" + classContainingMethod.qualifiedName());
598 cl.addMethod(method);
599 writeMethod(stream, method, false);
600 }
601 }
602
603 for (MethodInfo element : cl.annotationElements()) {
604 if (!element.isDocOnly()) {
605 writeAnnotationElement(stream, element);
606 }
607 }
608
Ying Wangae8cb832014-04-03 21:16:48 -0700609 for (FieldInfo field : cl.selfFields()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700610 if (!field.isDocOnly()) {
611 writeField(stream, field);
612 }
613 }
614
615 if (staticFieldNeedsInitialization) {
616 stream.print("static { ");
Ying Wangae8cb832014-04-03 21:16:48 -0700617 for (FieldInfo field : cl.selfFields()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700618 if (!field.isDocOnly() && field.isStatic() && field.isFinal() && !fieldIsInitialized(field)
619 && field.constantValue() == null) {
620 stream.print(field.name() + " = " + field.type().defaultValue() + "; ");
621 }
622 }
623 stream.println("}");
624 }
625
626 stream.println("}");
627 }
628
629
630 static void writeMethod(PrintStream stream, MethodInfo method, boolean isConstructor) {
631 String comma;
632
Tor Norbye73934882012-02-29 07:50:52 -0800633 writeAnnotations(stream, method.annotations(), method.isDeprecated());
634
Ben Dodson920dbbb2010-08-04 15:21:06 -0700635 stream.print(method.scope() + " ");
636 if (method.isStatic()) {
637 stream.print("static ");
638 }
639 if (method.isFinal()) {
640 stream.print("final ");
641 }
642 if (method.isAbstract()) {
643 stream.print("abstract ");
644 }
645 if (method.isSynchronized()) {
646 stream.print("synchronized ");
647 }
648 if (method.isNative()) {
649 stream.print("native ");
650 }
651 if (false /* method.isStictFP() */) {
652 stream.print("strictfp ");
653 }
654
655 stream.print(method.typeArgumentsName(new HashSet()) + " ");
656
657 if (!isConstructor) {
658 stream.print(method.returnType().fullName(method.typeVariables()) + " ");
659 }
660 String n = method.name();
661 int pos = n.lastIndexOf('.');
662 if (pos >= 0) {
663 n = n.substring(pos + 1);
664 }
665 stream.print(n + "(");
666 comma = "";
667 int count = 1;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700668 int size = method.parameters().size();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700669 for (ParameterInfo param : method.parameters()) {
670 stream.print(comma + fullParameterTypeName(method, param.type(), count == size) + " "
671 + param.name());
672 comma = ", ";
673 count++;
674 }
675 stream.print(")");
676
677 comma = "";
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700678 if (method.thrownExceptions().size() > 0) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700679 stream.print(" throws ");
680 for (ClassInfo thrown : method.thrownExceptions()) {
681 stream.print(comma + thrown.qualifiedName());
682 comma = ", ";
683 }
684 }
685 if (method.isAbstract() || method.isNative() || method.containingClass().isInterface()) {
686 stream.println(";");
687 } else {
688 stream.print(" { ");
689 if (isConstructor) {
690 stream.print(superCtorCall(method.containingClass(), method.thrownExceptions()));
691 }
692 stream.println("throw new RuntimeException(\"Stub!\"); }");
693 }
694 }
695
696 static void writeField(PrintStream stream, FieldInfo field) {
Tor Norbye73934882012-02-29 07:50:52 -0800697 writeAnnotations(stream, field.annotations(), field.isDeprecated());
698
Ben Dodson920dbbb2010-08-04 15:21:06 -0700699 stream.print(field.scope() + " ");
700 if (field.isStatic()) {
701 stream.print("static ");
702 }
703 if (field.isFinal()) {
704 stream.print("final ");
705 }
706 if (field.isTransient()) {
707 stream.print("transient ");
708 }
709 if (field.isVolatile()) {
710 stream.print("volatile ");
711 }
712
713 stream.print(field.type().fullName());
714 stream.print(" ");
715 stream.print(field.name());
716
717 if (fieldIsInitialized(field)) {
718 stream.print(" = " + field.constantLiteralValue());
719 }
720
721 stream.println(";");
722 }
723
724 static boolean fieldIsInitialized(FieldInfo field) {
725 return (field.isFinal() && field.constantValue() != null)
726 || !field.type().dimension().equals("") || field.containingClass().isInterface();
727 }
728
729 // Returns 'true' if the method is an @Override of a visible parent
730 // method implementation, and thus does not affect the API.
Joe Onorato04099252011-03-09 13:34:18 -0800731 static boolean methodIsOverride(HashSet<ClassInfo> notStrippable, MethodInfo mi) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700732 // Abstract/static/final methods are always listed in the API description
733 if (mi.isAbstract() || mi.isStatic() || mi.isFinal()) {
734 return false;
735 }
736
737 // Find any relevant ancestor declaration and inspect it
738 MethodInfo om = mi.findSuperclassImplementation(notStrippable);
739 if (om != null) {
740 // Visibility mismatch is an API change, so check for it
741 if (mi.mIsPrivate == om.mIsPrivate && mi.mIsPublic == om.mIsPublic
742 && mi.mIsProtected == om.mIsProtected) {
743 // Look only for overrides of an ancestor class implementation,
744 // not of e.g. an abstract or interface method declaration
745 if (!om.isAbstract()) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800746 // If the parent is hidden or removed, we can't rely on it to provide
Ben Dodson920dbbb2010-08-04 15:21:06 -0700747 // the API
Hui Shu5118ffe2014-02-18 14:06:42 -0800748 if (!om.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700749 // If the only "override" turns out to be in our own class
750 // (which sometimes happens in concrete subclasses of
751 // abstract base classes), it's not really an override
752 if (!mi.mContainingClass.equals(om.mContainingClass)) {
753 return true;
754 }
755 }
756 }
757 }
758 }
759 return false;
760 }
761
762 static boolean canCallMethod(ClassInfo from, MethodInfo m) {
763 if (m.isPublic() || m.isProtected()) {
764 return true;
765 }
766 if (m.isPackagePrivate()) {
767 String fromPkg = from.containingPackage().name();
768 String pkg = m.containingClass().containingPackage().name();
769 if (fromPkg.equals(pkg)) {
770 return true;
771 }
772 }
773 return false;
774 }
775
776 // call a constructor, any constructor on this class's superclass.
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700777 static String superCtorCall(ClassInfo cl, ArrayList<ClassInfo> thrownExceptions) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700778 ClassInfo base = cl.realSuperclass();
779 if (base == null) {
780 return "";
781 }
782 HashSet<String> exceptionNames = new HashSet<String>();
783 if (thrownExceptions != null) {
784 for (ClassInfo thrown : thrownExceptions) {
785 exceptionNames.add(thrown.name());
786 }
787 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700788 ArrayList<MethodInfo> ctors = base.constructors();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700789 MethodInfo ctor = null;
790 // bad exception indicates that the exceptions thrown by the super constructor
791 // are incompatible with the constructor we're using for the sub class.
792 Boolean badException = false;
793 for (MethodInfo m : ctors) {
794 if (canCallMethod(cl, m)) {
795 if (m.thrownExceptions() != null) {
796 for (ClassInfo thrown : m.thrownExceptions()) {
797 if (!exceptionNames.contains(thrown.name())) {
798 badException = true;
799 }
800 }
801 }
802 if (badException) {
803 badException = false;
804 continue;
805 }
806 // if it has no args, we're done
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700807 if (m.parameters().isEmpty()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700808 return "";
809 }
810 ctor = m;
811 }
812 }
813 if (ctor != null) {
814 String result = "";
815 result += "super(";
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700816 ArrayList<ParameterInfo> params = ctor.parameters();
817 for (ParameterInfo param : params) {
818 TypeInfo t = param.type();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700819 if (t.isPrimitive() && t.dimension().equals("")) {
820 String n = t.simpleTypeName();
821 if (("byte".equals(n) || "short".equals(n) || "int".equals(n) || "long".equals(n)
822 || "float".equals(n) || "double".equals(n))
823 && t.dimension().equals("")) {
824 result += "0";
825 } else if ("char".equals(n)) {
826 result += "'\\0'";
827 } else if ("boolean".equals(n)) {
828 result += "false";
829 } else {
830 result += "<<unknown-" + n + ">>";
831 }
832 } else {
833 // put null in each super class method. Cast null to the correct type
834 // to avoid collisions with other constructors. If the type is generic
835 // don't cast it
836 result +=
837 (!t.isTypeVariable() ? "(" + t.qualifiedTypeName() + t.dimension() + ")" : "")
838 + "null";
839 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700840 if (param != params.get(params.size()-1)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700841 result += ",";
842 }
843 }
844 result += "); ";
845 return result;
846 } else {
847 return "";
848 }
849 }
850
Tor Norbye73934882012-02-29 07:50:52 -0800851 /**
852 * Write out the given list of annotations. If the {@code isDeprecated}
853 * flag is true also write out a {@code @Deprecated} annotation if it did not
854 * already appear in the list of annotations. (This covers APIs that mention
855 * {@code @deprecated} in their documentation but fail to add
856 * {@code @Deprecated} as an annotation.
857 * <p>
858 * {@code @Override} annotations are deliberately skipped.
859 */
860 static void writeAnnotations(PrintStream stream, List<AnnotationInstanceInfo> annotations,
861 boolean isDeprecated) {
862 assert annotations != null;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700863 for (AnnotationInstanceInfo ann : annotations) {
Tor Norbye73934882012-02-29 07:50:52 -0800864 // Skip @Override annotations: the stubs do not need it and in some cases it leads
865 // to compilation errors with the way the stubs are generated
866 if (ann.type() != null && ann.type().qualifiedName().equals("java.lang.Override")) {
867 continue;
868 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800869 if (!ann.type().isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700870 stream.println(ann.toString());
Tor Norbye73934882012-02-29 07:50:52 -0800871 if (isDeprecated && ann.type() != null
872 && ann.type().qualifiedName().equals("java.lang.Deprecated")) {
873 isDeprecated = false; // Prevent duplicate annotations
874 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700875 }
876 }
Tor Norbye73934882012-02-29 07:50:52 -0800877 if (isDeprecated) {
878 stream.println("@Deprecated");
879 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700880 }
881
882 static void writeAnnotationElement(PrintStream stream, MethodInfo ann) {
883 stream.print(ann.returnType().fullName());
884 stream.print(" ");
885 stream.print(ann.name());
886 stream.print("()");
887 AnnotationValueInfo def = ann.defaultAnnotationElementValue();
888 if (def != null) {
889 stream.print(" default ");
890 stream.print(def.valueString());
891 }
892 stream.println(";");
893 }
894
895 static void writeXML(PrintStream xmlWriter, HashMap<PackageInfo, List<ClassInfo>> allClasses,
Joe Onorato04099252011-03-09 13:34:18 -0800896 HashSet<ClassInfo> notStrippable) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700897 // extract the set of packages, sort them by name, and write them out in that order
898 Set<PackageInfo> allClassKeys = allClasses.keySet();
899 PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]);
900 Arrays.sort(allPackages, PackageInfo.comparator);
901
902 xmlWriter.println("<api>");
903 for (PackageInfo pack : allPackages) {
904 writePackageXML(xmlWriter, pack, allClasses.get(pack), notStrippable);
905 }
906 xmlWriter.println("</api>");
907 }
908
Joe Onorato04099252011-03-09 13:34:18 -0800909 public static void writeXml(PrintStream xmlWriter, Collection<PackageInfo> pkgs) {
910 final PackageInfo[] packages = pkgs.toArray(new PackageInfo[pkgs.size()]);
911 Arrays.sort(packages, PackageInfo.comparator);
912
913 HashSet<ClassInfo> notStrippable = new HashSet();
914 for (PackageInfo pkg: packages) {
915 for (ClassInfo cl: pkg.allClasses().values()) {
916 notStrippable.add(cl);
917 }
918 }
919 xmlWriter.println("<api>");
920 for (PackageInfo pkg: packages) {
921 writePackageXML(xmlWriter, pkg, pkg.allClasses().values(), notStrippable);
922 }
923 xmlWriter.println("</api>");
924 }
925
926 static void writePackageXML(PrintStream xmlWriter, PackageInfo pack,
927 Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700928 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
929 Arrays.sort(classes, ClassInfo.comparator);
930 // Work around the bogus "Array" class we invent for
931 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
932 if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) {
933 return;
934 }
935 xmlWriter.println("<package name=\"" + pack.name() + "\"\n"
936 // + " source=\"" + pack.position() + "\"\n"
937 + ">");
938 for (ClassInfo cl : classes) {
939 writeClassXML(xmlWriter, cl, notStrippable);
940 }
941 xmlWriter.println("</package>");
942
943
944 }
945
Joe Onorato04099252011-03-09 13:34:18 -0800946 static void writeClassXML(PrintStream xmlWriter, ClassInfo cl, HashSet<ClassInfo> notStrippable) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700947 String scope = cl.scope();
948 String deprecatedString = "";
949 String declString = (cl.isInterface()) ? "interface" : "class";
950 if (cl.isDeprecated()) {
951 deprecatedString = "deprecated";
952 } else {
953 deprecatedString = "not deprecated";
954 }
955 xmlWriter.println("<" + declString + " name=\"" + cl.name() + "\"");
956 if (!cl.isInterface() && !cl.qualifiedName().equals("java.lang.Object")) {
957 xmlWriter.println(" extends=\""
958 + ((cl.realSuperclass() == null) ? "java.lang.Object" : cl.realSuperclass()
959 .qualifiedName()) + "\"");
960 }
961 xmlWriter.println(" abstract=\"" + cl.isAbstract() + "\"\n" + " static=\"" + cl.isStatic()
962 + "\"\n" + " final=\"" + cl.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString
963 + "\"\n" + " visibility=\"" + scope + "\"\n"
964 // + " source=\"" + cl.position() + "\"\n"
965 + ">");
966
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700967 ArrayList<ClassInfo> interfaces = cl.realInterfaces();
968 Collections.sort(interfaces, ClassInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700969 for (ClassInfo iface : interfaces) {
970 if (notStrippable.contains(iface)) {
971 xmlWriter.println("<implements name=\"" + iface.qualifiedName() + "\">");
972 xmlWriter.println("</implements>");
973 }
974 }
975
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700976 ArrayList<MethodInfo> constructors = cl.constructors();
977 Collections.sort(constructors, MethodInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700978 for (MethodInfo mi : constructors) {
979 writeConstructorXML(xmlWriter, mi);
980 }
981
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700982 ArrayList<MethodInfo> methods = cl.allSelfMethods();
983 Collections.sort(methods, MethodInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700984 for (MethodInfo mi : methods) {
Joe Onorato04099252011-03-09 13:34:18 -0800985 if (!methodIsOverride(notStrippable, mi)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700986 writeMethodXML(xmlWriter, mi);
987 }
988 }
989
Ying Wangae8cb832014-04-03 21:16:48 -0700990 ArrayList<FieldInfo> fields = cl.selfFields();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700991 Collections.sort(fields, FieldInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700992 for (FieldInfo fi : fields) {
993 writeFieldXML(xmlWriter, fi);
994 }
995 xmlWriter.println("</" + declString + ">");
996
997 }
998
999 static void writeMethodXML(PrintStream xmlWriter, MethodInfo mi) {
1000 String scope = mi.scope();
1001
1002 String deprecatedString = "";
1003 if (mi.isDeprecated()) {
1004 deprecatedString = "deprecated";
1005 } else {
1006 deprecatedString = "not deprecated";
1007 }
1008 xmlWriter.println("<method name=\""
1009 + mi.name()
1010 + "\"\n"
1011 + ((mi.returnType() != null) ? " return=\""
1012 + makeXMLcompliant(fullParameterTypeName(mi, mi.returnType(), false)) + "\"\n" : "")
1013 + " abstract=\"" + mi.isAbstract() + "\"\n" + " native=\"" + mi.isNative() + "\"\n"
1014 + " synchronized=\"" + mi.isSynchronized() + "\"\n" + " static=\"" + mi.isStatic() + "\"\n"
1015 + " final=\"" + mi.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString + "\"\n"
1016 + " visibility=\"" + scope + "\"\n"
1017 // + " source=\"" + mi.position() + "\"\n"
1018 + ">");
1019
1020 // write parameters in declaration order
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001021 int numParameters = mi.parameters().size();
Ben Dodson920dbbb2010-08-04 15:21:06 -07001022 int count = 0;
1023 for (ParameterInfo pi : mi.parameters()) {
1024 count++;
1025 writeParameterXML(xmlWriter, mi, pi, count == numParameters);
1026 }
1027
1028 // but write exceptions in canonicalized order
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001029 ArrayList<ClassInfo> exceptions = mi.thrownExceptions();
1030 Collections.sort(exceptions, ClassInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -07001031 for (ClassInfo pi : exceptions) {
1032 xmlWriter.println("<exception name=\"" + pi.name() + "\" type=\"" + pi.qualifiedName()
1033 + "\">");
1034 xmlWriter.println("</exception>");
1035 }
1036 xmlWriter.println("</method>");
1037 }
1038
1039 static void writeConstructorXML(PrintStream xmlWriter, MethodInfo mi) {
1040 String scope = mi.scope();
1041 String deprecatedString = "";
1042 if (mi.isDeprecated()) {
1043 deprecatedString = "deprecated";
1044 } else {
1045 deprecatedString = "not deprecated";
1046 }
1047 xmlWriter.println("<constructor name=\"" + mi.name() + "\"\n" + " type=\""
1048 + mi.containingClass().qualifiedName() + "\"\n" + " static=\"" + mi.isStatic() + "\"\n"
1049 + " final=\"" + mi.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString + "\"\n"
1050 + " visibility=\"" + scope + "\"\n"
1051 // + " source=\"" + mi.position() + "\"\n"
1052 + ">");
1053
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001054 int numParameters = mi.parameters().size();
Ben Dodson920dbbb2010-08-04 15:21:06 -07001055 int count = 0;
1056 for (ParameterInfo pi : mi.parameters()) {
1057 count++;
1058 writeParameterXML(xmlWriter, mi, pi, count == numParameters);
1059 }
1060
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001061 ArrayList<ClassInfo> exceptions = mi.thrownExceptions();
1062 Collections.sort(exceptions, ClassInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -07001063 for (ClassInfo pi : exceptions) {
1064 xmlWriter.println("<exception name=\"" + pi.name() + "\" type=\"" + pi.qualifiedName()
1065 + "\">");
1066 xmlWriter.println("</exception>");
1067 }
1068 xmlWriter.println("</constructor>");
1069 }
1070
1071 static void writeParameterXML(PrintStream xmlWriter, MethodInfo method, ParameterInfo pi,
1072 boolean isLast) {
1073 xmlWriter.println("<parameter name=\"" + pi.name() + "\" type=\""
1074 + makeXMLcompliant(fullParameterTypeName(method, pi.type(), isLast)) + "\">");
1075 xmlWriter.println("</parameter>");
1076 }
1077
1078 static void writeFieldXML(PrintStream xmlWriter, FieldInfo fi) {
1079 String scope = fi.scope();
1080 String deprecatedString = "";
1081 if (fi.isDeprecated()) {
1082 deprecatedString = "deprecated";
1083 } else {
1084 deprecatedString = "not deprecated";
1085 }
1086 // need to make sure value is valid XML
1087 String value = makeXMLcompliant(fi.constantLiteralValue());
1088
Ying Wang5db09cd2014-08-14 15:41:39 -07001089 String fullTypeName = makeXMLcompliant(fi.type().fullName());
Ben Dodson920dbbb2010-08-04 15:21:06 -07001090
1091 xmlWriter.println("<field name=\"" + fi.name() + "\"\n" + " type=\"" + fullTypeName + "\"\n"
1092 + " transient=\"" + fi.isTransient() + "\"\n" + " volatile=\"" + fi.isVolatile() + "\"\n"
1093 + (fieldIsInitialized(fi) ? " value=\"" + value + "\"\n" : "") + " static=\""
1094 + fi.isStatic() + "\"\n" + " final=\"" + fi.isFinal() + "\"\n" + " deprecated=\""
1095 + deprecatedString + "\"\n" + " visibility=\"" + scope + "\"\n"
1096 // + " source=\"" + fi.position() + "\"\n"
1097 + ">");
1098 xmlWriter.println("</field>");
1099 }
1100
1101 static String makeXMLcompliant(String s) {
1102 String returnString = "";
1103 returnString = s.replaceAll("&", "&amp;");
1104 returnString = returnString.replaceAll("<", "&lt;");
1105 returnString = returnString.replaceAll(">", "&gt;");
1106 returnString = returnString.replaceAll("\"", "&quot;");
C. Sean Young0ec37642015-06-12 16:01:17 -05001107 returnString = returnString.replaceAll("'", "&apos;");
Ben Dodson920dbbb2010-08-04 15:21:06 -07001108 return returnString;
1109 }
1110
Hui Shu5118ffe2014-02-18 14:06:42 -08001111 static void writeRemovedApi(PrintStream apiWriter, HashMap<PackageInfo,
1112 List<ClassInfo>> allPackageClassMap, Set<ClassInfo> notStrippable) {
1113 final PackageInfo[] packages = allPackageClassMap.keySet().toArray(new PackageInfo[0]);
1114 Arrays.sort(packages, PackageInfo.comparator);
1115 for (PackageInfo pkg : packages) {
1116 // beware that pkg.allClasses() has no class in it at the moment
1117 final List<ClassInfo> classes = allPackageClassMap.get(pkg);
1118 Collections.sort(classes, ClassInfo.comparator);
1119 boolean hasWrittenPackageHead = false;
1120 for (ClassInfo cl : classes) {
1121 if (cl.hasRemovedSelfMembers()) {
1122 if (!hasWrittenPackageHead) {
1123 hasWrittenPackageHead = true;
1124 apiWriter.print("package ");
1125 apiWriter.print(pkg.qualifiedName());
1126 apiWriter.print(" {\n\n");
1127 }
1128 writeClassRemovedSelfMembers(apiWriter, cl, notStrippable);
1129 }
1130 }
1131
1132 // the package contains some classes with some removed members
1133 if (hasWrittenPackageHead) {
1134 apiWriter.print("}\n\n");
1135 }
1136 }
1137 }
1138
1139 /**
1140 * Write the removed members of the class to removed.txt
1141 */
1142 private static void writeClassRemovedSelfMembers(PrintStream apiWriter, ClassInfo cl,
1143 Set<ClassInfo> notStrippable) {
1144 apiWriter.print(" ");
1145 apiWriter.print(cl.scope());
1146 if (cl.isStatic()) {
1147 apiWriter.print(" static");
1148 }
1149 if (cl.isFinal()) {
1150 apiWriter.print(" final");
1151 }
1152 if (cl.isAbstract()) {
1153 apiWriter.print(" abstract");
1154 }
1155 if (cl.isDeprecated()) {
1156 apiWriter.print(" deprecated");
1157 }
1158 apiWriter.print(" ");
1159 apiWriter.print(cl.isInterface() ? "interface" : "class");
1160 apiWriter.print(" ");
1161 apiWriter.print(cl.name());
1162
1163 if (!cl.isInterface()
1164 && !"java.lang.Object".equals(cl.qualifiedName())
1165 && cl.realSuperclass() != null
1166 && !"java.lang.Object".equals(cl.realSuperclass().qualifiedName())) {
1167 apiWriter.print(" extends ");
1168 apiWriter.print(cl.realSuperclass().qualifiedName());
1169 }
1170
1171 ArrayList<ClassInfo> interfaces = cl.realInterfaces();
1172 Collections.sort(interfaces, ClassInfo.comparator);
1173 boolean first = true;
1174 for (ClassInfo iface : interfaces) {
1175 if (notStrippable.contains(iface)) {
1176 if (first) {
1177 apiWriter.print(" implements");
1178 first = false;
1179 }
1180 apiWriter.print(" ");
1181 apiWriter.print(iface.qualifiedName());
1182 }
1183 }
1184
1185 apiWriter.print(" {\n");
1186
1187 List<MethodInfo> constructors = cl.getRemovedConstructors();
1188 for (MethodInfo mi : constructors) {
1189 writeConstructorApi(apiWriter, mi);
1190 }
1191
1192 List<MethodInfo> methods = cl.getRemovedSelfMethods();
1193 for (MethodInfo mi : methods) {
1194 writeMethodApi(apiWriter, mi);
1195 }
1196
1197 List<FieldInfo> enums = cl.getRemovedSelfEnumConstants();
1198 for (FieldInfo fi : enums) {
1199 writeFieldApi(apiWriter, fi, "enum_constant");
1200 }
1201
1202 List<FieldInfo> fields = cl.getRemovedSelfFields();
1203 for (FieldInfo fi : fields) {
1204 writeFieldApi(apiWriter, fi, "field");
1205 }
1206
1207 apiWriter.print(" }\n\n");
1208 }
1209
Joe Onorato04099252011-03-09 13:34:18 -08001210 public static void writeApi(PrintStream apiWriter, Collection<PackageInfo> pkgs) {
1211 final PackageInfo[] packages = pkgs.toArray(new PackageInfo[pkgs.size()]);
1212 Arrays.sort(packages, PackageInfo.comparator);
1213
1214 HashSet<ClassInfo> notStrippable = new HashSet();
1215 for (PackageInfo pkg: packages) {
1216 for (ClassInfo cl: pkg.allClasses().values()) {
1217 notStrippable.add(cl);
1218 }
1219 }
1220 for (PackageInfo pkg: packages) {
1221 writePackageApi(apiWriter, pkg, pkg.allClasses().values(), notStrippable);
1222 }
1223 }
1224
1225 static void writeApi(PrintStream apiWriter, HashMap<PackageInfo, List<ClassInfo>> allClasses,
1226 HashSet<ClassInfo> notStrippable) {
1227 // extract the set of packages, sort them by name, and write them out in that order
1228 Set<PackageInfo> allClassKeys = allClasses.keySet();
1229 PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]);
1230 Arrays.sort(allPackages, PackageInfo.comparator);
1231
1232 for (PackageInfo pack : allPackages) {
1233 writePackageApi(apiWriter, pack, allClasses.get(pack), notStrippable);
1234 }
1235 }
1236
1237 static void writePackageApi(PrintStream apiWriter, PackageInfo pack,
1238 Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) {
1239 // Work around the bogus "Array" class we invent for
1240 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
1241 if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) {
1242 return;
1243 }
1244
1245 apiWriter.print("package ");
1246 apiWriter.print(pack.qualifiedName());
1247 apiWriter.print(" {\n\n");
1248
1249 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
1250 Arrays.sort(classes, ClassInfo.comparator);
1251 for (ClassInfo cl : classes) {
1252 writeClassApi(apiWriter, cl, notStrippable);
1253 }
1254
1255 apiWriter.print("}\n\n");
1256 }
1257
1258 static void writeClassApi(PrintStream apiWriter, ClassInfo cl, HashSet<ClassInfo> notStrippable) {
1259 boolean first;
1260
1261 apiWriter.print(" ");
1262 apiWriter.print(cl.scope());
1263 if (cl.isStatic()) {
1264 apiWriter.print(" static");
1265 }
1266 if (cl.isFinal()) {
1267 apiWriter.print(" final");
1268 }
1269 if (cl.isAbstract()) {
1270 apiWriter.print(" abstract");
1271 }
1272 if (cl.isDeprecated()) {
1273 apiWriter.print(" deprecated");
1274 }
1275 apiWriter.print(" ");
1276 apiWriter.print(cl.isInterface() ? "interface" : "class");
1277 apiWriter.print(" ");
1278 apiWriter.print(cl.name());
1279
1280 if (!cl.isInterface()
1281 && !"java.lang.Object".equals(cl.qualifiedName())
1282 && cl.realSuperclass() != null
1283 && !"java.lang.Object".equals(cl.realSuperclass().qualifiedName())) {
1284 apiWriter.print(" extends ");
1285 apiWriter.print(cl.realSuperclass().qualifiedName());
1286 }
1287
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001288 ArrayList<ClassInfo> interfaces = cl.realInterfaces();
1289 Collections.sort(interfaces, ClassInfo.comparator);
Joe Onorato04099252011-03-09 13:34:18 -08001290 first = true;
1291 for (ClassInfo iface : interfaces) {
1292 if (notStrippable.contains(iface)) {
1293 if (first) {
1294 apiWriter.print(" implements");
1295 first = false;
1296 }
1297 apiWriter.print(" ");
1298 apiWriter.print(iface.qualifiedName());
1299 }
1300 }
1301
1302 apiWriter.print(" {\n");
1303
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001304 ArrayList<MethodInfo> constructors = cl.constructors();
1305 Collections.sort(constructors, MethodInfo.comparator);
Joe Onorato04099252011-03-09 13:34:18 -08001306 for (MethodInfo mi : constructors) {
1307 writeConstructorApi(apiWriter, mi);
1308 }
1309
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001310 ArrayList<MethodInfo> methods = cl.allSelfMethods();
1311 Collections.sort(methods, MethodInfo.comparator);
Joe Onorato04099252011-03-09 13:34:18 -08001312 for (MethodInfo mi : methods) {
1313 if (!methodIsOverride(notStrippable, mi)) {
1314 writeMethodApi(apiWriter, mi);
1315 }
1316 }
1317
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001318 ArrayList<FieldInfo> enums = cl.enumConstants();
1319 Collections.sort(enums, FieldInfo.comparator);
Joe Onorato132afe42011-05-31 18:13:38 -07001320 for (FieldInfo fi : enums) {
1321 writeFieldApi(apiWriter, fi, "enum_constant");
1322 }
1323
Ying Wangae8cb832014-04-03 21:16:48 -07001324 ArrayList<FieldInfo> fields = cl.selfFields();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001325 Collections.sort(fields, FieldInfo.comparator);
Joe Onorato04099252011-03-09 13:34:18 -08001326 for (FieldInfo fi : fields) {
Joe Onorato132afe42011-05-31 18:13:38 -07001327 writeFieldApi(apiWriter, fi, "field");
Joe Onorato04099252011-03-09 13:34:18 -08001328 }
1329
1330 apiWriter.print(" }\n\n");
1331 }
1332
1333 static void writeConstructorApi(PrintStream apiWriter, MethodInfo mi) {
1334 apiWriter.print(" ctor ");
1335 apiWriter.print(mi.scope());
1336 if (mi.isDeprecated()) {
1337 apiWriter.print(" deprecated");
1338 }
1339 apiWriter.print(" ");
1340 apiWriter.print(mi.name());
1341
1342 writeParametersApi(apiWriter, mi, mi.parameters());
1343 writeThrowsApi(apiWriter, mi.thrownExceptions());
1344 apiWriter.print(";\n");
1345 }
1346
1347 static void writeMethodApi(PrintStream apiWriter, MethodInfo mi) {
1348 apiWriter.print(" method ");
1349 apiWriter.print(mi.scope());
1350 if (mi.isStatic()) {
1351 apiWriter.print(" static");
1352 }
1353 if (mi.isFinal()) {
1354 apiWriter.print(" final");
1355 }
1356 if (mi.isAbstract()) {
1357 apiWriter.print(" abstract");
1358 }
1359 if (mi.isDeprecated()) {
1360 apiWriter.print(" deprecated");
1361 }
1362 if (mi.isSynchronized()) {
1363 apiWriter.print(" synchronized");
1364 }
1365 apiWriter.print(" ");
1366 if (mi.returnType() == null) {
1367 apiWriter.print("void");
1368 } else {
1369 apiWriter.print(fullParameterTypeName(mi, mi.returnType(), false));
1370 }
1371 apiWriter.print(" ");
1372 apiWriter.print(mi.name());
1373
1374 writeParametersApi(apiWriter, mi, mi.parameters());
1375 writeThrowsApi(apiWriter, mi.thrownExceptions());
1376
1377 apiWriter.print(";\n");
1378 }
1379
Hui Shu5118ffe2014-02-18 14:06:42 -08001380 static void writeParametersApi(PrintStream apiWriter, MethodInfo method,
1381 ArrayList<ParameterInfo> params) {
Joe Onorato04099252011-03-09 13:34:18 -08001382 apiWriter.print("(");
1383
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001384 for (ParameterInfo pi : params) {
1385 if (pi != params.get(0)) {
Joe Onorato04099252011-03-09 13:34:18 -08001386 apiWriter.print(", ");
1387 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001388 apiWriter.print(fullParameterTypeName(method, pi.type(), pi == params.get(params.size()-1)));
Joe Onorato04099252011-03-09 13:34:18 -08001389 // turn on to write the names too
1390 if (false) {
1391 apiWriter.print(" ");
1392 apiWriter.print(pi.name());
1393 }
1394 }
1395
1396 apiWriter.print(")");
1397 }
1398
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001399 static void writeThrowsApi(PrintStream apiWriter, ArrayList<ClassInfo> exceptions) {
Joe Onorato04099252011-03-09 13:34:18 -08001400 // write in a canonical order
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001401 exceptions = (ArrayList<ClassInfo>) exceptions.clone();
1402 Collections.sort(exceptions, ClassInfo.comparator);
1403 //final int N = exceptions.length;
Joe Onorato04099252011-03-09 13:34:18 -08001404 boolean first = true;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001405 for (ClassInfo ex : exceptions) {
Joe Onorato04099252011-03-09 13:34:18 -08001406 // Turn this off, b/c we need to regenrate the old xml files.
1407 if (true || !"java.lang.RuntimeException".equals(ex.qualifiedName())
1408 && !ex.isDerivedFrom("java.lang.RuntimeException")) {
1409 if (first) {
1410 apiWriter.print(" throws ");
1411 first = false;
1412 } else {
1413 apiWriter.print(", ");
1414 }
1415 apiWriter.print(ex.qualifiedName());
1416 }
1417 }
1418 }
1419
Joe Onorato132afe42011-05-31 18:13:38 -07001420 static void writeFieldApi(PrintStream apiWriter, FieldInfo fi, String label) {
1421 apiWriter.print(" ");
1422 apiWriter.print(label);
1423 apiWriter.print(" ");
Joe Onorato04099252011-03-09 13:34:18 -08001424 apiWriter.print(fi.scope());
1425 if (fi.isStatic()) {
1426 apiWriter.print(" static");
1427 }
1428 if (fi.isFinal()) {
1429 apiWriter.print(" final");
1430 }
1431 if (fi.isDeprecated()) {
1432 apiWriter.print(" deprecated");
1433 }
1434 if (fi.isTransient()) {
1435 apiWriter.print(" transient");
1436 }
1437 if (fi.isVolatile()) {
1438 apiWriter.print(" volatile");
1439 }
1440
1441 apiWriter.print(" ");
Ying Wang5db09cd2014-08-14 15:41:39 -07001442 apiWriter.print(fi.type().fullName());
Joe Onorato04099252011-03-09 13:34:18 -08001443
1444 apiWriter.print(" ");
1445 apiWriter.print(fi.name());
1446
1447 Object val = null;
1448 if (fi.isConstant() && fieldIsInitialized(fi)) {
1449 apiWriter.print(" = ");
1450 apiWriter.print(fi.constantLiteralValue());
1451 val = fi.constantValue();
1452 }
1453
1454 apiWriter.print(";");
1455
1456 if (val != null) {
1457 if (val instanceof Integer && "char".equals(fi.type().qualifiedTypeName())) {
1458 apiWriter.format(" // 0x%04x '%s'", val,
1459 FieldInfo.javaEscapeString("" + ((char)((Integer)val).intValue())));
1460 } else if (val instanceof Byte || val instanceof Short || val instanceof Integer) {
1461 apiWriter.format(" // 0x%x", val);
1462 } else if (val instanceof Long) {
1463 apiWriter.format(" // 0x%xL", val);
1464 }
1465 }
1466
1467 apiWriter.print("\n");
1468 }
1469
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001470 static void writeKeepList(PrintStream keepListWriter,
1471 HashMap<PackageInfo, List<ClassInfo>> allClasses, HashSet<ClassInfo> notStrippable) {
1472 // extract the set of packages, sort them by name, and write them out in that order
1473 Set<PackageInfo> allClassKeys = allClasses.keySet();
1474 PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]);
1475 Arrays.sort(allPackages, PackageInfo.comparator);
1476
1477 for (PackageInfo pack : allPackages) {
1478 writePackageKeepList(keepListWriter, pack, allClasses.get(pack), notStrippable);
1479 }
1480 }
1481
1482 static void writePackageKeepList(PrintStream keepListWriter, PackageInfo pack,
1483 Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) {
1484 // Work around the bogus "Array" class we invent for
1485 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
1486 if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) {
1487 return;
1488 }
1489
1490 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
1491 Arrays.sort(classes, ClassInfo.comparator);
1492 for (ClassInfo cl : classes) {
1493 writeClassKeepList(keepListWriter, cl, notStrippable);
1494 }
1495 }
1496
1497 static void writeClassKeepList(PrintStream keepListWriter, ClassInfo cl,
1498 HashSet<ClassInfo> notStrippable) {
1499 keepListWriter.print("-keep class ");
1500 keepListWriter.print(to$Class(cl.qualifiedName()));
1501
1502 keepListWriter.print(" {\n");
1503
1504 ArrayList<MethodInfo> constructors = cl.constructors();
1505 Collections.sort(constructors, MethodInfo.comparator);
1506 for (MethodInfo mi : constructors) {
1507 writeConstructorKeepList(keepListWriter, mi);
1508 }
1509
1510 keepListWriter.print("\n");
1511
1512 ArrayList<MethodInfo> methods = cl.allSelfMethods();
1513 Collections.sort(methods, MethodInfo.comparator);
1514 for (MethodInfo mi : methods) {
Adam Metcalf37abe682014-03-26 13:48:54 -07001515 // allSelfMethods is the non-hidden and visible methods. See Doclava.checkLevel.
1516 writeMethodKeepList(keepListWriter, mi);
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001517 }
1518
1519 keepListWriter.print("\n");
1520
1521 ArrayList<FieldInfo> enums = cl.enumConstants();
1522 Collections.sort(enums, FieldInfo.comparator);
1523 for (FieldInfo fi : enums) {
1524 writeFieldKeepList(keepListWriter, fi);
1525 }
1526
1527 keepListWriter.print("\n");
1528
Ying Wangae8cb832014-04-03 21:16:48 -07001529 ArrayList<FieldInfo> fields = cl.selfFields();
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001530 Collections.sort(fields, FieldInfo.comparator);
1531 for (FieldInfo fi : fields) {
1532 writeFieldKeepList(keepListWriter, fi);
1533 }
1534
1535 keepListWriter.print("}\n\n");
1536 }
1537
1538 static void writeConstructorKeepList(PrintStream keepListWriter, MethodInfo mi) {
1539 keepListWriter.print(" ");
1540 String name = mi.name();
1541 name = name.replace(".", "$");
1542 keepListWriter.print(name);
1543
1544 writeParametersKeepList(keepListWriter, mi, mi.parameters());
1545 keepListWriter.print(";\n");
1546 }
1547
1548 static void writeMethodKeepList(PrintStream keepListWriter, MethodInfo mi) {
1549 keepListWriter.print(" ");
1550 keepListWriter.print(mi.scope());
1551 if (mi.isStatic()) {
1552 keepListWriter.print(" static");
1553 }
1554 if (mi.isAbstract()) {
1555 keepListWriter.print(" abstract");
1556 }
1557 if (mi.isSynchronized()) {
1558 keepListWriter.print(" synchronized");
1559 }
1560 keepListWriter.print(" ");
1561 if (mi.returnType() == null) {
1562 keepListWriter.print("void");
1563 } else {
1564 keepListWriter.print(getCleanTypeName(mi.returnType()));
1565 }
1566 keepListWriter.print(" ");
1567 keepListWriter.print(mi.name());
1568
1569 writeParametersKeepList(keepListWriter, mi, mi.parameters());
1570
1571 keepListWriter.print(";\n");
1572 }
1573
1574 static void writeParametersKeepList(PrintStream keepListWriter, MethodInfo method,
1575 ArrayList<ParameterInfo> params) {
1576 keepListWriter.print("(");
1577
1578 for (ParameterInfo pi : params) {
1579 if (pi != params.get(0)) {
1580 keepListWriter.print(", ");
1581 }
Jeff Hamilton550a9de2012-07-23 23:37:06 -07001582 keepListWriter.print(getCleanTypeName(pi.type()));
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001583 }
1584
1585 keepListWriter.print(")");
1586 }
1587
1588 static void writeFieldKeepList(PrintStream keepListWriter, FieldInfo fi) {
1589 keepListWriter.print(" ");
1590 keepListWriter.print(fi.scope());
1591 if (fi.isStatic()) {
1592 keepListWriter.print(" static");
1593 }
1594 if (fi.isTransient()) {
1595 keepListWriter.print(" transient");
1596 }
1597 if (fi.isVolatile()) {
1598 keepListWriter.print(" volatile");
1599 }
1600
1601 keepListWriter.print(" ");
Sergei Datsenkoe64dc942014-09-08 17:34:59 +10001602 keepListWriter.print(getCleanTypeName(fi.type()));
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001603
1604 keepListWriter.print(" ");
1605 keepListWriter.print(fi.name());
1606
1607 keepListWriter.print(";\n");
1608 }
1609
Ben Dodson920dbbb2010-08-04 15:21:06 -07001610 static String fullParameterTypeName(MethodInfo method, TypeInfo type, boolean isLast) {
1611 String fullTypeName = type.fullName(method.typeVariables());
1612 if (isLast && method.isVarArgs()) {
1613 // TODO: note that this does not attempt to handle hypothetical
1614 // vararg methods whose last parameter is a list of arrays, e.g.
1615 // "Object[]...".
1616 fullTypeName = type.fullNameNoDimension(method.typeVariables()) + "...";
1617 }
1618 return fullTypeName;
1619 }
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001620
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001621 static String to$Class(String name) {
1622 int pos = 0;
1623 while ((pos = name.indexOf('.', pos)) > 0) {
1624 String n = name.substring(0, pos);
1625 if (Converter.obtainClass(n) != null) {
1626 return n + (name.substring(pos).replace('.', '$'));
1627 }
1628 pos = pos + 1;
1629 }
1630 return name;
1631 }
1632
1633 static String getCleanTypeName(TypeInfo t) {
Jeff Hamiltond6f2c2b2012-07-23 23:09:19 -07001634 return t.isPrimitive() ? t.simpleTypeName() + t.dimension() :
Jeff Hamilton1e0d3702012-06-21 04:21:54 -05001635 to$Class(t.asClassInfo().qualifiedName() + t.dimension());
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001636 }
Ben Dodson920dbbb2010-08-04 15:21:06 -07001637}