blob: deefce98b2db8ff1b8d5a72df2fd89f68aaa9335 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 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.internal.core;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import org.eclipse.core.runtime.Assert;
import org.eclipse.dltk.compiler.ISourceElementRequestor;
import org.eclipse.dltk.compiler.ISourceElementRequestorExtension;
import org.eclipse.dltk.compiler.SourceElementRequestorMode;
import org.eclipse.dltk.compiler.util.Util;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.INamespace;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ModelException;
public class SourceModuleStructureRequestor
implements ISourceElementRequestor, ISourceElementRequestorExtension {
private DuplicateResolver.Resolver counters = DuplicateResolver.create();
private final static String[] EMPTY = new String[0];
/**
* The handle to the source module being parsed
*/
private ISourceModule module;
/**
* The info object for the module being parsed
*/
private SourceModuleElementInfo moduleInfo;
private Stack<INamespace> namespaces = new Stack<>();
/**
* The import container info - null until created
*/
protected Map<String, ImportContainer> importContainers = null;
/**
* 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<IModelElement, ModelElementInfo> newElements;
/**
* 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<ModelElementInfo> infoStack;
/**
* Stack of parent handles, corresponding to the info stack. We keep both,
* since info objects do not have back pointers to handles.
*/
private Stack<ModelElement> handleStack;
protected boolean hasSyntaxErrors = false;
public SourceModuleStructureRequestor(ISourceModule module,
SourceModuleElementInfo moduleInfo, Map newElements) {
this.module = module;
this.moduleInfo = moduleInfo;
this.newElements = newElements;
}
/**
* Resolves duplicate handles by incrementing the occurrence count of the
* handle being created until there is no conflict.
*/
protected void resolveDuplicates(SourceRefElement handle) {
counters.resolveDuplicates(handle);
Assert.isTrue(!this.newElements.containsKey(handle));
}
@Override
public void enterModule() {
this.infoStack = new Stack<>();
this.handleStack = new Stack<>();
this.enterModuleRoot();
}
@Override
public void enterModuleRoot() {
this.infoStack.push(this.moduleInfo);
this.handleStack.push((ModelElement) this.module);
}
@Override
public void enterField(FieldInfo fieldInfo) {
ModelElementInfo parentInfo = this.infoStack.peek();
ModelElement parentHandle = this.handleStack.peek();
this.createField(fieldInfo, parentInfo, parentHandle);
}
private void createField(FieldInfo fieldInfo, ModelElementInfo parentInfo,
ModelElement parentHandle) {
ModelManager manager = ModelManager.getModelManager();
SourceField handle = new SourceField(parentHandle,
manager.intern(fieldInfo.name));
this.resolveDuplicates(handle);
SourceFieldElementInfo info = new SourceFieldElementInfo();
info.setNameSourceStart(fieldInfo.nameSourceStart);
info.setNameSourceEnd(fieldInfo.nameSourceEnd);
info.setSourceRangeStart(fieldInfo.declarationStart);
info.setFlags(fieldInfo.modifiers);
info.setType(fieldInfo.type);
parentInfo.addChild(handle);
this.newElements.put(handle, info);
this.infoStack.push(info);
this.handleStack.push(handle);
}
public boolean enterFieldCheckDuplicates(FieldInfo fieldInfo,
ModelElementInfo 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];
SourceFieldElementInfo info = (SourceFieldElementInfo) this.newElements
.get(handle);
this.infoStack.push(info);
this.handleStack.push(handle);
return true;
}
}
if (parentInfo instanceof SourceMethodElementInfo) {
SourceMethodElementInfo method = (SourceMethodElementInfo) 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;
}
@Override
public boolean enterFieldCheckDuplicates(FieldInfo fieldInfo) {
ModelElementInfo parentInfo = this.infoStack.peek();
ModelElement parentHandle = this.handleStack.peek();
return this.enterFieldCheckDuplicates(fieldInfo, parentInfo,
parentHandle);
}
@Override
public void enterMethodRemoveSame(MethodInfo methodInfo) {
ModelElementInfo parentInfo = this.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);
}
@Override
public void enterMethod(MethodInfo methodInfo) {
ModelElementInfo parentInfo = this.infoStack.peek();
ModelElement parentHandle = this.handleStack.peek();
this.processMethod(methodInfo, parentInfo, parentHandle);
}
private void processMethod(MethodInfo methodInfo,
ModelElementInfo parentInfo, ModelElement parentHandle) {
String nameString = methodInfo.name;
ModelManager manager = ModelManager.getModelManager();
SourceMethod handle = new SourceMethod(parentHandle,
manager.intern(nameString));
this.resolveDuplicates(handle);
SourceMethodElementInfo info = new SourceMethodElementInfo();
info.setSourceRangeStart(methodInfo.declarationStart);
info.setFlags(methodInfo.modifiers);
info.setNameSourceStart(methodInfo.nameSourceStart);
info.setNameSourceEnd(methodInfo.nameSourceEnd);
info.setIsConstructor(methodInfo.isConstructor);
info.setReturnType(methodInfo.returnType);
String[] parameterNames = methodInfo.parameterNames == null ? EMPTY
: methodInfo.parameterNames;
if (parameterNames.length == 0) {
info.setArguments(SourceMethodUtils.NO_PARAMETERS);
} else {
final MethodParameterInfo[] params = new MethodParameterInfo[parameterNames.length];
for (int i = 0, length = parameterNames.length; i < length; i++) {
String name = manager.intern(parameterNames[i]);
String type = null;
String defaultValue = null;
int flags = 0;
if (methodInfo.parameterTypes != null
&& i < methodInfo.parameterTypes.length) {
type = methodInfo.parameterTypes[i];
if (type != null) {
type = manager.intern(type);
}
}
if (methodInfo.parameterInitializers != null
&& i < methodInfo.parameterInitializers.length) {
defaultValue = methodInfo.parameterInitializers[i];
if (defaultValue != null) {
defaultValue = manager.intern(defaultValue);
}
}
if (methodInfo.parameterFlags != null
&& i < methodInfo.parameterFlags.length) {
flags = methodInfo.parameterFlags[i];
}
params[i] = new MethodParameterInfo(name, type, defaultValue,
flags);
}
info.setArguments(params);
}
parentInfo.addChild(handle);
this.newElements.put(handle, info);
this.infoStack.push(info);
this.handleStack.push(handle);
}
/**
* Returns type in which we currently are. If we are not in type, returns
* null.
*
* @return
*/
private SourceType getCurrentType() {
SourceType t = null;
for (ModelElement o : this.handleStack) {
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 delimiter, 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)) {
if (this.module != null) {
element = this.findTypeFrom(this.module.getChildren(), "", //$NON-NLS-1$
parentName, delimiter);
}
return element;
} else {
parentName = delimiter + parentName;
SourceType enc = this.getCurrentType();
if (enc == null) {
if (this.module != null) {
element = this.findTypeFrom(this.module.getChildren(),
"", parentName, delimiter); //$NON-NLS-1$
}
} else {
element = this.findTypeFrom(enc.getChildren(), "", //$NON-NLS-1$
parentName, delimiter);
}
return element;
}
} catch (ModelException e) {
e.printStackTrace();
}
return null;
}
private SourceType findTypeFrom(IModelElement[] childs, String name,
String parentName, String delimiter) {
try {
for (int i = 0; i < childs.length; ++i) {
if (childs[i] instanceof SourceType) {
SourceType type = (SourceType) childs[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) {
e.printStackTrace();
}
return null;
}
@Override
public void enterType(TypeInfo typeInfo) {
ModelElementInfo parentInfo = this.infoStack.peek();
ModelElement parentHandle = this.handleStack.peek();
this.processType(typeInfo, parentInfo, parentHandle);
}
@Override
public boolean enterTypeAppend(String fullName, String delimiter) {
try {
ModelElement element = this.getExistentType(fullName, delimiter);
if (element == null) {
return false;
} else {
ModelElementInfo info = (ModelElementInfo) element
.getElementInfo();
this.infoStack.push(info);
this.handleStack.push(element);
return true;
}
} catch (ModelException e) {
e.printStackTrace();
}
return false;
}
@Override
public void enterNamespace(String[] namespace) {
namespaces.push(new SourceNamespace(namespace));
}
@Override
public void exitNamespace() {
namespaces.pop();
}
private void processType(TypeInfo typeInfo, ModelElementInfo parentInfo,
ModelElement parentHandle) {
String nameString = typeInfo.name;
SourceType handle = new SourceType(parentHandle, nameString);
// NB: occurenceCount is computed in resolveDuplicates
this.resolveDuplicates(handle);
SourceTypeElementInfo info = new SourceTypeElementInfo();
if (parentHandle.getElementType() == IModelElement.SOURCE_MODULE
&& !namespaces.isEmpty()) {
// TODO review the condition above
info.setNamespace(namespaces.peek());
}
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);
parentInfo.addChild(handle);
this.newElements.put(handle, info);
this.infoStack.push(info);
this.handleStack.push(handle);
}
@Override
public void exitModule(int declarationEnd) {
this.moduleInfo.setSourceLength(declarationEnd + 1);
// determine if there were any parsing errors
this.moduleInfo.setIsStructureKnown(!this.hasSyntaxErrors);
}
@Override
public void exitModuleRoot() {
this.infoStack.pop();
this.handleStack.pop();
}
@Override
public void exitField(int declarationEnd) {
this.exitMember(declarationEnd);
}
@Override
public void exitMethod(int declarationEnd) {
this.exitMember(declarationEnd);
}
@Override
public void exitType(int declarationEnd) {
this.exitMember(declarationEnd);
}
protected void exitMember(int declarationEnd) {
Object object = this.infoStack.pop();
SourceRefElementInfo info = (SourceRefElementInfo) object;
info.setSourceRangeEnd(declarationEnd);
this.handleStack.pop();
}
@Override
public void acceptPackage(int declarationStart, int declarationEnd,
String name) {
ModelElementInfo parentInfo = this.infoStack.peek();
ModelElement parentHandle = this.handleStack.peek();
PackageDeclaration handle = null;
// if (parentHandle.getElementType() == IModelElement.SOURCE_MODULE) {
handle = new PackageDeclaration(parentHandle, name);
this.resolveDuplicates(handle);
SourceRefElementInfo info = new SourceRefElementInfo();
info.setSourceRangeStart(declarationStart);
info.setSourceRangeEnd(declarationEnd);
parentInfo.addChild(handle);
this.newElements.put(handle, info);
}
@Override
public void acceptFieldReference(String fieldName, int sourcePosition) {
}
@Override
public void acceptMethodReference(String methodName, int argCount,
int sourcePosition, int sourceEndPosition) {
}
@Override
public void acceptTypeReference(String typeName, int sourcePosition) {
}
@Override
public void acceptImport(ImportInfo importInfo) {
final ModelElement parentHandle = this.handleStack.peek();
final ISourceModule parentCU = parentHandle.getSourceModule();
final ImportContainerInfo importContainerInfo;
ImportContainer importContainer;
// create the import container and its info
if (this.importContainers == null) {
importContainers = new HashMap<>();
}
String containerName = importInfo.containerName;
if (containerName == null) {
containerName = Util.EMPTY_STRING;
}
importContainer = importContainers.get(containerName);
if (importContainer == null) {
importContainer = createImportContainer(parentCU, containerName);
importContainers.put(containerName, importContainer);
importContainerInfo = new ImportContainerInfo();
ModelElementInfo parentInfo = this.infoStack.peek();
parentInfo.addChild(importContainer);
this.newElements.put(importContainer, importContainerInfo);
} else {
importContainerInfo = (ImportContainerInfo) newElements
.get(importContainer);
}
String elementName = ModelManager.getModelManager()
.intern(importInfo.name);
ImportDeclaration handle = createImportDeclaration(importContainer,
elementName, importInfo.version, importInfo.alias,
importInfo.type, importInfo.modifiers);
resolveDuplicates(handle);
ImportDeclarationElementInfo info = new ImportDeclarationElementInfo();
info.setSourceRangeStart(importInfo.sourceStart);
info.setSourceRangeEnd(importInfo.sourceEnd);
importContainerInfo.addChild(handle);
this.newElements.put(handle, info);
}
protected ImportContainer createImportContainer(ISourceModule parent,
String container) {
return new ImportContainer((AbstractSourceModule) parent, container);
}
protected ImportDeclaration createImportDeclaration(ImportContainer parent,
String name, String version) {
return new ImportDeclaration(parent, name, version);
}
protected ImportDeclaration createImportDeclaration(ImportContainer parent,
String name, String version, String alias, int type, int flags) {
return new ImportDeclaration(parent, name, version, alias, type, flags);
}
@Override
public SourceElementRequestorMode getMode() {
return SourceElementRequestorMode.STRUCTURE;
}
}