/*******************************************************************************
* Copyright (c) 2012 Google, 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
*
* Contributors:
* Google, Inc. - initial API and implementation
*******************************************************************************/
package com.windowtester.eclipse.ui.convert;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import com.windowtester.runtime.IUIContext;
import com.windowtester.runtime.locator.IWidgetLocator;
public abstract class WTAPIAbstractVisitor extends ASTVisitor
implements WTConvertAPIRule
{
protected WTConvertAPIContext context;
public WTAPIAbstractVisitor(WTConvertAPIContext context) {
this.context = context;
}
public final void convert(WTConvertAPIContext context) {
this.context = context;
context.accept(this);
}
//=============================================================================
// Fields and Local Variables
/**
* A mechanism for caching names of fields and local variables that can be referenced
* by the local node being visited.
*/
private class VarScope
{
private final VarScope parent;
private Map<String, String> varTypeMap = new HashMap<String, String>();
private Map<String, Expression> varValueMap = new HashMap<String, Expression>();
private Map<ASTNode, String> nodeTypeMap = new HashMap<ASTNode, String>();
private Map<ASTNode, Expression> nodeValueMap = new HashMap<ASTNode, Expression>();
VarScope(VarScope parent) {
this.parent = parent;
}
VarScope getParent() {
return parent;
}
void setVarType(String name, String typeName) {
varTypeMap.put(name, typeName);
}
String getVarType(String name) {
String typeName = varTypeMap.get(name);
if (typeName == null && parent != null)
typeName = parent.getVarType(name);
return typeName;
}
void setVarValue(String name, Expression value) {
varValueMap.put(name, value);
}
Expression getVarValue(String name) {
Expression value = varValueMap.get(name);
if (value == null && parent != null)
value = parent.getVarValue(name);
return value;
}
void setNodeType(ASTNode node, String typeName) {
nodeTypeMap.put(node, typeName);
}
String getNodeType(ASTNode node) {
String typeName = nodeTypeMap.get(node);
if (typeName == null && parent != null)
typeName = parent.getNodeType(node);
return typeName;
}
void setNodeValue(ASTNode node, Expression value) {
nodeValueMap.put(node, value);
}
Expression getNodeValue(ASTNode node) {
Expression value = nodeValueMap.get(node);
if (value == null && parent != null)
value = parent.getNodeValue(node);
return value;
}
}
/**
* The current variable scope
*/
private VarScope currentScope;
/**
* Answer the type name for the specified variable or <code>null</code> if it is
* unknown
*/
public String getVarType(String name) {
return currentScope.getVarType(name);
}
/**
* Answer the current "value" for the specified variable as an expression or <code>null</code> if it is
* unknown. This expression is most likely already part of the AST tree, and thus must be copied before
* being used elsewhere.
*/
public Expression getVarValue(String name) {
return currentScope.getVarValue(name);
}
/**
* Answer the expression type name of the AST node last visited or <code>null</code>
* if unknown or if the last AST node was not an expression
*/
public String getNodeType(ASTNode node) {
return currentScope.getNodeType(node);
}
/**
* Answer the "value" of the expression in terms of a snippet of code such as "true",
* "0", "new CTabLocator(...)" or <code>null</code> if unknown
*/
public Expression getNodeValue(ASTNode node) {
return currentScope.getNodeValue(node);
}
//=============================================================================
// Visitor
public void endVisit(ArrayType node) {
String typeName = getNodeType(node.getComponentType());
if (typeName == null)
return;
currentScope.setNodeType(node, typeName + "[]");
}
/**
* Visits the assignment to determine its "value"
*/
public void endVisit(Assignment node) {
Expression expression = node.getLeftHandSide();
if (expression == null)
return;
String varName;
switch (expression.getNodeType()) {
case ASTNode.SIMPLE_NAME :
varName = ((SimpleName) expression).getFullyQualifiedName();
break;
default :
return;
}
expression = node.getRightHandSide();
if (expression == null)
return;
Expression value = getNodeValue(expression);
if (value == null)
return;
currentScope.setVarValue(varName, value);
}
/**
* Initialize the variable scope before visiting nodes
*/
@SuppressWarnings("unchecked")
public boolean visit(Block node) {
currentScope = new VarScope(currentScope);
ASTNode parent = node.getParent();
if (parent instanceof MethodDeclaration) {
List<SingleVariableDeclaration> parameters = ((MethodDeclaration) parent).parameters();
for (SingleVariableDeclaration param : parameters) {
Type type = param.getType();
String unresolvedTypeName;
if (type.isSimpleType())
unresolvedTypeName = ((SimpleType) type).getName().getFullyQualifiedName();
else if (type.isQualifiedType())
unresolvedTypeName = ((QualifiedType) type).getName().getFullyQualifiedName();
else
unresolvedTypeName = null;
String typeName = context.resolve(unresolvedTypeName);
if (typeName == null)
typeName = unresolvedTypeName;
String varName = param.getName().getFullyQualifiedName();
currentScope.setVarType(varName, typeName);
}
}
return true;
}
/**
* Clear the variable scope after visiting nodes
*/
public void endVisit(Block node) {
currentScope = currentScope.getParent();
}
/**
* Cache the expression information for this boolean literal
*/
public void endVisit(BooleanLiteral node) {
currentScope.setNodeType(node, Boolean.TYPE.getName());
currentScope.setNodeValue(node, node);
}
/**
* Cache the expression information for this cast expression.
*/
public void endVisit(CastExpression node) {
currentScope.setNodeType(node, getNodeType(node.getType()));
currentScope.setNodeValue(node, getNodeValue(node.getExpression()));
}
/**
* Visit the class instance creation and determine its expression type. Call
* {@link #visit(ClassInstanceCreation, String, String[])} with the type information
* found.
*/
public void endVisit(ClassInstanceCreation node) {
currentScope.setNodeType(node, getNodeType(node.getType()));
currentScope.setNodeValue(node, node);
}
/**
* Initialize the variable scope before visiting nodes
*/
public boolean visit(CompilationUnit node) {
currentScope = new VarScope(null);
currentScope.setNodeType(null, null);
currentScope.setNodeValue(null, null);
return true;
}
/**
* Clear the variable scope after visiting nodes
*/
public void endVisit(CompilationUnit node) {
currentScope = null;
}
/**
* Visit the field declaration and determine its expression type
*/
public void endVisit(FieldDeclaration node) {
String typeName = getNodeType(node.getType());
currentScope.setNodeType(node, typeName);
currentScope.setNodeValue(node, null);
for (Object each : node.fragments()) {
VariableDeclarationFragment fragment = (VariableDeclarationFragment) each;
String name = fragment.getName().getFullyQualifiedName();
currentScope.setVarType(name, typeName);
}
}
/**
* Visit the method invocation and determine its expression type. Call
* {@link #visit(MethodInvocation, String, String[])} with the type information found.
*/
@SuppressWarnings("unchecked")
public void endVisit(MethodInvocation node) {
List<ASTNode> arguments = node.arguments();
// TODO determine the return type and "value" of known WindowTester methods
String returnTypeName = null;
Expression returnValue = null;
// Special case - return type for getUI()
String methodName = node.getName().getFullyQualifiedName();
if (methodName.equals("getUI") && arguments.size() == 0) {
returnTypeName = IUIContext.class.getName();
}
// Return type determination like this could be generalized
else if (methodName.equals("findAll") && arguments.size() == 1
&& IUIContext.class.getName().equals(getNodeType(node.getExpression()))) {
returnTypeName = IWidgetLocator.class.getName() + "[]";
returnValue = getNodeValue(arguments.get(0));
}
currentScope.setNodeType(node, returnTypeName);
currentScope.setNodeValue(node, returnValue);
}
/**
* Cache the expression information for this numeric literal
*/
public void endVisit(NumberLiteral node) {
currentScope.setNodeType(node, Number.class.getName());
currentScope.setNodeValue(node, node);
}
/**
* Cache the expression information for this parenthesized expression
*/
public void endVisit(ParenthesizedExpression node) {
Expression expression = node.getExpression();
currentScope.setNodeType(node, getNodeType(expression));
currentScope.setNodeValue(node, getNodeValue(expression));
}
/**
* If the node represents a WindowTester type, then cache the expression information
* for the parent node.
*/
public void endVisit(QualifiedName node) {
String typeName = context.resolve(node.getFullyQualifiedName());
if (typeName != null) {
currentScope.setNodeType(node, typeName);
currentScope.setNodeValue(node, null);
}
}
/**
* If the node represents a WindowTester type, then cache the expression information
* for the parent node.
*/
public void endVisit(QualifiedType node) {
String unresolvedName = node.toString();
String typeName = context.resolve(unresolvedName);
currentScope.setNodeType(node, (typeName != null ? typeName : unresolvedName));
}
/**
* If the node represents a WindowTester type, then cache the expression information
* for the parent node.
*/
public void endVisit(SimpleName node) {
String name = node.getFullyQualifiedName();
currentScope.setNodeType(node, currentScope.getVarType(name));
currentScope.setNodeValue(node, currentScope.getVarValue(name));
}
/**
* If the node represents a WindowTester type, then cache the expression information
* for the parent node.
*/
public void endVisit(SimpleType node) {
String unresolvedName = node.getName().getFullyQualifiedName();
String typeName = context.resolve(unresolvedName);
currentScope.setNodeType(node, (typeName != null ? typeName : unresolvedName));
}
/**
* Cache the expression information for this string literal
*/
public void endVisit(StringLiteral node) {
currentScope.setNodeType(node, String.class.getName());
currentScope.setNodeValue(node, node);
}
/**
* Cache the expression information
*/
public void endVisit(ThisExpression node) {
String typeName = null;
Name qualifier = node.getQualifier();
if (qualifier != null)
typeName = context.resolve(qualifier.getFullyQualifiedName());
currentScope.setNodeType(node, typeName);
currentScope.setNodeValue(node, node);
}
/**
* Set the variable scope and then visit field then method nodes. Visit all the fields
* before visiting the methods to cache the field type information accessible by the
* methods.
*/
public boolean visit(TypeDeclaration node) {
Type superType = node.getSuperclassType();
if (superType != null)
superType.accept(this);
for (Object each : node.superInterfaceTypes())
((Type) each).accept(this);
currentScope = new VarScope(currentScope);
FieldDeclaration[] fields = node.getFields();
for (int i = 0; i < fields.length; i++)
fields[i].accept(this);
TypeDeclaration[] types = node.getTypes();
for (int i = 0; i < types.length; i++)
types[i].accept(this);
MethodDeclaration[] methods = node.getMethods();
for (int i = 0; i < methods.length; i++)
methods[i].accept(this);
currentScope = currentScope.getParent();
return false;
}
/**
* Cache the expression information for this type literal.
*/
public void endVisit(TypeLiteral node) {
String typeName = getNodeType(node.getType());
if (typeName != null)
typeName += ".class";
currentScope.setNodeType(node, typeName);
currentScope.setNodeValue(node, null);
}
/**
* Visits the variable initializer to determine its "value"
*/
public void endVisit(VariableDeclarationFragment node) {
Expression initializer = node.getInitializer();
if (initializer != null) {
String name = node.getName().getFullyQualifiedName();
currentScope.setVarValue(name, getNodeValue(initializer));
}
}
/**
* If this node represents a variable declaration of a WindowTester type then call
* {@link #visitWT(VariableDeclarationStatement, String, boolean)}
*/
public void endVisit(VariableDeclarationStatement node) {
Type type = node.getType();
String typeName = getNodeType(type);
for (Object each : node.fragments()) {
VariableDeclarationFragment fragment = (VariableDeclarationFragment) each;
String name = fragment.getName().getFullyQualifiedName();
currentScope.setVarType(name, typeName);
}
currentScope.setNodeType(node, typeName);
currentScope.setNodeValue(node, null);
}
}