/*******************************************************************************
* 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.evaluators;
import java.util.*;
import java.util.Map.Entry;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.ASTVisitor;
import org.eclipse.dltk.ast.Modifiers;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.expressions.CallExpression;
import org.eclipse.dltk.ast.expressions.Expression;
import org.eclipse.dltk.ast.statements.Statement;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.core.index2.search.ISearchEngine.MatchRule;
import org.eclipse.dltk.core.search.IDLTKSearchScope;
import org.eclipse.dltk.core.search.SearchEngine;
import org.eclipse.dltk.ti.GoalState;
import org.eclipse.dltk.ti.ISourceModuleContext;
import org.eclipse.dltk.ti.goals.ExpressionTypeGoal;
import org.eclipse.dltk.ti.goals.GoalEvaluator;
import org.eclipse.dltk.ti.goals.IGoal;
import org.eclipse.dltk.ti.types.IEvaluatedType;
import org.eclipse.php.core.compiler.PHPFlags;
import org.eclipse.php.core.compiler.ast.nodes.ConstantDeclaration;
import org.eclipse.php.core.compiler.ast.nodes.Scalar;
import org.eclipse.php.internal.core.PHPLanguageToolkit;
import org.eclipse.php.internal.core.model.PHPModelAccess;
import org.eclipse.php.internal.core.typeinference.PHPTypeInferenceUtils;
import org.eclipse.php.internal.core.typeinference.goals.ConstantDeclarationGoal;
public class ConstantDeclarationEvaluator extends GoalEvaluator {
private List<IEvaluatedType> evaluatedTypes = new LinkedList<IEvaluatedType>();
public ConstantDeclarationEvaluator(IGoal goal) {
super(goal);
}
public IGoal[] init() {
ConstantDeclarationGoal typedGoal = (ConstantDeclarationGoal) goal;
String constantName = typedGoal.getConstantName();
String typeName = typedGoal.getTypeName();
IDLTKSearchScope scope = null;
IScriptProject scriptProject = null;
ISourceModuleContext sourceModuleContext = (ISourceModuleContext) goal.getContext();
if (sourceModuleContext != null) {
scriptProject = sourceModuleContext.getSourceModule().getScriptProject();
scope = SearchEngine.createSearchScope(scriptProject);
}
if (scope == null) {
scope = SearchEngine.createWorkspaceScope(PHPLanguageToolkit.getDefault());
}
IType[] types = PHPModelAccess.getDefault().findTypes(typeName, MatchRule.EXACT, 0, Modifiers.AccNameSpace,
scope, null);
Set<IModelElement> elements = new HashSet<IModelElement>();
for (IType type : types) {
try {
IField field = type.getField(constantName);
if (field.exists() && PHPFlags.isConstant(field.getFlags())) {
elements.add(field);
}
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
Map<ISourceModule, SortedSet<ISourceRange>> offsets = new HashMap<ISourceModule, SortedSet<ISourceRange>>();
Comparator<ISourceRange> sourceRangeComparator = new Comparator<ISourceRange>() {
public int compare(ISourceRange o1, ISourceRange o2) {
return o1.getOffset() - o2.getOffset();
}
};
for (IModelElement element : elements) {
if (element instanceof IField) {
IField field = (IField) element;
ISourceModule sourceModule = field.getSourceModule();
if (!offsets.containsKey(sourceModule)) {
offsets.put(sourceModule, new TreeSet<ISourceRange>(sourceRangeComparator));
}
try {
offsets.get(sourceModule).add(field.getSourceRange());
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
}
List<IGoal> subGoals = new LinkedList<IGoal>();
for (Entry<ISourceModule, SortedSet<ISourceRange>> entry : offsets.entrySet()) {
final ISourceModule sourceModule = entry.getKey();
ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(sourceModule);
SortedSet<ISourceRange> fileOffsets = entry.getValue();
if (!fileOffsets.isEmpty()) {
ConstantDeclarationSearcher searcher = new ConstantDeclarationSearcher(fileOffsets, constantName);
try {
moduleDeclaration.traverse(searcher);
for (Scalar scalar : searcher.getDeclarations()) {
subGoals.add(new ExpressionTypeGoal(goal.getContext(), scalar));
}
} catch (Exception e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
}
return subGoals.toArray(new IGoal[subGoals.size()]);
}
public Object produceResult() {
return PHPTypeInferenceUtils.combineTypes(evaluatedTypes);
}
public IGoal[] subGoalDone(IGoal subgoal, Object result, GoalState state) {
if (state != GoalState.RECURSIVE && result != null) {
evaluatedTypes.add((IEvaluatedType) result);
}
return IGoal.NO_GOALS;
}
static class ConstantDeclarationSearcher extends ASTVisitor {
private String constantName;
private Iterator<ISourceRange> offsetsIt;
private int currentStart;
private int currentEnd;
private boolean stopProcessing;
private List<Scalar> declarations = new LinkedList<Scalar>();
public ConstantDeclarationSearcher(SortedSet<ISourceRange> offsets, String constantName) {
this.constantName = constantName;
offsetsIt = offsets.iterator();
setNextRange();
}
public List<Scalar> getDeclarations() {
return declarations;
}
private void setNextRange() {
if (offsetsIt.hasNext()) {
ISourceRange range = offsetsIt.next();
currentStart = range.getOffset();
currentEnd = currentStart + range.getLength();
} else {
stopProcessing = true;
}
}
private boolean interesting(ASTNode node) {
return !stopProcessing && node.sourceStart() <= currentStart && node.sourceEnd() >= currentEnd;
}
public boolean visit(CallExpression node) throws Exception {
if (!interesting(node)) {
return false;
}
if ("define".equalsIgnoreCase(node.getName())) { //$NON-NLS-1$
// report global constant:
List<ASTNode> args = node.getArgs().getChilds();
if (args.size() == 2) {
ASTNode firstArg = args.get(0);
ASTNode secondArg = args.get(0);
if (firstArg instanceof Scalar && secondArg instanceof Scalar) {
Scalar constantName = (Scalar) firstArg;
Scalar constantValue = (Scalar) secondArg;
if (this.constantName.equals(stripQuotes(constantName.getValue()))) {
declarations.add(constantValue);
}
}
}
}
return visitGeneral(node);
}
public boolean visit(ConstantDeclaration node) throws Exception {
if (!interesting(node)) {
return false;
}
Expression value = node.getConstantValue();
if (value instanceof Scalar) {
declarations.add((Scalar) value);
}
return visitGeneral(node);
}
public boolean visit(Expression node) throws Exception {
if (!interesting(node)) {
return false;
}
if (node instanceof CallExpression) {
return visit((CallExpression) node);
}
return visitGeneral(node);
}
public boolean endvisit(Statement s) throws Exception {
if (s instanceof ConstantDeclaration) {
return visit((ConstantDeclaration) s);
}
return visitGeneral(s);
}
public boolean visitGeneral(ASTNode node) throws Exception {
return interesting(node);
}
}
/**
* Strips single or double quotes from the start and from the end of the
* given string
*
* @param name
* String
* @return
*/
private static String stripQuotes(String name) {
int len = name.length();
if (len > 1 && (name.charAt(0) == '\'' && name.charAt(len - 1) == '\''
|| name.charAt(0) == '"' && name.charAt(len - 1) == '"')) {
name = name.substring(1, len - 1);
}
return name;
}
}