You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
8.9 KiB
311 lines
8.9 KiB
/* MultiClassType Copyright (C) 1998-2002 Jochen Hoenicke.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; see the file COPYING.LESSER. If not, write to
|
|
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
package net.sf.jode.type;
|
|
import java.util.Stack;
|
|
import java.util.Vector;
|
|
|
|
/**
|
|
* This class represents a type aproximation, consisting of multiple
|
|
* interfaces and a class type.<p>
|
|
*
|
|
* If this is the bottom boundary, this specifies, which class our
|
|
* type must extend and which interfaces it must implement.
|
|
*
|
|
* If this is the top boundary, this gives all interfaces and classes
|
|
* that may extend the type. I.e. at least one interface or class extends
|
|
* the searched type.
|
|
*
|
|
* @author Jochen Hoenicke */
|
|
public class MultiClassType extends ReferenceType {
|
|
|
|
ClassType classes[];
|
|
|
|
private MultiClassType(ClassType[] classes) {
|
|
super(TC_CLASSIFACE);
|
|
this.classes = classes;
|
|
}
|
|
|
|
public static ReferenceType create(ClassType[] classes) {
|
|
if (classes.length == 0)
|
|
return tObject;
|
|
if (classes.length == 1)
|
|
return classes[0];
|
|
return new MultiClassType(classes);
|
|
}
|
|
|
|
public Type getSubType() {
|
|
/* We don't implement the set of types, that are castable to some
|
|
* of the given classes or interfaces.
|
|
*/
|
|
throw new InternalError
|
|
("getSubType called on set of classes and interfaces!");
|
|
}
|
|
|
|
/**
|
|
* Returns true, iff this type implements all interfaces in type
|
|
* and extends all objects in type.
|
|
*/
|
|
public boolean isSubTypeOf(Type type) {
|
|
for (int i = 0; i < classes.length; i++)
|
|
if (!classes[i].isSubTypeOf(type))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns true, iff this type implements all interfaces in type
|
|
* and extends all objects in type.
|
|
*/
|
|
public boolean maybeSubTypeOf(Type type) {
|
|
for (int i = 0; i < classes.length; i++)
|
|
if (!classes[i].maybeSubTypeOf(type))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
public Type getHint() {
|
|
return getCanonic();
|
|
}
|
|
|
|
public Type getCanonic() {
|
|
return classes[0];
|
|
}
|
|
|
|
/**
|
|
* Create the type corresponding to the range from bottomType to
|
|
* this. This removes all classes that doesn't extend all classes
|
|
* in bottom. If no class remains, this is a type error.
|
|
* @param bottom the start point of the range
|
|
* @return the range type, or tError if range is empty.
|
|
*/
|
|
public Type createRangeType(ReferenceType bottomType) {
|
|
ReferenceType topType;
|
|
/**
|
|
* Check if we fully implement the bottom type.
|
|
*/
|
|
int j;
|
|
for (j=0; j < classes.length; j++) {
|
|
if (!bottomType.maybeSubTypeOf(classes[j]))
|
|
break;
|
|
}
|
|
|
|
if (j == classes.length)
|
|
topType = this;
|
|
else {
|
|
/* Now we have at least one class, that doesn't implement
|
|
* bottomType, remove all such classes.
|
|
*/
|
|
ClassType[] topClasses = new ClassType[classes.length - 1];
|
|
System.arraycopy(classes, 0, topClasses, 0, j);
|
|
int count = j;
|
|
for (j++; j < classes.length; j++) {
|
|
if (bottomType.isSubTypeOf(classes[j]))
|
|
topClasses[count++] = classes[j];
|
|
}
|
|
|
|
if (count == 0)
|
|
return tError;
|
|
if (count < topClasses.length - 1) {
|
|
ClassType[] shortClasses = new ClassType[count];
|
|
System.arraycopy(topClasses, 0, shortClasses, 0, count);
|
|
topClasses = shortClasses;
|
|
}
|
|
topType = create(topClasses);
|
|
}
|
|
if (topType.isSubTypeOf(bottomType))
|
|
/* This means that topType contains only classes that are also
|
|
* in bottomType. So topType is the whole range.
|
|
*/
|
|
return topType;
|
|
return tRange(bottomType, topType);
|
|
}
|
|
|
|
boolean containsSuperTypeOf(Type type) {
|
|
for (int i = 0; i < classes.length; i++)
|
|
if (type.isSubTypeOf(classes[i]))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the specialized type of this and type.
|
|
* We simple unify the lists of classes, but simplify them, to remove
|
|
* all classes that are already subtypes of some other class in the
|
|
* other list.
|
|
*/
|
|
public Type getSpecializedType(Type type) {
|
|
if (type instanceof RangeType)
|
|
type = ((RangeType) type).getBottom();
|
|
|
|
/* Most times (almost always) one of the two types is
|
|
* already more specialized. Optimize for this case.
|
|
*/
|
|
if (type.isSubTypeOf(this))
|
|
return this;
|
|
if (this.isSubTypeOf(type))
|
|
return type;
|
|
|
|
ClassType[] otherClasses;
|
|
if (type instanceof MultiClassType) {
|
|
otherClasses = ((MultiClassType) type).classes;
|
|
} else if (type instanceof ClassType) {
|
|
otherClasses = new ClassType[] { (ClassType) type };
|
|
} else
|
|
return tError;
|
|
|
|
/* The classes are simply the union of both classes set. But
|
|
* we can simplify this, if a class is implemented by another
|
|
* class in the other list, we can omit it.
|
|
*/
|
|
Vector destClasses = new Vector();
|
|
for (int i=0; i< classes.length; i++) {
|
|
ClassType clazz = classes[i];
|
|
if (!clazz.isSubTypeOf(type)) {
|
|
/* This interface is not implemented by any of the other
|
|
* classes. Add it to the destClasses.
|
|
*/
|
|
destClasses.addElement(clazz);
|
|
}
|
|
}
|
|
for (int i=0; i< otherClasses.length; i++) {
|
|
ClassType clazz = otherClasses[i];
|
|
if (!clazz.isSubTypeOf(this)) {
|
|
/* This interface is not implemented by any of the other
|
|
* classes. Add it to the destClasses.
|
|
*/
|
|
destClasses.addElement(clazz);
|
|
}
|
|
}
|
|
|
|
ClassType[] classArray = new ClassType[destClasses.size()];
|
|
destClasses.copyInto(classArray);
|
|
return create(classArray);
|
|
}
|
|
|
|
/**
|
|
* Returns the generalized type of this and type. We have two
|
|
* classes and multiple interfaces. The result should be the
|
|
* object that is the the super class of both objects and all
|
|
* interfaces, that one class or interface of each type
|
|
* implements.
|
|
*/
|
|
public Type getGeneralizedType(Type type) {
|
|
if (type instanceof RangeType)
|
|
type = ((RangeType) type).getTop();
|
|
|
|
/* Often one of the two classes is already more generalized.
|
|
* Optimize for this case.
|
|
*/
|
|
if (type.isSubTypeOf(this))
|
|
return type;
|
|
if (this.isSubTypeOf(type))
|
|
return this;
|
|
|
|
if (!(type instanceof ReferenceType))
|
|
return tError;
|
|
|
|
Stack classTypes = new Stack();
|
|
for (int i = 0; i < classes.length; i++)
|
|
classTypes.push(classes[i]);
|
|
return ((ReferenceType)type).findCommonClassTypes(classTypes);
|
|
}
|
|
|
|
public String toString()
|
|
{
|
|
StringBuffer sb = new StringBuffer("{");
|
|
String comma = "";
|
|
for (int i=0; i< classes.length; i++) {
|
|
sb.append(comma).append(classes[i]);
|
|
comma = ", ";
|
|
}
|
|
return sb.append("}").toString();
|
|
}
|
|
|
|
public String getTypeSignature() {
|
|
return getCanonic().getTypeSignature();
|
|
}
|
|
|
|
public Class getTypeClass() throws ClassNotFoundException {
|
|
return getCanonic().getTypeClass();
|
|
}
|
|
|
|
/**
|
|
* Checks if we need to cast to a middle type, before we can cast from
|
|
* fromType to this type.
|
|
* @return the middle type, or null if it is not necessary.
|
|
*/
|
|
public Type getCastHelper(Type fromType) {
|
|
return getCanonic().getCastHelper(fromType);
|
|
}
|
|
|
|
/**
|
|
* Checks if this type represents a valid type instead of a list
|
|
* of minimum types.
|
|
*/
|
|
public boolean isValidType() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks if this is a class or array type (but not a null type).
|
|
* @XXX remove this?
|
|
* @return true if this is a class or array type.
|
|
*/
|
|
public boolean isClassType() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Generates the default name, that is the `natural' choice for
|
|
* local of this type.
|
|
* @return the default name of a local of this type.
|
|
*/
|
|
public String getDefaultName() {
|
|
return getCanonic().getDefaultName();
|
|
}
|
|
|
|
public int hashCode() {
|
|
int hash = 0;
|
|
for (int i=0; i < classes.length; i++) {
|
|
hash ^= classes[i].hashCode();
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
public boolean equals(Object o) {
|
|
if (o == this)
|
|
return true;
|
|
if (o instanceof MultiClassType) {
|
|
MultiClassType type = (MultiClassType) o;
|
|
if (type.classes.length == classes.length) {
|
|
big_loop:
|
|
for (int i=0; i< type.classes.length; i++) {
|
|
for (int j=0; j<classes.length; j++) {
|
|
if (type.classes[i] == classes[j])
|
|
continue big_loop;
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|