/*******************************************************************************
* Copyright (c) 2009 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.core.typeinference;
import java.util.*;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.ASTVisitor;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.expressions.CallArgumentsList;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.ast.statements.Statement;
import org.eclipse.dltk.core.*;
import org.eclipse.php.core.compiler.ast.nodes.*;
import org.eclipse.php.internal.core.Logger;
import org.eclipse.php.internal.core.compiler.ast.parser.ASTUtils;
public class DefineMethodUtils {
public static String DEFINE = "define"; //$NON-NLS-1$
public static PHPCallExpression getDefineNodeByField(ModuleDeclaration module, IField field) throws ModelException {
FunctionInvocationSearcher visitor = new FunctionInvocationSearcher(module, field);
try {
module.traverse(visitor);
} catch (Exception e) {
if (DLTKCore.DEBUG) {
Logger.logException(e);
}
}
return visitor.getResult();
}
public static PHPDocBlock getDefinePHPDocBlockByField(ModuleDeclaration module, IField field)
throws ModelException {
if (module instanceof PHPModuleDeclaration) {
if (getDefineNodeByField(module, field) == null) {
return null;
}
PHPModuleDeclaration phpModule = (PHPModuleDeclaration) module;
List<PHPDocBlock> phpDocBlocks = phpModule.getPHPDocBlocks();
if (phpDocBlocks != null && !phpDocBlocks.isEmpty()) {
List statements = phpModule.getStatements();
ISourceRange sourceRange = field.getNameRange();
ASTNode previousStatement = null;
for (Iterator iterator = statements.iterator(); iterator.hasNext();) {
ASTNode statement = (ASTNode) iterator.next();
if (statement.sourceStart() <= sourceRange.getOffset()
&& statement.sourceEnd() >= (sourceRange.getOffset() + sourceRange.getLength())) {
// define statement
phpDocBlocks = getPHPDocBlockBetweenStatements(previousStatement, statement, phpDocBlocks);
if (phpDocBlocks.isEmpty()) {
return null;
}
Collections.sort(phpDocBlocks, new Comparator<PHPDocBlock>() {
public int compare(PHPDocBlock o1, PHPDocBlock o2) {
return o1.sourceStart() - o2.sourceStart();
}
});
return phpDocBlocks.get(phpDocBlocks.size() - 1);
}
previousStatement = statement;
}
PHPCallExpression callExpression = getDefineNodeByField(phpModule, field);
callExpression.getReceiver();
}
}
return null;
}
private static List<PHPDocBlock> getPHPDocBlockBetweenStatements(ASTNode previousStatement, ASTNode statement,
List<PHPDocBlock> phpDocBlocks) {
if (previousStatement == null) {
return getPHPDocBlockBetweenRange(-1, statement.sourceStart(), phpDocBlocks);
} else {
return getPHPDocBlockBetweenRange(previousStatement.sourceEnd(), statement.sourceStart(), phpDocBlocks);
}
}
private static List<PHPDocBlock> getPHPDocBlockBetweenRange(int start, int end, List<PHPDocBlock> phpDocBlocks) {
List<PHPDocBlock> result = new ArrayList<PHPDocBlock>();
for (Iterator iterator = phpDocBlocks.iterator(); iterator.hasNext();) {
PHPDocBlock phpDocBlock = (PHPDocBlock) iterator.next();
if (phpDocBlock.sourceStart() >= start && phpDocBlock.sourceEnd() <= end) {
result.add(phpDocBlock);
}
}
return result;
}
public static class FunctionInvocationSearcher extends ASTVisitor {
private int bestScore = Integer.MAX_VALUE;
private int modelStart;
private int modelEnd;
private int modelCutoffStart;
private int modelCutoffEnd;
private String elementName;
private PHPCallExpression result;
public FunctionInvocationSearcher(ModuleDeclaration moduleDeclaration, IMember modelElement)
throws ModelException {
ISourceRange sourceRange = modelElement.getSourceRange();
modelStart = sourceRange.getOffset();
modelEnd = modelStart + sourceRange.getLength();
modelCutoffStart = modelStart - 100;
modelCutoffEnd = modelEnd + 100;
elementName = modelElement.getElementName();
}
public PHPCallExpression getResult() {
return result;
}
protected void checkElementDeclaration(PHPCallExpression s) {
if (s.getName().equals(DEFINE)) {
CallArgumentsList args = s.getArgs();
if (args != null && args.getChilds() != null) {
ASTNode argument = (ASTNode) args.getChilds().get(0);
if (argument instanceof Scalar) {
String constant = ASTUtils.stripQuotes(((Scalar) argument).getValue());
if (constant.equals(elementName)) {
int astStart = s.sourceStart();
int astEnd = s.sourceEnd();
int diff1 = modelStart - astStart;
int diff2 = modelEnd - astEnd;
int score = diff1 * diff1 + diff2 * diff2;
if (score < bestScore) {
bestScore = score;
result = s;
}
}
}
}
}
}
protected boolean interesting(ASTNode s) {
if (s.sourceStart() < 0 || s.sourceEnd() < s.sourceStart()) {
return true;
}
if (modelCutoffEnd < s.sourceStart() || modelCutoffStart >= s.sourceEnd()) {
return false;
}
return true;
}
public boolean visit(Expression s) throws Exception {
if (!interesting(s)) {
return false;
}
return true;
}
public boolean visit(Statement s) throws Exception {
if (!interesting(s)) {
return false;
}
if (s instanceof ExpressionStatement) {
if (((ExpressionStatement) s).getExpr() instanceof PHPCallExpression) {
checkElementDeclaration((PHPCallExpression) ((ExpressionStatement) s).getExpr());
}
}
return true;
}
public boolean visit(ModuleDeclaration s) throws Exception {
if (!interesting(s)) {
return false;
}
return true;
}
public boolean visitGeneral(ASTNode s) throws Exception {
if (!interesting(s)) {
return false;
}
return true;
}
}
}