| Index: third_party/jmake/src/org/pantsbuild/jmake/RefClassFinder.java
|
| diff --git a/third_party/jmake/src/org/pantsbuild/jmake/RefClassFinder.java b/third_party/jmake/src/org/pantsbuild/jmake/RefClassFinder.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..58c8884ab55b445e2e71cd51d44f3f62a887536a
|
| --- /dev/null
|
| +++ b/third_party/jmake/src/org/pantsbuild/jmake/RefClassFinder.java
|
| @@ -0,0 +1,697 @@
|
| +/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved
|
| + *
|
| + * This program is distributed under the terms of
|
| + * the GNU General Public License Version 2. See the LICENSE file
|
| + * at the top of the source tree.
|
| + */
|
| +package org.pantsbuild.jmake;
|
| +
|
| +import java.lang.reflect.Modifier;
|
| +import java.util.LinkedHashSet;
|
| +import java.util.Set;
|
| +
|
| +/**
|
| + * This class implements finding classes referencing other classes and members in various ways.
|
| + *
|
| + * @author Misha Dmitriev
|
| + * 12 March 2004
|
| + */
|
| +public class RefClassFinder {
|
| +
|
| + private boolean failOnDependentJar; // If true, will fail if a dependency of a sourceless class
|
| + // (coming from a .jar) on a "normal" class is detected
|
| + private boolean noWarnOnDependentJar; // If true, not even a warning will be issued in the above case.
|
| + private String checkedClassName;
|
| + private PCDManager pcdm;
|
| + private Set<String> affectedClassNames;
|
| + private boolean checkedClassIsFromJar;
|
| +
|
| + /** An instance of RefClassFinder is created once per session, passing it the global options that do not change */
|
| + public RefClassFinder(PCDManager pcdm, boolean failOnDependentJar, boolean noWarnOnDependentJar) {
|
| + this.pcdm = pcdm;
|
| + this.failOnDependentJar = failOnDependentJar;
|
| + this.noWarnOnDependentJar = noWarnOnDependentJar;
|
| + }
|
| +
|
| + /** This method is called every time we are going to check a new class */
|
| + public void initialize(String checkedClassName, boolean checkedClassIsFromJar) {
|
| + this.checkedClassName = checkedClassName;
|
| + this.checkedClassIsFromJar = checkedClassIsFromJar;
|
| + affectedClassNames = new LinkedHashSet<String>();
|
| + }
|
| +
|
| + /**
|
| + * Returns the names of project classes that were found potentially affec
|
| + * by the changes to the checked class.
|
| + */
|
| + public String[] getAffectedClassNames() {
|
| + int size = affectedClassNames.size();
|
| + if (size == 0) {
|
| + return null;
|
| + } else {
|
| + String[] ret = new String[size];
|
| + int i = 0;
|
| + for (String className : affectedClassNames) {
|
| + ret[i++] = className;
|
| + }
|
| + return ret;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Find all project classes that can access field fieldNo of class fieldClassInfo.
|
| + * Used if a compile-time constant is changed.
|
| + */
|
| + public void findAllProjectClasses(ClassInfo fieldClassInfo, int fieldNo) {
|
| + for (PCDEntry pcde : pcdm.entries()) {
|
| + if (pcde.checkResult == PCDEntry.CV_DELETED) {
|
| + continue;
|
| + }
|
| + if (pcde.javaFileFullPath.endsWith(".jar")) {
|
| + continue;
|
| + }
|
| + ClassInfo clientInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
| + if (clientInfo == null) {
|
| + continue; // New class
|
| + }
|
| + if (memberAccessibleFrom(fieldClassInfo, fieldNo, clientInfo, true)) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Find all project classes that reference class with the given name
|
| + * (but not its array class) directly from the constantpool.
|
| + */
|
| + public void findReferencingClasses0(ClassInfo classInfo) {
|
| + findReferencingClasses(classInfo, 0, false, null);
|
| + }
|
| +
|
| +
|
| + /* In the following "find...ReferencingClasses1" methods, "referencing C" means
|
| + * "referencing C or its array class directly from the constant pool, as a type of a data
|
| + * field, as a type in a method signature or a thrown exception, as a directly implemented
|
| + * interface or a direct superclass".
|
| + */
|
| + /** Used for deleted classes. */
|
| + public void findReferencingClassesForDeletedClass(ClassInfo classInfo) {
|
| + String packageName = classInfo.packageName;
|
| + boolean isPublic = classInfo.isPublic();
|
| + boolean isInterface = classInfo.isInterface();
|
| + for (PCDEntry pcde : pcdm.entries()) {
|
| + ClassInfo clientInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
| + if (clientInfo == null) {
|
| + continue; // New class
|
| + }
|
| + if (!isPublic && packageName.equals(clientInfo.packageName)) {
|
| + continue;
|
| + }
|
| + if (clientInfo.referencesClass(classInfo.name, isInterface, 1)) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + }
|
| + }
|
| +
|
| + }
|
| +
|
| + /**
|
| + * For the given class p.C, find each project class X referencing C, that is not a member of
|
| + * package p and is not a direct or indirect subclass of C's directly enclosing class.
|
| + * (public -> protected transformation)
|
| + */
|
| + public void findDiffPackageAndNotSubReferencingClasses1(ClassInfo classInfo) {
|
| + String packageName = classInfo.packageName;
|
| + String directlyEnclosingClass = classInfo.directlyEnclosingClass;
|
| + for (PCDEntry pcde : pcdm.entries()) {
|
| + ClassInfo clientInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
| + if (clientInfo == null) {
|
| + continue; // New class
|
| + }
|
| + if (packageName.equals(clientInfo.packageName) ||
|
| + clientInfo.isSubclassOf(directlyEnclosingClass, false)) {
|
| + continue;
|
| + }
|
| + if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * For class p.C, find each project class X referencing C, whose top level enclosing
|
| + * class is different from that of C.
|
| + * (public -> private transformation)
|
| + */
|
| + public void findReferencingClasses1(ClassInfo classInfo) {
|
| + String topLevelEnclosingClass = classInfo.topLevelEnclosingClass;
|
| + for (PCDEntry pcde : pcdm.entries()) {
|
| + ClassInfo clientInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
| + if (clientInfo == null) {
|
| + continue; // New class
|
| + }
|
| + if (topLevelEnclosingClass.equals(clientInfo.topLevelEnclosingClass)) {
|
| + continue;
|
| + }
|
| + if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * For class p.C, find each project class X referencing C, whose direct or indirect superclass
|
| + * is C's directly enclosing class, or which is a member of package p, whose top level enclosing
|
| + * class is different from that of C.
|
| + * (protected -> private transformation)
|
| + */
|
| + public void findThisPackageOrSubReferencingClasses1(ClassInfo classInfo) {
|
| + String directlyEnclosingClass = classInfo.directlyEnclosingClass;
|
| + String topLevelEnclosingClass = classInfo.topLevelEnclosingClass;
|
| + String packageName = classInfo.packageName;
|
| + for (PCDEntry entry : pcdm.entries()) {
|
| + ClassInfo clientInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, entry);
|
| + if (clientInfo == null) {
|
| + continue; // New class
|
| + }
|
| + if ((!clientInfo.packageName.equals(packageName)) &&
|
| + !clientInfo.isSubclassOf(directlyEnclosingClass, false)) {
|
| + continue;
|
| + }
|
| + if (clientInfo.topLevelEnclosingClass.equals(topLevelEnclosingClass)) {
|
| + continue;
|
| + }
|
| + if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * For class p.C, find each project class X referencing C, which is a member of package p and whose
|
| + * top level enclosing class is different from that of C.
|
| + * (default -> private transformation)
|
| + */
|
| + public void findThisPackageReferencingClasses1(ClassInfo classInfo) {
|
| + String topLevelEnclosingClass = classInfo.topLevelEnclosingClass;
|
| + String packageName = classInfo.packageName;
|
| + for (PCDEntry entry : pcdm.entries()) {
|
| + ClassInfo clientInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD,
|
| + entry);
|
| + if (clientInfo == null) {
|
| + continue; // New class
|
| + }
|
| + if (!clientInfo.packageName.equals(packageName)) {
|
| + continue;
|
| + }
|
| + if (topLevelEnclosingClass.equals(clientInfo.topLevelEnclosingClass)) {
|
| + continue;
|
| + }
|
| + if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * For class p.C, find each project class X referencing C, which is not a member of package p.
|
| + * (public -> default transformation)
|
| + */
|
| + public void findDiffPackageReferencingClasses1(ClassInfo classInfo) {
|
| + String packageName = classInfo.packageName;
|
| + for (PCDEntry pcde : pcdm.entries()) {
|
| + ClassInfo clientInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
| + if (clientInfo == null) {
|
| + continue; // New class
|
| + }
|
| + if (clientInfo.packageName.equals(packageName)) {
|
| + continue;
|
| + }
|
| + if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * For class p.C, find each project class X referencing C, which is not a member of package p and
|
| + * whose direct or indirect superclass is C's directly enclosing class.
|
| + * (protected -> default transformation)
|
| + */
|
| + public void findDiffPackageAndSubReferencingClasses1(ClassInfo classInfo) {
|
| + String packageName = classInfo.packageName;
|
| + String directlyEnclosingClass = classInfo.directlyEnclosingClass;
|
| + for (PCDEntry pcde : pcdm.entries()) {
|
| + ClassInfo clientInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
| + if (clientInfo == null) {
|
| + continue; // New class
|
| + }
|
| + if (clientInfo.packageName.equals(packageName)) {
|
| + continue;
|
| + }
|
| + if (!clientInfo.isSubclassOf(directlyEnclosingClass, false)) {
|
| + continue;
|
| + }
|
| + if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), 1)) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Find all project classes that reference both of the classes with the
|
| + * given names (or array classes of one or both) directly or indirectly from the
|
| + * constantpool, as a type of a data field, as a type in a method signature or a
|
| + * thrown exception, as a directly/indirectly implemented interface or a
|
| + * direct/indirect superclass.
|
| + */
|
| + public void findReferencingClasses2(ClassInfo classInfo1, ClassInfo classInfo2) {
|
| + Set<String> refClazz1 = new LinkedHashSet<String>();
|
| + findReferencingClasses(classInfo1, 2, false, refClazz1);
|
| + Set<String> refClazz2 = new LinkedHashSet<String>();
|
| + findReferencingClasses(classInfo2, 2, false, refClazz2);
|
| +
|
| + for (String className1 : refClazz1) {
|
| + if (refClazz2.contains(className1)) {
|
| + addToAffectedClassNames(className1);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /** Find all project classes which are direct subclasses of the given class */
|
| + public void findDirectSubclasses(ClassInfo classInfo) {
|
| + for (ClassInfo subclassInfo : classInfo.getDirectSubclasses()) {
|
| + addToAffectedClassNames(subclassInfo.name);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Find all non-abstract project classes that implement the given interface or any of its
|
| + * subclasses directly, and all non-abstract classes that are direct descendants of abstract
|
| + * classes that implement the given interface directly or indirectly. Class C implements
|
| + * interface I indirectly, if C or some superclass of C directly implements I or some sublcass of I.
|
| + */
|
| + public void findDirectlyAndOtherwiseImplementingConcreteClasses(ClassInfo intfInfo) {
|
| + for (PCDEntry entry : pcdm.entries()) {
|
| + ClassInfo clientInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, entry);
|
| + if (clientInfo == null) {
|
| + continue; // New class
|
| + }
|
| + if (clientInfo.isInterface()) {
|
| + continue;
|
| + }
|
| + if (clientInfo.isAbstract()) {
|
| + if (clientInfo.implementsInterfaceDirectlyOrIndirectly(intfInfo.name)) {
|
| + findAllNearestConcreteSubclasses(clientInfo);
|
| + }
|
| + } else {
|
| + if (clientInfo.implementsIntfOrSubintfDirectly(intfInfo.name)) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + private void findAllNearestConcreteSubclasses(ClassInfo classInfo) {
|
| + for (ClassInfo subclassInfo : classInfo.getDirectSubclasses()) {
|
| + if (subclassInfo.isAbstract()) {
|
| + findAllNearestConcreteSubclasses(subclassInfo);
|
| + } else {
|
| + addToAffectedClassNames(subclassInfo.name);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Find all interfaces and abstract classes that implement the given interface and declare or inherit
|
| + * a method with the given name. For those that overload this method, find referencing classes.
|
| + */
|
| + public void findAbstractSubtypesWithSameNameMethod(ClassInfo intfInfo, String mName, final String mSig) {
|
| + for (PCDEntry pcde : pcdm.entries()) {
|
| + ClassInfo ci =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
| + if (ci == null) {
|
| + continue; // New class or not in project
|
| + }
|
| + if (!(ci.isInterface() || ci.isAbstract())) {
|
| + continue;
|
| + }
|
| + if (ci.implementsInterfaceDirectlyOrIndirectly(intfInfo.name)) {
|
| + addToAffectedClassNames(ci.name);
|
| + // Check if the new method overloads an existing (declared or inherited) method. Overloading test is rough -
|
| + // we just check if the number of parameters is the same.
|
| + ci.findExistingSameNameMethods(mName, true, true, new ClassInfo.MethodHandler() {
|
| +
|
| + void handleMethod(ClassInfo classInfo, int otherMethodIdx) {
|
| + String otherMSig =
|
| + classInfo.methodSignatures[otherMethodIdx];
|
| + if ( (!mSig.equals(otherMSig)) &&
|
| + Utils.sameParamNumber(mSig, otherMSig)) {
|
| + findReferencingClassesForMethod(classInfo, otherMethodIdx);
|
| + }
|
| + }
|
| + });
|
| + }
|
| + }
|
| + }
|
| +
|
| + /** Find all project classes that reference the given field. */
|
| + public void findReferencingClassesForField(ClassInfo classInfo, int fieldNo) {
|
| + findReferencingClassesForMember(classInfo, fieldNo, true, false, false);
|
| + }
|
| +
|
| + /**
|
| + * Find all project classes that reference the given field and which are in
|
| + * different packages.
|
| + */
|
| + public void findDiffPackageReferencingClassesForField(ClassInfo classInfo, int fieldNo) {
|
| + findReferencingClassesForMember(classInfo, fieldNo, true, true, false);
|
| + }
|
| +
|
| + /**
|
| + * Find all project classes that reference the given field, which are in different
|
| + * packages and are direct or indirect subclasses of the member's declaring class
|
| + * (protected -> default transformation).
|
| + */
|
| + public void findDiffPackageAndSubReferencingClassesForField(ClassInfo classInfo, int fieldNo) {
|
| + findReferencingClassesForMember(classInfo, fieldNo, true, true, true);
|
| + }
|
| +
|
| + /** Find all project classes that reference the given method. */
|
| + public void findReferencingClassesForMethod(ClassInfo classInfo, int methodNo) {
|
| + findReferencingClassesForMember(classInfo, methodNo, false, false, false);
|
| + }
|
| +
|
| + /**
|
| + * Find all project classes that reference the given method and which are in
|
| + * different packages.
|
| + */
|
| + public void findDiffPackageReferencingClassesForMethod(ClassInfo classInfo, int methodNo) {
|
| + findReferencingClassesForMember(classInfo, methodNo, false, true, false);
|
| + }
|
| +
|
| + /**
|
| + * Find all project classes that reference the given method, which are in different
|
| + * packages and are direct or indirect subclasses of the member's declaring class
|
| + * (protected -> default transformation)
|
| + */
|
| + public void findDiffPackageAndSubReferencingClassesForMethod(ClassInfo classInfo, int methodNo) {
|
| + findReferencingClassesForMember(classInfo, methodNo, false, true, true);
|
| + }
|
| +
|
| + /**
|
| + * Find all project classes that re-implement the given method and that are
|
| + * direct/indirect subclasses of this method's declaring class. If some subclass C
|
| + * re-implements the given method, we don't have to search C's subclasses further.
|
| + */
|
| + public void findSubclassesReimplementingMethod(ClassInfo classInfo, int methodNo) {
|
| + findSubclassesReimplementingMethod(classInfo, classInfo, methodNo);
|
| + }
|
| +
|
| + private void findSubclassesReimplementingMethod(ClassInfo targetClass, ClassInfo methodDeclaringClass, int methodNo) {
|
| + for (ClassInfo subclass : targetClass.getDirectSubclasses()) {
|
| + if (subclass.declaresMethod(methodDeclaringClass, methodNo)) {
|
| + addToAffectedClassNames(subclass.name);
|
| + } else {
|
| + findSubclassesReimplementingMethod(subclass, methodDeclaringClass, methodNo);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * For a given class C, find all concrete direct subclasses, and all direct concrente subclasses of C's direct
|
| + * or indirect abstract subclasses.
|
| + */
|
| + public void findConcreteSubclasses(ClassInfo targetClass) {
|
| + for (ClassInfo subclass : targetClass.getDirectSubclasses()) {
|
| + if (subclass.isAbstract()) {
|
| + findConcreteSubclasses(subclass);
|
| + } else {
|
| + addToAffectedClassNames(subclass.name);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Find any concrete subclasses of targetClass that don't override or inherit a concrete implementation
|
| + * of the given method.
|
| + */
|
| + public void findConcreteSubclassesNotOverridingAbstractMethod(ClassInfo targetClass, ClassInfo methodDeclaringClass, int methodNo) {
|
| + for (ClassInfo subclass : targetClass.getDirectSubclasses()) {
|
| + int pos =
|
| + subclass.getDeclaredMethodPos(methodDeclaringClass, methodNo);
|
| + if (pos == -1) { // This method is not overridden in this class
|
| + if (!subclass.isAbstract()) {
|
| + addToAffectedClassNames(subclass.name);
|
| + } else {
|
| + findConcreteSubclassesNotOverridingAbstractMethod(subclass, methodDeclaringClass, methodNo);
|
| + }
|
| + } else { // A chance that this method is declared abstract once again...
|
| + if (Modifier.isAbstract(subclass.methodAccessFlags[pos])) {
|
| + findConcreteSubclassesNotOverridingAbstractMethod(subclass, methodDeclaringClass, methodNo);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /** Find all project classes that reference any method that throws the given exception. */
|
| + public void findRefsToMethodsThrowingException(ClassInfo excClassInfo) {
|
| + for (PCDEntry pcde : pcdm.entries()) {
|
| + ClassInfo classInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
| + if (classInfo == null) {
|
| + continue; // New class
|
| + }
|
| + int methodIdx = -1;
|
| + do {
|
| + methodIdx =
|
| + classInfo.hasMethodThrowingException(excClassInfo, methodIdx + 1);
|
| + if (methodIdx != -1) {
|
| + findReferencingClassesForMethod(classInfo, methodIdx);
|
| + }
|
| + } while (methodIdx != -1);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Find all project classes declaring a static field with the given name. Currently used only to look up
|
| + * classes referencing given class X via the "X.class" construct.
|
| + */
|
| + public void findClassesDeclaringField(String name, String signature, boolean isStatic, String packageToLookIn) {
|
| + for (PCDEntry pcde : pcdm.entries()) {
|
| + ClassInfo classInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
| + if (classInfo == null) {
|
| + continue; // New class
|
| + }
|
| + if (packageToLookIn != null &&
|
| + !classInfo.packageName.equals(packageToLookIn)) {
|
| + continue;
|
| + }
|
| + if (classInfo.declaresField(name, signature, isStatic)) {
|
| + addToAffectedClassNames(classInfo.name);
|
| + }
|
| + }
|
| + }
|
| +
|
| + public void addToAffectedClassNames(String className) {
|
| + String res = pcdm.classAlreadyRecompiledOrUncompileable(className);
|
| + if (res == null) {
|
| + affectedClassNames.add(className);
|
| + } else if (!"".equals(res)) { // The dependent class comes from a .jar.
|
| + if (checkedClassIsFromJar || noWarnOnDependentJar) {
|
| + return;
|
| + }
|
| + String message = "Class " + className + " is affected by a change to " + checkedClassName + ", but can't be recompiled, " +
|
| + "since it is located in archive " + res;
|
| + if (failOnDependentJar) {
|
| + throw new PrivateException(new PublicExceptions.JarDependsOnSourceException(message));
|
| + } else {
|
| + Utils.printWarningMessage("Warning: " + message);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Find all project classes that reference the class with the given name.
|
| + * The second parameter controls the "thoroughness degree", and its value is passed to ClassInfo.referencesClass()
|
| + * method (see the comment to it). The fromDiffPackages parameter defines whether all such classes
|
| + * or only classes from different packages are required.
|
| + */
|
| + private void findReferencingClasses(ClassInfo classInfo,
|
| + int thorDegree, boolean fromDiffPackages,
|
| + Set<String> ret) {
|
| + String packageName = classInfo.packageName;
|
| + for (PCDEntry pcde : pcdm.entries()) {
|
| + ClassInfo clientInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
| + if (clientInfo == null) {
|
| + continue; // New class
|
| + }
|
| + if (fromDiffPackages && packageName.equals(clientInfo.packageName)) {
|
| + continue;
|
| + }
|
| + // If thorDegree == 2, i.e. indirect references from the constantpool (e.g. a reference to a method which
|
| + // has classInfo as one of its formal parameter types) are taken into account, then we should check all of
|
| + // the classes, whether classInfo is directly accessible from them or not.
|
| + if (thorDegree != 2 && (!classAccessibleFrom(classInfo, clientInfo))) {
|
| + continue;
|
| + }
|
| +
|
| + if (clientInfo.referencesClass(classInfo.name, classInfo.isInterface(), thorDegree)) {
|
| + if (ret == null) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + } else {
|
| + ret.add(clientInfo.name);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Find all project classes that reference the given member. If fromDiffPackages
|
| + * is true, then only classes that do not belong to the package of the member's
|
| + * declaring class should be returned. If onlySubclasses is true, then only
|
| + * classes that are subclasses of member's declaring class should be returned.
|
| + */
|
| + private void findReferencingClassesForMember(ClassInfo declaringClassInfo, int memberNo,
|
| + boolean isField,
|
| + boolean fromDiffPackages, boolean onlySubclasses) {
|
| + String declaringClassName = declaringClassInfo.name;
|
| + String declaringClassPackage = declaringClassInfo.packageName;
|
| + for (PCDEntry pcde : pcdm.entries()) {
|
| + ClassInfo clientInfo =
|
| + pcdm.getClassInfoForPCDEntry(ClassInfo.VER_OLD, pcde);
|
| + if (clientInfo == null) {
|
| + continue; // New class
|
| + }
|
| + String className = clientInfo.name;
|
| + if (className.equals(declaringClassName)) {
|
| + continue;
|
| + }
|
| + if (!memberAccessibleFrom(declaringClassInfo, memberNo, clientInfo, isField)) {
|
| + continue;
|
| + }
|
| + if (fromDiffPackages &&
|
| + declaringClassPackage.equals(clientInfo.packageName)) {
|
| + continue;
|
| + }
|
| + if (onlySubclasses && !clientInfo.isSubclassOf(declaringClassName, false)) {
|
| + continue;
|
| + }
|
| +
|
| + if (isField) {
|
| + if (clientInfo.referencesField(declaringClassInfo, memberNo)) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + }
|
| + } else {
|
| + if (clientInfo.referencesMethod(declaringClassInfo, memberNo)) {
|
| + addToAffectedClassNames(clientInfo.name);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /** Checks if class classInfo is accessible from class clientClassInfo. */
|
| + private boolean classAccessibleFrom(ClassInfo classInfo, ClassInfo clientClassInfo) {
|
| + char classFlags = classInfo.accessFlags;
|
| + String classPackage = classInfo.packageName;
|
| + String clientClassPackage = clientClassInfo.packageName;
|
| +
|
| + if (Modifier.isPublic(classFlags)) {
|
| + return true;
|
| + } else if (Modifier.isProtected(classFlags)) {
|
| + if (classPackage.equals(clientClassPackage) ||
|
| + clientClassInfo.isSubclassOf(classInfo.directlyEnclosingClass, false)) {
|
| + return true;
|
| + }
|
| + } else if (Modifier.isPrivate(classFlags)) {
|
| + if (classInfo.topLevelEnclosingClass.equals(clientClassInfo.topLevelEnclosingClass)) {
|
| + return true;
|
| + }
|
| + } else {
|
| + if (classPackage.equals(clientClassPackage)) {
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + return false;
|
| + }
|
| +
|
| + /**
|
| + * Checks if member memberNo (which is a field if isField == true, and method otherwise) of class memberClassInfo is
|
| + * accessible from class clientClassInfo.
|
| + */
|
| + private boolean memberAccessibleFrom(ClassInfo memberClassInfo,
|
| + int memberNo, ClassInfo clientClassInfo, boolean isField) {
|
| + char memberClassFlags = memberClassInfo.accessFlags;
|
| + char memberFlags = isField ? memberClassInfo.fieldAccessFlags[memberNo]
|
| + : memberClassInfo.methodAccessFlags[memberNo];
|
| + String memberClassPackage = memberClassInfo.packageName;
|
| + String clientClassPackage = clientClassInfo.packageName;
|
| +
|
| + if (Modifier.isPublic(memberClassFlags)) {
|
| + if (Modifier.isPublic(memberFlags)) {
|
| + return true;
|
| + } else if (Modifier.isProtected(memberFlags) &&
|
| + (memberClassPackage.equals(clientClassPackage) ||
|
| + clientClassInfo.isSubclassOf(memberClassInfo.name, false))) {
|
| + return true;
|
| + } else if (Modifier.isPrivate(memberFlags)) {
|
| + if (memberClassInfo.topLevelEnclosingClass.equals(
|
| + clientClassInfo.topLevelEnclosingClass)) {
|
| + return true;
|
| + }
|
| + } else if (memberClassPackage.equals(clientClassPackage)) {
|
| + return true;
|
| + }
|
| + } else if (Modifier.isProtected(memberClassFlags)) {
|
| + if (!(memberClassPackage.equals(clientClassPackage) ||
|
| + clientClassInfo.isSubclassOf(memberClassInfo.directlyEnclosingClass, false))) {
|
| + return true;
|
| + }
|
| + if (Modifier.isPublic(memberFlags) ||
|
| + Modifier.isProtected(memberFlags)) {
|
| + return true;
|
| + } else if (Modifier.isPrivate(memberFlags)) {
|
| + if (memberClassInfo.topLevelEnclosingClass.equals(
|
| + clientClassInfo.topLevelEnclosingClass)) {
|
| + return true;
|
| + }
|
| + } else {
|
| + if (memberClassPackage.equals(clientClassPackage)) {
|
| + return true;
|
| + }
|
| + }
|
| + } else if (Modifier.isPrivate(memberClassFlags)) {
|
| + if (memberClassInfo.topLevelEnclosingClass.equals(
|
| + clientClassInfo.topLevelEnclosingClass)) {
|
| + return true;
|
| + }
|
| + } else { // memberClassInfo is package-private
|
| + if (!memberClassPackage.equals(clientClassPackage)) {
|
| + return false;
|
| + }
|
| + if (Modifier.isPublic(memberFlags) || Modifier.isProtected(memberFlags)) {
|
| + return true;
|
| + } else if (Modifier.isPrivate(memberFlags)) {
|
| + if (memberClassInfo.topLevelEnclosingClass.equals(
|
| + clientClassInfo.topLevelEnclosingClass)) {
|
| + return true;
|
| + }
|
| + } else {
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + return false;
|
| + }
|
| +}
|
|
|