/*******************************************************************************
* Copyright (c) 2005, 2012 eBay Inc.
* 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
*
*******************************************************************************/
package org.eclipse.dltk.mod.internal.core.hierarchy;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.vjet.dsf.jst.IJstType;
import org.eclipse.vjet.dsf.ts.type.TypeName;
import org.eclipse.vjet.eclipse.codeassist.CodeassistUtils;
import org.eclipse.vjet.eclipse.core.ClassFileConstants;
import org.eclipse.vjet.eclipse.core.IVjoSourceModule;
import org.eclipse.vjet.eclipse.core.IVjoTypeHierarchy;
import org.eclipse.vjet.eclipse.core.VjetPlugin;
import org.eclipse.vjet.vjo.tool.typespace.SourceTypeName;
import org.eclipse.vjet.vjo.tool.typespace.TypeSpaceMgr;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.dltk.mod.ast.Modifiers;
import org.eclipse.dltk.mod.core.DLTKCore;
import org.eclipse.dltk.mod.core.ElementChangedEvent;
import org.eclipse.dltk.mod.core.Flags;
import org.eclipse.dltk.mod.core.IElementChangedListener;
import org.eclipse.dltk.mod.core.IScriptProject;
import org.eclipse.dltk.mod.core.ISourceModule;
import org.eclipse.dltk.mod.core.IType;
import org.eclipse.dltk.mod.core.ITypeHierarchy;
import org.eclipse.dltk.mod.core.ITypeHierarchyChangedListener;
import org.eclipse.dltk.mod.core.ModelException;
import org.eclipse.dltk.mod.internal.core.JSSourceType;
import org.eclipse.dltk.mod.internal.core.JSSourceTypeElementInfo;
import org.eclipse.dltk.mod.internal.core.ModelElement;
import org.eclipse.dltk.mod.internal.core.ScriptProject;
import org.eclipse.dltk.mod.internal.core.TypeVector;
import org.eclipse.dltk.mod.internal.core.util.Messages;
import org.eclipse.dltk.mod.internal.core.util.Util;
/**
* Vjo type hierarchy implementation based on type space
*
*
*
*/
public class VjoTypeHierarchy implements ITypeHierarchy,
IElementChangedListener, IVjoTypeHierarchy {
public static final String VJO_OBJECT = "Object";
public static final String VJO_OBJECT_FULLNAME = "vjo.Object";
private static final boolean DEBUG = false;
protected static final IType[] NO_TYPE = new IType[0];
protected ArrayList<IType> interfaces = new ArrayList<IType>(10);
protected IType type;
protected ISourceModule module;
protected IProgressMonitor progressMonitor;
private boolean computeSubtypes;
private boolean needsRefresh = false;
public TypeSpaceMgr typeSpaceMgr = TypeSpaceMgr.getInstance();
public IJstType rootType;
protected ArrayList listeners = new ArrayList();
private boolean isNativeType = false;
protected Map<IType, IType> classToSuperclass = new HashMap<IType, IType>();
protected Map<IType, IType[]> typeToSuperInterfaces = new HashMap<IType, IType[]>();
protected Map<IType, TypeVector> typeToSubtypes = new HashMap<IType, TypeVector>();
protected TypeVector rootClasses = new TypeVector();
/**
* Caches the handle of the superclass for the specified type. As a side
* effect cache this type as a subtype of the superclass.
*/
protected void cacheSuperclass(IType type, IType superclass) {
if (superclass != null) {
this.classToSuperclass.put(type, superclass);
addSubtype(superclass, type);
}
}
/**
* Caches all of the superinterfaces that are specified for the type.
*/
protected void cacheSuperInterfaces(IType type, IType[] superinterfaces) {
this.typeToSuperInterfaces.put(type, superinterfaces);
for (int i = 0; i < superinterfaces.length; i++) {
IType superinterface = superinterfaces[i];
addInterface(superinterface);
if (superinterface != null) {
addSubtype(superinterface, type);
}
}
}
/**
* Adds the given subtype to the type.
*/
protected void addSubtype(IType type, IType subtype) {
if (subtype == null) {
return;
}
TypeVector subtypes = (TypeVector) this.typeToSubtypes.get(type);
if (subtypes == null) {
subtypes = new TypeVector();
this.typeToSubtypes.put(type, subtypes);
}
if (!subtypes.contains(subtype)) {
subtypes.add(subtype);
}
}
/**
* Creates a TypeHierarchy on the given type.
*/
public VjoTypeHierarchy(IType type) {
this.type = type;
this.module = type.getSourceModule();
this.isNativeType = CodeassistUtils.isNativeObject(type);
}
public boolean isNativeType() {
return isNativeType;
}
/**
* Compute this type hierarchy.
*/
protected void compute() throws ModelException, CoreException {
computeSuperClasses(type);
computeSubClasses(type);
}
/**
* Adds the type to the collection of interfaces.
*/
private void addInterface(IType type) {
this.interfaces.add(type);
}
/**
* Adds the type to the collection of root classes if the classes is not
* already present in the collection.
*/
private void addRootClass(IType type) {
if (this.rootClasses.contains(type))
return;
this.rootClasses.add(type);
}
private void computeInterfaces(IType fType) {
TypeName name = getTypeName(fType);
IJstType jsttype = findType(name);
if (jsttype != null) {
List interfaces = jsttype.getSatisfies();
if (interfaces == null || interfaces.isEmpty())
return;
List<IType> superInts = toITypes(fType, interfaces);
cacheSuperInterfaces(fType, toTypeArray(superInts));
}
}
private void computeSuperClasses(IType fType) {
TypeName name = getTypeName(fType);
computeInterfaces(fType);
IJstType jstType = findType(name);
if (jstType == null)
return;
IJstType superType = jstType.getExtend();
if (superType == null) {
rootType = jstType;
// if (hasNotObjectSuperType(superTypes)) {
// rootType = superTypes.get(1);
// }
addRootClass(CodeassistUtils.findType((ScriptProject) fType
.getScriptProject(), rootType));
} else {
IType superItype = CodeassistUtils.findType((ScriptProject) fType
.getScriptProject(), superType);
if (superItype != null) {
cacheSuperclass(fType, superItype);
computeSuperClasses(superItype);
}else{
VjetPlugin.error("Type hierarchy could created could not find dltk type for: " + superType.getName() );
}
}
}
private boolean hasNotObjectSuperType(List<IJstType> superTypes) {
if (superTypes.size() > 1) {
if (VJO_OBJECT.equals(rootType.getName())
|| VJO_OBJECT_FULLNAME.equals(rootType.getName())) {
// is our Object
if (CodeassistUtils.isNativeType(rootType)) {
return true;
}
}
}
return false;
}
/**
* Compute the given type's children.
*
* Add by Oliver for bug:5675 on 2009-10-31.
*
* @param type
* @return
*/
private List<IType> RegistryAllSubtypesForType(IType type) {
List<IType> subTypes = new ArrayList<IType>();
computeAndAddSubClasses(type, subTypes);
return subTypes;
}
/**
* Iterate all level children and add the parent / children relationship.
*
* Add by Oliver for bug:5675 on 2009-10-31.
*
* @param fType
* @param subs
*/
private void computeAndAddSubClasses(IType fType, List<IType> subs) {
TypeName name = getTypeName(fType);
List<IJstType> subTypes;
subTypes = typeSpaceMgr.findSubTypes(name);
IJstType currentType = typeSpaceMgr.findType(name);
if(currentType==null){
return;
}
if (isInterface(fType) || currentType.isInterface()) {
// find which class implements this interface
// FIXME backend API cannot find which interfaces extends my
// interface
List<IJstType> list = typeSpaceMgr.findSatisfiers(name);
for (IJstType jstType : list) {
subTypes.add(jstType);
}
}
for (IJstType subType : subTypes) {
if (subType != null) {
IType isubType = CodeassistUtils.findType((ScriptProject) fType
.getScriptProject(), subType);
if (isubType != null) {
addSubtype(fType, isubType);
subs.add(isubType);
computeAndAddSubClasses(isubType, subs);
}
}
}
}
private void computeSubClasses(IType fType) {
RegistryAllSubtypesForType(fType);
// Comment by Oliver. The old codes for computing the sub classes, if
// the new codes are
// stable will remove following codes.
// TypeName name = getTypeName(type);
// List<IJstType> subTypes;
// subTypes = typeSpaceMgr.findSubTypes(name);
// List<IType> subITypes = toITypes(subTypes);
// subClasses.addAll(subITypes);
//
// IJstType currentType = typeSpaceMgr.findType(name);
//
// if (isInterface(type) || currentType.isInterface()) {
// // find which class implements this interface
// // FIXME backend API cannot find which interfaces extends my
// // interface
// List<IJstType> list = typeSpaceMgr.findSatisfiers(name);
// for (IJstType jstType : list) {
// subITypes.add(toIType(jstType));
// }
// }
// for (IType subtype : subITypes) {
// addSubtype(fType, subtype);
// }
}
private List<IType> toITypes(IType itype, List<IJstType> list) {
List<IType> result = new ArrayList<IType>();
for (IJstType jstType : list) {
IType type = CodeassistUtils.findType((ScriptProject) itype
.getScriptProject(), jstType);
if (type != null) {
result.add(type);
}
}
return result;
}
/**
* @see ITypeHierarchy TODO (jerome) should use a PerThreadObject to build
* the hierarchy instead of synchronizing (see also
* isAffected(IJavaElementDelta))
*/
public synchronized void refresh(IProgressMonitor monitor)
throws ModelException {
try {
this.progressMonitor = monitor;
if (monitor != null) {
if (this.type != null) {
monitor.beginTask(Messages.bind(
Messages.hierarchy_creatingOnType, this.type
.getFullyQualifiedName()), 100);
} else {
monitor.beginTask(Messages.hierarchy_creating, 100);
}
}
long start = -1;
if (DEBUG) {
start = System.currentTimeMillis();
if (computeSubtypes) {
System.out
.println("CREATING TYPE HIERARCHY [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
System.out
.println("CREATING SUPER TYPE HIERARCHY [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (this.type != null) {
System.out
.println(" on type " + ((ModelElement) this.type).toStringWithAncestors()); //$NON-NLS-1$
}
}
compute();
this.needsRefresh = false;
if (DEBUG) {
if (this.computeSubtypes) {
System.out
.println("CREATED TYPE HIERARCHY in " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
} else {
System.out
.println("CREATED SUPER TYPE HIERARCHY in " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
}
System.out.println(this.toString());
}
} catch (ModelException e) {
throw e;
} catch (CoreException e) {
throw new ModelException(e);
} finally {
if (monitor != null) {
monitor.done();
}
this.progressMonitor = null;
}
}
public boolean hasFineGrainChanges() {
return false;
}
// public IType[] getAllSupertypes(IType type) {
// ArrayList supers = new ArrayList();
// if (type != null) {
// getAllSupertypes0(type, supers);
// }
// IType[] supertypes = new IType[supers.size()];
// supers.toArray(supertypes);
// return supertypes;
// }
//
// private void getAllSupertypes0(IType type, ArrayList supers) {
// TypeVector superTypes = new TypeVector();
// superTypes.addAll(getSupertypes(type));
// if (superTypes != null) {
// IType[] superclasses = superTypes.elements();
// if (superclasses.length != 0) {
// addAllCheckingDuplicates(supers, superclasses);
// for (int i = 0; i < superclasses.length; i++) {
// getAllSupertypes0(superclasses[i], supers);
// }
// }
// }
// }
// public IType[] getSupertypes(IType type) {
// IType[] types= getSuperclass(type);
// List<IType> result = new ArrayList<IType>();
// Collections.addAll(result, types);
//
//
// TypeName name = getTypeName(type);
// IJstType jstType=findType(name);
// // if(isInterface(type)){
// // List<IJstType> is;
// //IJstType jt = typeSpaceMgr.findType(name);
// List atisfies = jstType.getSatisfies();
// //jstType.getExtends()
//
// // is = typeSpaceMgr.findSatisfiers(name);
// if (atisfies != null ) {
// for (Object object : atisfies) {
// result.add(toIType((IJstType)object));
// }
//
// }
// // }
// return (IType[]) result.toArray(new IType[result.size()]);
// }
/**
* Adds all of the elements in the collection to the list if the element is
* not already in the list.
*/
private void addAllCheckingDuplicates(ArrayList list, IType[] collection) {
for (int i = 0; i < collection.length; i++) {
IType element = collection[i];
if (!list.contains(element)) {
list.add(element);
}
}
}
public void initialize(int size) {
}
public void store(OutputStream output, IProgressMonitor monitor)
throws ModelException {
}
public boolean contains(IType type) {
// interfaces
// if (this.interfaces.contains(type))
// return true;
// classes
// classes
if (this.classToSuperclass.get(type) != null) {
return true;
}
// root classes
if (this.rootClasses.contains(type))
return true;
// interfaces
if (this.interfaces.contains(type))
return true;
return false;
}
public void addTypeHierarchyChangedListener(
ITypeHierarchyChangedListener listener) {
// register with JavaCore to get Java element delta on first listener
// added
if (listeners.size() == 0) {
DLTKCore.addElementChangedListener(this);
}
// add listener only if it is not already present
if (listeners.indexOf(listener) == -1) {
listeners.add(listener);
}
}
public synchronized boolean exists() {
if (!needsRefresh) {
return true;
}
return (this.type == null || this.type.exists())
&& this.javaProject().exists();
}
public IType[] getAllClasses() {
// return toTypeArray(allClasses);
TypeVector classes = this.rootClasses.copy();
for (Iterator iter = this.classToSuperclass.keySet().iterator(); iter
.hasNext();) {
classes.add((IType) iter.next());
}
return classes.elements();
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllTypes() {
IType[] classes = getAllClasses();
int classesLength = classes.length;
IType[] allInterfaces = getAllInterfaces();
int interfacesLength = allInterfaces.length;
IType[] all = new IType[classesLength + interfacesLength];
System.arraycopy(classes, 0, all, 0, classesLength);
System
.arraycopy(allInterfaces, 0, all, classesLength,
interfacesLength);
return all;
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllInterfaces() {
IType[] collection = new IType[this.interfaces.size()];
this.interfaces.toArray(collection);
return collection;
}
private IType[] toTypeArray(List<IType> a) {
return (IType[]) a.toArray(new IType[a.size()]);
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllSubtypes(IType type) {
return getAllSubtypesForType(type);
}
/**
* @see #getAllSubtypes(IType)
*/
private IType[] getAllSubtypesForType(IType type) {
ArrayList subTypes = new ArrayList();
getAllSubtypesForType0(type, subTypes);
IType[] subClasses = new IType[subTypes.size()];
subTypes.toArray(subClasses);
return subClasses;
}
/**
*/
private void getAllSubtypesForType0(IType type, ArrayList subs) {
IType[] subTypes = getSubtypesForType(type);
if (subTypes.length != 0) {
for (int i = 0; i < subTypes.length; i++) {
IType subType = subTypes[i];
subs.add(subType);
getAllSubtypesForType0(subType, subs);
}
}
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllSuperclasses(IType type) {
IType[] superclasses = getSuperclass(type);
IType superclass = superclasses.length == 1 ? superclasses[0] : null;
TypeVector supers = new TypeVector();
while (superclass != null) {
supers.add(superclass);
superclasses = getSuperclass(superclass);
superclass = superclasses.length == 1 ? superclasses[0] : null;
}
return supers.elements();
}
/**
* Returns an array of subtypes for the given type - will never return null.
*/
private IType[] getSubtypesForType(IType type) {
TypeVector vector = (TypeVector) this.typeToSubtypes.get(type);
if (vector == null)
return NO_TYPE;
else
return vector.elements();
}
/**
*
*
* @see ITypeHierarchy
*/
public IType[] getAllSuperInterfaces(IType type) {
ArrayList supers = new ArrayList();
if (this.typeToSuperInterfaces.get(type) == null) {
return NO_TYPE;
}
getAllSuperInterfaces0(type, supers);
IType[] superinterfaces = new IType[supers.size()];
supers.toArray(superinterfaces);
return superinterfaces;
}
private void getAllSuperInterfaces0(IType type, ArrayList supers) {
IType[] superinterfaces = (IType[]) this.typeToSuperInterfaces
.get(type);
if (superinterfaces != null && superinterfaces.length != 0) {
addAllCheckingDuplicates(supers, superinterfaces);
for (int i = 0; i < superinterfaces.length; i++) {
getAllSuperInterfaces0(superinterfaces[i], supers);
}
}
IType superclass = (IType) this.classToSuperclass.get(type);
if (superclass != null) {
getAllSuperInterfaces0(superclass, supers);
}
}
/**
* @see ITypeHierarchy
*/
public IType[] getAllSupertypes(IType type) {
ArrayList supers = new ArrayList();
if (this.typeToSuperInterfaces.get(type) == null
&& this.classToSuperclass.get(type) == null) {
return NO_TYPE;
}
getAllSupertypes0(type, supers);
IType[] supertypes = new IType[supers.size()];
supers.toArray(supertypes);
return supertypes;
}
private void getAllSupertypes0(IType type, ArrayList supers) {
IType[] superinterfaces = (IType[]) this.typeToSuperInterfaces
.get(type);
if (superinterfaces != null && superinterfaces.length != 0) {
addAllCheckingDuplicates(supers, superinterfaces);
for (int i = 0; i < superinterfaces.length; i++) {
getAllSuperInterfaces0(superinterfaces[i], supers);
}
}
IType superclass = (IType) this.classToSuperclass.get(type);
if (superclass != null) {
supers.add(superclass);
getAllSupertypes0(superclass, supers);
}
}
public int getCachedFlags(IType type) {
JSSourceType sourceType = (JSSourceType) type;
int flags = 0;
try {
JSSourceTypeElementInfo info;
info = (JSSourceTypeElementInfo) sourceType.getElementInfo();
if (info != null) {
flags = info.getModifiers();
if ((flags & ClassFileConstants.AccInterface) != 0) {
flags = Modifiers.AccInterface;
}
}
} catch (ModelException e) {
DLTKCore.error(e.toString(), e);
}
return flags;
}
public IType[] getRootClasses() {
IType[] rootClasses = new IType[] { getType() };
if (rootType != null && type != null) {
IType atype = CodeassistUtils.findType((ScriptProject) type
.getScriptProject(), rootType);
if (atype != null) {
rootClasses = new IType[] { atype };
}
}
return rootClasses;
}
private boolean isInterface(IType type) {
try {
return Flags.isInterface(type.getFlags());
} catch (ModelException e) {
return false;
}
}
// public IType[] getSuperclass(IType type) {
// List<IJstType> result = new ArrayList<IJstType>();
//
// TypeName name = getTypeName(type);
//
// IJstType jstType=findType(name);
// if (jstType == null) {
// return new IType[0];
// }
//
// IJstType superType = jstType.getExtend();
// if (superType != null) {
// result.add(superType);
// }
//
//
// return toTypeArray(toITypes(result));
// }
// public IType[] getSubclasses(IType type) {
// List<IJstType> result = new ArrayList<IJstType>();
// // if (type.getResource().exists()) {
// TypeName name = getTypeName(type);
// List<IJstType> jstType = typeSpaceMgr.findSubTypes(name);
// result.addAll(jstType);
// return toTypeArray(toITypes(result));
// }
// public IType[] getSubtypes(IType type) {
// IType[] types = getSubclasses(type);
// List<IType> result = new ArrayList<IType>();
// Collections.addAll(result, types);
//
// TypeName name = getTypeName(type);
//
// if (isInterface(type)) {
// // find which class implements this interface
// // FIXME backend API cannot find which interfaces extends my
// // interface
// List<IJstType> list = typeSpaceMgr.findSatisfiers(name);
// for (IJstType jstType : list) {
// result.add(toIType(jstType));
// }
//
// }
//
// return (IType[]) result.toArray(new IType[result.size()]);
//
// }
private TypeName getTypeName(IType type) {
IVjoSourceModule vjoSourceModule = (IVjoSourceModule) type
.getSourceModule();
if (vjoSourceModule != null) {
TypeName typeName = vjoSourceModule.getTypeName();
TypeName typeName2 = new SourceTypeName(typeName.groupName(), type.getFullyQualifiedName("."));
// TypeName tName = vjoSourceModule.getTypeName();
return typeName2;
} else {
return null;
}
}
private IJstType findType(TypeName typeName) {
if (isNativeType()) {
return CodeassistUtils.findNativeJstType(typeName.typeName());
} else {
return typeSpaceMgr.findType(typeName);
}
}
public IType getType() {
return type;
}
public void removeTypeHierarchyChangedListener(
ITypeHierarchyChangedListener listener) {
// nothing
}
public void elementChanged(ElementChangedEvent event) {
if (event.getType() == ElementChangedEvent.POST_CHANGE) {
fireChanged();
}
}
public void fireChanged() {
// clone so that a listener cannot have a side-effect on this list when
// being notified
listeners = (ArrayList) listeners.clone();
for (int i = 0; i < listeners.size(); i++) {
final ITypeHierarchyChangedListener listener = (ITypeHierarchyChangedListener) listeners
.get(i);
SafeRunner.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
Util
.log(exception,
"Exception occurred in listener of Type hierarchy change notification"); //$NON-NLS-1$
}
public void run() throws Exception {
listener.typeHierarchyChanged(VjoTypeHierarchy.this);
}
});
}
}
public IScriptProject javaProject() {
return this.type.getScriptProject();
}
/**
* @see ITypeHierarchy
*/
public IType[] getSubclasses(IType type) {
if (this.isInterface(type)) {
return NO_TYPE;
}
TypeVector vector = (TypeVector) this.typeToSubtypes.get(type);
if (vector == null)
return NO_TYPE;
else
return vector.elements();
}
/**
* @see ITypeHierarchy
*/
public IType[] getSubtypes(IType type) {
return getSubtypesForType(type);
}
/**
* @see ITypeHierarchy
*/
public IType[] getSuperclass(IType type) {
if (type == null || this.isInterface(type)) {
return null;
}
return new IType[] { this.classToSuperclass.get(type) };
}
/**
* @see ITypeHierarchy
*/
public IType[] getSuperInterfaces(IType type) {
IType[] types = (IType[]) this.typeToSuperInterfaces.get(type);
if (types == null) {
return NO_TYPE;
}
return types;
}
/**
* @see ITypeHierarchy
*/
public IType[] getSupertypes(IType type) {
IType superclass = getSuperclass(type).length > 0 ? getSuperclass(type)[0]
: null;
if (superclass == null) {
return getSuperInterfaces(type);
} else {
TypeVector superTypes = new TypeVector(getSuperInterfaces(type));
superTypes.add(superclass);
return superTypes.elements();
}
}
}