/*******************************************************************************
* Copyright (c) 2010 Thiago Tonelli Bartolomei.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Thiago Tonelli Bartolomei - initial API and implementation
******************************************************************************/
package ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.impl;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.IHierarchyNode;
/**
* An Hierarchy Node is a decorator to an IType that keeps track
* of the inheritance hierarchy.
*
* @author Thiago Tonelli Bartolomei <ttonelli@gsd.uwaterloo.ca>
*/
public class HierarchyNode implements IHierarchyNode {
public static final IType[] EMPTY_TYPES_ARRAY = new IType[0];
protected boolean buggyAnonymous = false;
protected AnonymousClassDeclaration declaration = null;
protected int depth = 0;
protected boolean hasAnonymousType = false;
protected boolean hasExhaustiveSubtypes = false;
protected IType type = null;
protected IType superClass = null;
protected IType[] superInterfaces = null;
protected List<IType> subClasses = null;
protected List<IType> subInterfaces = null;
protected List<IType> anonymousTypes = null;
protected IType declaringType = null;
public HierarchyNode(IType type) {
this(type, null, null, 0);
}
public HierarchyNode(IType type, IType superClass) {
this(type, superClass, null, 1);
}
public HierarchyNode(IType type, IType superClass, IType[] superInterfaces, int depth) {
this.type = type;
this.superClass = superClass;
if (superInterfaces != null) {
this.superInterfaces = superInterfaces;
} else {
this.superInterfaces = EMPTY_TYPES_ARRAY;
}
this.depth = depth;
}
/**
* The type being decorated by this hierarchy node
*
* @return the decorated type
*/
public IType getType() {
return this.type;
}
/**
* Returns a list with all super types (classes and interfaces) of the
* decorated type. This list is equivalent to SuperInterfaces + SuperClass.
*
* @return
*/
public IType[] getSuperTypes() {
// Check if it is java.lang.Object or a root interface
if (superClass == null && superInterfaces.length == 0) {
return EMPTY_TYPES_ARRAY;
}
// Check if it a regular interface (no super class)
if (superClass == null) {
return superInterfaces;
}
// Regular class
IType[] types = new IType[superInterfaces.length + 1];
types[0] = superClass;
System.arraycopy(superInterfaces, 0, types, 1, superInterfaces.length);
return types;
}
/**
* Returns the super class of the decorated type.
*
* @return
*/
public IType getSuperClass() {
return this.superClass;
}
/**
* Returns the super interfaces of the decorated type.
*
* @return
*/
public IType[] getSuperInterfaces() {
return this.superInterfaces;
}
/**
* Returns the list of known sub types of the decorated type.
* This list is equivalent to SubClasses + SubInterfaces.
*
* @return
*/
public IType[] getSubTypes() {
if (subClasses == null && subInterfaces == null) {
return new IType[0];
}
if (subClasses == null) {
return getSubInterfaces();
}
if (subInterfaces == null) {
return getSubClasses();
}
IType[] types = new IType[subClasses.size() + subInterfaces.size()];
System.arraycopy(subClasses.toArray(EMPTY_TYPES_ARRAY), 0, types, 0, subClasses.size());
System.arraycopy(subInterfaces.toArray(EMPTY_TYPES_ARRAY), 0, types, subClasses.size(), subInterfaces.size());
return types;
}
/**
* Returns the list of known sub classes of the decorated type.
*
* @return
*/
public IType[] getSubClasses() {
if (subClasses == null) {
return EMPTY_TYPES_ARRAY;
}
return subClasses.toArray(EMPTY_TYPES_ARRAY);
}
/**
* Returns the list of known sub interfaces of the decorated type.
* Note that this list is always empty if the type is a class.
*
* @return
*/
public IType[] getSubInterfaces() {
if (subInterfaces == null) {
return EMPTY_TYPES_ARRAY;
}
return subInterfaces.toArray(EMPTY_TYPES_ARRAY);
}
/**
* Returns the list of known anonymous types declared directly on
* the decorated type.
*
* @return
*/
public IType[] getAnonymousTypes() {
if (anonymousTypes == null) {
return EMPTY_TYPES_ARRAY;
}
return anonymousTypes.toArray(EMPTY_TYPES_ARRAY);
}
/**
* Checks if there is at least one anonymous type between this
* type's nested types.
*
* @return
*/
public boolean hasAnonymousType() {
return anonymousTypes != null;
}
/**
* Returns the immediate declaring type.
*
* For nested and anonymous types, returns the outer type. For top level
* types, return null.
*
* @return
*/
public IType getDeclaringType() {
if (declaringType == null) {
declaringType = type.getDeclaringType();
}
return declaringType;
}
/**
* Gets the depth of this node in the inheritance hierarchy.
* java.lang.Object is at depth 0.
*
* @return
*/
public int depth() {
return depth;
}
/**
* Sets the declaring type.
*
* @param type
*/
public void setDeclaringType(IType type) {
this.declaringType = type;
}
/**
* Adds this sub type to the node (can be a class or interface)
*
* @param subType
*/
public void addSubType(IType subType) {
try {
if (subType.isInterface()) {
if (subInterfaces == null) {
subInterfaces = new ArrayList<IType>(1);
}
subInterfaces.add(subType);
return;
}
} catch (JavaModelException e) {
}
// consider it a class (maybe was an exception)
if (subClasses == null) {
subClasses = new ArrayList<IType>(1);
}
subClasses.add(subType);
}
/**
* Adds this type to the list of nested types
*
* @param nested
*/
public void addAnonymousType(IType nested) {
if (anonymousTypes == null) {
anonymousTypes = new ArrayList<IType>(1);
}
anonymousTypes.add(nested);
}
/**
* Checks if this type has already been exhaustively searched for sub types
*
* @return
*/
public boolean hasExhaustiveSubtypes() {
return this.hasExhaustiveSubtypes;
}
/**
* Sets hasExhaustiveSubtypes
*
* @param hasExhaustiveSubtypes
*/
public void setHasExhaustiveSubtypes(boolean hasExhaustiveSubtypes) {
this.hasExhaustiveSubtypes = hasExhaustiveSubtypes;
}
/**
* Checks if this node represents a buggy anonymous type.
*
* Buggy anonymous types don't have direct representation in disk (or it is in the wrong
* place), and their IType objects should not be used.
*
* @return
*/
public boolean isBuggyAnonymous() {
return buggyAnonymous;
}
/**
* @param buggyAnonymous the buggyAnonymous to set
*/
public void setBuggyAnonymous(boolean buggyAnonymous) {
this.buggyAnonymous = buggyAnonymous;
}
/**
* If this is a buggy anonymous, it carries the ASTNode from which it was created.
* TODO - REMOVE
* @return the ASTNode if it is a buggy anonymous, null otherwise
*/
public AnonymousClassDeclaration getDeclaration() {
return declaration;
}
/**
* TODO - REMOVE
* @param declaration
*/
public void setDeclaration(AnonymousClassDeclaration declaration) {
this.declaration = declaration;
}
public String toString() {
return type.toString();
}
}