/*******************************************************************************
* Copyright (c) 2005, 2007 IBM Corporation and others.
* 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;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;
import org.eclipse.vjet.eclipse.compiler.IJSSourceElementRequestor;
import org.eclipse.vjet.eclipse.core.IJSSourceModule;
import org.eclipse.vjet.eclipse.core.IJSTypeParameter;
import org.eclipse.core.runtime.Assert;
import org.eclipse.dltk.mod.compiler.CharOperation;
import org.eclipse.dltk.mod.core.DLTKCore;
import org.eclipse.dltk.mod.core.IModelElement;
import org.eclipse.dltk.mod.core.ISourceModule;
import org.eclipse.dltk.mod.core.ModelException;
public class VjoSourceModuleStructureRequestor implements
IJSSourceElementRequestor {
private final static String[] EMPTY = new String[0];
/*
* Map from ModelElementInfo to of ArrayList of IModelElement representing
* the children of the given info.
*/
protected HashMap m_children;
/**
* Stack of parent handles, corresponding to the info stack. We keep both,
* since info objects do not have back pointers to handles.
*/
private Stack m_handleStack;
protected boolean m_hasSyntaxErrors = false;
/**
* The import container info - null until created
*/
protected JSModelElementInfo m_importContainerInfo;
/**
* Stack of parent scope info objects. The info on the top of the stack is
* the parent of the next element found. For example, when we locate a
* method, the parent info object will be the type the method is contained
* in.
*/
private Stack m_infoStack;
/**
* The handle to the source module being parsed
*/
private ISourceModule m_module;
/**
* The info object for the module being parsed
*/
private JSSourceModuleElementInfo m_moduleInfo;
/**
* Hashtable of children elements of the source module. Children are added
* to the table as they are found by the parser. Keys are handles, values
* are corresponding info objects.
*/
private Map m_newElements;
public VjoSourceModuleStructureRequestor(ISourceModule module,
JSSourceModuleElementInfo moduleInfo, Map newElements) {
this.m_module = module;
this.m_moduleInfo = moduleInfo;
this.m_newElements = newElements;
}
public void acceptFieldReference(char[] fieldName, int sourcePosition) {
}
/*
* (non-Javadoc)
*
* @see org.eclipse.vjet.eclipse.compiler.IJSSourceElementRequestor#acceptImport(int,
* int, char[][], boolean, int)
*/
public void acceptImport(int declarationStart, int declarationEnd,
char[][] tokens, boolean onDemand, int modifiers) {
ModelElement parentHandle = (ModelElement) this.m_handleStack.peek();
if (!(parentHandle.getElementType() == IModelElement.SOURCE_MODULE)) {
Assert.isTrue(false); // Should not happen
}
IJSSourceModule parentCU = (IJSSourceModule) parentHandle;
// create the import container and its info
ImportContainer importContainer = (ImportContainer) parentCU
.getImportContainer();
if (this.m_importContainerInfo == null) {
this.m_importContainerInfo = new JSModelElementInfo();
IJSModelElementInfo parentInfo = (IJSModelElementInfo) this.m_infoStack
.peek();
// TODO is it the same? addToChildren(parentInfo, importContainer);
parentInfo.addChild(importContainer);
this.m_newElements.put(importContainer, this.m_importContainerInfo);
}
String elementName = ModelManager.getModelManager().intern(
new String(CharOperation.concatWith(tokens, '.')));
ImportDeclaration handle = new ImportDeclaration(importContainer,
elementName, onDemand);
resolveDuplicates(handle);
ImportDeclarationElementInfo info = new ImportDeclarationElementInfo();
info.setSourceRangeStart(declarationStart);
info.setSourceRangeEnd(declarationEnd);
info.setFlags(modifiers);
// TODO is it the same? addToChildren(this.importContainerInfo, handle);
m_importContainerInfo.addChild(handle);
this.m_newElements.put(handle, info);
}
public void acceptMethodReference(char[] methodName, int argCount,
int sourcePosition, int sourceEndPosition) {
}
public void acceptPackage(int declarationStart, int declarationEnd,
char[] name) {
IJSModelElementInfo parentInfo = (IJSModelElementInfo) this.m_infoStack
.peek();
ModelElement parentHandle = (ModelElement) this.m_handleStack.peek();
PackageDeclaration handle = null;
// if (parentHandle.getElementType() == IModelElement.SOURCE_MODULE) {
handle = new JSPackageDeclaration(parentHandle, new String(name));
this.resolveDuplicates(handle);
JSSourceRefElementInfo info = new JSSourceRefElementInfo();
info.setSourceRangeStart(declarationStart);
info.setSourceRangeEnd(declarationEnd);
// TODO is it the same? addToChildren(parentInfo, handle);
parentInfo.addChild(handle);
this.m_newElements.put(handle, info);
}
public void acceptTypeReference(char[] typeName, int sourcePosition) {
}
// public boolean enterFieldCheckDuplicates(FieldInfo fieldInfo,
// IJSModelElementInfo parentInfo, ModelElement parentHandle) {
// IModelElement[] childrens = parentInfo.getChildren();
// for (int i = 0; i < childrens.length; ++i) {
// if (childrens[i] instanceof SourceField
// && childrens[i].getElementName().equals(fieldInfo.name)) {
// // we should go inside existent element
// SourceField handle = (SourceField) childrens[i];
// JSSourceFieldElementInfo info = (JSSourceFieldElementInfo)
// this.newElements
// .get(handle);
// this.infoStack.push(info);
// this.handleStack.push(handle);
// return true;
// }
// }
// if (parentInfo instanceof JSSourceMethodElementInfo) {
// JSSourceMethodElementInfo method = (JSSourceMethodElementInfo)
// parentInfo;
// String[] args = method.getArgumentNames();
// for (int i = 0; i < args.length; ++i) {
// if (args[i].equals(fieldInfo.name)) {
// return false;
// }
// }
// }
// this.createField(fieldInfo, parentInfo, parentHandle);
// return true;
// }
public void acceptTypeReference(char[][] typeName, int sourceStart,
int sourceEnd) {
}
private void addToChildren(ModelElementInfo parentInfo, ModelElement handle) {
ArrayList childrenList = (ArrayList) this.m_children.get(parentInfo);
if (childrenList == null)
this.m_children.put(parentInfo, childrenList = new ArrayList());
childrenList.add(handle);
}
private void createField(FieldInfo fieldInfo, MemberElementInfo parentInfo,
ModelElement parentHandle) {
ModelManager manager = ModelManager.getModelManager();
JSFieldInfo jsFieldInfo = (JSFieldInfo) fieldInfo;
SourceField handle = new JSSourceField(parentHandle, manager
.intern(fieldInfo.name));
this.resolveDuplicates(handle);
//add by xingzhu, set actual resource
handle.setResource(jsFieldInfo.resource);
JSSourceFieldElementInfo info = new JSSourceFieldElementInfo();
info.setNameSourceStart(fieldInfo.nameSourceStart);
info.setNameSourceEnd(fieldInfo.nameSourceEnd);
info.setSourceRangeStart(fieldInfo.declarationStart);
info.setFlags(fieldInfo.modifiers);
info.setType(jsFieldInfo.m_type);
info.setInitializationSource(jsFieldInfo.m_initializationSource);
parentInfo.addChild(handle);
this.m_newElements.put(handle, info);
this.m_infoStack.push(info);
this.m_handleStack.push(handle);
}
public void enterField(FieldInfo fieldInfo) {
MemberElementInfo parentInfo = (MemberElementInfo) this.m_infoStack
.peek();
ModelElement parentHandle = (ModelElement) this.m_handleStack.peek();
this.createField(fieldInfo, parentInfo, parentHandle);
}
public boolean enterFieldCheckDuplicates(FieldInfo fieldInfo) {
// IJSModelElementInfo parentInfo = (IJSModelElementInfo) this.infoStack
// .peek();
// ModelElement parentHandle = (ModelElement) this.handleStack.peek();
// return this.enterFieldCheckDuplicates(fieldInfo, parentInfo,
// parentHandle);
return false;
}
public boolean enterFieldWithParentType(FieldInfo info, String parentName,
String delimiter) {
// try {
// ModelElement element = this.getExistentType(parentName, delimiter);
// if (element == null) {
// return false;
// }
// IJSModelElementInfo typeInfo = (IJSModelElementInfo) element
// .getElementInfo();
// this.enterFieldCheckDuplicates(info, typeInfo, element);
// return true;
// } catch (ModelException e) {
// if (DLTKCore.DEBUG) {
// e.printStackTrace();
// }
// }
return false;
}
/**
* @see IJSSourceElementRequestor
*/
public void enterInitializer(int declarationSourceStart, int modifiers) {
MemberElementInfo parentInfo = (MemberElementInfo) this.m_infoStack
.peek();
ModelElement parentHandle = (ModelElement) this.m_handleStack.peek();
JSInitializer handle = null;
if (parentHandle.getElementType() == IModelElement.TYPE) {
handle = new JSInitializer(parentHandle, 1);
} else {
Assert.isTrue(false); // Should not happen
}
resolveDuplicates(handle);
JSInitializerElementInfo info = new JSInitializerElementInfo();
info.setSourceRangeStart(declarationSourceStart);
info.setFlags(modifiers);
parentInfo.addChild(handle);
// addToChildren(parentInfo, handle);
this.m_newElements.put(handle, info);
this.m_infoStack.push(info);
this.m_handleStack.push(handle);
}
public void enterMethod(MethodInfo methodInfo) {
IJSModelElementInfo parentInfo = (IJSModelElementInfo) this.m_infoStack
.peek();
ModelElement parentHandle = (ModelElement) this.m_handleStack.peek();
this.processMethod(methodInfo, parentInfo, parentHandle);
}
public void enterMethodRemoveSame(MethodInfo methodInfo) {
IJSModelElementInfo parentInfo = (IJSModelElementInfo) this.m_infoStack
.peek();
IModelElement[] childrens = parentInfo.getChildren();
for (int i = 0; i < childrens.length; ++i) {
if (childrens[i].getElementName().equals(methodInfo.name)) {
parentInfo.removeChild(childrens[i]);
}
}
this.enterMethod(methodInfo);
}
public boolean enterMethodWithParentType(MethodInfo info,
String parentName, String delimiter) {
try {
ModelElement element = this.getExistentType(parentName, delimiter);
if (element == null) {
return false;
}
IJSModelElementInfo typeInfo = (IJSModelElementInfo) element
.getElementInfo();
IModelElement[] childrens = typeInfo.getChildren();
for (int i = 0; i < childrens.length; ++i) {
if (childrens[i].getElementName().equals(info.name)) {
typeInfo.removeChild(childrens[i]);
}
}
this.processMethod(info, typeInfo, element);
return true;
} catch (ModelException e) {
DLTKCore.error(e.toString(), e);
}
return false;
}
public void enterModule() {
this.m_infoStack = new Stack();
this.m_handleStack = new Stack();
this.m_children = new HashMap();
this.enterModuleRoot();
}
public void enterModuleRoot() {
this.m_infoStack.push(this.m_moduleInfo);
this.m_handleStack.push(this.m_module);
}
public void enterType(TypeInfo typeInfo) {
IJSModelElementInfo parentInfo = (IJSModelElementInfo) this.m_infoStack
.peek();
ModelElement parentHandle = (ModelElement) this.m_handleStack.peek();
this.processType(typeInfo, parentInfo, parentHandle);
}
public boolean enterTypeAppend(String fullName, String delimiter) {
try {
ModelElement element = this.getExistentType(fullName, delimiter);
if (element == null) {
return false;
} else {
IJSModelElementInfo info = (IJSModelElementInfo) element
.getElementInfo();
this.m_infoStack.push(info);
this.m_handleStack.push(element);
return true;
}
} catch (ModelException e) {
DLTKCore.error(e.toString(), e);
}
return false;
}
public void enterTypeParameter(JSTypeParameterInfo typeParameterInfo) {
IJSModelElementInfo parentInfo = (IJSModelElementInfo) this.m_infoStack
.peek();
ModelElement parentHandle = (ModelElement) this.m_handleStack.peek();
String nameString = new String(typeParameterInfo.name);
JSTypeParameter handle = new JSTypeParameter(parentHandle, nameString); // NB:
// occurenceCount
// is
// computed
// in
// resolveDuplicates
resolveDuplicates(handle);
JSTypeParameterElementInfo info = new JSTypeParameterElementInfo();
info.setSourceRangeStart(typeParameterInfo.declarationStart);
info.m_nameStart = typeParameterInfo.nameSourceStart;
info.m_nameEnd = typeParameterInfo.nameSourceEnd;
info.m_bounds = typeParameterInfo.m_bounds;
if (parentInfo instanceof JSSourceTypeElementInfo) {
JSSourceTypeElementInfo elementInfo = (JSSourceTypeElementInfo) parentInfo;
IJSTypeParameter[] typeParameters = elementInfo.m_typeParameters;
int length = typeParameters.length;
System.arraycopy(typeParameters, 0,
typeParameters = new IJSTypeParameter[length + 1], 0,
length);
typeParameters[length] = handle;
elementInfo.m_typeParameters = typeParameters;
} else {
JSSourceMethodElementInfo elementInfo = (JSSourceMethodElementInfo) parentInfo;
IJSTypeParameter[] typeParameters = elementInfo.typeParameters;
int length = typeParameters.length;
System.arraycopy(typeParameters, 0,
typeParameters = new IJSTypeParameter[length + 1], 0,
length);
typeParameters[length] = handle;
elementInfo.typeParameters = typeParameters;
}
this.m_newElements.put(handle, info);
this.m_infoStack.push(info);
this.m_handleStack.push(handle);
}
public void exitField(int declarationEnd) {
this.exitMember(declarationEnd);
}
/**
* @see IJSSourceElementRequestor
*/
public void exitInitializer(int declarationEnd) {
exitMember(declarationEnd);
}
protected void exitMember(int declarationEnd) {
Object object = this.m_infoStack.pop();
IJSSourceRefModelElementInfo info = (IJSSourceRefModelElementInfo) object;
info.setSourceRangeEnd(declarationEnd);
this.m_handleStack.pop();
}
public void exitMethod(int declarationEnd) {
this.exitMember(declarationEnd);
}
public void exitModule(int declarationEnd) {
// set import container children TODO check if necessary
// if (this.importContainerInfo != null) {
// setChildren(this.importContainerInfo);
// }
// TODO check if this is necessery set children
// setChildren(this.moduleInfo);
this.m_moduleInfo.setSourceLength(declarationEnd + 1);
// determine if there were any parsing errors
this.m_moduleInfo.setIsStructureKnown(!this.m_hasSyntaxErrors);
}
public void exitModuleRoot() {
this.m_infoStack.pop();
this.m_handleStack.pop();
}
public void exitType(int declarationEnd) {
this.exitMember(declarationEnd);
}
private SourceType findTypeFrom(IModelElement[] children, String name,
String parentName, String delimiter) {
try {
for (int i = 0; i < children.length; ++i) {
if (children[i] instanceof SourceType) {
SourceType type = (SourceType) children[i];
String qname = name + delimiter + type.getElementName();
if (qname.equals(parentName)) {
return type;
}
SourceType val = this.findTypeFrom(type.getChildren(),
qname, parentName, delimiter);
if (val != null) {
return val;
}
}
}
} catch (ModelException e) {
DLTKCore.error(e.toString(), e);
}
return null;
}
/**
* Returns type in which we currently are. If we are not in type, returns
* null.
*
* @return
*/
private SourceType getCurrentType() {
SourceType t = null;
for (Iterator iter = this.m_handleStack.iterator(); iter.hasNext();) {
Object o = iter.next();
if (o instanceof SourceType) {
t = (SourceType) o;
}
}
return t;
}
/**
* Searches for a type already in the model. If founds, returns it. If
* <code>parentName</code> starts with a delimeter, searches starting from
* current source module (i.e. in global), else from the current level.
*
* @param parentName
* @param delimiter
* @return null if type not found
*/
private SourceType getExistentType(String parentName, String delimiter) {
try {
SourceType element = null;
if (parentName.startsWith(delimiter)) {
element = this.findTypeFrom(this.m_module.getChildren(), "",
parentName, delimiter);
return element;
} else {
parentName = delimiter + parentName;
SourceType enc = this.getCurrentType();
if (enc == null) {
element = this.findTypeFrom(this.m_module.getChildren(),
"", parentName, delimiter);
} else {
element = this.findTypeFrom(enc.getChildren(), "",
parentName, delimiter);
}
return element;
}
} catch (ModelException e) {
DLTKCore.error(e.toString(), e);
}
return null;
}
private void processMethod(MethodInfo methodInfo,
IJSModelElementInfo parentInfo, ModelElement parentHandle) {
JSMethodInfo jsMethodInfo = (JSMethodInfo) methodInfo;
// translate nulls to empty arrays
if (jsMethodInfo.m_parameterTypes == null) {
jsMethodInfo.m_parameterTypes = CharOperation.NO_STRINGS;
}
if (methodInfo.parameterNames == null) {
methodInfo.parameterNames = CharOperation.NO_STRINGS;
}
if (methodInfo.exceptionTypes == null) {
methodInfo.exceptionTypes = CharOperation.NO_STRINGS;
}
String nameString = methodInfo.name;
ModelManager manager = ModelManager.getModelManager();
// TODO add param types here
JSSourceMethod handle = null;
if (parentHandle.getElementType() == IModelElement.TYPE || parentHandle.getElementType() == IModelElement.METHOD || parentHandle.getElementType() == IJSInitializer.ELEMENT_TYPE) {
if(parentHandle.getElementType()==11){
parentHandle = (ModelElement)parentHandle.getParent();
}
handle = new JSSourceMethod(parentHandle, manager
.intern(nameString), jsMethodInfo.m_parameterTypes);
} else {
Assert.isTrue(false); // Should not happen
}
this.resolveDuplicates(handle);
//add by xingzhu, set the actual resource
handle.setResource(jsMethodInfo.resource);
JSSourceMethodElementInfo info = new JSSourceMethodElementInfo();
info.setConstructor(jsMethodInfo.m_isConstructor);
info.setSourceRangeStart(methodInfo.nameSourceStart);
info.setFlags(methodInfo.modifiers);
info.setNameSourceStart(methodInfo.nameSourceStart);
info.setNameSourceEnd(methodInfo.nameSourceEnd);
String[] parameterNames = methodInfo.parameterNames == null ? EMPTY
: methodInfo.parameterNames;
String[] parameterInitializers = methodInfo.parameterInitializers == null ? EMPTY
: methodInfo.parameterInitializers;
if (parameterNames.length != parameterInitializers.length) {
parameterInitializers = new String[parameterNames.length];
}
for (int i = 0, length = parameterNames.length; i < length; i++) {
if (parameterNames[i] == null) {
parameterNames[i] = "";
}
//String name = manager.intern(parameterNames[i]); fix bug 2206, findbugs warning
if (parameterInitializers[i] != null) {
parameterInitializers[i] = manager
.intern(parameterInitializers[i]);
}
}
info.setArgumentNames(parameterNames);
info.setArgumentInializers(parameterInitializers);
info.setReturnType(manager.intern(jsMethodInfo.m_returnType)
.toCharArray());
info.setIsVariables(jsMethodInfo.b_isVariables);
parentInfo.addChild(handle);
this.m_newElements.put(handle, info);
this.m_infoStack.push(info);
this.m_handleStack.push(handle);
// TODO check if this should be here
if (jsMethodInfo.m_typeParameters != null) {
for (int i = 0, length = jsMethodInfo.m_typeParameters.length; i < length; i++) {
JSTypeParameterInfo typeParameterInfo = jsMethodInfo.m_typeParameters[i];
enterTypeParameter(typeParameterInfo);
// TODO check this exitMember(typeParameterInfo.declarationEnd);
}
}
}
private void processType(TypeInfo typeInfo, IJSModelElementInfo parentInfo,
ModelElement parentHandle) {
String nameString = typeInfo.name;
JSTypeInfo jsTypeInfo = (JSTypeInfo) typeInfo;
JSSourceType handle = new VjoSourceType(parentHandle, nameString); // NB:
// occurenceCount
// is
// computed
// in
// resolveDuplicates
this.resolveDuplicates(handle);
JSSourceTypeElementInfo info = new JSSourceTypeElementInfo();
info.setHandle(handle);
info.setSourceRangeStart(typeInfo.declarationStart);
info.setFlags(typeInfo.modifiers);
info.setNameSourceStart(typeInfo.nameSourceStart);
info.setNameSourceEnd(typeInfo.nameSourceEnd);
ModelManager manager = ModelManager.getModelManager();
String[] superclasses = typeInfo.superclasses;
for (int i = 0, length = superclasses == null ? 0 : superclasses.length; i < length; i++) {
superclasses[i] = manager.intern(superclasses[i]);
}
info.setSuperclassNames(superclasses);
String[] superinterfaceses = jsTypeInfo.superinterfaces;
for (int i = 0, length = superinterfaceses == null ? 0
: superinterfaceses.length; i < length; i++) {
superinterfaceses[i] = manager.intern(superinterfaceses[i]);
}
info.setSuperInterfaceNames(superinterfaceses);
parentInfo.addChild(handle);
this.m_newElements.put(handle, info);
this.m_infoStack.push(info);
this.m_handleStack.push(handle);
}
/**
* Resolves duplicate handles by incrementing the occurrence count of the
* handle being created until there is no conflict.
*/
protected void resolveDuplicates(SourceRefElement handle) {
while (this.m_newElements.containsKey(handle)) {
handle.occurrenceCount++;
}
}
private void setChildren(IJSModelElementInfo info) {
ArrayList childrenList = (ArrayList) this.m_children.get(info);
if (childrenList != null) {
int length = childrenList.size();
IModelElement[] elements = new IModelElement[length];
childrenList.toArray(elements);
info.setChildren(elements);
}
}
}