/** * Returns the given reference value that may or may not be null, ensuring that it is a * TypedReferenceValue, not a subclass. */ private static ReferenceValue typedReferenceValue( TypedReferenceValue referenceValue, boolean mayBeNull) { return referenceValue.getClass() == TypedReferenceValue.class ? referenceValue.generalizeMayBeNull(mayBeNull) : new TypedReferenceValue(referenceValue.type, referenceValue.referencedClass, mayBeNull); }
public ReferenceValue generalize(TypedReferenceValue 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 typedReferenceValue(this, 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) { // Is one class simply an extension of the other one? if (thisReferencedClass.extendsOrImplements(otherReferencedClass)) { return typedReferenceValue(other, mayBeNull); } if (otherReferencedClass.extendsOrImplements(thisReferencedClass)) { return typedReferenceValue(this, mayBeNull); } // Do the classes have a non-trivial common superclass? Clazz commonClass = findCommonClass(thisReferencedClass, otherReferencedClass, false); if (commonClass.getName().equals(ClassConstants.NAME_JAVA_LANG_OBJECT)) { // Otherwise, do the classes have a common interface? Clazz commonInterface = findCommonClass(thisReferencedClass, otherReferencedClass, true); if (commonInterface != null) { commonClass = commonInterface; } } return new TypedReferenceValue( 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 typedReferenceValue(other, mayBeNull); } } else if (thisDimensionCount < otherDimensionCount) { // See if this type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName( ClassUtil.internalClassNameFromClassType(thisType))) { return typedReferenceValue(this, 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 ? new TypedReferenceValue( ClassUtil.internalArrayTypeFromClassName( ClassConstants.NAME_JAVA_LANG_OBJECT, commonDimensionCount), null, mayBeNull) : mayBeNull ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL; }