1 /*
   2  * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 package com.sun.org.apache.bcel.internal.classfile;
  21 
  22 import java.io.ByteArrayOutputStream;
  23 import java.io.DataOutputStream;
  24 import java.io.File;
  25 import java.io.FileOutputStream;
  26 import java.io.IOException;
  27 import java.io.OutputStream;
  28 import java.util.ArrayList;
  29 import java.util.Objects;
  30 import java.util.StringTokenizer;
  31 import java.util.List;
  32 import java.util.Set;
  33 import java.util.TreeSet;
  34 
  35 import com.sun.org.apache.bcel.internal.Const;
  36 import com.sun.org.apache.bcel.internal.generic.Type;
  37 import com.sun.org.apache.bcel.internal.util.BCELComparator;
  38 import com.sun.org.apache.bcel.internal.util.ClassQueue;
  39 import com.sun.org.apache.bcel.internal.util.SyntheticRepository;
  40 
  41 /**
  42  * Represents a Java class, i.e., the data structures, constant pool,
  43  * fields, methods and commands contained in a Java .class file.
  44  * See <a href="http://docs.oracle.com/javase/specs/">JVM specification</a> for details.
  45  * The intent of this class is to represent a parsed or otherwise existing
  46  * class file.  Those interested in programatically generating classes
  47  * should see the <a href="../generic/ClassGen.html">ClassGen</a> class.
  48 
  49  * @see com.sun.org.apache.bcel.internal.generic.ClassGen
  50  * @LastModified: Jan 2020
  51  */
  52 public class JavaClass extends AccessFlags implements Cloneable, Node, Comparable<JavaClass> {
  53 
  54     private String file_name;
  55     private String package_name;
  56     private String source_file_name = "<Unknown>";
  57     private int class_name_index;
  58     private int superclass_name_index;
  59     private String class_name;
  60     private String superclass_name;
  61     private int major;
  62     private int minor; // Compiler version
  63     private ConstantPool constant_pool; // Constant pool
  64     private int[] interfaces; // implemented interfaces
  65     private String[] interface_names;
  66     private Field[] fields; // Fields, i.e., variables of class
  67     private Method[] methods; // methods defined in the class
  68     private Attribute[] attributes; // attributes defined in the class
  69     private AnnotationEntry[] annotations;   // annotations defined on the class
  70     private byte source = HEAP; // Generated in memory
  71     private boolean isAnonymous = false;
  72     private boolean isNested = false;
  73     private boolean computedNestedTypeStatus = false;
  74     public static final byte HEAP = 1;
  75     public static final byte FILE = 2;
  76     public static final byte ZIP = 3;
  77     private static final boolean debug = false;
  78 
  79     private static BCELComparator bcelComparator = new BCELComparator() {
  80 
  81         @Override
  82         public boolean equals( final Object o1, final Object o2 ) {
  83             final JavaClass THIS = (JavaClass) o1;
  84             final JavaClass THAT = (JavaClass) o2;
  85             return Objects.equals(THIS.getClassName(), THAT.getClassName());
  86         }
  87 
  88 
  89         @Override
  90         public int hashCode( final Object o ) {
  91             final JavaClass THIS = (JavaClass) o;
  92             return THIS.getClassName().hashCode();
  93         }
  94     };
  95     /**
  96      * In cases where we go ahead and create something,
  97      * use the default SyntheticRepository, because we
  98      * don't know any better.
  99      */
 100     private transient com.sun.org.apache.bcel.internal.util.Repository repository
 101             = SyntheticRepository.getInstance();
 102 
 103 
 104     /**
 105      * Constructor gets all contents as arguments.
 106      *
 107      * @param class_name_index Index into constant pool referencing a
 108      * ConstantClass that represents this class.
 109      * @param superclass_name_index Index into constant pool referencing a
 110      * ConstantClass that represents this class's superclass.
 111      * @param file_name File name
 112      * @param major Major compiler version
 113      * @param minor Minor compiler version
 114      * @param access_flags Access rights defined by bit flags
 115      * @param constant_pool Array of constants
 116      * @param interfaces Implemented interfaces
 117      * @param fields Class fields
 118      * @param methods Class methods
 119      * @param attributes Class attributes
 120      * @param source Read from file or generated in memory?
 121      */
 122     public JavaClass(final int class_name_index, final int superclass_name_index,
 123             final String file_name, final int major, final int minor, final int access_flags,
 124             final ConstantPool constant_pool, int[] interfaces, Field[] fields,
 125             Method[] methods, Attribute[] attributes, final byte source) {
 126         super(access_flags);
 127         if (interfaces == null) {
 128             interfaces = new int[0];
 129         }
 130         if (attributes == null) {
 131             attributes = new Attribute[0];
 132         }
 133         if (fields == null) {
 134             fields = new Field[0];
 135         }
 136         if (methods == null) {
 137             methods = new Method[0];
 138         }
 139         this.class_name_index = class_name_index;
 140         this.superclass_name_index = superclass_name_index;
 141         this.file_name = file_name;
 142         this.major = major;
 143         this.minor = minor;
 144         this.constant_pool = constant_pool;
 145         this.interfaces = interfaces;
 146         this.fields = fields;
 147         this.methods = methods;
 148         this.attributes = attributes;
 149         this.source = source;
 150         // Get source file name if available
 151         for (final Attribute attribute : attributes) {
 152             if (attribute instanceof SourceFile) {
 153                 source_file_name = ((SourceFile) attribute).getSourceFileName();
 154                 break;
 155             }
 156         }
 157         /* According to the specification the following entries must be of type
 158          * `ConstantClass' but we check that anyway via the
 159          * `ConstPool.getConstant' method.
 160          */
 161         class_name = constant_pool.getConstantString(class_name_index, Const.CONSTANT_Class);
 162         class_name = Utility.compactClassName(class_name, false);
 163         final int index = class_name.lastIndexOf('.');
 164         if (index < 0) {
 165             package_name = "";
 166         } else {
 167             package_name = class_name.substring(0, index);
 168         }
 169         if (superclass_name_index > 0) {
 170             // May be zero -> class is java.lang.Object
 171             superclass_name = constant_pool.getConstantString(superclass_name_index,
 172                     Const.CONSTANT_Class);
 173             superclass_name = Utility.compactClassName(superclass_name, false);
 174         } else {
 175             superclass_name = "java.lang.Object";
 176         }
 177         interface_names = new String[interfaces.length];
 178         for (int i = 0; i < interfaces.length; i++) {
 179             final String str = constant_pool.getConstantString(interfaces[i], Const.CONSTANT_Class);
 180             interface_names[i] = Utility.compactClassName(str, false);
 181         }
 182     }
 183 
 184 
 185     /**
 186      * Constructor gets all contents as arguments.
 187      *
 188      * @param class_name_index Class name
 189      * @param superclass_name_index Superclass name
 190      * @param file_name File name
 191      * @param major Major compiler version
 192      * @param minor Minor compiler version
 193      * @param access_flags Access rights defined by bit flags
 194      * @param constant_pool Array of constants
 195      * @param interfaces Implemented interfaces
 196      * @param fields Class fields
 197      * @param methods Class methods
 198      * @param attributes Class attributes
 199      */
 200     public JavaClass(final int class_name_index, final int superclass_name_index,
 201             final String file_name, final int major, final int minor, final int access_flags,
 202             final ConstantPool constant_pool, final int[] interfaces, final Field[] fields,
 203             final Method[] methods, final Attribute[] attributes) {
 204         this(class_name_index, superclass_name_index, file_name, major, minor, access_flags,
 205                 constant_pool, interfaces, fields, methods, attributes, HEAP);
 206     }
 207 
 208 
 209     /**
 210      * Called by objects that are traversing the nodes of the tree implicitely
 211      * defined by the contents of a Java class. I.e., the hierarchy of methods,
 212      * fields, attributes, etc. spawns a tree of objects.
 213      *
 214      * @param v Visitor object
 215      */
 216     @Override
 217     public void accept( final Visitor v ) {
 218         v.visitJavaClass(this);
 219     }
 220 
 221 
 222     /* Print debug information depending on `JavaClass.debug'
 223      */
 224     static void Debug( final String str ) {
 225         if (debug) {
 226             System.out.println(str);
 227         }
 228     }
 229 
 230 
 231     /**
 232      * Dump class to a file.
 233      *
 234      * @param file Output file
 235      * @throws IOException
 236      */
 237     public void dump(final File file) throws IOException {
 238         final String parent = file.getParent();
 239         if (parent != null) {
 240             final File dir = new File(parent);
 241             if (!dir.mkdirs()) { // either was not created or already existed
 242                 if (!dir.isDirectory()) {
 243                     throw new IOException("Could not create the directory " + dir);
 244                 }
 245             }
 246         }
 247         try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
 248             dump(dos);
 249         }
 250     }
 251 
 252 
 253     /**
 254      * Dump class to a file named file_name.
 255      *
 256      * @param _file_name Output file name
 257      * @throws IOException
 258      */
 259     public void dump( final String _file_name ) throws IOException {
 260         dump(new File(_file_name));
 261     }
 262 
 263 
 264     /**
 265      * @return class in binary format
 266      */
 267     public byte[] getBytes() {
 268         final ByteArrayOutputStream s = new ByteArrayOutputStream();
 269         final DataOutputStream ds = new DataOutputStream(s);
 270         try {
 271             dump(ds);
 272         } catch (final IOException e) {
 273             System.err.println("Error dumping class: " + e.getMessage());
 274         } finally {
 275             try {
 276                 ds.close();
 277             } catch (final IOException e2) {
 278                 System.err.println("Error dumping class: " + e2.getMessage());
 279             }
 280         }
 281         return s.toByteArray();
 282     }
 283 
 284 
 285     /**
 286      * Dump Java class to output stream in binary format.
 287      *
 288      * @param file Output stream
 289      * @throws IOException
 290      */
 291     public void dump( final OutputStream file ) throws IOException {
 292         dump(new DataOutputStream(file));
 293     }
 294 
 295 
 296     /**
 297      * Dump Java class to output stream in binary format.
 298      *
 299      * @param file Output stream
 300      * @throws IOException
 301      */
 302     public void dump( final DataOutputStream file ) throws IOException {
 303         file.writeInt(Const.JVM_CLASSFILE_MAGIC);
 304         file.writeShort(minor);
 305         file.writeShort(major);
 306         constant_pool.dump(file);
 307         file.writeShort(super.getAccessFlags());
 308         file.writeShort(class_name_index);
 309         file.writeShort(superclass_name_index);
 310         file.writeShort(interfaces.length);
 311         for (final int interface1 : interfaces) {
 312             file.writeShort(interface1);
 313         }
 314         file.writeShort(fields.length);
 315         for (final Field field : fields) {
 316             field.dump(file);
 317         }
 318         file.writeShort(methods.length);
 319         for (final Method method : methods) {
 320             method.dump(file);
 321         }
 322         if (attributes != null) {
 323             file.writeShort(attributes.length);
 324             for (final Attribute attribute : attributes) {
 325                 attribute.dump(file);
 326             }
 327         } else {
 328             file.writeShort(0);
 329         }
 330         file.flush();
 331     }
 332 
 333 
 334     /**
 335      * @return Attributes of the class.
 336      */
 337     public Attribute[] getAttributes() {
 338         return attributes;
 339     }
 340 
 341     /**
 342      * @return Annotations on the class
 343      * @since 6.0
 344      */
 345     public AnnotationEntry[] getAnnotationEntries() {
 346         if (annotations == null) {
 347             annotations = AnnotationEntry.createAnnotationEntries(getAttributes());
 348         }
 349 
 350         return annotations;
 351     }
 352 
 353     /**
 354      * @return Class name.
 355      */
 356     public String getClassName() {
 357         return class_name;
 358     }
 359 
 360 
 361     /**
 362      * @return Package name.
 363      */
 364     public String getPackageName() {
 365         return package_name;
 366     }
 367 
 368 
 369     /**
 370      * @return Class name index.
 371      */
 372     public int getClassNameIndex() {
 373         return class_name_index;
 374     }
 375 
 376 
 377     /**
 378      * @return Constant pool.
 379      */
 380     public ConstantPool getConstantPool() {
 381         return constant_pool;
 382     }
 383 
 384 
 385     /**
 386      * @return Fields, i.e., variables of the class. Like the JVM spec
 387      * mandates for the classfile format, these fields are those specific to
 388      * this class, and not those of the superclass or superinterfaces.
 389      */
 390     public Field[] getFields() {
 391         return fields;
 392     }
 393 
 394 
 395     /**
 396      * @return File name of class, aka SourceFile attribute value
 397      */
 398     public String getFileName() {
 399         return file_name;
 400     }
 401 
 402 
 403     /**
 404      * @return Names of implemented interfaces.
 405      */
 406     public String[] getInterfaceNames() {
 407         return interface_names;
 408     }
 409 
 410 
 411     /**
 412      * @return Indices in constant pool of implemented interfaces.
 413      */
 414     public int[] getInterfaceIndices() {
 415         return interfaces;
 416     }
 417 
 418 
 419     /**
 420      * @return Major number of class file version.
 421      */
 422     public int getMajor() {
 423         return major;
 424     }
 425 
 426 
 427     /**
 428      * @return Methods of the class.
 429      */
 430     public Method[] getMethods() {
 431         return methods;
 432     }
 433 
 434 
 435     /**
 436      * @return A {@link Method} corresponding to
 437      * java.lang.reflect.Method if any
 438      */
 439     public Method getMethod( final java.lang.reflect.Method m ) {
 440         for (final Method method : methods) {
 441             if (m.getName().equals(method.getName()) && (m.getModifiers() == method.getModifiers())
 442                     && Type.getSignature(m).equals(method.getSignature())) {
 443                 return method;
 444             }
 445         }
 446         return null;
 447     }
 448 
 449 
 450     /**
 451      * @return Minor number of class file version.
 452      */
 453     public int getMinor() {
 454         return minor;
 455     }
 456 
 457 
 458     /**
 459      * @return sbsolute path to file where this class was read from
 460      */
 461     public String getSourceFileName() {
 462         return source_file_name;
 463     }
 464 
 465 
 466     /**
 467      * returns the super class name of this class. In the case that this class is
 468      * java.lang.Object, it will return itself (java.lang.Object). This is probably incorrect
 469      * but isn't fixed at this time to not break existing clients.
 470      *
 471      * @return Superclass name.
 472      */
 473     public String getSuperclassName() {
 474         return superclass_name;
 475     }
 476 
 477 
 478     /**
 479      * @return Class name index.
 480      */
 481     public int getSuperclassNameIndex() {
 482         return superclass_name_index;
 483     }
 484 
 485     /**
 486      * @param attributes .
 487      */
 488     public void setAttributes( final Attribute[] attributes ) {
 489         this.attributes = attributes;
 490     }
 491 
 492 
 493     /**
 494      * @param class_name .
 495      */
 496     public void setClassName( final String class_name ) {
 497         this.class_name = class_name;
 498     }
 499 
 500 
 501     /**
 502      * @param class_name_index .
 503      */
 504     public void setClassNameIndex( final int class_name_index ) {
 505         this.class_name_index = class_name_index;
 506     }
 507 
 508 
 509     /**
 510      * @param constant_pool .
 511      */
 512     public void setConstantPool( final ConstantPool constant_pool ) {
 513         this.constant_pool = constant_pool;
 514     }
 515 
 516 
 517     /**
 518      * @param fields .
 519      */
 520     public void setFields( final Field[] fields ) {
 521         this.fields = fields;
 522     }
 523 
 524 
 525     /**
 526      * Set File name of class, aka SourceFile attribute value
 527      */
 528     public void setFileName( final String file_name ) {
 529         this.file_name = file_name;
 530     }
 531 
 532 
 533     /**
 534      * @param interface_names .
 535      */
 536     public void setInterfaceNames( final String[] interface_names ) {
 537         this.interface_names = interface_names;
 538     }
 539 
 540 
 541     /**
 542      * @param interfaces .
 543      */
 544     public void setInterfaces( final int[] interfaces ) {
 545         this.interfaces = interfaces;
 546     }
 547 
 548 
 549     /**
 550      * @param major .
 551      */
 552     public void setMajor( final int major ) {
 553         this.major = major;
 554     }
 555 
 556 
 557     /**
 558      * @param methods .
 559      */
 560     public void setMethods( final Method[] methods ) {
 561         this.methods = methods;
 562     }
 563 
 564 
 565     /**
 566      * @param minor .
 567      */
 568     public void setMinor( final int minor ) {
 569         this.minor = minor;
 570     }
 571 
 572 
 573     /**
 574      * Set absolute path to file this class was read from.
 575      */
 576     public void setSourceFileName( final String source_file_name ) {
 577         this.source_file_name = source_file_name;
 578     }
 579 
 580 
 581     /**
 582      * @param superclass_name .
 583      */
 584     public void setSuperclassName( final String superclass_name ) {
 585         this.superclass_name = superclass_name;
 586     }
 587 
 588 
 589     /**
 590      * @param superclass_name_index .
 591      */
 592     public void setSuperclassNameIndex( final int superclass_name_index ) {
 593         this.superclass_name_index = superclass_name_index;
 594     }
 595 
 596 
 597     /**
 598      * @return String representing class contents.
 599      */
 600     @Override
 601     public String toString() {
 602         String access = Utility.accessToString(super.getAccessFlags(), true);
 603         access = access.isEmpty() ? "" : (access + " ");
 604         final StringBuilder buf = new StringBuilder(128);
 605         buf.append(access).append(Utility.classOrInterface(super.getAccessFlags())).append(" ").append(
 606                 class_name).append(" extends ").append(
 607                 Utility.compactClassName(superclass_name, false)).append('\n');
 608         final int size = interfaces.length;
 609         if (size > 0) {
 610             buf.append("implements\t\t");
 611             for (int i = 0; i < size; i++) {
 612                 buf.append(interface_names[i]);
 613                 if (i < size - 1) {
 614                     buf.append(", ");
 615                 }
 616             }
 617             buf.append('\n');
 618         }
 619         buf.append("file name\t\t").append(file_name).append('\n');
 620         buf.append("compiled from\t\t").append(source_file_name).append('\n');
 621         buf.append("compiler version\t").append(major).append(".").append(minor).append('\n');
 622         buf.append("access flags\t\t").append(super.getAccessFlags()).append('\n');
 623         buf.append("constant pool\t\t").append(constant_pool.getLength()).append(" entries\n");
 624         buf.append("ACC_SUPER flag\t\t").append(isSuper()).append("\n");
 625         if (attributes.length > 0) {
 626             buf.append("\nAttribute(s):\n");
 627             for (final Attribute attribute : attributes) {
 628                 buf.append(indent(attribute));
 629             }
 630         }
 631         final AnnotationEntry[] annotations = getAnnotationEntries();
 632         if (annotations!=null && annotations.length>0) {
 633             buf.append("\nAnnotation(s):\n");
 634             for (final AnnotationEntry annotation : annotations) {
 635                 buf.append(indent(annotation));
 636             }
 637         }
 638         if (fields.length > 0) {
 639             buf.append("\n").append(fields.length).append(" fields:\n");
 640             for (final Field field : fields) {
 641                 buf.append("\t").append(field).append('\n');
 642             }
 643         }
 644         if (methods.length > 0) {
 645             buf.append("\n").append(methods.length).append(" methods:\n");
 646             for (final Method method : methods) {
 647                 buf.append("\t").append(method).append('\n');
 648             }
 649         }
 650         return buf.toString();
 651     }
 652 
 653 
 654     private static String indent( final Object obj ) {
 655         final StringTokenizer tok = new StringTokenizer(obj.toString(), "\n");
 656         final StringBuilder buf = new StringBuilder();
 657         while (tok.hasMoreTokens()) {
 658             buf.append("\t").append(tok.nextToken()).append("\n");
 659         }
 660         return buf.toString();
 661     }
 662 
 663 
 664     /**
 665      * @return deep copy of this class
 666      */
 667     public JavaClass copy() {
 668         JavaClass c = null;
 669         try {
 670             c = (JavaClass) clone();
 671             c.constant_pool = constant_pool.copy();
 672             c.interfaces = interfaces.clone();
 673             c.interface_names = interface_names.clone();
 674             c.fields = new Field[fields.length];
 675             for (int i = 0; i < fields.length; i++) {
 676                 c.fields[i] = fields[i].copy(c.constant_pool);
 677             }
 678             c.methods = new Method[methods.length];
 679             for (int i = 0; i < methods.length; i++) {
 680                 c.methods[i] = methods[i].copy(c.constant_pool);
 681             }
 682             c.attributes = new Attribute[attributes.length];
 683             for (int i = 0; i < attributes.length; i++) {
 684                 c.attributes[i] = attributes[i].copy(c.constant_pool);
 685             }
 686         } catch (final CloneNotSupportedException e) {
 687             // TODO should this throw?
 688         }
 689         return c;
 690     }
 691 
 692 
 693     public final boolean isSuper() {
 694         return (super.getAccessFlags() & Const.ACC_SUPER) != 0;
 695     }
 696 
 697 
 698     public final boolean isClass() {
 699         return (super.getAccessFlags() & Const.ACC_INTERFACE) == 0;
 700     }
 701 
 702     /**
 703      * @since 6.0
 704      */
 705     public final boolean isAnonymous() {
 706         computeNestedTypeStatus();
 707         return this.isAnonymous;
 708     }
 709 
 710     /**
 711      * @since 6.0
 712      */
 713     public final boolean isNested() {
 714         computeNestedTypeStatus();
 715         return this.isNested;
 716     }
 717 
 718     private void computeNestedTypeStatus() {
 719         if (computedNestedTypeStatus) {
 720             return;
 721         }
 722         for (final Attribute attribute : this.attributes) {
 723               if (attribute instanceof InnerClasses) {
 724                   final InnerClass[] innerClasses = ((InnerClasses) attribute).getInnerClasses();
 725                   for (final InnerClass innerClasse : innerClasses) {
 726                       boolean innerClassAttributeRefersToMe = false;
 727                       String inner_class_name = constant_pool.getConstantString(innerClasse.getInnerClassIndex(),
 728                                  Const.CONSTANT_Class);
 729                       inner_class_name = Utility.compactClassName(inner_class_name, false);
 730                       if (inner_class_name.equals(getClassName())) {
 731                           innerClassAttributeRefersToMe = true;
 732                       }
 733                       if (innerClassAttributeRefersToMe) {
 734                           this.isNested = true;
 735                           if (innerClasse.getInnerNameIndex() == 0) {
 736                               this.isAnonymous = true;
 737                           }
 738                       }
 739                   }
 740               }
 741         }
 742         this.computedNestedTypeStatus = true;
 743     }
 744 
 745 
 746     /** @return returns either HEAP (generated), FILE, or ZIP
 747      */
 748     public final byte getSource() {
 749         return source;
 750     }
 751 
 752 
 753     /********************* New repository functionality *********************/
 754     /**
 755      * Gets the ClassRepository which holds its definition. By default
 756      * this is the same as SyntheticRepository.getInstance();
 757      */
 758     public com.sun.org.apache.bcel.internal.util.Repository getRepository() {
 759         return repository;
 760     }
 761 
 762 
 763     /**
 764      * Sets the ClassRepository which loaded the JavaClass.
 765      * Should be called immediately after parsing is done.
 766      */
 767     public void setRepository( final com.sun.org.apache.bcel.internal.util.Repository repository ) { // TODO make protected?
 768         this.repository = repository;
 769     }
 770 
 771 
 772     /** Equivalent to runtime "instanceof" operator.
 773      *
 774      * @return true if this JavaClass is derived from the super class
 775      * @throws ClassNotFoundException if superclasses or superinterfaces
 776      *   of this object can't be found
 777      */
 778     public final boolean instanceOf( final JavaClass super_class ) throws ClassNotFoundException {
 779         if (this.equals(super_class)) {
 780             return true;
 781         }
 782         final JavaClass[] super_classes = getSuperClasses();
 783         for (final JavaClass super_classe : super_classes) {
 784             if (super_classe.equals(super_class)) {
 785                 return true;
 786             }
 787         }
 788         if (super_class.isInterface()) {
 789             return implementationOf(super_class);
 790         }
 791         return false;
 792     }
 793 
 794 
 795     /**
 796      * @return true, if this class is an implementation of interface inter
 797      * @throws ClassNotFoundException if superclasses or superinterfaces
 798      *   of this class can't be found
 799      */
 800     public boolean implementationOf( final JavaClass inter ) throws ClassNotFoundException {
 801         if (!inter.isInterface()) {
 802             throw new IllegalArgumentException(inter.getClassName() + " is no interface");
 803         }
 804         if (this.equals(inter)) {
 805             return true;
 806         }
 807         final JavaClass[] super_interfaces = getAllInterfaces();
 808         for (final JavaClass super_interface : super_interfaces) {
 809             if (super_interface.equals(inter)) {
 810                 return true;
 811             }
 812         }
 813         return false;
 814     }
 815 
 816 
 817     /**
 818      * @return the superclass for this JavaClass object, or null if this
 819      * is java.lang.Object
 820      * @throws ClassNotFoundException if the superclass can't be found
 821      */
 822     public JavaClass getSuperClass() throws ClassNotFoundException {
 823         if ("java.lang.Object".equals(getClassName())) {
 824             return null;
 825         }
 826         return repository.loadClass(getSuperclassName());
 827     }
 828 
 829 
 830     /**
 831      * @return list of super classes of this class in ascending order, i.e.,
 832      * java.lang.Object is always the last element
 833      * @throws ClassNotFoundException if any of the superclasses can't be found
 834      */
 835     public JavaClass[] getSuperClasses() throws ClassNotFoundException {
 836         JavaClass clazz = this;
 837         final List<JavaClass> allSuperClasses = new ArrayList<>();
 838         for (clazz = clazz.getSuperClass(); clazz != null; clazz = clazz.getSuperClass()) {
 839             allSuperClasses.add(clazz);
 840         }
 841         return allSuperClasses.toArray(new JavaClass[allSuperClasses.size()]);
 842     }
 843 
 844 
 845     /**
 846      * Get interfaces directly implemented by this JavaClass.
 847      */
 848     public JavaClass[] getInterfaces() throws ClassNotFoundException {
 849         final String[] _interfaces = getInterfaceNames();
 850         final JavaClass[] classes = new JavaClass[_interfaces.length];
 851         for (int i = 0; i < _interfaces.length; i++) {
 852             classes[i] = repository.loadClass(_interfaces[i]);
 853         }
 854         return classes;
 855     }
 856 
 857 
 858     /**
 859      * Get all interfaces implemented by this JavaClass (transitively).
 860      */
 861     public JavaClass[] getAllInterfaces() throws ClassNotFoundException {
 862         final ClassQueue queue = new ClassQueue();
 863         final Set<JavaClass> allInterfaces = new TreeSet<>();
 864         queue.enqueue(this);
 865         while (!queue.empty()) {
 866             final JavaClass clazz = queue.dequeue();
 867             final JavaClass souper = clazz.getSuperClass();
 868             final JavaClass[] _interfaces = clazz.getInterfaces();
 869             if (clazz.isInterface()) {
 870                 allInterfaces.add(clazz);
 871             } else {
 872                 if (souper != null) {
 873                     queue.enqueue(souper);
 874                 }
 875             }
 876             for (final JavaClass _interface : _interfaces) {
 877                 queue.enqueue(_interface);
 878             }
 879         }
 880         return allInterfaces.toArray(new JavaClass[allInterfaces.size()]);
 881     }
 882 
 883 
 884     /**
 885      * @return Comparison strategy object
 886      */
 887     public static BCELComparator getComparator() {
 888         return bcelComparator;
 889     }
 890 
 891 
 892     /**
 893      * @param comparator Comparison strategy object
 894      */
 895     public static void setComparator( final BCELComparator comparator ) {
 896         bcelComparator = comparator;
 897     }
 898 
 899 
 900     /**
 901      * Return value as defined by given BCELComparator strategy.
 902      * By default two JavaClass objects are said to be equal when
 903      * their class names are equal.
 904      *
 905      * @see java.lang.Object#equals(java.lang.Object)
 906      */
 907     @Override
 908     public boolean equals( final Object obj ) {
 909         return bcelComparator.equals(this, obj);
 910     }
 911 
 912 
 913     /**
 914      * Return the natural ordering of two JavaClasses.
 915      * This ordering is based on the class name
 916      * @since 6.0
 917      */
 918     @Override
 919     public int compareTo( final JavaClass obj ) {
 920         return getClassName().compareTo(obj.getClassName());
 921     }
 922 
 923 
 924     /**
 925      * Return value as defined by given BCELComparator strategy.
 926      * By default return the hashcode of the class name.
 927      *
 928      * @see java.lang.Object#hashCode()
 929      */
 930     @Override
 931     public int hashCode() {
 932         return bcelComparator.hashCode(this);
 933     }
 934 }