blob: 2b313215496ddbc94ce84cb536cee13787aace0e [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;
30import java.util.List;
31import java.util.Set;
Ben Dodson920dbbb2010-08-04 15:21:06 -070032
33public class Stubs {
Jeff Hamilton970f13f2012-06-22 00:21:56 -050034 public static void writeStubsAndApi(String stubsDir, String apiFile, String keepListFile,
Hui Shu5118ffe2014-02-18 14:06:42 -080035 String removedApiFile, HashSet<String> stubPackages) {
Ben Dodson920dbbb2010-08-04 15:21:06 -070036 // figure out which classes we need
Joe Onorato04099252011-03-09 13:34:18 -080037 final HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
Ben Dodson920dbbb2010-08-04 15:21:06 -070038 ClassInfo[] all = Converter.allClasses();
Joe Onorato04099252011-03-09 13:34:18 -080039 PrintStream apiWriter = null;
Jeff Hamilton970f13f2012-06-22 00:21:56 -050040 PrintStream keepListWriter = null;
Hui Shu5118ffe2014-02-18 14:06:42 -080041 PrintStream removedApiWriter = null;
42
Joe Onorato04099252011-03-09 13:34:18 -080043 if (apiFile != null) {
Ben Dodson920dbbb2010-08-04 15:21:06 -070044 try {
Joe Onorato04099252011-03-09 13:34:18 -080045 File xml = new File(apiFile);
Ben Dodson920dbbb2010-08-04 15:21:06 -070046 xml.getParentFile().mkdirs();
Brian Carlstromed8f7972011-06-10 11:10:48 -070047 apiWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(xml)));
Ben Dodson920dbbb2010-08-04 15:21:06 -070048 } catch (FileNotFoundException e) {
Joe Onorato04099252011-03-09 13:34:18 -080049 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(apiFile, 0, 0),
Ben Dodson920dbbb2010-08-04 15:21:06 -070050 "Cannot open file for write.");
51 }
52 }
Jeff Hamilton970f13f2012-06-22 00:21:56 -050053 if (keepListFile != null) {
54 try {
55 File keepList = new File(keepListFile);
56 keepList.getParentFile().mkdirs();
57 keepListWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(keepList)));
58 } catch (FileNotFoundException e) {
59 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(keepListFile, 0, 0),
60 "Cannot open file for write.");
61 }
62 }
Hui Shu5118ffe2014-02-18 14:06:42 -080063 if (removedApiFile != null) {
64 try {
65 File removedApi = new File(removedApiFile);
66 removedApi.getParentFile().mkdirs();
67 removedApiWriter = new PrintStream(
68 new BufferedOutputStream(new FileOutputStream(removedApi)));
69 } catch (FileNotFoundException e) {
70 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(removedApiFile, 0, 0),
71 "Cannot open file for write");
72 }
73 }
Ben Dodson920dbbb2010-08-04 15:21:06 -070074 // If a class is public or protected, not hidden, and marked as included,
75 // then we can't strip it
76 for (ClassInfo cl : all) {
77 if (cl.checkLevel() && cl.isIncluded()) {
78 cantStripThis(cl, notStrippable, "0:0");
79 }
80 }
81
82 // complain about anything that looks includeable but is not supposed to
83 // be written, e.g. hidden things
84 for (ClassInfo cl : notStrippable) {
Hui Shu5118ffe2014-02-18 14:06:42 -080085 if (!cl.isHiddenOrRemoved()) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070086 for (MethodInfo m : cl.selfMethods()) {
Hui Shu5118ffe2014-02-18 14:06:42 -080087 if (m.isHiddenOrRemoved()) {
88 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to unavailable method "
Ben Dodson920dbbb2010-08-04 15:21:06 -070089 + m.name());
90 } else if (m.isDeprecated()) {
91 // don't bother reporting deprecated methods
92 // unless they are public
93 Errors.error(Errors.DEPRECATED, m.position(), "Method " + cl.qualifiedName() + "."
94 + m.name() + " is deprecated");
95 }
96
97 ClassInfo returnClass = m.returnType().asClassInfo();
Hui Shu5118ffe2014-02-18 14:06:42 -080098 if (returnClass != null && returnClass.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -070099 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Method " + cl.qualifiedName()
100 + "." + m.name() + " returns unavailable type " + returnClass.name());
101 }
102
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700103 for (ParameterInfo p : m.parameters()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700104 TypeInfo t = p.type();
105 if (!t.isPrimitive()) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800106 if (t.asClassInfo().isHiddenOrRemoved()) {
107 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Parameter of unavailable type "
Ben Dodson920dbbb2010-08-04 15:21:06 -0700108 + t.fullName() + " in " + cl.qualifiedName() + "." + m.name() + "()");
109 }
110 }
111 }
112 }
113
114 // annotations are handled like methods
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700115 for (MethodInfo m : cl.annotationElements()) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800116 if (m.isHiddenOrRemoved()) {
117 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to unavailable annotation "
Ben Dodson920dbbb2010-08-04 15:21:06 -0700118 + m.name());
119 }
120
121 ClassInfo returnClass = m.returnType().asClassInfo();
Hui Shu5118ffe2014-02-18 14:06:42 -0800122 if (returnClass != null && returnClass.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700123 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Annotation '" + m.name()
124 + "' returns unavailable type " + returnClass.name());
125 }
126
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700127 for (ParameterInfo p : m.parameters()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700128 TypeInfo t = p.type();
129 if (!t.isPrimitive()) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800130 if (t.asClassInfo().isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700131 Errors.error(Errors.UNAVAILABLE_SYMBOL, p.position(),
132 "Reference to unavailable annotation class " + t.fullName());
133 }
134 }
135 }
136 }
137 } else if (cl.isDeprecated()) {
138 // not hidden, but deprecated
139 Errors.error(Errors.DEPRECATED, cl.position(), "Class " + cl.qualifiedName()
140 + " is deprecated");
141 }
142 }
143
Hui Shu5118ffe2014-02-18 14:06:42 -0800144 // packages contains all the notStrippable classes mapped by their containing packages
Ben Dodson920dbbb2010-08-04 15:21:06 -0700145 HashMap<PackageInfo, List<ClassInfo>> packages = new HashMap<PackageInfo, List<ClassInfo>>();
146 for (ClassInfo cl : notStrippable) {
147 if (!cl.isDocOnly()) {
148 if (stubPackages == null || stubPackages.contains(cl.containingPackage().name())) {
149 // write out the stubs
150 if (stubsDir != null) {
Joe Onorato04099252011-03-09 13:34:18 -0800151 writeClassFile(stubsDir, notStrippable, cl);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700152 }
Jeff Hamilton970f13f2012-06-22 00:21:56 -0500153 // build class list for api file or keep list file
154 if (apiWriter != null || keepListWriter != null) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700155 if (packages.containsKey(cl.containingPackage())) {
156 packages.get(cl.containingPackage()).add(cl);
157 } else {
158 ArrayList<ClassInfo> classes = new ArrayList<ClassInfo>();
159 classes.add(cl);
160 packages.put(cl.containingPackage(), classes);
161 }
162 }
163 }
164 }
165 }
Joe Onorato04099252011-03-09 13:34:18 -0800166 // write out the Api
167 if (apiWriter != null) {
168 writeApi(apiWriter, packages, notStrippable);
169 apiWriter.close();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700170 }
Jeff Hamilton970f13f2012-06-22 00:21:56 -0500171
172 // write out the keep list
173 if (keepListWriter != null) {
174 writeKeepList(keepListWriter, packages, notStrippable);
175 keepListWriter.close();
176 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800177
178 HashMap<PackageInfo, List<ClassInfo>> allPackageClassMap =
179 new HashMap<PackageInfo, List<ClassInfo>>();
180 for (ClassInfo cl : Converter.allClasses()) {
181 if (allPackageClassMap.containsKey(cl.containingPackage())) {
182 allPackageClassMap.get(cl.containingPackage()).add(cl);
183 } else {
184 ArrayList<ClassInfo> classes = new ArrayList<ClassInfo>();
185 classes.add(cl);
186 allPackageClassMap.put(cl.containingPackage(), classes);
187 }
188 }
189 // write out the removed Api
190 if (removedApiWriter != null) {
191 writeRemovedApi(removedApiWriter, allPackageClassMap, notStrippable);
192 removedApiWriter.close();
193 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700194 }
195
196 public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why) {
197
198 if (!notStrippable.add(cl)) {
199 // slight optimization: if it already contains cl, it already contains
200 // all of cl's parents
201 return;
202 }
203 cl.setReasonIncluded(why);
204
205 // cant strip annotations
206 /*
207 * if (cl.annotations() != null){ for (AnnotationInstanceInfo ai : cl.annotations()){ if
208 * (ai.type() != null){ cantStripThis(ai.type(), notStrippable, "1:" + cl.qualifiedName()); } }
209 * }
210 */
211 // cant strip any public fields or their generics
Ying Wangae8cb832014-04-03 21:16:48 -0700212 if (cl.selfFields() != null) {
213 for (FieldInfo fInfo : cl.selfFields()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700214 if (fInfo.type() != null) {
215 if (fInfo.type().asClassInfo() != null) {
216 cantStripThis(fInfo.type().asClassInfo(), notStrippable, "2:" + cl.qualifiedName());
217 }
218 if (fInfo.type().typeArguments() != null) {
219 for (TypeInfo tTypeInfo : fInfo.type().typeArguments()) {
220 if (tTypeInfo.asClassInfo() != null) {
221 cantStripThis(tTypeInfo.asClassInfo(), notStrippable, "3:" + cl.qualifiedName());
222 }
223 }
224 }
225 }
226 }
227 }
228 // cant strip any of the type's generics
229 if (cl.asTypeInfo() != null) {
230 if (cl.asTypeInfo().typeArguments() != null) {
231 for (TypeInfo tInfo : cl.asTypeInfo().typeArguments()) {
232 if (tInfo.asClassInfo() != null) {
233 cantStripThis(tInfo.asClassInfo(), notStrippable, "4:" + cl.qualifiedName());
234 }
235 }
236 }
237 }
238 // cant strip any of the annotation elements
239 // cantStripThis(cl.annotationElements(), notStrippable);
240 // take care of methods
241 cantStripThis(cl.allSelfMethods(), notStrippable);
242 cantStripThis(cl.allConstructors(), notStrippable);
243 // blow the outer class open if this is an inner class
244 if (cl.containingClass() != null) {
245 cantStripThis(cl.containingClass(), notStrippable, "5:" + cl.qualifiedName());
246 }
247 // blow open super class and interfaces
248 ClassInfo supr = cl.realSuperclass();
249 if (supr != null) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800250 if (supr.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700251 // cl is a public class declared as extending a hidden superclass.
252 // this is not a desired practice but it's happened, so we deal
Mathieu Chartier2da2d642012-09-21 15:42:59 -0700253 // with it by finding the first super class which passes checklevel for purposes of
Ben Dodson920dbbb2010-08-04 15:21:06 -0700254 // generating the doc & stub information, and proceeding normally.
255 cl.init(cl.asTypeInfo(), cl.realInterfaces(), cl.realInterfaceTypes(), cl.innerClasses(),
256 cl.allConstructors(), cl.allSelfMethods(), cl.annotationElements(), cl.allSelfFields(),
Mathieu Chartier2da2d642012-09-21 15:42:59 -0700257 cl.enumConstants(), cl.containingPackage(), cl.containingClass(),
Elliott Hughesbf322c12013-06-05 15:47:13 -0700258 supr.superclass(), supr.superclassType(), cl.annotations());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700259 Errors.error(Errors.HIDDEN_SUPERCLASS, cl.position(), "Public class " + cl.qualifiedName()
260 + " stripped of unavailable superclass " + supr.qualifiedName());
261 } else {
262 cantStripThis(supr, notStrippable, "6:" + cl.realSuperclass().name() + cl.qualifiedName());
263 }
264 }
265 }
266
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700267 private static void cantStripThis(ArrayList<MethodInfo> mInfos, HashSet<ClassInfo> notStrippable) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700268 // for each method, blow open the parameters, throws and return types. also blow open their
269 // generics
270 if (mInfos != null) {
271 for (MethodInfo mInfo : mInfos) {
272 if (mInfo.getTypeParameters() != null) {
273 for (TypeInfo tInfo : mInfo.getTypeParameters()) {
274 if (tInfo.asClassInfo() != null) {
275 cantStripThis(tInfo.asClassInfo(), notStrippable, "8:"
276 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
277 }
278 }
279 }
280 if (mInfo.parameters() != null) {
281 for (ParameterInfo pInfo : mInfo.parameters()) {
282 if (pInfo.type() != null && pInfo.type().asClassInfo() != null) {
283 cantStripThis(pInfo.type().asClassInfo(), notStrippable, "9:"
284 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
285 if (pInfo.type().typeArguments() != null) {
286 for (TypeInfo tInfoType : pInfo.type().typeArguments()) {
287 if (tInfoType.asClassInfo() != null) {
288 ClassInfo tcl = tInfoType.asClassInfo();
Hui Shu5118ffe2014-02-18 14:06:42 -0800289 if (tcl.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700290 Errors
291 .error(Errors.UNAVAILABLE_SYMBOL, mInfo.position(),
292 "Parameter of hidden type " + tInfoType.fullName() + " in "
293 + mInfo.containingClass().qualifiedName() + '.' + mInfo.name()
294 + "()");
295 } else {
296 cantStripThis(tcl, notStrippable, "10:"
297 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
298 }
299 }
300 }
301 }
302 }
303 }
304 }
305 for (ClassInfo thrown : mInfo.thrownExceptions()) {
306 cantStripThis(thrown, notStrippable, "11:" + mInfo.realContainingClass().qualifiedName()
307 + ":" + mInfo.name());
308 }
309 if (mInfo.returnType() != null && mInfo.returnType().asClassInfo() != null) {
310 cantStripThis(mInfo.returnType().asClassInfo(), notStrippable, "12:"
311 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
312 if (mInfo.returnType().typeArguments() != null) {
313 for (TypeInfo tyInfo : mInfo.returnType().typeArguments()) {
314 if (tyInfo.asClassInfo() != null) {
315 cantStripThis(tyInfo.asClassInfo(), notStrippable, "13:"
316 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name());
317 }
318 }
319 }
320 }
321 }
322 }
323 }
324
325 static String javaFileName(ClassInfo cl) {
326 String dir = "";
327 PackageInfo pkg = cl.containingPackage();
328 if (pkg != null) {
329 dir = pkg.name();
330 dir = dir.replace('.', '/') + '/';
331 }
332 return dir + cl.name() + ".java";
333 }
334
Joe Onorato04099252011-03-09 13:34:18 -0800335 static void writeClassFile(String stubsDir, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700336 // inner classes are written by their containing class
337 if (cl.containingClass() != null) {
338 return;
339 }
340
341 // Work around the bogus "Array" class we invent for
342 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
343 if (cl.containingPackage() != null
344 && cl.containingPackage().name().equals(PackageInfo.DEFAULT_PACKAGE)) {
345 return;
346 }
347
348 String filename = stubsDir + '/' + javaFileName(cl);
349 File file = new File(filename);
350 ClearPage.ensureDirectory(file);
351
352 PrintStream stream = null;
353 try {
Brian Carlstromed8f7972011-06-10 11:10:48 -0700354 stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(file)));
Joe Onorato04099252011-03-09 13:34:18 -0800355 writeClassFile(stream, notStrippable, cl);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700356 } catch (FileNotFoundException e) {
357 System.err.println("error writing file: " + filename);
358 } finally {
359 if (stream != null) {
360 stream.close();
361 }
362 }
363 }
364
Joe Onorato04099252011-03-09 13:34:18 -0800365 static void writeClassFile(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700366 PackageInfo pkg = cl.containingPackage();
367 if (pkg != null) {
368 stream.println("package " + pkg.name() + ";");
369 }
Joe Onorato04099252011-03-09 13:34:18 -0800370 writeClass(stream, notStrippable, cl);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700371 }
372
Joe Onorato04099252011-03-09 13:34:18 -0800373 static void writeClass(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) {
Tor Norbye73934882012-02-29 07:50:52 -0800374 writeAnnotations(stream, cl.annotations(), cl.isDeprecated());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700375
376 stream.print(cl.scope() + " ");
377 if (cl.isAbstract() && !cl.isAnnotation() && !cl.isInterface()) {
378 stream.print("abstract ");
379 }
380 if (cl.isStatic()) {
381 stream.print("static ");
382 }
383 if (cl.isFinal() && !cl.isEnum()) {
384 stream.print("final ");
385 }
386 if (false) {
387 stream.print("strictfp ");
388 }
389
390 HashSet<String> classDeclTypeVars = new HashSet();
391 String leafName = cl.asTypeInfo().fullName(classDeclTypeVars);
392 int bracket = leafName.indexOf('<');
393 if (bracket < 0) bracket = leafName.length() - 1;
394 int period = leafName.lastIndexOf('.', bracket);
395 if (period < 0) period = -1;
396 leafName = leafName.substring(period + 1);
397
398 String kind = cl.kind();
399 stream.println(kind + " " + leafName);
400
401 TypeInfo base = cl.superclassType();
402
403 if (!"enum".equals(kind)) {
404 if (base != null && !"java.lang.Object".equals(base.qualifiedTypeName())) {
405 stream.println(" extends " + base.fullName(classDeclTypeVars));
406 }
407 }
408
Ben Dodson920dbbb2010-08-04 15:21:06 -0700409 List<TypeInfo> usedInterfaces = new ArrayList<TypeInfo>();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700410 for (TypeInfo iface : cl.realInterfaceTypes()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700411 if (notStrippable.contains(iface.asClassInfo()) && !iface.asClassInfo().isDocOnly()) {
412 usedInterfaces.add(iface);
413 }
414 }
415 if (usedInterfaces.size() > 0 && !cl.isAnnotation()) {
416 // can java annotations extend other ones?
417 if (cl.isInterface() || cl.isAnnotation()) {
418 stream.print(" extends ");
419 } else {
420 stream.print(" implements ");
421 }
422 String comma = "";
423 for (TypeInfo iface : usedInterfaces) {
424 stream.print(comma + iface.fullName(classDeclTypeVars));
425 comma = ", ";
426 }
427 stream.println();
428 }
429
430 stream.println("{");
431
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700432 ArrayList<FieldInfo> enumConstants = cl.enumConstants();
433 int N = enumConstants.size();
434 int i = 0;
435 for (FieldInfo field : enumConstants) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700436 if (!field.constantLiteralValue().equals("null")) {
437 stream.println(field.name() + "(" + field.constantLiteralValue()
438 + (i == N - 1 ? ");" : "),"));
439 } else {
440 stream.println(field.name() + "(" + (i == N - 1 ? ");" : "),"));
441 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700442 i++;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700443 }
444
445 for (ClassInfo inner : cl.getRealInnerClasses()) {
446 if (notStrippable.contains(inner) && !inner.isDocOnly()) {
Joe Onorato04099252011-03-09 13:34:18 -0800447 writeClass(stream, notStrippable, inner);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700448 }
449 }
450
451
452 for (MethodInfo method : cl.constructors()) {
453 if (!method.isDocOnly()) {
454 writeMethod(stream, method, true);
455 }
456 }
457
458 boolean fieldNeedsInitialization = false;
459 boolean staticFieldNeedsInitialization = false;
Ying Wangae8cb832014-04-03 21:16:48 -0700460 for (FieldInfo field : cl.selfFields()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700461 if (!field.isDocOnly()) {
462 if (!field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
463 fieldNeedsInitialization = true;
464 }
465 if (field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
466 staticFieldNeedsInitialization = true;
467 }
468 }
469 }
470
471 // The compiler includes a default public constructor that calls the super classes
472 // default constructor in the case where there are no written constructors.
473 // So, if we hide all the constructors, java may put in a constructor
474 // that calls a nonexistent super class constructor. So, if there are no constructors,
475 // and the super class doesn't have a default constructor, write in a private constructor
476 // that works. TODO -- we generate this as protected, but we really should generate
477 // it as private unless it also exists in the real code.
Hui Shu5118ffe2014-02-18 14:06:42 -0800478 if ((cl.constructors().isEmpty() && (!cl.getNonWrittenConstructors().isEmpty() ||
479 fieldNeedsInitialization)) && !cl.isAnnotation() && !cl.isInterface() && !cl.isEnum()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700480 // Errors.error(Errors.HIDDEN_CONSTRUCTOR,
481 // cl.position(), "No constructors " +
482 // "found and superclass has no parameterless constructor. A constructor " +
483 // "that calls an appropriate superclass constructor " +
484 // "was automatically written to stubs.\n");
485 stream.println(cl.leafName() + "() { " + superCtorCall(cl, null) + "throw new"
486 + " RuntimeException(\"Stub!\"); }");
487 }
488
489 for (MethodInfo method : cl.allSelfMethods()) {
490 if (cl.isEnum()) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800491 if (("values".equals(method.name()) && "()".equals(method.signature())) ||
492 ("valueOf".equals(method.name()) &&
493 "(java.lang.String)".equals(method.signature()))) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700494 // skip these two methods on enums, because they're synthetic,
495 // although for some reason javadoc doesn't mark them as synthetic,
496 // maybe because they still want them documented
497 continue;
498 }
499 }
500 if (!method.isDocOnly()) {
501 writeMethod(stream, method, false);
502 }
503 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800504 // Write all methods that are hidden or removed, but override abstract methods or interface methods.
Ben Dodson920dbbb2010-08-04 15:21:06 -0700505 // These can't be hidden.
Hui Shu5118ffe2014-02-18 14:06:42 -0800506 List<MethodInfo> hiddenAndRemovedMethods = cl.getHiddenMethods();
507 hiddenAndRemovedMethods.addAll(cl.getRemovedMethods());
508 for (MethodInfo method : hiddenAndRemovedMethods) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700509 MethodInfo overriddenMethod =
510 method.findRealOverriddenMethod(method.name(), method.signature(), notStrippable);
511 ClassInfo classContainingMethod =
512 method.findRealOverriddenClass(method.name(), method.signature());
Hui Shu5118ffe2014-02-18 14:06:42 -0800513 if (overriddenMethod != null && !overriddenMethod.isHiddenOrRemoved() &&
514 !overriddenMethod.isDocOnly() &&
515 (overriddenMethod.isAbstract() || overriddenMethod.containingClass().isInterface())) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700516 method.setReason("1:" + classContainingMethod.qualifiedName());
517 cl.addMethod(method);
518 writeMethod(stream, method, false);
519 }
520 }
521
522 for (MethodInfo element : cl.annotationElements()) {
523 if (!element.isDocOnly()) {
524 writeAnnotationElement(stream, element);
525 }
526 }
527
Ying Wangae8cb832014-04-03 21:16:48 -0700528 for (FieldInfo field : cl.selfFields()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700529 if (!field.isDocOnly()) {
530 writeField(stream, field);
531 }
532 }
533
534 if (staticFieldNeedsInitialization) {
535 stream.print("static { ");
Ying Wangae8cb832014-04-03 21:16:48 -0700536 for (FieldInfo field : cl.selfFields()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700537 if (!field.isDocOnly() && field.isStatic() && field.isFinal() && !fieldIsInitialized(field)
538 && field.constantValue() == null) {
539 stream.print(field.name() + " = " + field.type().defaultValue() + "; ");
540 }
541 }
542 stream.println("}");
543 }
544
545 stream.println("}");
546 }
547
548
549 static void writeMethod(PrintStream stream, MethodInfo method, boolean isConstructor) {
550 String comma;
551
Tor Norbye73934882012-02-29 07:50:52 -0800552 writeAnnotations(stream, method.annotations(), method.isDeprecated());
553
Ben Dodson920dbbb2010-08-04 15:21:06 -0700554 stream.print(method.scope() + " ");
555 if (method.isStatic()) {
556 stream.print("static ");
557 }
558 if (method.isFinal()) {
559 stream.print("final ");
560 }
561 if (method.isAbstract()) {
562 stream.print("abstract ");
563 }
564 if (method.isSynchronized()) {
565 stream.print("synchronized ");
566 }
567 if (method.isNative()) {
568 stream.print("native ");
569 }
570 if (false /* method.isStictFP() */) {
571 stream.print("strictfp ");
572 }
573
574 stream.print(method.typeArgumentsName(new HashSet()) + " ");
575
576 if (!isConstructor) {
577 stream.print(method.returnType().fullName(method.typeVariables()) + " ");
578 }
579 String n = method.name();
580 int pos = n.lastIndexOf('.');
581 if (pos >= 0) {
582 n = n.substring(pos + 1);
583 }
584 stream.print(n + "(");
585 comma = "";
586 int count = 1;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700587 int size = method.parameters().size();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700588 for (ParameterInfo param : method.parameters()) {
589 stream.print(comma + fullParameterTypeName(method, param.type(), count == size) + " "
590 + param.name());
591 comma = ", ";
592 count++;
593 }
594 stream.print(")");
595
596 comma = "";
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700597 if (method.thrownExceptions().size() > 0) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700598 stream.print(" throws ");
599 for (ClassInfo thrown : method.thrownExceptions()) {
600 stream.print(comma + thrown.qualifiedName());
601 comma = ", ";
602 }
603 }
604 if (method.isAbstract() || method.isNative() || method.containingClass().isInterface()) {
605 stream.println(";");
606 } else {
607 stream.print(" { ");
608 if (isConstructor) {
609 stream.print(superCtorCall(method.containingClass(), method.thrownExceptions()));
610 }
611 stream.println("throw new RuntimeException(\"Stub!\"); }");
612 }
613 }
614
615 static void writeField(PrintStream stream, FieldInfo field) {
Tor Norbye73934882012-02-29 07:50:52 -0800616 writeAnnotations(stream, field.annotations(), field.isDeprecated());
617
Ben Dodson920dbbb2010-08-04 15:21:06 -0700618 stream.print(field.scope() + " ");
619 if (field.isStatic()) {
620 stream.print("static ");
621 }
622 if (field.isFinal()) {
623 stream.print("final ");
624 }
625 if (field.isTransient()) {
626 stream.print("transient ");
627 }
628 if (field.isVolatile()) {
629 stream.print("volatile ");
630 }
631
632 stream.print(field.type().fullName());
633 stream.print(" ");
634 stream.print(field.name());
635
636 if (fieldIsInitialized(field)) {
637 stream.print(" = " + field.constantLiteralValue());
638 }
639
640 stream.println(";");
641 }
642
643 static boolean fieldIsInitialized(FieldInfo field) {
644 return (field.isFinal() && field.constantValue() != null)
645 || !field.type().dimension().equals("") || field.containingClass().isInterface();
646 }
647
648 // Returns 'true' if the method is an @Override of a visible parent
649 // method implementation, and thus does not affect the API.
Joe Onorato04099252011-03-09 13:34:18 -0800650 static boolean methodIsOverride(HashSet<ClassInfo> notStrippable, MethodInfo mi) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700651 // Abstract/static/final methods are always listed in the API description
652 if (mi.isAbstract() || mi.isStatic() || mi.isFinal()) {
653 return false;
654 }
655
656 // Find any relevant ancestor declaration and inspect it
657 MethodInfo om = mi.findSuperclassImplementation(notStrippable);
658 if (om != null) {
659 // Visibility mismatch is an API change, so check for it
660 if (mi.mIsPrivate == om.mIsPrivate && mi.mIsPublic == om.mIsPublic
661 && mi.mIsProtected == om.mIsProtected) {
662 // Look only for overrides of an ancestor class implementation,
663 // not of e.g. an abstract or interface method declaration
664 if (!om.isAbstract()) {
Hui Shu5118ffe2014-02-18 14:06:42 -0800665 // If the parent is hidden or removed, we can't rely on it to provide
Ben Dodson920dbbb2010-08-04 15:21:06 -0700666 // the API
Hui Shu5118ffe2014-02-18 14:06:42 -0800667 if (!om.isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700668 // If the only "override" turns out to be in our own class
669 // (which sometimes happens in concrete subclasses of
670 // abstract base classes), it's not really an override
671 if (!mi.mContainingClass.equals(om.mContainingClass)) {
672 return true;
673 }
674 }
675 }
676 }
677 }
678 return false;
679 }
680
681 static boolean canCallMethod(ClassInfo from, MethodInfo m) {
682 if (m.isPublic() || m.isProtected()) {
683 return true;
684 }
685 if (m.isPackagePrivate()) {
686 String fromPkg = from.containingPackage().name();
687 String pkg = m.containingClass().containingPackage().name();
688 if (fromPkg.equals(pkg)) {
689 return true;
690 }
691 }
692 return false;
693 }
694
695 // call a constructor, any constructor on this class's superclass.
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700696 static String superCtorCall(ClassInfo cl, ArrayList<ClassInfo> thrownExceptions) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700697 ClassInfo base = cl.realSuperclass();
698 if (base == null) {
699 return "";
700 }
701 HashSet<String> exceptionNames = new HashSet<String>();
702 if (thrownExceptions != null) {
703 for (ClassInfo thrown : thrownExceptions) {
704 exceptionNames.add(thrown.name());
705 }
706 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700707 ArrayList<MethodInfo> ctors = base.constructors();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700708 MethodInfo ctor = null;
709 // bad exception indicates that the exceptions thrown by the super constructor
710 // are incompatible with the constructor we're using for the sub class.
711 Boolean badException = false;
712 for (MethodInfo m : ctors) {
713 if (canCallMethod(cl, m)) {
714 if (m.thrownExceptions() != null) {
715 for (ClassInfo thrown : m.thrownExceptions()) {
716 if (!exceptionNames.contains(thrown.name())) {
717 badException = true;
718 }
719 }
720 }
721 if (badException) {
722 badException = false;
723 continue;
724 }
725 // if it has no args, we're done
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700726 if (m.parameters().isEmpty()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700727 return "";
728 }
729 ctor = m;
730 }
731 }
732 if (ctor != null) {
733 String result = "";
734 result += "super(";
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700735 ArrayList<ParameterInfo> params = ctor.parameters();
736 for (ParameterInfo param : params) {
737 TypeInfo t = param.type();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700738 if (t.isPrimitive() && t.dimension().equals("")) {
739 String n = t.simpleTypeName();
740 if (("byte".equals(n) || "short".equals(n) || "int".equals(n) || "long".equals(n)
741 || "float".equals(n) || "double".equals(n))
742 && t.dimension().equals("")) {
743 result += "0";
744 } else if ("char".equals(n)) {
745 result += "'\\0'";
746 } else if ("boolean".equals(n)) {
747 result += "false";
748 } else {
749 result += "<<unknown-" + n + ">>";
750 }
751 } else {
752 // put null in each super class method. Cast null to the correct type
753 // to avoid collisions with other constructors. If the type is generic
754 // don't cast it
755 result +=
756 (!t.isTypeVariable() ? "(" + t.qualifiedTypeName() + t.dimension() + ")" : "")
757 + "null";
758 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700759 if (param != params.get(params.size()-1)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700760 result += ",";
761 }
762 }
763 result += "); ";
764 return result;
765 } else {
766 return "";
767 }
768 }
769
Tor Norbye73934882012-02-29 07:50:52 -0800770 /**
771 * Write out the given list of annotations. If the {@code isDeprecated}
772 * flag is true also write out a {@code @Deprecated} annotation if it did not
773 * already appear in the list of annotations. (This covers APIs that mention
774 * {@code @deprecated} in their documentation but fail to add
775 * {@code @Deprecated} as an annotation.
776 * <p>
777 * {@code @Override} annotations are deliberately skipped.
778 */
779 static void writeAnnotations(PrintStream stream, List<AnnotationInstanceInfo> annotations,
780 boolean isDeprecated) {
781 assert annotations != null;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700782 for (AnnotationInstanceInfo ann : annotations) {
Tor Norbye73934882012-02-29 07:50:52 -0800783 // Skip @Override annotations: the stubs do not need it and in some cases it leads
784 // to compilation errors with the way the stubs are generated
785 if (ann.type() != null && ann.type().qualifiedName().equals("java.lang.Override")) {
786 continue;
787 }
Hui Shu5118ffe2014-02-18 14:06:42 -0800788 if (!ann.type().isHiddenOrRemoved()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700789 stream.println(ann.toString());
Tor Norbye73934882012-02-29 07:50:52 -0800790 if (isDeprecated && ann.type() != null
791 && ann.type().qualifiedName().equals("java.lang.Deprecated")) {
792 isDeprecated = false; // Prevent duplicate annotations
793 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700794 }
795 }
Tor Norbye73934882012-02-29 07:50:52 -0800796 if (isDeprecated) {
797 stream.println("@Deprecated");
798 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700799 }
800
801 static void writeAnnotationElement(PrintStream stream, MethodInfo ann) {
802 stream.print(ann.returnType().fullName());
803 stream.print(" ");
804 stream.print(ann.name());
805 stream.print("()");
806 AnnotationValueInfo def = ann.defaultAnnotationElementValue();
807 if (def != null) {
808 stream.print(" default ");
809 stream.print(def.valueString());
810 }
811 stream.println(";");
812 }
813
814 static void writeXML(PrintStream xmlWriter, HashMap<PackageInfo, List<ClassInfo>> allClasses,
Joe Onorato04099252011-03-09 13:34:18 -0800815 HashSet<ClassInfo> notStrippable) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700816 // extract the set of packages, sort them by name, and write them out in that order
817 Set<PackageInfo> allClassKeys = allClasses.keySet();
818 PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]);
819 Arrays.sort(allPackages, PackageInfo.comparator);
820
821 xmlWriter.println("<api>");
822 for (PackageInfo pack : allPackages) {
823 writePackageXML(xmlWriter, pack, allClasses.get(pack), notStrippable);
824 }
825 xmlWriter.println("</api>");
826 }
827
Joe Onorato04099252011-03-09 13:34:18 -0800828 public static void writeXml(PrintStream xmlWriter, Collection<PackageInfo> pkgs) {
829 final PackageInfo[] packages = pkgs.toArray(new PackageInfo[pkgs.size()]);
830 Arrays.sort(packages, PackageInfo.comparator);
831
832 HashSet<ClassInfo> notStrippable = new HashSet();
833 for (PackageInfo pkg: packages) {
834 for (ClassInfo cl: pkg.allClasses().values()) {
835 notStrippable.add(cl);
836 }
837 }
838 xmlWriter.println("<api>");
839 for (PackageInfo pkg: packages) {
840 writePackageXML(xmlWriter, pkg, pkg.allClasses().values(), notStrippable);
841 }
842 xmlWriter.println("</api>");
843 }
844
845 static void writePackageXML(PrintStream xmlWriter, PackageInfo pack,
846 Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700847 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
848 Arrays.sort(classes, ClassInfo.comparator);
849 // Work around the bogus "Array" class we invent for
850 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
851 if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) {
852 return;
853 }
854 xmlWriter.println("<package name=\"" + pack.name() + "\"\n"
855 // + " source=\"" + pack.position() + "\"\n"
856 + ">");
857 for (ClassInfo cl : classes) {
858 writeClassXML(xmlWriter, cl, notStrippable);
859 }
860 xmlWriter.println("</package>");
861
862
863 }
864
Joe Onorato04099252011-03-09 13:34:18 -0800865 static void writeClassXML(PrintStream xmlWriter, ClassInfo cl, HashSet<ClassInfo> notStrippable) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700866 String scope = cl.scope();
867 String deprecatedString = "";
868 String declString = (cl.isInterface()) ? "interface" : "class";
869 if (cl.isDeprecated()) {
870 deprecatedString = "deprecated";
871 } else {
872 deprecatedString = "not deprecated";
873 }
874 xmlWriter.println("<" + declString + " name=\"" + cl.name() + "\"");
875 if (!cl.isInterface() && !cl.qualifiedName().equals("java.lang.Object")) {
876 xmlWriter.println(" extends=\""
877 + ((cl.realSuperclass() == null) ? "java.lang.Object" : cl.realSuperclass()
878 .qualifiedName()) + "\"");
879 }
880 xmlWriter.println(" abstract=\"" + cl.isAbstract() + "\"\n" + " static=\"" + cl.isStatic()
881 + "\"\n" + " final=\"" + cl.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString
882 + "\"\n" + " visibility=\"" + scope + "\"\n"
883 // + " source=\"" + cl.position() + "\"\n"
884 + ">");
885
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700886 ArrayList<ClassInfo> interfaces = cl.realInterfaces();
887 Collections.sort(interfaces, ClassInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700888 for (ClassInfo iface : interfaces) {
889 if (notStrippable.contains(iface)) {
890 xmlWriter.println("<implements name=\"" + iface.qualifiedName() + "\">");
891 xmlWriter.println("</implements>");
892 }
893 }
894
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700895 ArrayList<MethodInfo> constructors = cl.constructors();
896 Collections.sort(constructors, MethodInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700897 for (MethodInfo mi : constructors) {
898 writeConstructorXML(xmlWriter, mi);
899 }
900
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700901 ArrayList<MethodInfo> methods = cl.allSelfMethods();
902 Collections.sort(methods, MethodInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700903 for (MethodInfo mi : methods) {
Joe Onorato04099252011-03-09 13:34:18 -0800904 if (!methodIsOverride(notStrippable, mi)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700905 writeMethodXML(xmlWriter, mi);
906 }
907 }
908
Ying Wangae8cb832014-04-03 21:16:48 -0700909 ArrayList<FieldInfo> fields = cl.selfFields();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700910 Collections.sort(fields, FieldInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700911 for (FieldInfo fi : fields) {
912 writeFieldXML(xmlWriter, fi);
913 }
914 xmlWriter.println("</" + declString + ">");
915
916 }
917
918 static void writeMethodXML(PrintStream xmlWriter, MethodInfo mi) {
919 String scope = mi.scope();
920
921 String deprecatedString = "";
922 if (mi.isDeprecated()) {
923 deprecatedString = "deprecated";
924 } else {
925 deprecatedString = "not deprecated";
926 }
927 xmlWriter.println("<method name=\""
928 + mi.name()
929 + "\"\n"
930 + ((mi.returnType() != null) ? " return=\""
931 + makeXMLcompliant(fullParameterTypeName(mi, mi.returnType(), false)) + "\"\n" : "")
932 + " abstract=\"" + mi.isAbstract() + "\"\n" + " native=\"" + mi.isNative() + "\"\n"
933 + " synchronized=\"" + mi.isSynchronized() + "\"\n" + " static=\"" + mi.isStatic() + "\"\n"
934 + " final=\"" + mi.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString + "\"\n"
935 + " visibility=\"" + scope + "\"\n"
936 // + " source=\"" + mi.position() + "\"\n"
937 + ">");
938
939 // write parameters in declaration order
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700940 int numParameters = mi.parameters().size();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700941 int count = 0;
942 for (ParameterInfo pi : mi.parameters()) {
943 count++;
944 writeParameterXML(xmlWriter, mi, pi, count == numParameters);
945 }
946
947 // but write exceptions in canonicalized order
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700948 ArrayList<ClassInfo> exceptions = mi.thrownExceptions();
949 Collections.sort(exceptions, ClassInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700950 for (ClassInfo pi : exceptions) {
951 xmlWriter.println("<exception name=\"" + pi.name() + "\" type=\"" + pi.qualifiedName()
952 + "\">");
953 xmlWriter.println("</exception>");
954 }
955 xmlWriter.println("</method>");
956 }
957
958 static void writeConstructorXML(PrintStream xmlWriter, MethodInfo mi) {
959 String scope = mi.scope();
960 String deprecatedString = "";
961 if (mi.isDeprecated()) {
962 deprecatedString = "deprecated";
963 } else {
964 deprecatedString = "not deprecated";
965 }
966 xmlWriter.println("<constructor name=\"" + mi.name() + "\"\n" + " type=\""
967 + mi.containingClass().qualifiedName() + "\"\n" + " static=\"" + mi.isStatic() + "\"\n"
968 + " final=\"" + mi.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString + "\"\n"
969 + " visibility=\"" + scope + "\"\n"
970 // + " source=\"" + mi.position() + "\"\n"
971 + ">");
972
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700973 int numParameters = mi.parameters().size();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700974 int count = 0;
975 for (ParameterInfo pi : mi.parameters()) {
976 count++;
977 writeParameterXML(xmlWriter, mi, pi, count == numParameters);
978 }
979
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700980 ArrayList<ClassInfo> exceptions = mi.thrownExceptions();
981 Collections.sort(exceptions, ClassInfo.comparator);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700982 for (ClassInfo pi : exceptions) {
983 xmlWriter.println("<exception name=\"" + pi.name() + "\" type=\"" + pi.qualifiedName()
984 + "\">");
985 xmlWriter.println("</exception>");
986 }
987 xmlWriter.println("</constructor>");
988 }
989
990 static void writeParameterXML(PrintStream xmlWriter, MethodInfo method, ParameterInfo pi,
991 boolean isLast) {
992 xmlWriter.println("<parameter name=\"" + pi.name() + "\" type=\""
993 + makeXMLcompliant(fullParameterTypeName(method, pi.type(), isLast)) + "\">");
994 xmlWriter.println("</parameter>");
995 }
996
997 static void writeFieldXML(PrintStream xmlWriter, FieldInfo fi) {
998 String scope = fi.scope();
999 String deprecatedString = "";
1000 if (fi.isDeprecated()) {
1001 deprecatedString = "deprecated";
1002 } else {
1003 deprecatedString = "not deprecated";
1004 }
1005 // need to make sure value is valid XML
1006 String value = makeXMLcompliant(fi.constantLiteralValue());
1007
Ying Wang77518172014-08-14 15:41:39 -07001008 String fullTypeName = makeXMLcompliant(fi.type().fullName());
Ben Dodson920dbbb2010-08-04 15:21:06 -07001009
1010 xmlWriter.println("<field name=\"" + fi.name() + "\"\n" + " type=\"" + fullTypeName + "\"\n"
1011 + " transient=\"" + fi.isTransient() + "\"\n" + " volatile=\"" + fi.isVolatile() + "\"\n"
1012 + (fieldIsInitialized(fi) ? " value=\"" + value + "\"\n" : "") + " static=\""
1013 + fi.isStatic() + "\"\n" + " final=\"" + fi.isFinal() + "\"\n" + " deprecated=\""
1014 + deprecatedString + "\"\n" + " visibility=\"" + scope + "\"\n"
1015 // + " source=\"" + fi.position() + "\"\n"
1016 + ">");
1017 xmlWriter.println("</field>");
1018 }
1019
1020 static String makeXMLcompliant(String s) {
1021 String returnString = "";
1022 returnString = s.replaceAll("&", "&amp;");
1023 returnString = returnString.replaceAll("<", "&lt;");
1024 returnString = returnString.replaceAll(">", "&gt;");
1025 returnString = returnString.replaceAll("\"", "&quot;");
1026 returnString = returnString.replaceAll("'", "&pos;");
1027 return returnString;
1028 }
1029
Hui Shu5118ffe2014-02-18 14:06:42 -08001030 static void writeRemovedApi(PrintStream apiWriter, HashMap<PackageInfo,
1031 List<ClassInfo>> allPackageClassMap, Set<ClassInfo> notStrippable) {
1032 final PackageInfo[] packages = allPackageClassMap.keySet().toArray(new PackageInfo[0]);
1033 Arrays.sort(packages, PackageInfo.comparator);
1034 for (PackageInfo pkg : packages) {
1035 // beware that pkg.allClasses() has no class in it at the moment
1036 final List<ClassInfo> classes = allPackageClassMap.get(pkg);
1037 Collections.sort(classes, ClassInfo.comparator);
1038 boolean hasWrittenPackageHead = false;
1039 for (ClassInfo cl : classes) {
1040 if (cl.hasRemovedSelfMembers()) {
1041 if (!hasWrittenPackageHead) {
1042 hasWrittenPackageHead = true;
1043 apiWriter.print("package ");
1044 apiWriter.print(pkg.qualifiedName());
1045 apiWriter.print(" {\n\n");
1046 }
1047 writeClassRemovedSelfMembers(apiWriter, cl, notStrippable);
1048 }
1049 }
1050
1051 // the package contains some classes with some removed members
1052 if (hasWrittenPackageHead) {
1053 apiWriter.print("}\n\n");
1054 }
1055 }
1056 }
1057
1058 /**
1059 * Write the removed members of the class to removed.txt
1060 */
1061 private static void writeClassRemovedSelfMembers(PrintStream apiWriter, ClassInfo cl,
1062 Set<ClassInfo> notStrippable) {
1063 apiWriter.print(" ");
1064 apiWriter.print(cl.scope());
1065 if (cl.isStatic()) {
1066 apiWriter.print(" static");
1067 }
1068 if (cl.isFinal()) {
1069 apiWriter.print(" final");
1070 }
1071 if (cl.isAbstract()) {
1072 apiWriter.print(" abstract");
1073 }
1074 if (cl.isDeprecated()) {
1075 apiWriter.print(" deprecated");
1076 }
1077 apiWriter.print(" ");
1078 apiWriter.print(cl.isInterface() ? "interface" : "class");
1079 apiWriter.print(" ");
1080 apiWriter.print(cl.name());
1081
1082 if (!cl.isInterface()
1083 && !"java.lang.Object".equals(cl.qualifiedName())
1084 && cl.realSuperclass() != null
1085 && !"java.lang.Object".equals(cl.realSuperclass().qualifiedName())) {
1086 apiWriter.print(" extends ");
1087 apiWriter.print(cl.realSuperclass().qualifiedName());
1088 }
1089
1090 ArrayList<ClassInfo> interfaces = cl.realInterfaces();
1091 Collections.sort(interfaces, ClassInfo.comparator);
1092 boolean first = true;
1093 for (ClassInfo iface : interfaces) {
1094 if (notStrippable.contains(iface)) {
1095 if (first) {
1096 apiWriter.print(" implements");
1097 first = false;
1098 }
1099 apiWriter.print(" ");
1100 apiWriter.print(iface.qualifiedName());
1101 }
1102 }
1103
1104 apiWriter.print(" {\n");
1105
1106 List<MethodInfo> constructors = cl.getRemovedConstructors();
1107 for (MethodInfo mi : constructors) {
1108 writeConstructorApi(apiWriter, mi);
1109 }
1110
1111 List<MethodInfo> methods = cl.getRemovedSelfMethods();
1112 for (MethodInfo mi : methods) {
1113 writeMethodApi(apiWriter, mi);
1114 }
1115
1116 List<FieldInfo> enums = cl.getRemovedSelfEnumConstants();
1117 for (FieldInfo fi : enums) {
1118 writeFieldApi(apiWriter, fi, "enum_constant");
1119 }
1120
1121 List<FieldInfo> fields = cl.getRemovedSelfFields();
1122 for (FieldInfo fi : fields) {
1123 writeFieldApi(apiWriter, fi, "field");
1124 }
1125
1126 apiWriter.print(" }\n\n");
1127 }
1128
Joe Onorato04099252011-03-09 13:34:18 -08001129 public static void writeApi(PrintStream apiWriter, Collection<PackageInfo> pkgs) {
1130 final PackageInfo[] packages = pkgs.toArray(new PackageInfo[pkgs.size()]);
1131 Arrays.sort(packages, PackageInfo.comparator);
1132
1133 HashSet<ClassInfo> notStrippable = new HashSet();
1134 for (PackageInfo pkg: packages) {
1135 for (ClassInfo cl: pkg.allClasses().values()) {
1136 notStrippable.add(cl);
1137 }
1138 }
1139 for (PackageInfo pkg: packages) {
1140 writePackageApi(apiWriter, pkg, pkg.allClasses().values(), notStrippable);
1141 }
1142 }
1143
1144 static void writeApi(PrintStream apiWriter, HashMap<PackageInfo, List<ClassInfo>> allClasses,
1145 HashSet<ClassInfo> notStrippable) {
1146 // extract the set of packages, sort them by name, and write them out in that order
1147 Set<PackageInfo> allClassKeys = allClasses.keySet();
1148 PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]);
1149 Arrays.sort(allPackages, PackageInfo.comparator);
1150
1151 for (PackageInfo pack : allPackages) {
1152 writePackageApi(apiWriter, pack, allClasses.get(pack), notStrippable);
1153 }
1154 }
1155
1156 static void writePackageApi(PrintStream apiWriter, PackageInfo pack,
1157 Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) {
1158 // Work around the bogus "Array" class we invent for
1159 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
1160 if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) {
1161 return;
1162 }
1163
1164 apiWriter.print("package ");
1165 apiWriter.print(pack.qualifiedName());
1166 apiWriter.print(" {\n\n");
1167
1168 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
1169 Arrays.sort(classes, ClassInfo.comparator);
1170 for (ClassInfo cl : classes) {
1171 writeClassApi(apiWriter, cl, notStrippable);
1172 }
1173
1174 apiWriter.print("}\n\n");
1175 }
1176
1177 static void writeClassApi(PrintStream apiWriter, ClassInfo cl, HashSet<ClassInfo> notStrippable) {
1178 boolean first;
1179
1180 apiWriter.print(" ");
1181 apiWriter.print(cl.scope());
1182 if (cl.isStatic()) {
1183 apiWriter.print(" static");
1184 }
1185 if (cl.isFinal()) {
1186 apiWriter.print(" final");
1187 }
1188 if (cl.isAbstract()) {
1189 apiWriter.print(" abstract");
1190 }
1191 if (cl.isDeprecated()) {
1192 apiWriter.print(" deprecated");
1193 }
1194 apiWriter.print(" ");
1195 apiWriter.print(cl.isInterface() ? "interface" : "class");
1196 apiWriter.print(" ");
1197 apiWriter.print(cl.name());
1198
1199 if (!cl.isInterface()
1200 && !"java.lang.Object".equals(cl.qualifiedName())
1201 && cl.realSuperclass() != null
1202 && !"java.lang.Object".equals(cl.realSuperclass().qualifiedName())) {
1203 apiWriter.print(" extends ");
1204 apiWriter.print(cl.realSuperclass().qualifiedName());
1205 }
1206
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001207 ArrayList<ClassInfo> interfaces = cl.realInterfaces();
1208 Collections.sort(interfaces, ClassInfo.comparator);
Joe Onorato04099252011-03-09 13:34:18 -08001209 first = true;
1210 for (ClassInfo iface : interfaces) {
1211 if (notStrippable.contains(iface)) {
1212 if (first) {
1213 apiWriter.print(" implements");
1214 first = false;
1215 }
1216 apiWriter.print(" ");
1217 apiWriter.print(iface.qualifiedName());
1218 }
1219 }
1220
1221 apiWriter.print(" {\n");
1222
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001223 ArrayList<MethodInfo> constructors = cl.constructors();
1224 Collections.sort(constructors, MethodInfo.comparator);
Joe Onorato04099252011-03-09 13:34:18 -08001225 for (MethodInfo mi : constructors) {
1226 writeConstructorApi(apiWriter, mi);
1227 }
1228
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001229 ArrayList<MethodInfo> methods = cl.allSelfMethods();
1230 Collections.sort(methods, MethodInfo.comparator);
Joe Onorato04099252011-03-09 13:34:18 -08001231 for (MethodInfo mi : methods) {
1232 if (!methodIsOverride(notStrippable, mi)) {
1233 writeMethodApi(apiWriter, mi);
1234 }
1235 }
1236
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001237 ArrayList<FieldInfo> enums = cl.enumConstants();
1238 Collections.sort(enums, FieldInfo.comparator);
Joe Onorato132afe42011-05-31 18:13:38 -07001239 for (FieldInfo fi : enums) {
1240 writeFieldApi(apiWriter, fi, "enum_constant");
1241 }
1242
Ying Wangae8cb832014-04-03 21:16:48 -07001243 ArrayList<FieldInfo> fields = cl.selfFields();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001244 Collections.sort(fields, FieldInfo.comparator);
Joe Onorato04099252011-03-09 13:34:18 -08001245 for (FieldInfo fi : fields) {
Joe Onorato132afe42011-05-31 18:13:38 -07001246 writeFieldApi(apiWriter, fi, "field");
Joe Onorato04099252011-03-09 13:34:18 -08001247 }
1248
1249 apiWriter.print(" }\n\n");
1250 }
1251
1252 static void writeConstructorApi(PrintStream apiWriter, MethodInfo mi) {
1253 apiWriter.print(" ctor ");
1254 apiWriter.print(mi.scope());
1255 if (mi.isDeprecated()) {
1256 apiWriter.print(" deprecated");
1257 }
1258 apiWriter.print(" ");
1259 apiWriter.print(mi.name());
1260
1261 writeParametersApi(apiWriter, mi, mi.parameters());
1262 writeThrowsApi(apiWriter, mi.thrownExceptions());
1263 apiWriter.print(";\n");
1264 }
1265
1266 static void writeMethodApi(PrintStream apiWriter, MethodInfo mi) {
1267 apiWriter.print(" method ");
1268 apiWriter.print(mi.scope());
1269 if (mi.isStatic()) {
1270 apiWriter.print(" static");
1271 }
1272 if (mi.isFinal()) {
1273 apiWriter.print(" final");
1274 }
1275 if (mi.isAbstract()) {
1276 apiWriter.print(" abstract");
1277 }
1278 if (mi.isDeprecated()) {
1279 apiWriter.print(" deprecated");
1280 }
1281 if (mi.isSynchronized()) {
1282 apiWriter.print(" synchronized");
1283 }
1284 apiWriter.print(" ");
1285 if (mi.returnType() == null) {
1286 apiWriter.print("void");
1287 } else {
1288 apiWriter.print(fullParameterTypeName(mi, mi.returnType(), false));
1289 }
1290 apiWriter.print(" ");
1291 apiWriter.print(mi.name());
1292
1293 writeParametersApi(apiWriter, mi, mi.parameters());
1294 writeThrowsApi(apiWriter, mi.thrownExceptions());
1295
1296 apiWriter.print(";\n");
1297 }
1298
Hui Shu5118ffe2014-02-18 14:06:42 -08001299 static void writeParametersApi(PrintStream apiWriter, MethodInfo method,
1300 ArrayList<ParameterInfo> params) {
Joe Onorato04099252011-03-09 13:34:18 -08001301 apiWriter.print("(");
1302
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001303 for (ParameterInfo pi : params) {
1304 if (pi != params.get(0)) {
Joe Onorato04099252011-03-09 13:34:18 -08001305 apiWriter.print(", ");
1306 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001307 apiWriter.print(fullParameterTypeName(method, pi.type(), pi == params.get(params.size()-1)));
Joe Onorato04099252011-03-09 13:34:18 -08001308 // turn on to write the names too
1309 if (false) {
1310 apiWriter.print(" ");
1311 apiWriter.print(pi.name());
1312 }
1313 }
1314
1315 apiWriter.print(")");
1316 }
1317
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001318 static void writeThrowsApi(PrintStream apiWriter, ArrayList<ClassInfo> exceptions) {
Joe Onorato04099252011-03-09 13:34:18 -08001319 // write in a canonical order
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001320 exceptions = (ArrayList<ClassInfo>) exceptions.clone();
1321 Collections.sort(exceptions, ClassInfo.comparator);
1322 //final int N = exceptions.length;
Joe Onorato04099252011-03-09 13:34:18 -08001323 boolean first = true;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001324 for (ClassInfo ex : exceptions) {
Joe Onorato04099252011-03-09 13:34:18 -08001325 // Turn this off, b/c we need to regenrate the old xml files.
1326 if (true || !"java.lang.RuntimeException".equals(ex.qualifiedName())
1327 && !ex.isDerivedFrom("java.lang.RuntimeException")) {
1328 if (first) {
1329 apiWriter.print(" throws ");
1330 first = false;
1331 } else {
1332 apiWriter.print(", ");
1333 }
1334 apiWriter.print(ex.qualifiedName());
1335 }
1336 }
1337 }
1338
Joe Onorato132afe42011-05-31 18:13:38 -07001339 static void writeFieldApi(PrintStream apiWriter, FieldInfo fi, String label) {
1340 apiWriter.print(" ");
1341 apiWriter.print(label);
1342 apiWriter.print(" ");
Joe Onorato04099252011-03-09 13:34:18 -08001343 apiWriter.print(fi.scope());
1344 if (fi.isStatic()) {
1345 apiWriter.print(" static");
1346 }
1347 if (fi.isFinal()) {
1348 apiWriter.print(" final");
1349 }
1350 if (fi.isDeprecated()) {
1351 apiWriter.print(" deprecated");
1352 }
1353 if (fi.isTransient()) {
1354 apiWriter.print(" transient");
1355 }
1356 if (fi.isVolatile()) {
1357 apiWriter.print(" volatile");
1358 }
1359
1360 apiWriter.print(" ");
Ying Wang77518172014-08-14 15:41:39 -07001361 apiWriter.print(fi.type().fullName());
Joe Onorato04099252011-03-09 13:34:18 -08001362
1363 apiWriter.print(" ");
1364 apiWriter.print(fi.name());
1365
1366 Object val = null;
1367 if (fi.isConstant() && fieldIsInitialized(fi)) {
1368 apiWriter.print(" = ");
1369 apiWriter.print(fi.constantLiteralValue());
1370 val = fi.constantValue();
1371 }
1372
1373 apiWriter.print(";");
1374
1375 if (val != null) {
1376 if (val instanceof Integer && "char".equals(fi.type().qualifiedTypeName())) {
1377 apiWriter.format(" // 0x%04x '%s'", val,
1378 FieldInfo.javaEscapeString("" + ((char)((Integer)val).intValue())));
1379 } else if (val instanceof Byte || val instanceof Short || val instanceof Integer) {
1380 apiWriter.format(" // 0x%x", val);
1381 } else if (val instanceof Long) {
1382 apiWriter.format(" // 0x%xL", val);
1383 }
1384 }
1385
1386 apiWriter.print("\n");
1387 }
1388
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001389 static void writeKeepList(PrintStream keepListWriter,
1390 HashMap<PackageInfo, List<ClassInfo>> allClasses, HashSet<ClassInfo> notStrippable) {
1391 // extract the set of packages, sort them by name, and write them out in that order
1392 Set<PackageInfo> allClassKeys = allClasses.keySet();
1393 PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]);
1394 Arrays.sort(allPackages, PackageInfo.comparator);
1395
1396 for (PackageInfo pack : allPackages) {
1397 writePackageKeepList(keepListWriter, pack, allClasses.get(pack), notStrippable);
1398 }
1399 }
1400
1401 static void writePackageKeepList(PrintStream keepListWriter, PackageInfo pack,
1402 Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) {
1403 // Work around the bogus "Array" class we invent for
1404 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
1405 if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) {
1406 return;
1407 }
1408
1409 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
1410 Arrays.sort(classes, ClassInfo.comparator);
1411 for (ClassInfo cl : classes) {
1412 writeClassKeepList(keepListWriter, cl, notStrippable);
1413 }
1414 }
1415
1416 static void writeClassKeepList(PrintStream keepListWriter, ClassInfo cl,
1417 HashSet<ClassInfo> notStrippable) {
1418 keepListWriter.print("-keep class ");
1419 keepListWriter.print(to$Class(cl.qualifiedName()));
1420
1421 keepListWriter.print(" {\n");
1422
1423 ArrayList<MethodInfo> constructors = cl.constructors();
1424 Collections.sort(constructors, MethodInfo.comparator);
1425 for (MethodInfo mi : constructors) {
1426 writeConstructorKeepList(keepListWriter, mi);
1427 }
1428
1429 keepListWriter.print("\n");
1430
1431 ArrayList<MethodInfo> methods = cl.allSelfMethods();
1432 Collections.sort(methods, MethodInfo.comparator);
1433 for (MethodInfo mi : methods) {
Adam Metcalf37abe682014-03-26 13:48:54 -07001434 // allSelfMethods is the non-hidden and visible methods. See Doclava.checkLevel.
1435 writeMethodKeepList(keepListWriter, mi);
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001436 }
1437
1438 keepListWriter.print("\n");
1439
1440 ArrayList<FieldInfo> enums = cl.enumConstants();
1441 Collections.sort(enums, FieldInfo.comparator);
1442 for (FieldInfo fi : enums) {
1443 writeFieldKeepList(keepListWriter, fi);
1444 }
1445
1446 keepListWriter.print("\n");
1447
Ying Wangae8cb832014-04-03 21:16:48 -07001448 ArrayList<FieldInfo> fields = cl.selfFields();
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001449 Collections.sort(fields, FieldInfo.comparator);
1450 for (FieldInfo fi : fields) {
1451 writeFieldKeepList(keepListWriter, fi);
1452 }
1453
1454 keepListWriter.print("}\n\n");
1455 }
1456
1457 static void writeConstructorKeepList(PrintStream keepListWriter, MethodInfo mi) {
1458 keepListWriter.print(" ");
1459 String name = mi.name();
1460 name = name.replace(".", "$");
1461 keepListWriter.print(name);
1462
1463 writeParametersKeepList(keepListWriter, mi, mi.parameters());
1464 keepListWriter.print(";\n");
1465 }
1466
1467 static void writeMethodKeepList(PrintStream keepListWriter, MethodInfo mi) {
1468 keepListWriter.print(" ");
1469 keepListWriter.print(mi.scope());
1470 if (mi.isStatic()) {
1471 keepListWriter.print(" static");
1472 }
1473 if (mi.isAbstract()) {
1474 keepListWriter.print(" abstract");
1475 }
1476 if (mi.isSynchronized()) {
1477 keepListWriter.print(" synchronized");
1478 }
1479 keepListWriter.print(" ");
1480 if (mi.returnType() == null) {
1481 keepListWriter.print("void");
1482 } else {
1483 keepListWriter.print(getCleanTypeName(mi.returnType()));
1484 }
1485 keepListWriter.print(" ");
1486 keepListWriter.print(mi.name());
1487
1488 writeParametersKeepList(keepListWriter, mi, mi.parameters());
1489
1490 keepListWriter.print(";\n");
1491 }
1492
1493 static void writeParametersKeepList(PrintStream keepListWriter, MethodInfo method,
1494 ArrayList<ParameterInfo> params) {
1495 keepListWriter.print("(");
1496
1497 for (ParameterInfo pi : params) {
1498 if (pi != params.get(0)) {
1499 keepListWriter.print(", ");
1500 }
Jeff Hamilton550a9de2012-07-23 23:37:06 -07001501 keepListWriter.print(getCleanTypeName(pi.type()));
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001502 }
1503
1504 keepListWriter.print(")");
1505 }
1506
1507 static void writeFieldKeepList(PrintStream keepListWriter, FieldInfo fi) {
1508 keepListWriter.print(" ");
1509 keepListWriter.print(fi.scope());
1510 if (fi.isStatic()) {
1511 keepListWriter.print(" static");
1512 }
1513 if (fi.isTransient()) {
1514 keepListWriter.print(" transient");
1515 }
1516 if (fi.isVolatile()) {
1517 keepListWriter.print(" volatile");
1518 }
1519
1520 keepListWriter.print(" ");
Sergei Datsenkoe64dc942014-09-08 17:34:59 +10001521 keepListWriter.print(getCleanTypeName(fi.type()));
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001522
1523 keepListWriter.print(" ");
1524 keepListWriter.print(fi.name());
1525
1526 keepListWriter.print(";\n");
1527 }
1528
Ben Dodson920dbbb2010-08-04 15:21:06 -07001529 static String fullParameterTypeName(MethodInfo method, TypeInfo type, boolean isLast) {
1530 String fullTypeName = type.fullName(method.typeVariables());
1531 if (isLast && method.isVarArgs()) {
1532 // TODO: note that this does not attempt to handle hypothetical
1533 // vararg methods whose last parameter is a list of arrays, e.g.
1534 // "Object[]...".
1535 fullTypeName = type.fullNameNoDimension(method.typeVariables()) + "...";
1536 }
1537 return fullTypeName;
1538 }
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001539
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001540 static String to$Class(String name) {
1541 int pos = 0;
1542 while ((pos = name.indexOf('.', pos)) > 0) {
1543 String n = name.substring(0, pos);
1544 if (Converter.obtainClass(n) != null) {
1545 return n + (name.substring(pos).replace('.', '$'));
1546 }
1547 pos = pos + 1;
1548 }
1549 return name;
1550 }
1551
1552 static String getCleanTypeName(TypeInfo t) {
Jeff Hamiltond6f2c2b2012-07-23 23:09:19 -07001553 return t.isPrimitive() ? t.simpleTypeName() + t.dimension() :
Jeff Hamilton1e0d3702012-06-21 04:21:54 -05001554 to$Class(t.asClassInfo().qualifiedName() + t.dimension());
Jeff Hamilton970f13f2012-06-22 00:21:56 -05001555 }
Ben Dodson920dbbb2010-08-04 15:21:06 -07001556}