blob: c5bdb4bc9b93fc4244e3c07909987349dd882789 [file] [log] [blame]
Stuart McCullochbb014372012-06-07 21:57:32 +00001package aQute.bnd.differ;
2
3import static aQute.bnd.service.diff.Delta.*;
4import static aQute.bnd.service.diff.Type.*;
5import static java.lang.reflect.Modifier.*;
6
7import java.lang.reflect.*;
8import java.util.*;
9import java.util.Map.Entry;
10import java.util.concurrent.atomic.*;
11import java.util.jar.*;
12
13import aQute.bnd.annotation.*;
14import aQute.bnd.service.diff.*;
15import aQute.bnd.service.diff.Type;
16import aQute.lib.collections.*;
17import aQute.lib.osgi.*;
18import aQute.lib.osgi.Clazz.JAVA;
19import aQute.lib.osgi.Clazz.MethodDef;
20import aQute.lib.osgi.Descriptors.PackageRef;
21import aQute.lib.osgi.Descriptors.TypeRef;
22import aQute.libg.generics.*;
23import aQute.libg.header.*;
24import aQute.libg.version.Version;
25
26/**
27 * An element that compares the access field in a binary compatible way. This
28 * element is used for classes, methods, constructors, and fields. For that
29 * reason we also included the only method that uses this class as a static
30 * method.
31 * <p>
32 * Packages
33 * <ul>
34 * <li>MAJOR - Remove a public type
35 * <li>MINOR - Add a public class
36 * <li>MINOR - Add an interface
37 * <li>MINOR - Add a method to a class
38 * <li>MINOR - Add a method to a provider interface
39 * <li>MAJOR - Add a method to a consumer interface
40 * <li>MINOR - Add a field
41 * <li>MICRO - Add an annotation to a member
42 * <li>MINOR - Change the value of a constant
43 * <li>MICRO - -abstract
44 * <li>MICRO - -final
45 * <li>MICRO - -protected
46 * <li>MAJOR - +abstract
47 * <li>MAJOR - +final
48 * <li>MAJOR - +protected
49 * </ul>
Stuart McCullochbb014372012-06-07 21:57:32 +000050 */
51
52class JavaElement {
Stuart McCulloch2286f232012-06-15 13:27:53 +000053 final static EnumSet<Type> INHERITED = EnumSet.of(FIELD, METHOD, EXTENDS, IMPLEMENTS);
54 private static final Element PROTECTED = new Element(ACCESS, "protected", null, MAJOR, MINOR, null);
55 private static final Element STATIC = new Element(ACCESS, "static", null, MAJOR, MAJOR, null);
56 private static final Element ABSTRACT = new Element(ACCESS, "abstract", null, MAJOR, MINOR, null);
57 private static final Element FINAL = new Element(ACCESS, "final", null, MAJOR, MINOR, null);
Stuart McCullochbb014372012-06-07 21:57:32 +000058 // private static final Element DEPRECATED = new Element(ACCESS,
59 // "deprecated", null,
60 // CHANGED, CHANGED, null);
61
62 final Analyzer analyzer;
Stuart McCulloch2286f232012-06-15 13:27:53 +000063 final Map<PackageRef,Instructions> providerMatcher = Create.map();
Stuart McCullochbb014372012-06-07 21:57:32 +000064 final Set<TypeRef> notAccessible = Create.set();
Stuart McCulloch2286f232012-06-15 13:27:53 +000065 final Map<Object,Element> cache = Create.map();
Stuart McCullochbb014372012-06-07 21:57:32 +000066 MultiMap<PackageRef, //
67 Element> packages;
68 final MultiMap<TypeRef, //
Stuart McCulloch2286f232012-06-15 13:27:53 +000069 Element> covariant = new MultiMap<TypeRef,Element>();
Stuart McCullochbb014372012-06-07 21:57:32 +000070 final Set<JAVA> javas = Create.set();
71 final Packages exports;
72
73 /**
74 * Create an element for the API. We take the exported packages and traverse
75 * those for their classes. If there is no manifest or it does not describe
76 * a bundle we assume the whole contents is exported.
77 *
78 * @param infos
79 */
80 JavaElement(Analyzer analyzer) throws Exception {
81 this.analyzer = analyzer;
82
83 Manifest manifest = analyzer.getJar().getManifest();
Stuart McCulloch2286f232012-06-15 13:27:53 +000084 if (manifest != null && manifest.getMainAttributes().getValue(Constants.BUNDLE_MANIFESTVERSION) != null) {
Stuart McCullochbb014372012-06-07 21:57:32 +000085 exports = new Packages();
Stuart McCulloch2286f232012-06-15 13:27:53 +000086 for (Map.Entry<String,Attrs> entry : OSGiHeader.parseHeader(
Stuart McCullochbb014372012-06-07 21:57:32 +000087 manifest.getMainAttributes().getValue(Constants.EXPORT_PACKAGE)).entrySet())
88 exports.put(analyzer.getPackageRef(entry.getKey()), entry.getValue());
89 } else
90 exports = analyzer.getContained();
91 //
92 // We have to gather the -providers and parse them into instructions
93 // so we can efficiently match them during class parsing to find
94 // out who the providers and consumers are
95 //
96
Stuart McCulloch2286f232012-06-15 13:27:53 +000097 for (Entry<PackageRef,Attrs> entry : exports.entrySet()) {
Stuart McCullochbb014372012-06-07 21:57:32 +000098 String value = entry.getValue().get(Constants.PROVIDER_TYPE_DIRECTIVE);
99 if (value != null) {
100 providerMatcher.put(entry.getKey(), new Instructions(value));
101 }
102 }
103
104 // we now need to gather all the packages but without
105 // creating the packages yet because we do not yet know
106 // which classes are accessible
107
Stuart McCulloch2286f232012-06-15 13:27:53 +0000108 packages = new MultiMap<PackageRef,Element>();
Stuart McCullochbb014372012-06-07 21:57:32 +0000109
110 for (Clazz c : analyzer.getClassspace().values()) {
111 if (c.isPublic() || c.isProtected()) {
112 PackageRef packageName = c.getClassName().getPackageRef();
113
114 if (exports.containsKey(packageName)) {
115 Element cdef = classElement(c);
116 packages.add(packageName, cdef);
117 }
118 }
119 }
120
121 }
122
123 static Element getAPI(Analyzer analyzer) throws Exception {
124 analyzer.analyze();
125 JavaElement te = new JavaElement(analyzer);
126 return te.getLocalAPI();
127 }
128
129 private Element getLocalAPI() throws Exception {
130 List<Element> result = new ArrayList<Element>();
131
Stuart McCulloch2286f232012-06-15 13:27:53 +0000132 for (Map.Entry<PackageRef,List<Element>> entry : packages.entrySet()) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000133 List<Element> set = entry.getValue();
134 for (Iterator<Element> i = set.iterator(); i.hasNext();) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000135
136 if (notAccessible.contains(analyzer.getTypeRefFromFQN(i.next().getName())))
Stuart McCullochbb014372012-06-07 21:57:32 +0000137 i.remove();
Stuart McCulloch2286f232012-06-15 13:27:53 +0000138
Stuart McCullochbb014372012-06-07 21:57:32 +0000139 }
140 String version = exports.get(entry.getKey()).get(Constants.VERSION_ATTRIBUTE);
141 if (version != null) {
142 Version v = new Version(version);
Stuart McCulloch2286f232012-06-15 13:27:53 +0000143 set.add(new Element(Type.VERSION, v.getWithoutQualifier().toString(), null, IGNORED, IGNORED, null));
Stuart McCullochbb014372012-06-07 21:57:32 +0000144 }
145 Element pd = new Element(Type.PACKAGE, entry.getKey().getFQN(), set, MINOR, MAJOR, null);
146 result.add(pd);
147 }
148
149 for (JAVA java : javas) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000150 result.add(new Element(CLASS_VERSION, java.toString(), null, Delta.CHANGED, Delta.CHANGED, null));
Stuart McCullochbb014372012-06-07 21:57:32 +0000151 }
152
153 return new Element(Type.API, "<api>", result, CHANGED, CHANGED, null);
154 }
155
156 /**
157 * Calculate the class element. This requires parsing the class file and
158 * finding all the methods that were added etc. The parsing will take super
159 * interfaces and super classes into account. For this reason it maintains a
160 * queue of classes/interfaces to parse.
161 *
162 * @param analyzer
163 * @param clazz
164 * @param infos
165 * @return
166 * @throws Exception
167 */
168 Element classElement(final Clazz clazz) throws Exception {
169 Element e = cache.get(clazz);
170 if (e != null)
171 return e;
172
173 final StringBuilder comment = new StringBuilder();
174 final Set<Element> members = new HashSet<Element>();
175 final Set<MethodDef> methods = Create.set();
176 final Set<Clazz.FieldDef> fields = Create.set();
Stuart McCulloch2286f232012-06-15 13:27:53 +0000177 final MultiMap<Clazz.Def,Element> annotations = new MultiMap<Clazz.Def,Element>();
Stuart McCullochbb014372012-06-07 21:57:32 +0000178
179 final TypeRef name = clazz.getClassName();
180
181 final String fqn = name.getFQN();
182 final String shortName = name.getShortName();
183
184 // Check if this clazz is actually a provider or not
185 // providers must be listed in the exported package in the
186 // PROVIDER_TYPE directive.
187 Instructions matchers = providerMatcher.get(name.getPackageRef());
188 boolean p = matchers != null && matchers.matches(shortName);
189 final AtomicBoolean provider = new AtomicBoolean(p);
190
191 //
192 // Check if we already had this clazz in the cache
193 //
194
195 Element before = cache.get(clazz); // for super classes
196 if (before != null)
197 return before;
198
199 clazz.parseClassFileWithCollector(new ClassDataCollector() {
200 boolean memberEnd;
201 Clazz.FieldDef last;
202
Stuart McCulloch2286f232012-06-15 13:27:53 +0000203 @Override
204 public void version(int minor, int major) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000205 javas.add(Clazz.JAVA.getJava(major, minor));
206 }
207
Stuart McCulloch2286f232012-06-15 13:27:53 +0000208 @Override
209 public void method(MethodDef defined) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000210 if ((defined.isProtected() || defined.isPublic())) {
211 last = defined;
212 methods.add(defined);
213 } else {
214 last = null;
215 }
216 }
217
Stuart McCulloch2286f232012-06-15 13:27:53 +0000218 @Override
219 public void deprecated() {
Stuart McCullochbb014372012-06-07 21:57:32 +0000220 if (memberEnd)
221 clazz.setDeprecated(true);
222 else
223 last.setDeprecated(true);
224 }
225
Stuart McCulloch2286f232012-06-15 13:27:53 +0000226 @Override
227 public void field(Clazz.FieldDef defined) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000228 if (defined.isProtected() || defined.isPublic()) {
229 last = defined;
230 fields.add(defined);
231 } else
232 last = null;
233 }
234
Stuart McCulloch2286f232012-06-15 13:27:53 +0000235 @Override
236 public void constant(Object o) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000237 if (last != null) {
238 // Must be accessible now
239 last.setConstant(o);
240 }
241 }
242
Stuart McCulloch2286f232012-06-15 13:27:53 +0000243 @Override
244 public void extendsClass(TypeRef name) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000245 String comment = null;
246 if (!clazz.isInterface())
247 comment = inherit(members, name);
248
249 Clazz c = analyzer.findClass(name);
250 if ((c == null || c.isPublic()) && !name.isObject())
Stuart McCulloch2286f232012-06-15 13:27:53 +0000251 members.add(new Element(Type.EXTENDS, name.getFQN(), null, MICRO, MAJOR, comment));
Stuart McCullochbb014372012-06-07 21:57:32 +0000252 }
253
Stuart McCulloch2286f232012-06-15 13:27:53 +0000254 @Override
255 public void implementsInterfaces(TypeRef names[]) throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000256 // TODO is interface reordering important for binary
257 // compatibility??
258
259 for (TypeRef name : names) {
260
261 String comment = null;
262 if (clazz.isInterface() || clazz.isAbstract())
263 comment = inherit(members, name);
Stuart McCulloch2286f232012-06-15 13:27:53 +0000264 members.add(new Element(Type.IMPLEMENTS, name.getFQN(), null, MINOR, MAJOR, comment));
Stuart McCullochbb014372012-06-07 21:57:32 +0000265 }
266 }
267
268 /**
269 * @param members
270 * @param name
271 * @param comment
272 * @return
273 */
274 Set<Element> OBJECT = Create.set();
275
276 public String inherit(final Set<Element> members, TypeRef name) throws Exception {
277 if (name.isObject()) {
278 if (OBJECT.isEmpty()) {
279 Clazz c = analyzer.findClass(name);
280 Element s = classElement(c);
281 for (Element child : s.children) {
282 if (INHERITED.contains(child.type)) {
283 String n = child.getName();
284 if (child.type == METHOD) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000285 if (n.startsWith("<init>") || "getClass()".equals(child.getName())
Stuart McCullochbb014372012-06-07 21:57:32 +0000286 || n.startsWith("wait(") || n.startsWith("notify(")
287 || n.startsWith("notifyAll("))
288 continue;
289 }
290 OBJECT.add(child);
291 }
292 }
293 }
294 members.addAll(OBJECT);
295 } else {
296
297 Clazz c = analyzer.findClass(name);
298 if (c == null) {
299 return "Cannot load " + name;
300 } else {
301 Element s = classElement(c);
302 for (Element child : s.children) {
303 if (INHERITED.contains(child.type) && !child.name.startsWith("<")) {
304 members.add(child);
305 }
306 }
307 }
308 }
309 return null;
310 }
311
Stuart McCulloch2286f232012-06-15 13:27:53 +0000312 @Override
313 public void annotation(Annotation annotation) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000314 Collection<Element> properties = Create.set();
315 if (Deprecated.class.getName().equals(annotation.getName().getFQN())) {
316 if (memberEnd)
317 clazz.setDeprecated(true);
318 else
319 last.setDeprecated(true);
320 return;
321 }
322
323 for (String key : annotation.keySet()) {
324 StringBuilder sb = new StringBuilder();
325 sb.append(key);
326 sb.append('=');
327 toString(sb, annotation.get(key));
328
Stuart McCulloch2286f232012-06-15 13:27:53 +0000329 properties.add(new Element(Type.PROPERTY, sb.toString(), null, CHANGED, CHANGED, null));
Stuart McCullochbb014372012-06-07 21:57:32 +0000330 }
331
332 if (memberEnd) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000333 members.add(new Element(Type.ANNOTATED, annotation.getName().getFQN(), properties, CHANGED,
334 CHANGED, null));
Stuart McCullochbb014372012-06-07 21:57:32 +0000335 if (ProviderType.class.getName().equals(annotation.getName().getFQN())) {
336 provider.set(true);
337 } else if (ConsumerType.class.getName().equals(annotation.getName().getFQN())) {
338 provider.set(false);
339 }
340 } else if (last != null)
Stuart McCulloch2286f232012-06-15 13:27:53 +0000341 annotations.add(last, new Element(Type.ANNOTATED, annotation.getName().getFQN(), properties,
342 CHANGED, CHANGED, null));
Stuart McCullochbb014372012-06-07 21:57:32 +0000343 }
344
345 private void toString(StringBuilder sb, Object object) {
346
347 if (object.getClass().isArray()) {
348 sb.append('[');
349 int l = Array.getLength(object);
350 for (int i = 0; i < l; i++)
351 toString(sb, Array.get(object, i));
352 sb.append(']');
353 } else
354 sb.append(object);
355 }
356
Stuart McCulloch2286f232012-06-15 13:27:53 +0000357 @Override
358 public void innerClass(TypeRef innerClass, TypeRef outerClass, String innerName, int innerClassAccessFlags)
359 throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000360 Clazz clazz = analyzer.findClass(innerClass);
361 if (clazz != null)
362 clazz.setInnerAccess(innerClassAccessFlags);
363
Stuart McCulloch2286f232012-06-15 13:27:53 +0000364 if (Modifier.isProtected(innerClassAccessFlags) || Modifier.isPublic(innerClassAccessFlags))
Stuart McCullochbb014372012-06-07 21:57:32 +0000365 return;
366 notAccessible.add(innerClass);
367 }
368
Stuart McCulloch2286f232012-06-15 13:27:53 +0000369 @Override
370 public void memberEnd() {
Stuart McCullochbb014372012-06-07 21:57:32 +0000371 memberEnd = true;
372 }
373 });
374
375 // This is the heart of the semantic versioning. If we
376 // add or remove a method from an interface then
377 Delta add;
378 Delta remove;
379 Type type;
380
381 // Calculate the type of the clazz. A class
382 // can be an interface, class, enum, or annotation
383
384 if (clazz.isInterface())
385 if (clazz.isAnnotation())
386 type = Type.INTERFACE;
387 else
388 type = Type.ANNOTATION;
389 else if (clazz.isEnum())
390 type = Type.ENUM;
391 else
392 type = Type.CLASS;
393
394 if (type == Type.INTERFACE) {
395 if (provider.get()) {
396 // Adding a method for a provider is not an issue
397 // because it must be aware of the changes
398 add = MINOR;
399
400 // Removing a method influences consumers since they
401 // tend to call this guy.
402 remove = MAJOR;
403 } else {
404 // Adding a method is a major change
405 // because the consumer has to implement it
406 // or the provider will call a non existent
407 // method on the consumer
408 add = MAJOR;
409
410 // Removing a method is not an issue because the
411 // provider, which calls this contract must be
412 // aware of the removal
413
414 remove = MINOR;
415 }
416 } else {
417 // Adding a method to a class can never do any harm
418 add = MINOR;
419
420 // Removing it will likely hurt consumers
421 remove = MAJOR;
422 }
423
424 // Remove all synthetic methods, we need
425 // to treat them special for the covariant returns
426
427 Set<MethodDef> synthetic = Create.set();
428 for (Iterator<MethodDef> i = methods.iterator(); i.hasNext();) {
429 MethodDef m = i.next();
430 if (m.isSynthetic()) {
431 synthetic.add(m);
432 i.remove();
433 }
434 }
435
436 for (MethodDef m : methods) {
437 List<Element> children = annotations.get(m);
438 if (children == null)
439 children = new ArrayList<Element>();
440
441 access(children, m.getAccess(), m.isDeprecated());
442
443 // A final class cannot be extended, ergo,
444 // all methods defined in it are by definition
445 // final. However, marking them final (either
446 // on the method or inheriting it from the class)
447 // will create superfluous changes if we
448 // override a method from a super class that was not
449 // final. So we actually remove the final for methods
450 // in a final class.
451 if (clazz.isFinal())
452 children.remove(FINAL);
453
454 // for covariant types we need to add the return types
455 // and all the implemented and extended types. This is already
456 // do for us when we get the element of the return type.
457
458 getCovariantReturns(children, m.getType());
459
460 for (Iterator<MethodDef> i = synthetic.iterator(); i.hasNext();) {
461 MethodDef s = i.next();
Stuart McCulloch2286f232012-06-15 13:27:53 +0000462 if (s.getName().equals(m.getName()) && Arrays.equals(s.getPrototype(), m.getPrototype())) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000463 i.remove();
464 getCovariantReturns(children, s.getType());
465 }
466 }
467
Stuart McCulloch2286f232012-06-15 13:27:53 +0000468 Element member = new Element(Type.METHOD, m.getName() + toString(m.getPrototype()), children, add, remove,
469 null);
Stuart McCullochbb014372012-06-07 21:57:32 +0000470
471 if (!members.add(member)) {
472 members.remove(member);
473 members.add(member);
474 }
475 }
476
477 /**
478 * Repeat for the remaining synthetic methods
479 */
480 for (MethodDef m : synthetic) {
481 List<Element> children = annotations.get(m);
482 if (children == null)
483 children = new ArrayList<Element>();
484 access(children, m.getAccess(), m.isDeprecated());
485
486 // A final class cannot be extended, ergo,
487 // all methods defined in it are by definition
488 // final. However, marking them final (either
489 // on the method or inheriting it from the class)
490 // will create superfluous changes if we
491 // override a method from a super class that was not
492 // final. So we actually remove the final for methods
493 // in a final class.
494 if (clazz.isFinal())
495 children.remove(FINAL);
496
497 // for covariant types we need to add the return types
498 // and all the implemented and extended types. This is already
499 // do for us when we get the element of the return type.
500
501 getCovariantReturns(children, m.getType());
502
Stuart McCulloch2286f232012-06-15 13:27:53 +0000503 Element member = new Element(Type.METHOD, m.getName() + toString(m.getPrototype()), children, add, remove,
504 "synthetic");
Stuart McCullochbb014372012-06-07 21:57:32 +0000505
506 if (!members.add(member)) {
507 members.remove(member);
508 members.add(member);
509 }
510 }
511
512 for (Clazz.FieldDef f : fields) {
513 List<Element> children = annotations.get(f);
514 if (children == null)
515 children = new ArrayList<Element>();
516
517 // Fields can have a constant value, this is a new element
518 if (f.getConstant() != null) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000519 children.add(new Element(Type.CONSTANT, f.getConstant().toString(), null, CHANGED, CHANGED, null));
Stuart McCullochbb014372012-06-07 21:57:32 +0000520 }
521
522 access(children, f.getAccess(), f.isDeprecated());
Stuart McCulloch2286f232012-06-15 13:27:53 +0000523 Element member = new Element(Type.FIELD, f.getType().getFQN() + " " + f.getName(), children, MINOR, MAJOR,
524 null);
Stuart McCullochbb014372012-06-07 21:57:32 +0000525
526 if (!members.add(member)) {
527 members.remove(member);
528 members.add(member);
529 }
530 }
531
532 access(members, clazz.getAccess(), clazz.isDeprecated());
533
534 // And make the result
Stuart McCulloch2286f232012-06-15 13:27:53 +0000535 Element s = new Element(type, fqn, members, MINOR, MAJOR, comment.length() == 0 ? null : comment.toString());
Stuart McCullochbb014372012-06-07 21:57:32 +0000536 cache.put(clazz, s);
537 return s;
538 }
539
540 private String toString(TypeRef[] prototype) {
541 StringBuilder sb = new StringBuilder();
542 sb.append("(");
543 String del = "";
544 for (TypeRef ref : prototype) {
545 sb.append(del);
546 sb.append(ref.getFQN());
547 del = ",";
548 }
549 sb.append(")");
550 return sb.toString();
551 }
552
553 static Element BOOLEAN_R = new Element(RETURN, "boolean");
554 static Element BYTE_R = new Element(RETURN, "byte");
555 static Element SHORT_R = new Element(RETURN, "short");
556 static Element CHAR_R = new Element(RETURN, "char");
557 static Element INT_R = new Element(RETURN, "int");
558 static Element LONG_R = new Element(RETURN, "long");
559 static Element FLOAT_R = new Element(RETURN, "float");
560 static Element DOUBLE_R = new Element(RETURN, "double");
561
562 private void getCovariantReturns(Collection<Element> elements, TypeRef type) throws Exception {
563 if (type == null || type.isObject())
564 return;
565
566 if (type.isPrimitive()) {
567 if (type.getFQN().equals("void"))
568 return;
569
570 String name = type.getBinary();
571 Element e;
572 switch (name.charAt(0)) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000573 case 'Z' :
574 e = BOOLEAN_R;
575 break;
576 case 'S' :
577 e = SHORT_R;
578 break;
579 case 'I' :
580 e = INT_R;
581 break;
582 case 'B' :
583 e = BYTE_R;
584 break;
585 case 'C' :
586 e = CHAR_R;
587 break;
588 case 'J' :
589 e = LONG_R;
590 break;
591 case 'F' :
592 e = FLOAT_R;
593 break;
594 case 'D' :
595 e = DOUBLE_R;
596 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000597
Stuart McCulloch2286f232012-06-15 13:27:53 +0000598 default :
599 throw new IllegalArgumentException("Unknown primitive " + type);
Stuart McCullochbb014372012-06-07 21:57:32 +0000600 }
601 elements.add(e);
602 return;
603 }
604
605 List<Element> set = covariant.get(type);
606 if (set != null) {
607 elements.addAll(set);
608 return;
609 }
610
611 Element current = new Element(RETURN, type.getFQN());
612 Clazz clazz = analyzer.findClass(type);
613 if (clazz == null) {
614 elements.add(current);
615 return;
616 }
617
618 set = Create.list();
619 set.add(current);
620 getCovariantReturns(set, clazz.getSuper());
621
622 TypeRef[] interfaces = clazz.getInterfaces();
623 if (interfaces != null)
624 for (TypeRef intf : interfaces) {
625 getCovariantReturns(set, intf);
626 }
627
628 covariant.put(type, set);
629 elements.addAll(set);
630 }
631
632 private static void access(Collection<Element> children, int access, boolean deprecated) {
633 if (!isPublic(access))
634 children.add(PROTECTED);
635 if (isAbstract(access))
636 children.add(ABSTRACT);
637 if (isFinal(access))
638 children.add(FINAL);
639 if (isStatic(access))
640 children.add(STATIC);
641
642 // Ignore for now
643 // if (deprecated)
644 // children.add(DEPRECATED);
645
646 }
647
648}