/*******************************************************************************
* Copyright (c) 2009, 2015 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.codeassist;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.dltk.ast.declarations.ModuleDeclaration;
import org.eclipse.dltk.ast.references.VariableReference;
import org.eclipse.dltk.core.*;
import org.eclipse.dltk.evaluation.types.AmbiguousType;
import org.eclipse.dltk.evaluation.types.MultiTypeType;
import org.eclipse.dltk.ti.BasicContext;
import org.eclipse.dltk.ti.IContext;
import org.eclipse.dltk.ti.ISourceModuleContext;
import org.eclipse.dltk.ti.goals.ExpressionTypeGoal;
import org.eclipse.dltk.ti.types.IEvaluatedType;
import org.eclipse.php.core.PHPVersion;
import org.eclipse.php.core.compiler.PHPFlags;
import org.eclipse.php.core.compiler.ast.nodes.ArrayVariableReference;
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.Logger;
import org.eclipse.php.internal.core.PHPCorePlugin;
import org.eclipse.php.internal.core.compiler.ast.parser.ASTUtils;
import org.eclipse.php.internal.core.typeinference.*;
import org.eclipse.php.internal.core.typeinference.context.FileContext;
import org.eclipse.php.internal.core.typeinference.context.TypeContext;
import org.eclipse.php.internal.core.typeinference.goals.ClassVariableDeclarationGoal;
import org.eclipse.php.internal.core.typeinference.goals.MethodElementReturnTypeGoal;
import org.eclipse.php.internal.core.typeinference.goals.phpdoc.PHPDocClassVariableGoal;
import org.eclipse.php.internal.core.typeinference.goals.phpdoc.PHPDocMethodReturnTypeGoal;
import org.eclipse.php.internal.core.util.text.PHPTextSequenceUtilities;
import org.eclipse.php.internal.core.util.text.TextSequence;
/**
* This is a common utility used by completion and selection engines for PHP
* elements retrieval.
*
* @author michael
*/
public class CodeAssistUtils {
/**
* Whether to use PHPDoc in type inference
*/
public static final int USE_PHPDOC = 1 << 5;
private static final String DOLLAR = "$"; //$NON-NLS-1$
private static final String PAAMAYIM_NEKUDOTAIM = "::"; //$NON-NLS-1$
protected static final String OBJECT_FUNCTIONS_TRIGGER = "->"; //$NON-NLS-1$
private static final String[] KEYWORD_FUNCTION_NAMES = { "return", "yield", "print", "echo" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
private static final String NEW = "new"; //$NON-NLS-1$
private static final Pattern globalPattern = Pattern
.compile("\\$GLOBALS[ \\t\\n\\r]*\\[[ \\t\\n\\r]*[\\'\\\"][\\w]+[\\'\\\"][ \\t\\n\\r]*\\]"); //$NON-NLS-1$
private static final IType[] EMPTY_TYPES = new IType[0];
/**
* Returns type of a class field defined by name.
*
* @param types
* @param propertyName
* @param offset
* @return
*/
public static IType[] getVariableType(IType[] types, String propertyName, int offset) {
if (types != null) {
for (IType type : types) {
PHPClassType classType = PHPClassType.fromIType(type);
ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(type.getSourceModule(),
null);
FileContext fileContext = new FileContext(type.getSourceModule(), moduleDeclaration, offset);
TypeContext typeContext = new TypeContext(fileContext, classType);
PHPTypeInferencer typeInferencer = new PHPTypeInferencer();
if (!propertyName.startsWith(DOLLAR)) {
propertyName = DOLLAR + propertyName;
}
PHPDocClassVariableGoal phpDocGoal = new PHPDocClassVariableGoal(typeContext, propertyName, offset);
IEvaluatedType evaluatedType = typeInferencer.evaluateTypePHPDoc(phpDocGoal, 3000);
IType[] modelElements = PHPTypeInferenceUtils.getModelElements(evaluatedType, fileContext, offset);
if (modelElements != null) {
return modelElements;
}
ClassVariableDeclarationGoal goal = new ClassVariableDeclarationGoal(typeContext, types, propertyName);
evaluatedType = typeInferencer.evaluateType(goal);
modelElements = PHPTypeInferenceUtils.getModelElements(evaluatedType, fileContext, offset);
if (modelElements != null) {
return modelElements;
}
}
}
return EMPTY_TYPES;
}
/**
* Returns type of a variable defined by name.
*
* @param sourceModule
* @param variableName
* @param position
* @return
*/
public static IType[] getArrayVariableType(ISourceModule sourceModule, String variableName, int position) {
ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(sourceModule, null);
IContext context = ASTUtils.findContext(sourceModule, moduleDeclaration, position);
if (context != null) {
VariableReference varReference = getVariableReference(variableName, position);
ExpressionTypeGoal goal = new ExpressionTypeGoal(context, varReference);
PHPTypeInferencer typeInferencer = new PHPTypeInferencer();
IEvaluatedType evaluatedType = typeInferencer.evaluateType(goal);
if (evaluatedType instanceof MultiTypeType || evaluatedType instanceof AmbiguousType) {
return getTypes(position, context, evaluatedType);
}
IType[] modelElements = PHPTypeInferenceUtils.getModelElements(evaluatedType,
(ISourceModuleContext) context, position);
if (modelElements != null) {
return modelElements;
}
}
return EMPTY_TYPES;
}
private static IType[] getTypes(int position, IContext context, IEvaluatedType evaluatedType) {
List<IType> tmpList = new LinkedList<IType>();
List<IEvaluatedType> possibleTypes = new LinkedList<IEvaluatedType>();
if (evaluatedType instanceof MultiTypeType) {
possibleTypes = ((MultiTypeType) evaluatedType).getTypes();
} else if (evaluatedType instanceof AmbiguousType) {
possibleTypes.addAll(Arrays.asList(((AmbiguousType) evaluatedType).getPossibleTypes()));
} else {
possibleTypes.add(evaluatedType);
}
for (IEvaluatedType possibleType : possibleTypes) {
IType[] tmpArray;
if (possibleType instanceof MultiTypeType || possibleType instanceof AmbiguousType) {
tmpArray = getTypes(position, context, possibleType);
} else {
tmpArray = PHPTypeInferenceUtils.getModelElements(possibleType, (ISourceModuleContext) context,
position, (IModelAccessCache) null);
}
if (tmpArray != null) {
tmpList.addAll(Arrays.asList(tmpArray));
}
}
// the elements are filtered already
return tmpList.toArray(new IType[tmpList.size()]);
}
/**
* Returns type of a variable defined by name.
*
* @param sourceModule
* @param variableName
* @param position
* @return
*/
public static IType[] getVariableType(ISourceModule sourceModule, String variableName, int position) {
ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(sourceModule, null);
IContext context = ASTUtils.findContext(sourceModule, moduleDeclaration, position);
if (context != null) {
VariableReference varReference = getVariableReference(variableName, position);
ExpressionTypeGoal goal = new ExpressionTypeGoal(context, varReference);
PHPTypeInferencer typeInferencer = new PHPTypeInferencer();
IEvaluatedType evaluatedType = typeInferencer.evaluateType(goal);
IType[] modelElements = getTypes(position, context, evaluatedType);
// IType[] modelElements = PHPTypeInferenceUtils.getModelElements(
// evaluatedType, (ISourceModuleContext) context, position);
if (modelElements != null) {
return modelElements;
}
}
return EMPTY_TYPES;
}
private static VariableReference getVariableReference(String variableName, int position) {
String start = ""; //$NON-NLS-1$
int arrayType = 0;
if (variableName.endsWith("]")) { //$NON-NLS-1$
start = "["; //$NON-NLS-1$
arrayType = ArrayVariableReference.VARIABLE_ARRAY;
} else if (variableName.endsWith("}")) { //$NON-NLS-1$
start = "{"; //$NON-NLS-1$
arrayType = ArrayVariableReference.VARIABLE_HASHTABLE;
}
if (!"".equals(start)) { //$NON-NLS-1$
int startIndex = variableName.indexOf(start);
String name = variableName.substring(0, startIndex);
return new ArrayVariableReference(position, position + variableName.length(), name, null, arrayType);
}
return new VariableReference(position, position + variableName.length(), variableName);
}
/**
* Determines the return type of the given method element.
*
* @param method
* @param function
* @param offset
* @return
*/
public static IType[] getFunctionReturnType(IType[] types, String method,
org.eclipse.dltk.core.ISourceModule sourceModule, int offset) {
return getFunctionReturnType(types, method, USE_PHPDOC, sourceModule, offset);
}
public static IType[] getFunctionReturnType(IType[] types, String method, int mask,
org.eclipse.dltk.core.ISourceModule sourceModule, int offset) {
return getFunctionReturnType(types, method, mask, sourceModule, offset, null);
}
/**
* Determines the return type of the given method element.
*
* @param method
* @param mask
* @param offset
* @return
*/
public static IType[] getFunctionReturnType(IType[] types, String method, int mask,
org.eclipse.dltk.core.ISourceModule sourceModule, int offset, String[] argNames) {
PHPTypeInferencer typeInferencer = new PHPTypeInferencer();
ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(sourceModule, null);
IContext context = ASTUtils.findContext(sourceModule, moduleDeclaration, offset);
IEvaluatedType evaluatedType;
IType[] modelElements;
boolean usePhpDoc = (mask & USE_PHPDOC) != 0;
if (usePhpDoc) {
PHPDocMethodReturnTypeGoal phpDocGoal = new PHPDocMethodReturnTypeGoal(context, types, method, argNames,
offset);
evaluatedType = typeInferencer.evaluateTypePHPDoc(phpDocGoal);
modelElements = PHPTypeInferenceUtils.getModelElements(evaluatedType, (ISourceModuleContext) context,
offset);
if (modelElements != null && modelElements.length > 0) {
return modelElements;
}
}
MethodElementReturnTypeGoal methodGoal = new MethodElementReturnTypeGoal(context, types, method, argNames,
offset);
evaluatedType = typeInferencer.evaluateType(methodGoal);
if (evaluatedType instanceof PHPThisClassType && ((PHPThisClassType) evaluatedType).getType() != null) {
modelElements = new IType[] { ((PHPThisClassType) evaluatedType).getType() };
} else {
modelElements = PHPTypeInferenceUtils.getModelElements(evaluatedType, (ISourceModuleContext) context,
offset);
}
if (modelElements != null) {
return modelElements;
}
return EMPTY_TYPES;
}
/**
* The "self" function needs to be added only if we are in a class method
* and it is not an abstract class or an interface
*
* @param fileData
* @param offset
* @return the self class data or null in case not found
*/
public static IType getSelfClassData(ISourceModule sourceModule, int offset) {
IType type = PHPModelUtils.getCurrentType(sourceModule, offset);
IMethod method = PHPModelUtils.getCurrentMethod(sourceModule, offset);
if (type != null && method != null) {
try {
int flags = type.getFlags();
if (!PHPFlags.isAbstract(flags) && !PHPFlags.isInterface(flags) && !PHPFlags.isInterface(flags)) {
return type;
}
} catch (ModelException e) {
PHPCorePlugin.log(e);
}
}
return null;
}
/**
* This method finds types for the receiver in the statement text.
*
* @param sourceModule
* @param statementText
* @param endPosition
* @param offset
* @return
*/
public static IType[] getTypesFor(ISourceModule sourceModule, TextSequence statementText, int endPosition,
int offset) {
return getTypesFor(sourceModule, statementText, endPosition, offset, null);
}
protected static IType[] getTypesFor(ISourceModule sourceModule, TextSequence statementText, int endPosition,
int offset, String triggerText) {
endPosition = PHPTextSequenceUtilities.readBackwardSpaces(statementText, endPosition); // read
// whitespace
boolean isClassTrigger = false;
if (endPosition < 2) {
return EMPTY_TYPES;
}
int propertyEndPosition = endPosition;
if (triggerText == null) {
triggerText = statementText.subSequence(endPosition - 2, endPosition).toString();
propertyEndPosition = PHPTextSequenceUtilities.readBackwardSpaces(statementText,
endPosition - triggerText.length());
}
if (triggerText.equals(OBJECT_FUNCTIONS_TRIGGER)) {
} else if (triggerText.equals(PAAMAYIM_NEKUDOTAIM)) {
isClassTrigger = true;
} else {
return EMPTY_TYPES;
}
int lastObjectOperator = PHPTextSequenceUtilities.getPreviousTriggerIndex(statementText, propertyEndPosition);
String text = statementText.subSequence(0, propertyEndPosition).toString();
if (lastObjectOperator == -1
|| (text.indexOf('>') >= 0 && text.indexOf("=>") != text.indexOf('>') - 1 && text.indexOf("->") < 0)) { //$NON-NLS-1$ //$NON-NLS-2$
// if there is no "->" or "::" in the left sequence then we need to
// calc the object type
return innerGetClassName(sourceModule, statementText, propertyEndPosition, isClassTrigger, offset);
}
int propertyStartPosition = PHPTextSequenceUtilities.readForwardSpaces(statementText,
lastObjectOperator + triggerText.length());
String propertyName = statementText.subSequence(propertyStartPosition, propertyEndPosition).toString();
IType[] types = getTypesFor(sourceModule, statementText, propertyStartPosition, offset);
int bracketIndex = propertyName.indexOf('(');
if (bracketIndex == -1) {
// meaning its a class variable and not a function
return getVariableType(types, propertyName, offset);
}
boolean arrayReference = false;
PHPVersion version = ProjectOptions.getPHPVersion(sourceModule.getScriptProject().getProject());
if (propertyName.endsWith("]") //$NON-NLS-1$
&& version.isGreaterThan(PHPVersion.PHP5_3)) {
int closeBracketIndex = propertyName.lastIndexOf(')');
if (closeBracketIndex >= 0) {
if (propertyName.indexOf('[', closeBracketIndex) > closeBracketIndex) {
arrayReference = true;
}
}
}
String functionName = propertyName.substring(0, bracketIndex).trim();
String[] argNames = PHPTextSequenceUtilities.getArgNames(version, propertyName.substring(bracketIndex));
Set<IType> result = new LinkedHashSet<IType>();
IType[] returnTypes = null;
if (arrayReference) {
returnTypes = getFunctionArrayReturnType(types, functionName, USE_PHPDOC, sourceModule, offset, argNames);
} else {
returnTypes = getFunctionReturnType(types, functionName, USE_PHPDOC, sourceModule, offset, argNames);
}
if (returnTypes != null) {
result.addAll(Arrays.asList(returnTypes));
}
return result.toArray(new IType[result.size()]);
}
public static IType[] getTraitsFor(ISourceModule sourceModule, TextSequence statementText, int endPosition,
int offset) {
PHPVersion phpVersion = ProjectOptions.getPHPVersion(sourceModule.getScriptProject().getProject());
if (phpVersion.isLessThan(PHPVersion.PHP5_4)) {
return EMPTY_TYPES;
}
endPosition = PHPTextSequenceUtilities.readBackwardSpaces(statementText, endPosition); // read
// whitespace
if (endPosition < 2) {
return EMPTY_TYPES;
}
String triggerText = statementText.subSequence(endPosition - 2, endPosition).toString();
if (triggerText.equals(OBJECT_FUNCTIONS_TRIGGER)) {
} else if (triggerText.equals(PAAMAYIM_NEKUDOTAIM)) {
} else {
return EMPTY_TYPES;
}
int propertyEndPosition = PHPTextSequenceUtilities.readBackwardSpaces(statementText,
endPosition - triggerText.length());
// int lastObjectOperator = PHPTextSequenceUtilities
// .getPreviousTriggerIndex(statementText, propertyEndPosition);
// String text = statementText.subSequence(0, propertyEndPosition)
// .toString();
int classNameStart = PHPTextSequenceUtilities.readIdentifierStartIndex(phpVersion, statementText,
propertyEndPosition, true);
String className = classNameStart < 0 ? "" //$NON-NLS-1$
: statementText.subSequence(classNameStart, propertyEndPosition).toString();
if (className.length() > 0) {
ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(sourceModule, null);
FileContext context = new FileContext(sourceModule, moduleDeclaration, offset);
IEvaluatedType type = PHPClassType.fromTraitName(className, sourceModule, offset);
IType[] modelElements = PHPTypeInferenceUtils.getModelElements(type, context, offset);
if (modelElements != null) {
return modelElements;
}
}
return EMPTY_TYPES;
}
// /**
// * example:(new class1())->avc2()[1][1]->avc1()
// *
// * @param types
// * @param method
// * @param mask
// * @param sourceModule
// * @param offset
// * @return
// */
// private static IType[] getFunctionArrayReturnType(IType[] types, String
// method, ISourceModule sourceModule,
// int offset) {
// return getFunctionArrayReturnType(types, method, USE_PHPDOC,
// sourceModule, offset);
// }
// /**
// * example:(new class1())->avc2()[1][1]->avc1()
// *
// * @param types
// * @param method
// * @param mask
// * @param sourceModule
// * @param offset
// * @return
// */
// private static IType[] getFunctionArrayReturnType(IType[] types, String
// method, int mask,
// ISourceModule sourceModule, int offset) {
// return getFunctionArrayReturnType(types, method, mask, sourceModule,
// offset, null);
// }
/**
* example:(new class1())->avc2()[1][1]->avc1()
*
* @param types
* @param method
* @param mask
* @param sourceModule
* @param offset
* @return
*/
private static IType[] getFunctionArrayReturnType(IType[] types, String method, int mask,
ISourceModule sourceModule, int offset, String[] argNames) {
PHPTypeInferencer typeInferencer = new PHPTypeInferencer();
ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(sourceModule, null);
IContext context = ASTUtils.findContext(sourceModule, moduleDeclaration, offset);
// XXX context cannot be null
if (context == null) {
context = new BasicContext(sourceModule, moduleDeclaration);
Logger.log(Logger.WARNING, "Context is null!"); //$NON-NLS-1$
}
IEvaluatedType evaluatedType;
boolean usePhpDoc = (mask & USE_PHPDOC) != 0;
if (usePhpDoc) {
PHPDocMethodReturnTypeGoal phpDocGoal = new PHPDocMethodReturnTypeGoal(context, types, method, argNames,
offset);
evaluatedType = typeInferencer.evaluateTypePHPDoc(phpDocGoal);
List<IEvaluatedType> possibleTypes = null;
if (!PHPTypeInferenceUtils.isSimple(evaluatedType)) {
if (evaluatedType instanceof MultiTypeType) {
possibleTypes = ((MultiTypeType) evaluatedType).getTypes();
} else if (evaluatedType instanceof AmbiguousType) {
possibleTypes = new ArrayList<IEvaluatedType>();
for (IEvaluatedType pType : ((AmbiguousType) evaluatedType).getPossibleTypes()) {
if (pType instanceof MultiTypeType) {
possibleTypes.addAll(((MultiTypeType) pType).getTypes());
}
}
}
if (possibleTypes != null && possibleTypes.size() > 0) {
List<IType> tmpList = new LinkedList<IType>();
for (IEvaluatedType possibleType : possibleTypes) {
IType[] tmpArray = PHPTypeInferenceUtils.getModelElements(possibleType,
(ISourceModuleContext) context, offset, (IModelAccessCache) null);
if (tmpArray != null && tmpArray.length > 0) {
tmpList.addAll(Arrays.asList(tmpArray));
}
}
// the elements are filtered already
return tmpList.toArray(new IType[tmpList.size()]);
}
}
// modelElements = PHPTypeInferenceUtils.getModelElements(
// evaluatedType, (ISourceModuleContext) context, offset);
// if (modelElements != null) {
// return modelElements;
// }
}
MethodElementReturnTypeGoal methodGoal = new MethodElementReturnTypeGoal(context, types, method, argNames,
offset);
evaluatedType = typeInferencer.evaluateType(methodGoal);
if (evaluatedType instanceof MultiTypeType) {
List<IType> tmpList = new LinkedList<IType>();
List<IEvaluatedType> possibleTypes = ((MultiTypeType) evaluatedType).getTypes();
for (IEvaluatedType possibleType : possibleTypes) {
IType[] tmpArray = PHPTypeInferenceUtils.getModelElements(possibleType, (ISourceModuleContext) context,
offset, (IModelAccessCache) null);
if (tmpArray != null) {
tmpList.addAll(Arrays.asList(tmpArray));
}
}
// the elements are filtered already
return tmpList.toArray(new IType[tmpList.size()]);
}
return EMPTY_TYPES;
}
/**
* Getting an instance and finding its type.
*/
private static IType[] innerGetClassName(ISourceModule sourceModule, TextSequence statementText,
int propertyEndPosition, boolean isClassTriger, int offset) {
PHPVersion phpVersion = ProjectOptions.getPHPVersion(sourceModule.getScriptProject().getProject());
int classNameStart = PHPTextSequenceUtilities.readIdentifierStartIndex(phpVersion, statementText,
propertyEndPosition, true);
String className = classNameStart < 0 ? "" //$NON-NLS-1$
: statementText.subSequence(classNameStart, propertyEndPosition).toString();
if (isClassTriger && className != null && className.length() != 0) {
final String comparable = PHPVersion.PHP5_4.isLessThan(phpVersion) ? className.toLowerCase() : className;
if ("self".equals(comparable) //$NON-NLS-1$
|| "parent".equals(comparable) //$NON-NLS-1$
|| (phpVersion.isGreaterThan(PHPVersion.PHP5) && "static" //$NON-NLS-1$
.equals(comparable))) {
IType classData = PHPModelUtils.getCurrentType(sourceModule, offset - className.length() - 2); // the
// offset
// before
// "self::",
// "parent::" or
// "static::"
if (classData != null) {
return new IType[] { classData };
}
}
if (className.length() > 0) {
if (className.startsWith("$") //$NON-NLS-1$
&& phpVersion.isGreaterThan(PHPVersion.PHP5)) {
int statementStart = statementText.getOriginalOffset(classNameStart);
return getVariableType(sourceModule, className, statementStart);
} else {
ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(sourceModule, null);
FileContext context = new FileContext(sourceModule, moduleDeclaration, offset);
IEvaluatedType type = PHPClassType.fromTypeName(className, sourceModule, offset);
IType[] modelElements = PHPTypeInferenceUtils.getModelElements(type, context, offset);
if (modelElements != null) {
return modelElements;
}
return EMPTY_TYPES;
}
}
}
if (className != null && className.length() == 0) {
// this can happen if the first char before the property is ']'
String testedVar = statementText.subSequence(0, propertyEndPosition).toString().trim();
if (testedVar != null && testedVar.length() != 0) {
// check for $GLOBALS['myVar'] scenario
Matcher m = globalPattern.matcher(testedVar);
if (m.matches()) {
// $GLOBALS['myVar'] => 'myVar'
String quotedVarName = testedVar.substring(testedVar.indexOf('[') + 1, testedVar.indexOf(']'))
.trim();
// 'myVar' => $myVar
className = DOLLAR + quotedVarName.substring(1, quotedVarName.length() - 1); // $NON-NLS-1$
// check for $array[0] scenario
} else if (testedVar.endsWith("}")) { //$NON-NLS-1$
className = testedVar;
} else if (testedVar.endsWith("]")) { //$NON-NLS-1$
if (statementText.toString().lastIndexOf('[') > 0) {
int end = statementText.toString().lastIndexOf('[');
int classNameStart1 = PHPTextSequenceUtilities.readIdentifierStartIndex(phpVersion,
statementText, end, true);
className = classNameStart1 < 0 ? "" //$NON-NLS-1$
: statementText.subSequence(classNameStart1, end).toString();
// if its object call calc the object type.
if (className.length() > 0 && className.charAt(0) == '$') {
int statementStart = statementText.getOriginalOffset(classNameStart);
return getArrayVariableType(sourceModule, className, statementStart);
}
}
className = testedVar;
}
}
}
// if its object call calc the object type.
if (className != null && className.length() > 0 && className.charAt(0) == '$') {
int statementStart = statementText.getOriginalOffset(classNameStart);
return getVariableType(sourceModule, className, statementStart);
}
boolean arrayReference = false;
if (propertyEndPosition > 0 && statementText.charAt(propertyEndPosition - 1) == ']'
&& phpVersion.isGreaterThan(PHPVersion.PHP5_3)) {
int closeBracketIndex = statementText.toString().lastIndexOf(')');
if (closeBracketIndex >= 0) {
if (statementText.toString().indexOf('[', closeBracketIndex) > closeBracketIndex) {
propertyEndPosition = closeBracketIndex + 1;
arrayReference = true;
}
}
}
// if its function call calc the return type.
if (propertyEndPosition > 0 && statementText.charAt(propertyEndPosition - 1) == ')') {
int functionNameEnd = PHPModelUtils.getFunctionNameEndOffset(statementText, propertyEndPosition - 1);
int functionNameStart = PHPTextSequenceUtilities.readIdentifierStartIndex(phpVersion, statementText,
functionNameEnd, false);
String functionName = functionNameStart < 0 ? "" //$NON-NLS-1$
: statementText.subSequence(functionNameStart, functionNameEnd).toString().trim();
if (isKeyword(functionName)) {
functionName = "";
functionNameStart = functionNameEnd = PHPTextSequenceUtilities.readForwardSpaces(statementText,
functionNameEnd);
}
// if its a non class function
Set<IType> returnTypes = new LinkedHashSet<IType>();
if (functionNameStart == functionNameEnd && statementText.charAt(functionNameStart) == '('
&& propertyEndPosition - 1 > functionNameStart + 1 && phpVersion.isGreaterThan(PHPVersion.PHP5_3)) {
TextSequence newClassStatementText = statementText.subTextSequence(functionNameStart + 1,
propertyEndPosition - 1);
if (newClassStatementText.length() < 4
|| !newClassStatementText.subSequence(0, 3).toString().equals(NEW)) {
int innerStart = PHPTextSequenceUtilities.readForwardSpaces(newClassStatementText, 0);
if (newClassStatementText.charAt(innerStart) == '('
|| phpVersion.isGreaterThan(PHPVersion.PHP5_6)) {
return getTypesFor(sourceModule, newClassStatementText, newClassStatementText.length(),
newClassStatementText.length(),
isClassTriger ? PAAMAYIM_NEKUDOTAIM : OBJECT_FUNCTIONS_TRIGGER);
}
return EMPTY_TYPES;
}
String newClassName = PHPModelUtils.getClassNameForNewStatement(newClassStatementText, phpVersion);
try {
return PHPModelUtils.getTypes(newClassName, sourceModule, offset, null, null);
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
Logger.logException(e);
}
}
// String newClassName = statementText
// .subSequence(functionNameStart + 1,
// propertyEndPosition - 1).toString().trim();
// if (newClassName.startsWith("new")
// && newClassName.endsWith(")")) {
// int newClassNameEnd = PHPModelUtils.getFunctionNameEndOffset(
// newClassStatementText,
// newClassStatementText.length() - 1);
// int newClassNameStart = PHPTextSequenceUtilities
// .readIdentifierStartIndex(phpVersion,
// newClassStatementText, newClassNameEnd,
// false);
// if (newClassNameStart > 3) {// should have blank chars after
// // 'new'
// newClassName = newClassStatementText.subSequence(
// newClassNameStart, newClassNameEnd).toString();
//
// }
// }
} else {
String[] argNames = PHPTextSequenceUtilities.getArgNames(phpVersion,
statementText.subSequence(functionNameEnd, propertyEndPosition - 1));
if (arrayReference) {
IType[] types = getFunctionArrayReturnType(null, functionName, USE_PHPDOC, sourceModule, offset,
argNames);
if (types != null) {
returnTypes.addAll(Arrays.asList(types));
}
} else {
IType[] types = getFunctionReturnType(null, functionName, USE_PHPDOC, sourceModule, offset,
argNames);
if (types != null && types.length > 0) {
returnTypes.addAll(Arrays.asList(types));
} else {
IType namespace = PHPModelUtils.getCurrentNamespace(sourceModule, offset);
ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(sourceModule);
Map<String, UsePart> useParts = PHPModelUtils.getAliasToNSMap(functionName, moduleDeclaration,
offset, namespace, true);
if (useParts.containsKey(functionName)) {
String name = useParts.get(functionName).getNamespace().getFullyQualifiedName();
name = NamespaceReference.NAMESPACE_SEPARATOR + name;
types = getFunctionReturnType(null, name, USE_PHPDOC, sourceModule, offset, argNames);
if (types != null) {
returnTypes.addAll(Arrays.asList(types));
}
}
}
}
}
return returnTypes.toArray(new IType[returnTypes.size()]);
}
return EMPTY_TYPES;
}
private static boolean isKeyword(String functionName) {
for (String k : KEYWORD_FUNCTION_NAMES) {
if (k.equalsIgnoreCase(functionName)) {
return true;
}
}
return false;
}
/**
* This method checks whether the specified function name refers to existing
* method in the given list of classes.
*
* @param sourceModule
* @param className
* @param functionName
* @return
*/
public static boolean isClassFunctionCall(ISourceModule sourceModule, IType[] className, String functionName) {
for (IType type : className) {
IMethod[] classMethod;
try {
classMethod = PHPModelUtils.getTypeHierarchyMethod(type, functionName, true, null);
if (classMethod != null) {
return true;
}
} catch (CoreException e) {
PHPCorePlugin.log(e);
}
}
return false;
}
/**
* Removes elements based on element name.
*
*
* @param elements
* array of IModelElement's
* @return list of elements without duplicates
*/
public static <T extends IModelElement> List<T> removeDuplicatedElements(T[] elements) {
List<T> result = new ArrayList<T>();
Set<String> names = new HashSet<String>();
for (T element : elements) {
if (!names.contains(element.getElementName())) {
result.add(element);
names.add(element.getElementName());
}
}
return result;
}
}