/*******************************************************************************
* Copyright (c) 2009, 2016 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
* Dawid PakuĊa [469267]
*******************************************************************************/
package org.eclipse.php.internal.core.typeinference.evaluators;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.dltk.ast.ASTListNode;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.ASTVisitor;
import org.eclipse.dltk.ast.declarations.MethodDeclaration;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.declarations.TypeDeclaration;
import org.eclipse.dltk.ast.references.SimpleReference;
import org.eclipse.dltk.ast.references.TypeReference;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.evaluation.types.AmbiguousType;
import org.eclipse.dltk.internal.core.ImportDeclaration;
import org.eclipse.dltk.ti.GoalState;
import org.eclipse.dltk.ti.IContext;
import org.eclipse.dltk.ti.ISourceModuleContext;
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.PHPVersion;
import org.eclipse.php.core.compiler.ast.nodes.ClassDeclaration;
import org.eclipse.php.core.compiler.ast.nodes.FullyQualifiedReference;
import org.eclipse.php.core.compiler.ast.nodes.NamespaceReference;
import org.eclipse.php.core.compiler.ast.nodes.UsePart;
import org.eclipse.php.core.project.ProjectOptions;
import org.eclipse.php.internal.core.typeinference.PHPClassType;
import org.eclipse.php.internal.core.typeinference.PHPModelUtils;
import org.eclipse.php.internal.core.typeinference.PHPSimpleTypes;
import org.eclipse.php.internal.core.typeinference.context.INamespaceContext;
import org.eclipse.php.internal.core.typeinference.context.MethodContext;
public class TypeReferenceEvaluator extends GoalEvaluator {
private TypeReference typeReference;
private IEvaluatedType result;
private PHPVersion phpVersion;
public TypeReferenceEvaluator(IGoal goal, TypeReference typeReference) {
super(goal);
this.typeReference = typeReference;
if (goal.getContext() instanceof ISourceModuleContext) {
phpVersion = ProjectOptions.getPHPVersion(((ISourceModuleContext) goal.getContext()).getSourceModule());
}
}
private boolean isSelfOrStatic() {
String name = typeReference.getName();
if (phpVersion != null && PHPVersion.PHP5_4.isLessThan(phpVersion)) {
name = name.toLowerCase();
}
return "self".equals(name) || "static".equals(name); //$NON-NLS-1$ //$NON-NLS-2$
}
private boolean isParent() {
String name = typeReference.getName();
if (phpVersion != null && PHPVersion.PHP5_4.isLessThan(phpVersion)) {
name = name.toLowerCase();
}
return "parent".equals(name); //$NON-NLS-1$
}
public IGoal[] init() {
final IContext context = goal.getContext();
String className = typeReference.getName();
if (isSelfOrStatic()) {
if (context instanceof MethodContext) {
MethodContext methodContext = (MethodContext) context;
IEvaluatedType instanceType = methodContext.getInstanceType();
if (instanceType instanceof PHPClassType) {
result = instanceType;
}
}
} else if (isParent()) { // $NON-NLS-1$
if (context instanceof MethodContext) {
final MethodContext methodContext = (MethodContext) context;
ModuleDeclaration rootNode = methodContext.getRootNode();
ISourceModule sourceModule = ((ISourceModuleContext) context).getSourceModule();
final IType currentNamespace = PHPModelUtils.getCurrentNamespace(sourceModule, rootNode.sourceStart());
final ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(sourceModule);
final MethodDeclaration methodDecl = methodContext.getMethodNode();
// Look for parent class types:
final List<IEvaluatedType> types = new LinkedList<IEvaluatedType>();
try {
rootNode.traverse(new ASTVisitor() {
private TypeDeclaration currentType;
private boolean found;
public boolean visit(MethodDeclaration s) throws Exception {
if (s == methodDecl && currentType instanceof ClassDeclaration) {
ClassDeclaration classDecl = (ClassDeclaration) currentType;
ASTListNode superClasses = classDecl.getSuperClasses();
List<ASTNode> childs = superClasses.getChilds();
for (Iterator<ASTNode> iterator = childs.iterator(); iterator.hasNext();) {
ASTNode node = (ASTNode) iterator.next();
NamespaceReference namespace = null;
SimpleReference reference = null;
if (node instanceof SimpleReference) {
reference = (SimpleReference) node;
String typeName = reference.getName();
if (reference instanceof FullyQualifiedReference) {
FullyQualifiedReference ref = (FullyQualifiedReference) node;
namespace = ref.getNamespace();
}
if (namespace != null && !namespace.getName().equals("")) { //$NON-NLS-1$
String nsName = namespace.getName();
if (nsName.equals("\\")) { //$NON-NLS-1$
typeName = nsName + typeName;
} else {
if (nsName.startsWith("namespace\\")) { //$NON-NLS-1$
nsName = nsName.replace("namespace\\", ""); //$NON-NLS-1$ //$NON-NLS-2$
}
typeName = nsName + NamespaceReference.NAMESPACE_SEPARATOR + typeName;
}
}
if (typeName.indexOf(NamespaceReference.NAMESPACE_SEPARATOR) > 0) {
// check if the first part
// is an
// alias,then get the full
// name
String prefix = typeName.substring(0,
typeName.indexOf(NamespaceReference.NAMESPACE_SEPARATOR));
final Map<String, UsePart> result = PHPModelUtils.getAliasToNSMap(prefix,
moduleDeclaration, reference.sourceStart(), currentNamespace, true);
if (result.containsKey(prefix)) {
String fullName = result.get(prefix).getNamespace()
.getFullyQualifiedName();
typeName = typeName.replace(prefix, fullName);
}
} else if (typeName.indexOf(NamespaceReference.NAMESPACE_SEPARATOR) < 0) {
String prefix = typeName;
final Map<String, UsePart> result = PHPModelUtils.getAliasToNSMap(prefix,
moduleDeclaration, reference.sourceStart(), currentNamespace, true);
if (result.containsKey(prefix)) {
String fullName = result.get(prefix).getNamespace()
.getFullyQualifiedName();
typeName = fullName;
}
}
IEvaluatedType type = PHPSimpleTypes.fromString(typeName);
if (type == null) {
if (typeName.indexOf(NamespaceReference.NAMESPACE_SEPARATOR) != -1
|| currentNamespace == null) {
type = new PHPClassType(typeName);
} else {
type = new PHPClassType(currentNamespace.getElementName(), typeName);
}
}
types.add(type);
}
// if (namespace == null
// || namespace.getName().equals("")) {
// types.add(new PHPClassType(reference
// .getName()));
// } else {
// types.add(new
// PHPClassType(namespace.getName(),
// reference.getName()));
// }
}
found = true;
}
return !found;
}
public boolean visit(TypeDeclaration s) throws Exception {
this.currentType = s;
return !found;
}
public boolean endvisit(TypeDeclaration s) throws Exception {
this.currentType = null;
return super.endvisit(s);
}
public boolean visit(ASTNode n) throws Exception {
return !found;
}
});
} catch (Exception e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
if (types.size() == 1) {
result = types.get(0);
} else if (types.size() > 1) {
result = new AmbiguousType(types.toArray(new IEvaluatedType[types.size()]));
}
}
} else if (PHPSimpleTypes.isHintable(typeReference.getName(), phpVersion)) {
result = PHPSimpleTypes.fromString(typeReference.getName());
} else {
String parentNamespace = null;
// Check current context - if we are under some namespace:
if (context instanceof INamespaceContext) {
parentNamespace = ((INamespaceContext) context).getNamespace();
}
String fullyQualifiedName;
// If the namespace was prefixed explicitly - use it:
if (typeReference instanceof FullyQualifiedReference) {
fullyQualifiedName = ((FullyQualifiedReference) typeReference).getFullyQualifiedName();
} else {
fullyQualifiedName = typeReference.getName();
className = PHPEvaluationUtils.extractArrayType(fullyQualifiedName);
className = PHPModelUtils.extractElementName(className);
}
ISourceModule sourceModule = ((ISourceModuleContext) context).getSourceModule();
int offset = typeReference.sourceStart();
try {
// for use statement, extract namespace and class name directly
if (sourceModule.getElementAt(offset) instanceof ImportDeclaration) {
String namespace = PHPModelUtils.extractNameSpaceName(fullyQualifiedName);
className = PHPModelUtils.extractElementName(fullyQualifiedName);
if (namespace != null) {
result = new PHPClassType(namespace, className);
} else {
result = new PHPClassType(className);
}
return IGoal.NO_GOALS;
}
} catch (ModelException e) {
}
String extractedNamespace = PHPModelUtils.extractNamespaceName(fullyQualifiedName, sourceModule, offset);
if (extractedNamespace != null) {
parentNamespace = extractedNamespace;
className = PHPModelUtils.getRealName(fullyQualifiedName, sourceModule, offset, className);
}
if (PHPModelUtils.isInUseTraitStatement(((ISourceModuleContext) context).getRootNode(),
typeReference.sourceStart())) {
if (parentNamespace != null) {
result = new PHPTraitType(parentNamespace, className);
} else {
result = new PHPTraitType(className);
}
} else {
if (parentNamespace != null) {
result = new PHPClassType(parentNamespace, className);
} else {
result = new PHPClassType(className);
}
}
}
return IGoal.NO_GOALS;
}
public Object produceResult() {
return result;
}
public IGoal[] subGoalDone(IGoal subgoal, Object result, GoalState state) {
return IGoal.NO_GOALS;
}
}