package org.maziarz.yiiclipse.hyperlinks;
import java.util.LinkedList;
import org.eclipse.dltk.ast.ASTVisitor;
import org.eclipse.dltk.ast.declarations.MethodDeclaration;
import org.eclipse.dltk.ast.declarations.TypeDeclaration;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.ast.references.VariableReference;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.jface.text.Region;
import org.eclipse.php.internal.core.compiler.ast.nodes.ArrayCreation;
import org.eclipse.php.internal.core.compiler.ast.nodes.ArrayElement;
import org.eclipse.php.internal.core.compiler.ast.nodes.Assignment;
import org.eclipse.php.internal.core.compiler.ast.nodes.FieldAccess;
import org.eclipse.php.internal.core.compiler.ast.nodes.PHPCallExpression;
import org.eclipse.php.internal.core.compiler.ast.nodes.PHPFieldDeclaration;
import org.eclipse.php.internal.core.compiler.ast.nodes.Scalar;
import org.maziarz.yiiclipse.hyperlinks.HyperlinkTargetCandidate.HyperlinkTargetType;
import org.maziarz.yiiclipse.utils.ASTUtils;
import org.maziarz.yiiclipse.utils.StringUtils;
import org.maziarz.yiiclipse.utils.YiiPathResolver;
public class YiiHyperlinkASTVisitor2 extends ASTVisitor {
private boolean found = false;
private TypeDeclaration currentType;
private MethodDeclaration currentMethod;
private int offset;
private String file;
private Region selectRegion;
private int visitedExpressions = 0;
private LinkedList<HyperlinkTargetCandidate> results = new LinkedList<HyperlinkTargetCandidate>();
private ISourceModule sourceModule;
private YiiPathResolver pathResolver;
public YiiHyperlinkASTVisitor2(ISourceModule sourceModule, YiiPathResolver pathResolver, int offset, String file,
Region selectRegion) {
this.sourceModule = sourceModule;
this.offset = offset;
this.pathResolver = pathResolver;
}
public String getFile() {
return file;
}
public Region getSelectRegion() {
return selectRegion;
}
@Override
public boolean visit(TypeDeclaration s) throws Exception {
this.currentType = s;
// make sure offset is inside type declaration
if (s.sourceStart() < offset && offset < s.sourceEnd()) {
for (Object o : s.getStatements()) {
if (o instanceof PHPFieldDeclaration) {
PHPFieldDeclaration fieldDeclaration = (PHPFieldDeclaration) o;
if ("$layout".equals(fieldDeclaration.getName())) {
if (fieldDeclaration.sourceStart() < offset && offset < fieldDeclaration.sourceEnd()) {
if (fieldDeclaration.getVariableValue() instanceof Scalar) {
String view = ASTUtils.stripQuotes(((Scalar) fieldDeclaration.getVariableValue()).getValue());
Expression variableValue = fieldDeclaration.getVariableValue();
results.add(new HyperlinkTargetCandidate(variableValue, view, HyperlinkTargetType.LAYOUT));
}
}
}
}
}
} else {
return false;
}
return super.visit(s);
}
@Override
public boolean endvisit(TypeDeclaration s) throws Exception {
if (results.size() > 0) {
HyperlinkTargetCandidate theLast = results.getLast();
selectFileAndRegion(theLast.getExpression(), theLast.getView(), theLast.getType());
}
this.currentType = null;
return super.visit(s);
}
@Override
public boolean visit(MethodDeclaration method) throws Exception {
if (method.sourceStart() < offset && offset < method.sourceEnd()) {
this.currentMethod = method;
return true;
}
return false;
}
@Override
public boolean endvisit(MethodDeclaration method) throws Exception {
this.currentMethod = null;
return super.endvisit(method);
}
@Override
public boolean visit(Expression expr) throws Exception {
visitedExpressions++;
if (expr.sourceStart() < offset && offset < expr.sourceEnd()) {
if (expr instanceof Assignment) {
Assignment assignment = (Assignment) expr;
Expression e = assignment.getVariable();
if (e instanceof FieldAccess) {
FieldAccess fa = (FieldAccess) e;
Expression vref = fa.getDispatcher();
if (vref instanceof VariableReference) {
// probably could be worth make sure $this is instance of Controller
// but at the moment it seems to be not crucial (WorseIsBetter)
if ("$this".equals(((VariableReference) vref).getName()) == false) {
return !found;
}
}
Expression accessedField = fa.getField();
if (accessedField instanceof SimpleReference) {
if ("layout".equals(((SimpleReference) accessedField).getName()) == false) {
return !found;
}
}
}
Expression valueExpr = assignment.getValue();
if (valueExpr instanceof Scalar) {
String viewPath = StringUtils.stripQuotes(((Scalar) valueExpr).getValue());
results.add(new HyperlinkTargetCandidate(valueExpr, viewPath));
}
} else {
if (expr instanceof PHPCallExpression) {
PHPCallExpression callExpr = (PHPCallExpression) expr;
String functionName = callExpr.getCallName().getName();
if ("beginWidget".equals(functionName) || "import".equals(functionName)) {
String widget = null;
for (Object arg : callExpr.getArgs().getChilds()) {
if (arg instanceof Scalar) {
Scalar widgetPath = ((Scalar) arg);
widget = StringUtils.stripQuotes(widgetPath.getValue());
String filePath = pathResolver.resolveWidgetPath(widget, sourceModule);
if (!filePath.isEmpty() && widgetPath.sourceStart() < offset && offset < widgetPath.sourceEnd()) {
file = filePath;
int startIndex = widgetPath.sourceStart() + 1;
int length = widget.length();
selectRegion = new Region(startIndex, length);
found = true;
return !found;
}
}
}
} else if ("widget".equals(functionName)) {
String widget = null;
for (Object arg : callExpr.getArgs().getChilds()) {
if (arg instanceof Scalar) {
Scalar widgetPath = ((Scalar) arg);
widget = StringUtils.stripQuotes(widgetPath.getValue());
String filePath = pathResolver.resolveWidgetPath(widget, sourceModule);
if (widgetPath.sourceStart() < offset && offset < widgetPath.sourceEnd()) {
file = filePath;
int startIndex = widgetPath.sourceStart() + 1;
int length = widget.length();
selectRegion = new Region(startIndex, length);
found = true;
return !found;
}
} else {
if (arg instanceof ArrayCreation) {
ArrayCreation arr = (ArrayCreation) arg;
for (ArrayElement element : arr.getElements()) {
if (element.getKey() instanceof Scalar) {
Scalar key = (Scalar) element.getKey();
if ("itemView".equals(StringUtils.stripQuotes(key.getValue()))) {
if (element.getValue().sourceStart() < offset
&& offset < element.getValue().sourceEnd()) {
if (element.getValue() instanceof Scalar) {
String view = StringUtils.stripQuotes(((Scalar) element.getValue())
.getValue());
results.add(new HyperlinkTargetCandidate(element.getValue(), view));
}
}
}
}
}
;
}
}
}
} // end of "widget"
else if ("render".equals(functionName) || "renderPartial".equals(functionName)) {
Object[] arguments = callExpr.getArgs().getChilds().toArray();
if (arguments.length > 0) {
Object arg1 = arguments[0];
if (arg1 instanceof Scalar) {
Scalar view = (Scalar) arg1;
if (view.sourceStart() < offset && offset < view.sourceEnd()) {
String viewName = StringUtils.stripQuotes(view.getValue());
results.add(new HyperlinkTargetCandidate(view, viewName));
}
}
}
} // end of "render"
else if ("registerCssFile".equals(functionName)) {
Object[] arguments = callExpr.getArgs().getChilds().toArray();
if (arguments.length > 0) {
Object arg1 = arguments[0];
if (arg1 instanceof Scalar) {
Scalar scalar = (Scalar) arg1;
String cssName = StringUtils.stripQuotes(scalar.getValue());
String cssPath = pathResolver.resolveAliasPath("application", sourceModule) + "/" + cssName;
if (scalar.sourceStart() < offset && offset < scalar.sourceEnd()) {
file = cssPath;
int startIndex = scalar.sourceStart() + 1;
int lenght = cssName.length();
selectRegion = new Region(startIndex, lenght);
found = true;
return !found;
}
}
}
} // end of "registerCssFile"
else if ("beginContent".equals(functionName)) {
Object[] arguments = callExpr.getArgs().getChilds().toArray();
if (arguments.length > 0) {
Object arg1 = arguments[0];
if (arg1 instanceof Scalar) {
Scalar view = (Scalar) arg1;
if (view.sourceStart() < offset && offset < view.sourceEnd()) {
String viewName = StringUtils.stripQuotes(view.getValue());
results.add(new HyperlinkTargetCandidate(view, viewName, HyperlinkTargetType.LAYOUT));
}
}
}
} // end of "beginContent"
}
}
}
if (currentType == null && results.size() > 0) {
HyperlinkTargetCandidate thelast = results.getLast();
selectFileAndRegion(thelast.getExpression(), thelast.getView(), thelast.getType());
}
return !found;
}
private void selectFileAndRegion(Expression expression, String view, HyperlinkTargetType type) {
int startIndex = expression.sourceStart() + 1;
selectRegion = new Region(startIndex, view.length());
if (type == HyperlinkTargetType.LAYOUT && !view.startsWith("//")) {
view = "//layouts/" + view;
}
String typeName = (currentType != null) ? currentType.getName() : null;
String viewFilePath = YiiPathResolver.resolveViewPath(sourceModule, typeName, view);
file = viewFilePath;
found = true;
}
public void setPathResolver(YiiPathResolver resolver) {
pathResolver = resolver;
}
}