blob: ce709df3f0cc61f3bef2ff0cda296fcab45e356d [file] [log] [blame]
Stuart McCullochf29ef3f2008-12-04 07:58:07 +00001/* Copyright 2006 aQute SARL
2 * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */
3package aQute.lib.osgi;
4
5import java.io.*;
6import java.nio.*;
7import java.util.*;
8
9public class Clazz {
10 public static enum QUERY {
11 IMPLEMENTS, EXTENDS, IMPORTS, NAMED, ANY, VERSION
12 };
13
14 static protected class Assoc {
15 Assoc(byte tag, int a, int b) {
16 this.tag = tag;
17 this.a = a;
18 this.b = b;
19 }
20
21 byte tag;
22 int a;
23 int b;
24 }
25
26 final static byte SkipTable[] = { 0, // 0 non existent
27 -1, // 1 CONSTANT_utf8 UTF 8, handled in
28 // method
29 -1, // 2
30 4, // 3 CONSTANT_Integer
31 4, // 4 CONSTANT_Float
32 8, // 5 CONSTANT_Long (index +=2!)
33 8, // 6 CONSTANT_Double (index +=2!)
34 -1, // 7 CONSTANT_Class
35 2, // 8 CONSTANT_String
36 4, // 9 CONSTANT_FieldRef
37 4, // 10 CONSTANT_MethodRef
38 4, // 11 CONSTANT_InterfaceMethodRef
39 4, // 12 CONSTANT_NameAndType
40 };
41
42 String className;
43 Object pool[];
44 int intPool[];
45 Map<String, Map<String, String>> imports = new HashMap<String, Map<String, String>>();
46 String path;
47
48 // static String type = "([BCDFIJSZ\\[]|L[^<>]+;)";
49 // static Pattern descriptor = Pattern.compile("\\(" + type + "*\\)(("
50 // + type + ")|V)");
51 int minor = 0;
52 int major = 0;
53
54 String sourceFile;
55 Set<String> xref;
56 Set<Integer> classes;
57 Set<Integer> descriptors;
58 int forName = 0;
59 int class$ = 0;
60 String[] interfaces;
61 String zuper;
Stuart McCullocha75a5152009-01-29 07:46:54 +000062 ClassDataCollector cd = new ClassDataCollector() {
63 public void addReference(
64 String token) {
65 }
66
67 public void classBegin(
68 int access,
69 String name) {
70 }
71
72 public void classEnd() {
73 }
74
75 public void extendsClass(
76 String name) {
77 }
78
79 public void field(
80 int access,
81 String descriptor) {
82 }
83
84 public void implementsInterfaces(
85 String[] name) {
86 }
87
88 public void method(
89 int access,
90 String descriptor) {
91 }
92 };
Stuart McCullochf29ef3f2008-12-04 07:58:07 +000093
94 public Clazz(String path) {
95 this.path = path;
96 }
97
98 public Clazz(String path, InputStream in) throws IOException {
99 this.path = path;
100 DataInputStream din = new DataInputStream(in);
101 parseClassFile(din);
102 din.close();
103 }
104
105 Set<String> parseClassFile(DataInputStream in) throws IOException {
Stuart McCullocha75a5152009-01-29 07:46:54 +0000106
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000107 xref = new HashSet<String>();
108 classes = new HashSet<Integer>();
109 descriptors = new HashSet<Integer>();
110
111 boolean crawl = false; // Crawl the byte code
112 int magic = in.readInt();
113 if (magic != 0xCAFEBABE)
114 throw new IOException("Not a valid class file (no CAFEBABE header)");
115
116 minor = in.readUnsignedShort(); // minor version
117 major = in.readUnsignedShort(); // major version
118 int count = in.readUnsignedShort();
119 pool = new Object[count];
120 intPool = new int[count];
121
122 process: for (int poolIndex = 1; poolIndex < count; poolIndex++) {
123 byte tag = in.readByte();
124 switch (tag) {
125 case 0:
126 break process;
127 case 1:
128 constantUtf8(in, poolIndex);
129 break;
130
131 // For some insane optimization reason are
132 // the long and the double two entries in the
133 // constant pool. See 4.4.5
134 case 5:
135 constantLong(in, poolIndex);
136 poolIndex++;
137 break;
138
139 case 6:
140 constantDouble(in, poolIndex);
141 poolIndex++;
142 break;
143
144 case 7:
145 constantClass(in, poolIndex);
146 break;
147
148 case 8:
149 constantString(in, poolIndex);
150 break;
151
152 case 10: // Method ref
153 methodRef(in, poolIndex);
154 break;
155
156 // Name and Type
157 case 12:
158 nameAndType(in, poolIndex, tag);
159 break;
160
161 // We get the skip count for each record type
162 // from the SkipTable. This will also automatically
163 // abort when
164 default:
165 if (tag == 2)
166 throw new IOException("Invalid tag " + tag);
167 in.skipBytes(SkipTable[tag]);
168 break;
169 }
170 }
171
172 pool(pool, intPool);
173 /*
174 * Parse after the constant pool, code thanks to Hans Christian
175 * Falkenberg
176 */
177
Stuart McCullocha75a5152009-01-29 07:46:54 +0000178 int access_flags = in.readUnsignedShort(); // access
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000179 int this_class = in.readUnsignedShort();
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000180 className = (String) pool[intPool[this_class]];
Stuart McCullocha75a5152009-01-29 07:46:54 +0000181 cd.classBegin(access_flags, className);
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000182
Stuart McCullocha75a5152009-01-29 07:46:54 +0000183 try {
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000184
Stuart McCullocha75a5152009-01-29 07:46:54 +0000185 int super_class = in.readUnsignedShort();
186 zuper = (String) pool[intPool[super_class]];
187 if (zuper != null) {
188 addReference(zuper);
189 cd.extendsClass(zuper);
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000190 }
191
Stuart McCullocha75a5152009-01-29 07:46:54 +0000192 int interfacesCount = in.readUnsignedShort();
193 if (interfacesCount > 0) {
194 interfaces = new String[interfacesCount];
195 for (int i = 0; i < interfacesCount; i++)
196 interfaces[i] = (String) pool[intPool[in
197 .readUnsignedShort()]];
198 cd.implementsInterfaces(interfaces);
199 }
200
201 int fieldsCount = in.readUnsignedShort();
202 for (int i = 0; i < fieldsCount; i++) {
203 access_flags = in.readUnsignedShort(); // skip access flags
204 int name_index = in.readUnsignedShort();
205 int descriptor_index = in.readUnsignedShort();
206
207 // Java prior to 1.5 used a weird
208 // static variable to hold the com.X.class
209 // result construct. If it did not find it
210 // it would create a variable class$com$X
211 // that would be used to hold the class
212 // object gotten with Class.forName ...
213 // Stupidly, they did not actively use the
214 // class name for the field type, so bnd
215 // would not see a reference. We detect
216 // this case and add an artificial descriptor
217 String name = pool[name_index].toString(); // name_index
218 if (name.startsWith("class$")) {
219 crawl = true;
220 }
221 cd.field(access_flags, pool[descriptor_index].toString());
222 descriptors.add(new Integer(descriptor_index));
223 doAttributes(in, false);
224 }
225
226 //
227 // Check if we have to crawl the code to find
228 // the ldc(_w) <string constant> invokestatic Class.forName
229 // if so, calculate the method ref index so we
230 // can do this efficiently
231 //
232 if (crawl) {
233 forName = findMethod("java/lang/Class", "forName",
234 "(Ljava/lang/String;)Ljava/lang/Class;");
235 class$ = findMethod(className, "class$",
236 "(Ljava/lang/String;)Ljava/lang/Class;");
237 }
238
239 //
240 // Handle the methods
241 //
242 int methodCount = in.readUnsignedShort();
243 for (int i = 0; i < methodCount; i++) {
244 access_flags = in.readUnsignedShort();
245 /* int name_index = */in.readUnsignedShort();
246 int descriptor_index = in.readUnsignedShort();
247 // String s = (String) pool[name_index];
248 descriptors.add(new Integer(descriptor_index));
249 cd.method(access_flags, pool[descriptor_index].toString());
250 doAttributes(in, crawl);
251 }
252
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000253 doAttributes(in, false);
Stuart McCullocha75a5152009-01-29 07:46:54 +0000254
255 //
256 // Now iterate over all classes we found and
257 // parse those as well. We skip duplicates
258 //
259
260 for (Iterator<Integer> e = classes.iterator(); e.hasNext();) {
261 int class_index = e.next().shortValue();
262 doClassReference((String) pool[class_index]);
263 }
264
265 //
266 // Parse all the descriptors we found
267 //
268
269 for (Iterator<Integer> e = descriptors.iterator(); e.hasNext();) {
270 Integer index = e.next();
271 String prototype = (String) pool[index.intValue()];
272 if (prototype != null)
273 parseDescriptor(prototype);
274 else
275 System.err.println("Unrecognized descriptor: " + index);
276 }
277 Set<String> xref = this.xref;
278 reset();
279 return xref;
280 } finally {
281 cd.classEnd();
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000282 }
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000283 }
284
285 protected void pool(Object[] pool, int[] intPool) {
286 }
287
288 /**
289 * @param in
290 * @param poolIndex
291 * @param tag
292 * @throws IOException
293 */
294 protected void nameAndType(DataInputStream in, int poolIndex, byte tag)
295 throws IOException {
296 int name_index = in.readUnsignedShort();
297 int descriptor_index = in.readUnsignedShort();
298 descriptors.add(new Integer(descriptor_index));
299 pool[poolIndex] = new Assoc(tag, name_index, descriptor_index);
300 }
301
302 /**
303 * @param in
304 * @param poolIndex
305 * @param tag
306 * @throws IOException
307 */
308 private void methodRef(DataInputStream in, int poolIndex)
309 throws IOException {
310 int class_index = in.readUnsignedShort();
311 int name_and_type_index = in.readUnsignedShort();
312 pool[poolIndex] = new Assoc((byte) 10, class_index, name_and_type_index);
313 }
314
315 /**
316 * @param in
317 * @param poolIndex
318 * @throws IOException
319 */
320 private void constantString(DataInputStream in, int poolIndex)
321 throws IOException {
322 int string_index = in.readUnsignedShort();
323 intPool[poolIndex] = string_index;
324 }
325
326 /**
327 * @param in
328 * @param poolIndex
329 * @throws IOException
330 */
331 protected void constantClass(DataInputStream in, int poolIndex)
332 throws IOException {
333 int class_index = in.readUnsignedShort();
334 classes.add(new Integer(class_index));
335 intPool[poolIndex] = class_index;
336 }
337
338 /**
339 * @param in
340 * @throws IOException
341 */
342 protected void constantDouble(DataInputStream in, int poolIndex)
343 throws IOException {
344 in.skipBytes(8);
345 }
346
347 /**
348 * @param in
349 * @throws IOException
350 */
351 protected void constantLong(DataInputStream in, int poolIndex)
352 throws IOException {
353 in.skipBytes(8);
354 }
355
356 /**
357 * @param in
358 * @param poolIndex
359 * @throws IOException
360 */
361 protected void constantUtf8(DataInputStream in, int poolIndex)
362 throws IOException {
363 // CONSTANT_Utf8
364
365 String name = in.readUTF();
366 xref.add(name);
367 pool[poolIndex] = name;
368 }
369
370 /**
371 * Find a method reference in the pool that points to the given class,
372 * methodname and descriptor.
373 *
374 * @param clazz
375 * @param methodname
376 * @param descriptor
377 * @return index in constant pool
378 */
379 private int findMethod(String clazz, String methodname, String descriptor) {
380 for (int i = 1; i < pool.length; i++) {
381 if (pool[i] instanceof Assoc) {
382 Assoc methodref = (Assoc) pool[i];
383 if (methodref.tag == 10) {
384 // Method ref
385 int class_index = methodref.a;
386 int class_name_index = intPool[class_index];
387 if (clazz.equals(pool[class_name_index])) {
388 int name_and_type_index = methodref.b;
389 Assoc name_and_type = (Assoc) pool[name_and_type_index];
390 if (name_and_type.tag == 12) {
391 // Name and Type
392 int name_index = name_and_type.a;
393 int type_index = name_and_type.b;
394 if (methodname.equals(pool[name_index])) {
395 if (descriptor.equals(pool[type_index])) {
396 return i;
397 }
398 }
399 }
400 }
401 }
402 }
403 }
404 return -1;
405 }
406
407 private void doClassReference(String next) {
408 if (next != null) {
409 String normalized = normalize(next);
410 if (normalized != null) {
Stuart McCullocha75a5152009-01-29 07:46:54 +0000411 cd.addReference(next);
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000412 String pack = getPackage(normalized);
413 packageReference(pack);
414 }
415 } else
416 throw new IllegalArgumentException("Invalid class, parent=");
417 }
418
419 /**
420 * Called for each attribute in the class, field, or method.
421 *
422 * @param in
423 * The stream
424 * @throws IOException
425 */
426 private void doAttributes(DataInputStream in, boolean crawl)
427 throws IOException {
428 int attributesCount = in.readUnsignedShort();
429 for (int j = 0; j < attributesCount; j++) {
430 // skip name CONSTANT_Utf8 pointer
431 doAttribute(in, crawl);
432 }
433 }
434
435 /**
436 * Process a single attribute, if not recognized, skip it.
437 *
438 * @param in
439 * the data stream
440 * @throws IOException
441 */
442 private void doAttribute(DataInputStream in, boolean crawl)
443 throws IOException {
444 int attribute_name_index = in.readUnsignedShort();
445 String attributeName = (String) pool[attribute_name_index];
446 if (attribute_name_index == 560)
447 System.out.println("Index " + attribute_name_index + ":"
448 + attributeName);
449 long attribute_length = in.readInt();
450 attribute_length &= 0xFFFFFFFF;
451 if ("RuntimeVisibleAnnotations".equals(attributeName))
452 doAnnotations(in);
453 else if ("RuntimeVisibleParameterAnnotations".equals(attributeName))
454 doParameterAnnotations(in);
455 else if ("SourceFile".equals(attributeName))
456 doSourceFile(in);
457 else if ("Code".equals(attributeName) && crawl)
458 doCode(in);
459 else {
460 if (attribute_length > 0x7FFFFFFF) {
461 throw new IllegalArgumentException("Attribute > 2Gb");
462 }
463 in.skipBytes((int) attribute_length);
464 }
465 }
466
467 /**
468 * <pre>
469 * Code_attribute {
470 * u2 attribute_name_index;
471 * u4 attribute_length;
472 * u2 max_stack;
473 * u2 max_locals;
474 * u4 code_length;
475 * u1 code[code_length];
476 * u2 exception_table_length;
477 * { u2 start_pc;
478 * u2 end_pc;
479 * u2 handler_pc;
480 * u2 catch_type;
481 * } exception_table[exception_table_length];
482 * u2 attributes_count;
483 * attribute_info attributes[attributes_count];
484 * }
485 * </pre>
486 *
487 * @param in
488 * @param pool
489 * @throws IOException
490 */
491 private void doCode(DataInputStream in) throws IOException {
492 /* int max_stack = */in.readUnsignedShort();
493 /* int max_locals = */in.readUnsignedShort();
494 int code_length = in.readInt();
495 byte code[] = new byte[code_length];
496 in.readFully(code);
497 crawl(code);
498 int exception_table_length = in.readUnsignedShort();
499 in.skipBytes(exception_table_length * 8);
500 doAttributes(in, false);
501 }
502
503 /**
504 * We must find Class.forName references ...
505 *
506 * @param code
507 */
508 protected void crawl(byte[] code) {
509 ByteBuffer bb = ByteBuffer.wrap(code);
510 bb.order(ByteOrder.BIG_ENDIAN);
511 int lastReference = -1;
512
513 while (bb.remaining() > 0) {
514 int instruction = 0xFF & bb.get();
515 switch (instruction) {
516 case OpCodes.ldc:
517 lastReference = 0xFF & bb.get();
518 break;
519
520 case OpCodes.ldc_w:
521 lastReference = 0xFFFF & bb.getShort();
522 break;
523
524 case OpCodes.invokestatic:
525 int methodref = 0xFFFF & bb.getShort();
526 if ((methodref == forName || methodref == class$)
527 && lastReference != -1
528 && pool[intPool[lastReference]] instanceof String) {
529 String clazz = (String) pool[intPool[lastReference]];
530 doClassReference(clazz.replace('.', '/'));
531 }
532 break;
533
534 case OpCodes.tableswitch:
535 // Skip to place divisible by 4
536 while ((bb.position() & 0x3) != 0)
537 bb.get();
538 /* int deflt = */
539 bb.getInt();
540 int low = bb.getInt();
541 int high = bb.getInt();
542 bb.position(bb.position() + (high - low + 1) * 4);
543 lastReference = -1;
544 break;
545
546 case OpCodes.lookupswitch:
547 // Skip to place divisible by 4
548 while ((bb.position() & 0x3) != 0)
549 bb.get();
550 /* deflt = */
551 bb.getInt();
552 int npairs = bb.getInt();
553 bb.position(bb.position() + npairs * 8);
554 lastReference = -1;
555 break;
556
557 default:
558 lastReference = -1;
559 bb.position(bb.position() + OpCodes.OFFSETS[instruction]);
560 }
561 }
562 }
563
564 private void doSourceFile(DataInputStream in) throws IOException {
565 int sourcefile_index = in.readUnsignedShort();
566 this.sourceFile = pool[sourcefile_index].toString();
567 }
568
569 private void doParameterAnnotations(DataInputStream in) throws IOException {
570 int num_parameters = in.readUnsignedByte();
571 for (int p = 0; p < num_parameters; p++) {
572 int num_annotations = in.readUnsignedShort(); // # of annotations
573 for (int a = 0; a < num_annotations; a++) {
574 doAnnotation(in);
575 }
576 }
577 }
578
579 private void doAnnotations(DataInputStream in) throws IOException {
580 int num_annotations = in.readUnsignedShort(); // # of annotations
581 for (int a = 0; a < num_annotations; a++) {
582 doAnnotation(in);
583 }
584 }
585
586 private void doAnnotation(DataInputStream in) throws IOException {
587 int type_index = in.readUnsignedShort();
588 descriptors.add(new Integer(type_index));
589 int num_element_value_pairs = in.readUnsignedShort();
590 for (int v = 0; v < num_element_value_pairs; v++) {
591 /* int element_name_index = */in.readUnsignedShort();
592 doElementValue(in);
593 }
594 }
595
596 private void doElementValue(DataInputStream in) throws IOException {
597 int tag = in.readUnsignedByte();
598 switch (tag) {
599 case 'B':
600 case 'C':
601 case 'D':
602 case 'F':
603 case 'I':
604 case 'J':
605 case 'S':
606 case 'Z':
607 case 's':
608 /* int const_value_index = */
609 in.readUnsignedShort();
610 break;
611
612 case 'e':
613 int type_name_index = in.readUnsignedShort();
614 descriptors.add(new Integer(type_name_index));
615 /* int const_name_index = */
616 in.readUnsignedShort();
617 break;
618
619 case 'c':
620 int class_info_index = in.readUnsignedShort();
621 descriptors.add(new Integer(class_info_index));
622 break;
623
624 case '@':
625 doAnnotation(in);
626 break;
627
628 case '[':
629 int num_values = in.readUnsignedShort();
630 for (int i = 0; i < num_values; i++) {
631 doElementValue(in);
632 }
633 break;
634
635 default:
636 throw new IllegalArgumentException(
637 "Invalid value for Annotation ElementValue tag " + tag);
638 }
639 }
640
641 void packageReference(String pack) {
642 if (pack.indexOf('<') >= 0)
643 System.out.println("Oops: " + pack);
644 if (!imports.containsKey(pack))
645 imports.put(pack, new LinkedHashMap<String, String>());
646 }
647
Stuart McCullocha75a5152009-01-29 07:46:54 +0000648 public void parseDescriptor(String prototype) {
649 if (prototype.startsWith("("))
650 parseMethodDescriptor(prototype);
651 else
652 addReference(prototype);
653 }
654
655 void parseMethodDescriptor(String prototype) {
656 int last = prototype.indexOf(')');
657 if (last < 0)
658 throw new IllegalArgumentException(
659 "Invalid method descriptor in class file: " + className
660 + " " + prototype);
661
662 for (int i = 1; i < last; i++) {
663 if (prototype.charAt(i) == 'L') {
664 int next = prototype.indexOf(';', i);
665 addReference(prototype.substring(i, next));
666 i = next;
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000667 }
668 }
Stuart McCullocha75a5152009-01-29 07:46:54 +0000669 addReference(prototype.substring(last + 1));
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000670 }
671
672 private void addReference(String token) {
Stuart McCullocha75a5152009-01-29 07:46:54 +0000673 cd.addReference(token);
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000674 while (token.startsWith("["))
675 token = token.substring(1);
676
677 if (token.startsWith("L")) {
678 String clazz = normalize(token.substring(1));
679 if (clazz.startsWith("java/"))
680 return;
681 String pack = getPackage(clazz);
682 packageReference(pack);
683 }
684 }
685
686 static String normalize(String s) {
687 if (s.startsWith("[L"))
688 return normalize(s.substring(2));
689 if (s.startsWith("["))
690 if (s.length() == 2)
691 return null;
692 else
693 return normalize(s.substring(1));
694 if (s.endsWith(";"))
695 return normalize(s.substring(0, s.length() - 1));
696 return s + ".class";
697 }
698
699 public static String getPackage(String clazz) {
700 int n = clazz.lastIndexOf('/');
701 if (n < 0)
702 return ".";
703 return clazz.substring(0, n).replace('/', '.');
704 }
705
706 public Map<String, Map<String, String>> getReferred() {
707 return imports;
708 }
709
710 String getClassName() {
711 return className;
712 }
713
714 public String getPath() {
715 return path;
716 }
717
718 public Set<String> xref(InputStream in) throws IOException {
719 DataInputStream din = new DataInputStream(in);
720 Set<String> set = parseClassFile(din);
721 din.close();
722 return set;
723 }
724
725 public String getSourceFile() {
726 return sourceFile;
727 }
728
729 /**
730 * .class construct for different compilers
731 *
732 * sun 1.1 Detect static variable class$com$acme$MyClass 1.2 " 1.3 " 1.4 "
733 * 1.5 ldc_w (class) 1.6 "
734 *
735 * eclipse 1.1 class$0, ldc (string), invokestatic Class.forName 1.2 " 1.3 "
736 * 1.5 ldc (class) 1.6 "
737 *
738 * 1.5 and later is not an issue, sun pre 1.5 is easy to detect the static
739 * variable that decodes the class name. For eclipse, the class$0 gives away
740 * we have a reference encoded in a string.
741 * compilerversions/compilerversions.jar contains test versions of all
742 * versions/compilers.
743 */
744
745 public void reset() {
746 pool = null;
747 intPool = null;
748 xref = null;
749 classes = null;
750 descriptors = null;
751 }
752
Stuart McCullocha75a5152009-01-29 07:46:54 +0000753 public boolean is(QUERY query, Instruction instr,
754 Map<String, Clazz> classspace) {
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000755 switch (query) {
756 case ANY:
757 return true;
758
759 case NAMED:
Stuart McCullocha75a5152009-01-29 07:46:54 +0000760 if (instr.matches(getClassName()))
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000761 return !instr.isNegated();
762 return false;
Stuart McCullocha75a5152009-01-29 07:46:54 +0000763
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000764 case VERSION:
765 String v = major + "/" + minor;
Stuart McCullocha75a5152009-01-29 07:46:54 +0000766 if (instr.matches(v))
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000767 return !instr.isNegated();
768 return false;
Stuart McCullocha75a5152009-01-29 07:46:54 +0000769
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000770 case IMPLEMENTS:
Stuart McCullocha75a5152009-01-29 07:46:54 +0000771 for (int i = 0; interfaces != null && i < interfaces.length; i++) {
772 if (instr.matches(interfaces[i]))
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000773 return !instr.isNegated();
774 }
775 break;
776 case EXTENDS:
Stuart McCullocha75a5152009-01-29 07:46:54 +0000777 if (zuper == null)
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000778 return false;
Stuart McCullocha75a5152009-01-29 07:46:54 +0000779
780 if (instr.matches(zuper))
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000781 return !instr.isNegated();
782 break;
Stuart McCullocha75a5152009-01-29 07:46:54 +0000783
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000784 case IMPORTS:
Stuart McCullocha75a5152009-01-29 07:46:54 +0000785 for (String imp : imports.keySet()) {
786 if (instr.matches(imp.replace('.', '/')))
787 return !instr.isNegated();
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000788 }
789 }
Stuart McCullocha75a5152009-01-29 07:46:54 +0000790
791 if (zuper == null || classspace == null)
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000792 return false;
793
794 Clazz clazz = classspace.get(zuper);
795 if (clazz == null)
796 return false;
797
798 return clazz.is(query, instr, classspace);
799 }
800
801 public String toString() {
802 return getFQN();
803 }
804
805 public String getFQN() {
806 return getClassName().replace('/', '.');
807 }
Stuart McCullocha75a5152009-01-29 07:46:54 +0000808
809 public void setClassDataCollector(ClassDataCollector cd) {
810 this.cd = cd;
811 }
Stuart McCullochf29ef3f2008-12-04 07:58:07 +0000812}