blob: 0be044b2296d2c314347e6099ee874ca9cf8f13b [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.*;
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +000014import aQute.bnd.header.*;
15import aQute.bnd.osgi.*;
16import aQute.bnd.osgi.Clazz.JAVA;
17import aQute.bnd.osgi.Clazz.MethodDef;
18import aQute.bnd.osgi.Descriptors.PackageRef;
19import aQute.bnd.osgi.Descriptors.TypeRef;
20import aQute.bnd.osgi.Version;
Stuart McCullochbb014372012-06-07 21:57:32 +000021import aQute.bnd.service.diff.*;
22import aQute.bnd.service.diff.Type;
23import aQute.lib.collections.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000024import aQute.libg.generics.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000025
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;
Stuart McCullochd4826102012-06-26 16:34:24 +0000300 }
301 Element s = classElement(c);
302 for (Element child : s.children) {
303 if (INHERITED.contains(child.type) && !child.name.startsWith("<")) {
304 members.add(child);
Stuart McCullochbb014372012-06-07 21:57:32 +0000305 }
306 }
307 }
308 return null;
309 }
310
Stuart McCulloch2286f232012-06-15 13:27:53 +0000311 @Override
312 public void annotation(Annotation annotation) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000313 Collection<Element> properties = Create.set();
314 if (Deprecated.class.getName().equals(annotation.getName().getFQN())) {
315 if (memberEnd)
316 clazz.setDeprecated(true);
317 else
318 last.setDeprecated(true);
319 return;
320 }
321
322 for (String key : annotation.keySet()) {
323 StringBuilder sb = new StringBuilder();
324 sb.append(key);
325 sb.append('=');
326 toString(sb, annotation.get(key));
327
Stuart McCulloch2286f232012-06-15 13:27:53 +0000328 properties.add(new Element(Type.PROPERTY, sb.toString(), null, CHANGED, CHANGED, null));
Stuart McCullochbb014372012-06-07 21:57:32 +0000329 }
330
331 if (memberEnd) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000332 members.add(new Element(Type.ANNOTATED, annotation.getName().getFQN(), properties, CHANGED,
333 CHANGED, null));
Stuart McCullochbb014372012-06-07 21:57:32 +0000334 if (ProviderType.class.getName().equals(annotation.getName().getFQN())) {
335 provider.set(true);
336 } else if (ConsumerType.class.getName().equals(annotation.getName().getFQN())) {
337 provider.set(false);
338 }
339 } else if (last != null)
Stuart McCulloch2286f232012-06-15 13:27:53 +0000340 annotations.add(last, new Element(Type.ANNOTATED, annotation.getName().getFQN(), properties,
341 CHANGED, CHANGED, null));
Stuart McCullochbb014372012-06-07 21:57:32 +0000342 }
343
344 private void toString(StringBuilder sb, Object object) {
345
346 if (object.getClass().isArray()) {
347 sb.append('[');
348 int l = Array.getLength(object);
349 for (int i = 0; i < l; i++)
350 toString(sb, Array.get(object, i));
351 sb.append(']');
352 } else
353 sb.append(object);
354 }
355
Stuart McCulloch2286f232012-06-15 13:27:53 +0000356 @Override
357 public void innerClass(TypeRef innerClass, TypeRef outerClass, String innerName, int innerClassAccessFlags)
358 throws Exception {
Stuart McCullochbb014372012-06-07 21:57:32 +0000359 Clazz clazz = analyzer.findClass(innerClass);
360 if (clazz != null)
361 clazz.setInnerAccess(innerClassAccessFlags);
362
Stuart McCulloch2286f232012-06-15 13:27:53 +0000363 if (Modifier.isProtected(innerClassAccessFlags) || Modifier.isPublic(innerClassAccessFlags))
Stuart McCullochbb014372012-06-07 21:57:32 +0000364 return;
365 notAccessible.add(innerClass);
366 }
367
Stuart McCulloch2286f232012-06-15 13:27:53 +0000368 @Override
369 public void memberEnd() {
Stuart McCullochbb014372012-06-07 21:57:32 +0000370 memberEnd = true;
371 }
372 });
373
374 // This is the heart of the semantic versioning. If we
375 // add or remove a method from an interface then
376 Delta add;
377 Delta remove;
378 Type type;
379
380 // Calculate the type of the clazz. A class
381 // can be an interface, class, enum, or annotation
382
383 if (clazz.isInterface())
384 if (clazz.isAnnotation())
385 type = Type.INTERFACE;
386 else
387 type = Type.ANNOTATION;
388 else if (clazz.isEnum())
389 type = Type.ENUM;
390 else
391 type = Type.CLASS;
392
393 if (type == Type.INTERFACE) {
394 if (provider.get()) {
395 // Adding a method for a provider is not an issue
396 // because it must be aware of the changes
397 add = MINOR;
398
399 // Removing a method influences consumers since they
400 // tend to call this guy.
401 remove = MAJOR;
402 } else {
403 // Adding a method is a major change
404 // because the consumer has to implement it
405 // or the provider will call a non existent
406 // method on the consumer
407 add = MAJOR;
408
409 // Removing a method is not an issue because the
410 // provider, which calls this contract must be
411 // aware of the removal
412
413 remove = MINOR;
414 }
415 } else {
416 // Adding a method to a class can never do any harm
417 add = MINOR;
418
419 // Removing it will likely hurt consumers
420 remove = MAJOR;
421 }
422
423 // Remove all synthetic methods, we need
424 // to treat them special for the covariant returns
425
426 Set<MethodDef> synthetic = Create.set();
427 for (Iterator<MethodDef> i = methods.iterator(); i.hasNext();) {
428 MethodDef m = i.next();
429 if (m.isSynthetic()) {
430 synthetic.add(m);
431 i.remove();
432 }
433 }
434
435 for (MethodDef m : methods) {
436 List<Element> children = annotations.get(m);
437 if (children == null)
438 children = new ArrayList<Element>();
439
440 access(children, m.getAccess(), m.isDeprecated());
441
442 // A final class cannot be extended, ergo,
443 // all methods defined in it are by definition
444 // final. However, marking them final (either
445 // on the method or inheriting it from the class)
446 // will create superfluous changes if we
447 // override a method from a super class that was not
448 // final. So we actually remove the final for methods
449 // in a final class.
450 if (clazz.isFinal())
451 children.remove(FINAL);
452
453 // for covariant types we need to add the return types
454 // and all the implemented and extended types. This is already
455 // do for us when we get the element of the return type.
456
457 getCovariantReturns(children, m.getType());
458
459 for (Iterator<MethodDef> i = synthetic.iterator(); i.hasNext();) {
460 MethodDef s = i.next();
Stuart McCulloch2286f232012-06-15 13:27:53 +0000461 if (s.getName().equals(m.getName()) && Arrays.equals(s.getPrototype(), m.getPrototype())) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000462 i.remove();
463 getCovariantReturns(children, s.getType());
464 }
465 }
466
Stuart McCulloch2286f232012-06-15 13:27:53 +0000467 Element member = new Element(Type.METHOD, m.getName() + toString(m.getPrototype()), children, add, remove,
468 null);
Stuart McCullochbb014372012-06-07 21:57:32 +0000469
470 if (!members.add(member)) {
471 members.remove(member);
472 members.add(member);
473 }
474 }
475
476 /**
477 * Repeat for the remaining synthetic methods
478 */
479 for (MethodDef m : synthetic) {
480 List<Element> children = annotations.get(m);
481 if (children == null)
482 children = new ArrayList<Element>();
483 access(children, m.getAccess(), m.isDeprecated());
484
485 // A final class cannot be extended, ergo,
486 // all methods defined in it are by definition
487 // final. However, marking them final (either
488 // on the method or inheriting it from the class)
489 // will create superfluous changes if we
490 // override a method from a super class that was not
491 // final. So we actually remove the final for methods
492 // in a final class.
493 if (clazz.isFinal())
494 children.remove(FINAL);
495
496 // for covariant types we need to add the return types
497 // and all the implemented and extended types. This is already
498 // do for us when we get the element of the return type.
499
500 getCovariantReturns(children, m.getType());
501
Stuart McCulloch2286f232012-06-15 13:27:53 +0000502 Element member = new Element(Type.METHOD, m.getName() + toString(m.getPrototype()), children, add, remove,
503 "synthetic");
Stuart McCullochbb014372012-06-07 21:57:32 +0000504
505 if (!members.add(member)) {
506 members.remove(member);
507 members.add(member);
508 }
509 }
510
511 for (Clazz.FieldDef f : fields) {
512 List<Element> children = annotations.get(f);
513 if (children == null)
514 children = new ArrayList<Element>();
515
516 // Fields can have a constant value, this is a new element
517 if (f.getConstant() != null) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000518 children.add(new Element(Type.CONSTANT, f.getConstant().toString(), null, CHANGED, CHANGED, null));
Stuart McCullochbb014372012-06-07 21:57:32 +0000519 }
520
521 access(children, f.getAccess(), f.isDeprecated());
Stuart McCulloch2286f232012-06-15 13:27:53 +0000522 Element member = new Element(Type.FIELD, f.getType().getFQN() + " " + f.getName(), children, MINOR, MAJOR,
523 null);
Stuart McCullochbb014372012-06-07 21:57:32 +0000524
525 if (!members.add(member)) {
526 members.remove(member);
527 members.add(member);
528 }
529 }
530
531 access(members, clazz.getAccess(), clazz.isDeprecated());
532
533 // And make the result
Stuart McCulloch2286f232012-06-15 13:27:53 +0000534 Element s = new Element(type, fqn, members, MINOR, MAJOR, comment.length() == 0 ? null : comment.toString());
Stuart McCullochbb014372012-06-07 21:57:32 +0000535 cache.put(clazz, s);
536 return s;
537 }
538
539 private String toString(TypeRef[] prototype) {
540 StringBuilder sb = new StringBuilder();
541 sb.append("(");
542 String del = "";
543 for (TypeRef ref : prototype) {
544 sb.append(del);
545 sb.append(ref.getFQN());
546 del = ",";
547 }
548 sb.append(")");
549 return sb.toString();
550 }
551
552 static Element BOOLEAN_R = new Element(RETURN, "boolean");
553 static Element BYTE_R = new Element(RETURN, "byte");
554 static Element SHORT_R = new Element(RETURN, "short");
555 static Element CHAR_R = new Element(RETURN, "char");
556 static Element INT_R = new Element(RETURN, "int");
557 static Element LONG_R = new Element(RETURN, "long");
558 static Element FLOAT_R = new Element(RETURN, "float");
559 static Element DOUBLE_R = new Element(RETURN, "double");
560
561 private void getCovariantReturns(Collection<Element> elements, TypeRef type) throws Exception {
562 if (type == null || type.isObject())
563 return;
564
565 if (type.isPrimitive()) {
566 if (type.getFQN().equals("void"))
567 return;
568
569 String name = type.getBinary();
570 Element e;
571 switch (name.charAt(0)) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000572 case 'Z' :
573 e = BOOLEAN_R;
574 break;
575 case 'S' :
576 e = SHORT_R;
577 break;
578 case 'I' :
579 e = INT_R;
580 break;
581 case 'B' :
582 e = BYTE_R;
583 break;
584 case 'C' :
585 e = CHAR_R;
586 break;
587 case 'J' :
588 e = LONG_R;
589 break;
590 case 'F' :
591 e = FLOAT_R;
592 break;
593 case 'D' :
594 e = DOUBLE_R;
595 break;
Stuart McCullochbb014372012-06-07 21:57:32 +0000596
Stuart McCulloch2286f232012-06-15 13:27:53 +0000597 default :
598 throw new IllegalArgumentException("Unknown primitive " + type);
Stuart McCullochbb014372012-06-07 21:57:32 +0000599 }
600 elements.add(e);
601 return;
602 }
603
604 List<Element> set = covariant.get(type);
605 if (set != null) {
606 elements.addAll(set);
607 return;
608 }
609
610 Element current = new Element(RETURN, type.getFQN());
611 Clazz clazz = analyzer.findClass(type);
612 if (clazz == null) {
613 elements.add(current);
614 return;
615 }
616
617 set = Create.list();
618 set.add(current);
619 getCovariantReturns(set, clazz.getSuper());
620
621 TypeRef[] interfaces = clazz.getInterfaces();
622 if (interfaces != null)
623 for (TypeRef intf : interfaces) {
624 getCovariantReturns(set, intf);
625 }
626
627 covariant.put(type, set);
628 elements.addAll(set);
629 }
630
Stuart McCullochd4826102012-06-26 16:34:24 +0000631 private static void access(Collection<Element> children, int access, @SuppressWarnings("unused") boolean deprecated) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000632 if (!isPublic(access))
633 children.add(PROTECTED);
634 if (isAbstract(access))
635 children.add(ABSTRACT);
636 if (isFinal(access))
637 children.add(FINAL);
638 if (isStatic(access))
639 children.add(STATIC);
640
641 // Ignore for now
642 // if (deprecated)
643 // children.add(DEPRECATED);
644
645 }
646
647}