/** Returns if the number of superclasses of the given class in the given set of classes. */ private int superClassCount(Clazz subClass, Set classes) { int count = 0; Iterator iterator = classes.iterator(); while (iterator.hasNext()) { Clazz clazz = (Clazz) iterator.next(); if (subClass.extendsOrImplements(clazz)) { count++; } } return count; }
/** * Returns the most specific common superclass or interface of the given classes. * * @param class1 the first class. * @param class2 the second class. * @param interfaces specifies whether to look for a superclass or for an interface. * @return the common class. */ private Clazz findCommonClass(Clazz class1, Clazz class2, boolean interfaces) { // Collect the superclasses or the interfaces of this class. Set superClasses1 = new HashSet(); class1.hierarchyAccept( !interfaces, !interfaces, interfaces, false, new ClassCollector(superClasses1)); int superClasses1Count = superClasses1.size(); if (superClasses1Count == 0) { if (interfaces) { return null; } else if (class1.getSuperName() != null) { throw new IllegalArgumentException( "Can't find any super classes of [" + class1.getName() + "] (not even immediate super class [" + class1.getSuperName() + "])"); } } // Collect the superclasses or the interfaces of the other class. Set superClasses2 = new HashSet(); class2.hierarchyAccept( !interfaces, !interfaces, interfaces, false, new ClassCollector(superClasses2)); int superClasses2Count = superClasses2.size(); if (superClasses2Count == 0) { if (interfaces) { return null; } else if (class2.getSuperName() != null) { throw new IllegalArgumentException( "Can't find any super classes of [" + class2.getName() + "] (not even immediate super class [" + class2.getSuperName() + "])"); } } if (DEBUG) { System.out.println( "ReferenceValue.generalize this [" + class1.getName() + "] with other [" + class2.getName() + "] (interfaces = " + interfaces + ")"); System.out.println(" This super classes: " + superClasses1); System.out.println(" Other super classes: " + superClasses2); } // Find the common superclasses. superClasses1.retainAll(superClasses2); if (DEBUG) { System.out.println(" Common super classes: " + superClasses1); } if (interfaces && superClasses1.isEmpty()) { return null; } // Find a class that is a subclass of all common superclasses, // or that at least has the maximum number of common superclasses. Clazz commonClass = null; int maximumSuperClassCount = -1; // Go over all common superclasses to find it. In case of // multiple subclasses, keep the lowest one alphabetically, // in order to ensure that the choice is deterministic. Iterator commonSuperClasses = superClasses1.iterator(); while (commonSuperClasses.hasNext()) { Clazz commonSuperClass = (Clazz) commonSuperClasses.next(); int superClassCount = superClassCount(commonSuperClass, superClasses1); if (maximumSuperClassCount < superClassCount || (maximumSuperClassCount == superClassCount && commonClass != null && commonClass.getName().compareTo(commonSuperClass.getName()) > 0)) { commonClass = commonSuperClass; maximumSuperClassCount = superClassCount; } } if (commonClass == null) { throw new IllegalArgumentException( "Can't find common super class of [" + class1.getName() + "] (with " + superClasses1Count + " known super classes) and [" + class2.getName() + "] (with " + superClasses2Count + " known super classes)"); } if (DEBUG) { System.out.println(" Best common class: [" + commonClass.getName() + "]"); } return commonClass; }
/** Returns the generalization of this ReferenceValue and the given other ReferenceValue. */ public ReferenceValue generalize(ReferenceValue other) { // If both types are identical, the generalization is the same too. if (this.equals(other)) { return this; } String thisType = this.type; String otherType = other.type; // If both types are nul, the generalization is null too. if (thisType == null && otherType == null) { return ValueFactory.REFERENCE_VALUE_NULL; } // If this type is null, the generalization is the other type, maybe null. if (thisType == null) { return other.generalizeMayBeNull(true); } // If the other type is null, the generalization is this type, maybe null. if (otherType == null) { return this.generalizeMayBeNull(true); } boolean mayBeNull = this.mayBeNull || other.mayBeNull; // If the two types are equal, the generalization remains the same, maybe null. if (thisType.equals(otherType)) { return this.generalizeMayBeNull(mayBeNull); } // Start taking into account the type dimensions. int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType); int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType); int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount); if (thisDimensionCount == otherDimensionCount) { // See if we can take into account the referenced classes. Clazz thisReferencedClass = this.referencedClass; Clazz otherReferencedClass = other.referencedClass; if (thisReferencedClass != null && otherReferencedClass != null) { if (thisReferencedClass.extendsOrImplements(otherReferencedClass)) { return other.generalizeMayBeNull(mayBeNull); } if (otherReferencedClass.extendsOrImplements(thisReferencedClass)) { return this.generalizeMayBeNull(mayBeNull); } // Collect the superclasses and interfaces of this class. Set thisSuperClasses = new HashSet(); thisReferencedClass.hierarchyAccept( false, true, true, false, new ClassCollector(thisSuperClasses)); int thisSuperClassesCount = thisSuperClasses.size(); if (thisSuperClassesCount == 0 && thisReferencedClass.getSuperName() != null) { throw new IllegalArgumentException( "Can't find any super classes of [" + thisType + "] (not even immediate super class [" + thisReferencedClass.getSuperName() + "])"); } // Collect the superclasses and interfaces of the other class. Set otherSuperClasses = new HashSet(); otherReferencedClass.hierarchyAccept( false, true, true, false, new ClassCollector(otherSuperClasses)); int otherSuperClassesCount = otherSuperClasses.size(); if (otherSuperClassesCount == 0 && otherReferencedClass.getSuperName() != null) { throw new IllegalArgumentException( "Can't find any super classes of [" + otherType + "] (not even immediate super class [" + otherReferencedClass.getSuperName() + "])"); } if (DEBUG) { System.out.println( "ReferenceValue.generalize this [" + thisReferencedClass.getName() + "] with other [" + otherReferencedClass.getName() + "]"); System.out.println(" This super classes: " + thisSuperClasses); System.out.println(" Other super classes: " + otherSuperClasses); } // Find the common superclasses. thisSuperClasses.retainAll(otherSuperClasses); if (DEBUG) { System.out.println(" Common super classes: " + thisSuperClasses); } // Find a class that is a subclass of all common superclasses, // or that at least has the maximum number of common superclasses. Clazz commonClass = null; int maximumSuperClassCount = -1; // Go over all common superclasses to find it. In case of // multiple subclasses, keep the lowest one alphabetically, // in order to ensure that the choice is deterministic. Iterator commonSuperClasses = thisSuperClasses.iterator(); while (commonSuperClasses.hasNext()) { Clazz commonSuperClass = (Clazz) commonSuperClasses.next(); int superClassCount = superClassCount(commonSuperClass, thisSuperClasses); if (maximumSuperClassCount < superClassCount || (maximumSuperClassCount == superClassCount && commonClass != null && commonClass.getName().compareTo(commonSuperClass.getName()) > 0)) { commonClass = commonSuperClass; maximumSuperClassCount = superClassCount; } } if (commonClass == null) { throw new IllegalArgumentException( "Can't find common super class of [" + thisType + "] (with " + thisSuperClassesCount + " known super classes) and [" + otherType + "] (with " + otherSuperClassesCount + " known super classes)"); } if (DEBUG) { System.out.println(" Best common class: [" + commonClass.getName() + "]"); } // TODO: Handle more difficult cases, with multiple global subclasses. return new ReferenceValue( commonDimensionCount == 0 ? commonClass.getName() : ClassUtil.internalArrayTypeFromClassName( commonClass.getName(), commonDimensionCount), commonClass, mayBeNull); } } else if (thisDimensionCount > otherDimensionCount) { // See if the other type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName( ClassUtil.internalClassNameFromClassType(otherType))) { return other.generalizeMayBeNull(mayBeNull); } } else if (thisDimensionCount < otherDimensionCount) { // See if this type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName( ClassUtil.internalClassNameFromClassType(thisType))) { return this.generalizeMayBeNull(mayBeNull); } } // Reduce the common dimension count if either type is an array of // primitives type of this dimension. if (commonDimensionCount > 0 && (ClassUtil.isInternalPrimitiveType(otherType.charAt(commonDimensionCount))) || ClassUtil.isInternalPrimitiveType(thisType.charAt(commonDimensionCount))) { commonDimensionCount--; } // Fall back on a basic Object or array of Objects type. return commonDimensionCount == 0 ? mayBeNull ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL : new ReferenceValue( ClassUtil.internalArrayTypeFromClassName( ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT, commonDimensionCount), null, mayBeNull); }