/*******************************************************************************
* Copyright (c) 2014 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
*
* Contributors:
* IBM Corporation - initial API and implementation
* Zend Technologies
*******************************************************************************/
package org.eclipse.php.internal.ui.editor.hyperlink;
import org.eclipse.core.runtime.Path;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.ASTVisitor;
import org.eclipse.dltk.ast.expressions.CallArgumentsList;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.jface.text.Region;
import org.eclipse.php.core.compiler.ast.nodes.Include;
import org.eclipse.php.core.compiler.ast.nodes.InfixExpression;
import org.eclipse.php.core.compiler.ast.nodes.PHPCallExpression;
import org.eclipse.php.core.compiler.ast.nodes.Scalar;
import org.eclipse.php.internal.core.compiler.ast.parser.ASTUtils;
public class IncludeHyperlinkVisitor extends ASTVisitor {
private final static String DIRNAME_METHOD = "dirname"; //$NON-NLS-1$
private final static String DIR_CONST = "__DIR__"; //$NON-NLS-1$
private final static String FILE_CONST = "__FILE__"; //$NON-NLS-1$
private boolean found = false;
private int offset;
private ISourceModule sourceModule;
private StringBuilder filePath;
private Region selectRegion;
public IncludeHyperlinkVisitor(int offset, ISourceModule sourceModule) {
this.offset = offset;
this.sourceModule = sourceModule;
this.filePath = new StringBuilder();
}
@Override
public boolean visit(Expression expr) throws ModelException {
if (expr.sourceStart() < offset && expr.sourceEnd() > offset) {
if (expr instanceof Include) {
Expression fileExpr = ((Include) expr).getExpr();
if (fileExpr instanceof InfixExpression) {
processInfixExpression((InfixExpression) fileExpr);
} else if (fileExpr instanceof Scalar) {
processScalar((Scalar) fileExpr);
}
selectRegion = new Region(fileExpr.sourceStart(), fileExpr.sourceEnd() - fileExpr.sourceStart());
found = true;
return false;
}
}
return !found;
}
private void processInfixExpression(InfixExpression infixExpression) {
if (!infixExpression.getOperator().equals(".")) { //$NON-NLS-1$
return;
}
for (ASTNode child : infixExpression.getChilds()) {
if (child instanceof InfixExpression) {
processInfixExpression((InfixExpression) child);
} else if (child instanceof Scalar) {
processScalar((Scalar) child);
} else if (child instanceof PHPCallExpression) {
String tmpPath = resolvePHPCallExpression((PHPCallExpression) child);
if (tmpPath != null) {
filePath = filePath.append(tmpPath);
}
}
}
}
private void processScalar(Scalar scalar) {
String resolvedValue = resolveScalarValue(scalar);
if (resolvedValue == null) {
return;
}
filePath = filePath.append(resolvedValue);
}
/**
* Resolves call expression. Actually only expressions with 'dirname' method
* and only first argument from call list is resolved.
*
* @param callExpression
* @return path resolved from expression
*/
private String resolvePHPCallExpression(PHPCallExpression callExpression) {
if (callExpression.getCallName().getName().equals(DIRNAME_METHOD)) {
CallArgumentsList argsList = callExpression.getArgs();
for (ASTNode astNode : argsList.getChilds()) {
if (astNode instanceof Scalar) {
return resolveDirname(resolveScalarValue((Scalar) astNode));
} else if (astNode instanceof PHPCallExpression) {
return resolveDirname(resolvePHPCallExpression((PHPCallExpression) astNode));
}
}
}
return null;
}
private String resolveScalarValue(Scalar scalar) {
if (scalar.getValue() == DIR_CONST) {
return sourceModule.getResource().getLocation().removeLastSegments(1).toOSString();
} else if (scalar.getValue() == FILE_CONST) {
return sourceModule.getResource().getLocation().toOSString();
} else {
String value = scalar.getValue();
return ASTUtils.stripQuotes(value).trim();
}
}
private String resolveDirname(String path) {
if (path == null) {
return null;
}
return new Path(path).removeLastSegments(1).toOSString();
}
@Override
public boolean visitGeneral(ASTNode n) {
return !found;
}
public String getFile() {
return filePath.toString();
}
public Region getSelectRegion() {
return selectRegion;
}
};