package org.eclipse.dltk.internal.javascript.parser.structure;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Stack;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.compiler.IElementRequestor.FieldInfo;
import org.eclipse.dltk.compiler.IElementRequestor.ImportInfo;
import org.eclipse.dltk.compiler.IElementRequestor.MethodInfo;
import org.eclipse.dltk.compiler.IElementRequestor.TypeInfo;
import org.eclipse.dltk.compiler.ISourceElementRequestor;
import org.eclipse.dltk.javascript.ast.Expression;
import org.eclipse.dltk.javascript.ast.FunctionStatement;
import org.eclipse.dltk.javascript.ast.Identifier;
import org.eclipse.dltk.javascript.structure.IDeclaration;
import org.eclipse.dltk.javascript.structure.IStructureRequestor;
import org.eclipse.dltk.javascript.typeinfo.IModelBuilder.IMethod;
import org.eclipse.dltk.javascript.typeinfo.IModelBuilder.IParameter;
import org.eclipse.dltk.javascript.typeinfo.ITypeNames;
import org.eclipse.dltk.javascript.typeinfo.model.ArrayType;
import org.eclipse.dltk.javascript.typeinfo.model.ClassType;
import org.eclipse.dltk.javascript.typeinfo.model.FunctionType;
import org.eclipse.dltk.javascript.typeinfo.model.JSType;
import org.eclipse.dltk.javascript.typeinfo.model.MapType;
import org.eclipse.dltk.javascript.typeinfo.model.SimpleType;
import org.eclipse.dltk.javascript.typeinfo.model.Type;
import org.eclipse.emf.ecore.EObject;
public class StructureRequestor implements IStructureRequestor {
private static enum ElementType {
FIELD, FIELD_LOCAL
}
private final ISourceElementRequestor requestor;
private final Stack<ElementType> elementTypes = new Stack<ElementType>();
public StructureRequestor(ISourceElementRequestor requestor) {
this.requestor = requestor;
}
public void acceptImport(ImportInfo importInfo) {
requestor.acceptImport(importInfo);
}
public void acceptTypeReference(ASTNode node, String typeName) {
requestor.acceptTypeReference(typeName, node.sourceStart());
}
public void acceptLocalReference(Identifier node, IDeclaration target) {
}
public void enterLocal(Identifier identifer, JSType type) {
acceptTypeReference(identifer.start(), type);
}
public void exitLocal(int sourceEnd) {
}
public void acceptFieldReference(Identifier node) {
requestor.acceptFieldReference(node.getName(), node.sourceStart());
}
public void acceptMethodReference(Identifier node, int argCount) {
requestor.acceptMethodReference(node.getName(), argCount,
node.sourceStart(), node.sourceEnd() - 1);
}
public void enterNamespace(String[] namespace) {
requestor.enterModuleRoot();
requestor.enterNamespace(namespace);
}
public void exitNamespace() {
requestor.exitNamespace();
requestor.exitModuleRoot();
}
public void enterType(TypeInfo typeInfo) {
requestor.enterType(typeInfo);
}
public void exitType(int sourceEnd) {
requestor.exitType(sourceEnd);
}
public void enterMethod(MethodInfo methodInfo, Expression identifier,
FunctionStatement function, IMethod method) {
requestor.enterMethod(methodInfo);
reportTypeRef(method.getType(), methodInfo.declarationStart,
methodInfo.returnType != null);
reportTypeRef(method.getThisType(), methodInfo.declarationStart, false);
for (IParameter parameter : method.getParameters()) {
reportTypeRef(parameter.getType(), methodInfo.declarationStart,
methodInfo.parameterTypes != null);
}
}
public void exitMethod(int sourceEnd) {
requestor.exitMethod(sourceEnd);
}
public void exitField(int sourceEnd) {
if (elementTypes.pop() == ElementType.FIELD) {
requestor.exitField(sourceEnd);
}
}
public void enterField(FieldInfo fieldInfo, Expression identifer,
JSType type, boolean local) {
elementTypes.push(local ? ElementType.FIELD_LOCAL : ElementType.FIELD);
if (!local) {
requestor.enterField(fieldInfo);
reportTypeRef(type, fieldInfo.declarationStart,
fieldInfo.type != null);
} else {
requestor.acceptFieldReference(fieldInfo.name,
identifer.sourceStart());
}
}
public void acceptTypeReference(int position, JSType type) {
reportTypeRef(type, position, false);
}
private void reportTypeRef(JSType type, int position, boolean skipSimple) {
if (type == null || skipSimple && type instanceof SimpleType) {
return;
}
for (String typeName : collectContainedTypeNames(type)) {
requestor.acceptTypeReference(toShortName(typeName), position);
}
}
public static Collection<String> collectContainedTypeNames(JSType type) {
Set<String> result = null;
{
final String typeName = reportSimpleTypeRef(type);
if (typeName != null) {
if (result == null) {
result = new HashSet<String>();
}
result.add(typeName);
}
}
for (Iterator<EObject> i = type.eAllContents(); i.hasNext();) {
final EObject child = i.next();
if (child instanceof JSType) {
final String typeName = reportSimpleTypeRef((JSType) child);
if (typeName != null) {
if (result == null) {
result = new HashSet<String>();
}
result.add(typeName);
}
}
}
return result != null ? result : Collections.<String> emptyList();
}
private static String reportSimpleTypeRef(JSType type) {
if (type instanceof MapType) {
return ITypeNames.OBJECT;
} else if (type instanceof ArrayType) {
return ITypeNames.ARRAY;
} else if (type instanceof FunctionType) {
return ITypeNames.FUNCTION;
} else if (type instanceof SimpleType) {
final Type t = ((SimpleType) type).getTarget();
return t != null ? t.getName() : null;
} else if (type instanceof ClassType) {
final Type t = ((ClassType) type).getTarget();
return t != null ? t.getName() : null;
} else {
return null;
}
}
private static String toShortName(String name) {
final int slash = name.lastIndexOf('/');
if (slash >= 0) {
return name.substring(slash + 1);
}
final int dot = name.lastIndexOf('.');
if (dot >= 0) {
return name.substring(dot + 1);
}
return name;
}
}