/*******************************************************************************
* 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.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.dltk.ast.ASTNode;
import org.eclipse.dltk.ast.ASTVisitor;
import org.eclipse.dltk.ast.Modifiers;
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.expressions.Expression;
import org.eclipse.dltk.ast.references.VariableReference;
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.internal.core.ModelElement;
import org.eclipse.dltk.internal.core.SourceField;
import org.eclipse.dltk.internal.core.SourceRefElement;
import org.eclipse.jface.text.IRegion;
import org.eclipse.php.core.compiler.PHPFlags;
import org.eclipse.php.internal.core.Logger;
import org.eclipse.php.internal.core.PHPCoreConstants;
import org.eclipse.php.internal.core.PHPCorePlugin;
import org.eclipse.php.internal.core.PHPVersion;
import org.eclipse.php.internal.core.ast.nodes.Identifier;
import org.eclipse.php.internal.core.ast.nodes.NamespaceName;
import org.eclipse.php.internal.core.codeassist.CodeAssistUtils;
import org.eclipse.php.internal.core.compiler.ast.nodes.*;
import org.eclipse.php.internal.core.compiler.ast.parser.ASTUtils;
import org.eclipse.php.internal.core.compiler.ast.visitor.PHPASTVisitor;
import org.eclipse.php.internal.core.filenetwork.FileNetworkUtility;
import org.eclipse.php.internal.core.filenetwork.ReferenceTree;
import org.eclipse.php.internal.core.language.LanguageModelInitializer;
import org.eclipse.php.internal.core.model.PhpModelAccess;
import org.eclipse.php.internal.core.typeinference.DeclarationSearcher.DeclarationType;
import org.eclipse.php.internal.core.util.text.PHPTextSequenceUtilities;
import org.eclipse.php.internal.core.util.text.TextSequence;
public class PHPModelUtils {
public static final String ENCLOSING_TYPE_SEPARATOR = new String(
new char[] { NamespaceReference.NAMESPACE_SEPARATOR }); //$NON-NLS-1$
private static final IType[] EMPTY_TYPES = new IType[0];
/**
* Extracts the element name from the given fully qualified name
*
* @param element
* Element name
* @return element name without the namespace prefix
*/
public static String extractElementName(String element) {
if (element != null) {
int i = element.lastIndexOf(NamespaceReference.NAMESPACE_SEPARATOR);
if (i != -1) {
element = element.substring(i + 1).trim();
}
}
return element;
}
/**
* Extracts the name space name from the given fully qualified name
*
* @param element
* Element name
* @return namespace prefix
*/
public static String extractNameSapceName(String element) {
String nameSpaceName = null;
if (element != null) {
int i = element.lastIndexOf(NamespaceReference.NAMESPACE_SEPARATOR);
if (i != -1) {
nameSpaceName = element.substring(0, i).trim();
}
}
return nameSpaceName;
}
/**
* if the elementName is a class alias for a namespace class,we get its
* original name from its alias
*
* @param elementName
* @param sourceModule
* @param offset
* @param defaultClassName
* @return
*/
public static String getRealName(String elementName,
ISourceModule sourceModule, final int offset,
String defaultClassName) {
// Check class name aliasing:
ModuleDeclaration moduleDeclaration = SourceParserUtil
.getModuleDeclaration(sourceModule);
UsePart usePart = ASTUtils.findUseStatementByAlias(moduleDeclaration,
elementName, offset);
if (usePart != null) {
elementName = usePart.getNamespace().getFullyQualifiedName();
int nsIndex = elementName
.lastIndexOf(NamespaceReference.NAMESPACE_SEPARATOR);
if (nsIndex != -1) {
defaultClassName = elementName.substring(nsIndex + 1);
} else {
defaultClassName = elementName;
}
}
return defaultClassName;
}
/**
* Extracts the namespace name from the specified element name and resolves
* it using USE statements that present in the file.
*
* @param elementName
* The name of the element, like: \A\B or A\B\C.
* @param sourceModule
* Source module where the element is referenced
* @param offset
* The offset where element is referenced
* @return namespace name:
*
* <pre>
* 1. <code>""</code> (empty string) indicates global namespace
* 2. non-empty string indicates a real namespace
* 3. <code>null</code> indicates that there's no namespace prefix in element name
* </pre>
*/
public static String extractNamespaceName(String elementName,
ISourceModule sourceModule, final int offset) {
// Check class name aliasing:
ModuleDeclaration moduleDeclaration = SourceParserUtil
.getModuleDeclaration(sourceModule);
UsePart usePart = ASTUtils.findUseStatementByAlias(moduleDeclaration,
elementName, offset);
if (usePart != null) {
elementName = usePart.getNamespace().getFullyQualifiedName();
if (elementName != null
&& elementName.length() > 0
&& elementName.charAt(0) != NamespaceReference.NAMESPACE_SEPARATOR) {
elementName = NamespaceReference.NAMESPACE_SEPARATOR
+ elementName;
}
}
boolean isGlobal = false;
int nsIndex = -1;
if (elementName != null) {
nsIndex = elementName
.lastIndexOf(NamespaceReference.NAMESPACE_SEPARATOR);
if (elementName.length() > 0
&& elementName.charAt(0) == NamespaceReference.NAMESPACE_SEPARATOR) {
isGlobal = true;
}
}
if (nsIndex != -1) {
String namespace = elementName.substring(0, nsIndex);
if (isGlobal && namespace.length() > 0) {
namespace = namespace.substring(1);
}
if (!isGlobal) {
// 1. It can be a special 'namespace' keyword, which points to
// the current namespace:
if ("namespace".equalsIgnoreCase(namespace)) { //$NON-NLS-1$
IType currentNamespace = PHPModelUtils.getCurrentNamespace(
sourceModule, offset);
return currentNamespace.getElementName();
}
// 2. it can be an alias - try to find relevant USE statement
if (namespace.indexOf('\\') == -1) {
usePart = ASTUtils.findUseStatementByAlias(
moduleDeclaration, namespace, offset);
if (usePart != null) {
return usePart.getNamespace().getFullyQualifiedName();
}
} else {
nsIndex = namespace
.indexOf(NamespaceReference.NAMESPACE_SEPARATOR);
String alias = namespace.substring(0, nsIndex);
usePart = ASTUtils.findUseStatementByAlias(
moduleDeclaration, alias, offset);
if (usePart != null) {
return usePart.getNamespace().getFullyQualifiedName()
+ NamespaceReference.NAMESPACE_SEPARATOR
+ namespace.substring(nsIndex + 1);
}
}
// 3. it can be a sub-namespace of the current namespace:
IType currentNamespace = PHPModelUtils.getCurrentNamespace(
sourceModule, offset);
if (currentNamespace != null) {
return new StringBuilder(currentNamespace.getElementName())
.append(NamespaceReference.NAMESPACE_SEPARATOR)
.append(namespace).toString();
}
}
// global namespace:
return namespace;
}
return null;
}
/**
* Filters model elements using file network.
*
* @param sourceModule
* Source module
* @param elements
* Model elements to filter
* @param cache
* Temporary model cache instance
* @param monitor
* Progress monitor
* @return
*/
public static <T extends IModelElement> Collection<T> fileNetworkFilter(
ISourceModule sourceModule, Collection<T> elements,
IModelAccessCache cache, IProgressMonitor monitor) {
if (elements != null && elements.size() > 0) {
List<T> filteredElements = new LinkedList<T>();
// If some of elements belong to current file return just it:
for (T element : elements) {
if (sourceModule.equals(element.getOpenable())) {
filteredElements.add(element);
}
}
if (filteredElements.size() == 0) {
ReferenceTree referenceTree;
if (cache != null) {
referenceTree = cache.getFileHierarchy(sourceModule,
monitor);
} else {
// Filter by includes network
referenceTree = FileNetworkUtility
.buildReferencedFilesTree(sourceModule, monitor);
}
for (T element : elements) {
if (LanguageModelInitializer
.isLanguageModelElement(element)
|| referenceTree.find(((ModelElement) element)
.getSourceModule())) {
filteredElements.add(element);
}
}
}
if (filteredElements.size() > 0) {
elements = filteredElements;
}
}
return elements;
}
public static <T extends IModelElement> Collection<T> fileNetworkFilterTypes(
ISourceModule sourceModule, Collection<T> elements,
IModelAccessCache cache, boolean isNs, IProgressMonitor monitor) {
if (elements != null && elements.size() > 0) {
List<T> filteredElements = new LinkedList<T>();
// If some of elements belong to current file return just it:
for (T element : elements) {
try {
if (sourceModule.equals(element.getOpenable())
&& (isNs
&& PHPFlags.isNamespace(((IType) element)
.getFlags()) || !isNs
&& !PHPFlags.isNamespace(((IType) element)
.getFlags()))) {
filteredElements.add(element);
}
} catch (ModelException e) {
}
}
if (filteredElements.size() == 0) {
ReferenceTree referenceTree;
if (cache != null) {
referenceTree = cache.getFileHierarchy(sourceModule,
monitor);
} else {
// Filter by includes network
referenceTree = FileNetworkUtility
.buildReferencedFilesTree(sourceModule, monitor);
}
for (T element : elements) {
if (LanguageModelInitializer
.isLanguageModelElement(element)
|| referenceTree.find(((ModelElement) element)
.getSourceModule())) {
try {
if ((isNs
&& PHPFlags.isNamespace(((IType) element)
.getFlags()) || !isNs
&& !PHPFlags.isNamespace(((IType) element)
.getFlags()))) {
filteredElements.add(element);
}
} catch (ModelException e) {
}
}
}
}
if (filteredElements.size() > 0) {
elements = filteredElements;
}
}
return elements;
}
/**
* Determine whether givent elements represent the same type and name, but
* declared in different files (determine whether file network filtering can
* be used)
*
* @param elements
* Model elements list
* @return
*/
private static <T extends IModelElement> boolean canUseFileNetworkFilter(
Collection<T> elements) {
int elementType = 0;
String elementName = null;
for (T element : elements) {
if (element == null) {
continue;
}
if (elementName == null) {
elementType = element.getElementType();
elementName = element.getElementName();
continue;
}
if (!elementName.equalsIgnoreCase(element.getElementName())
|| elementType != element.getElementType()) {
return false;
}
}
return true;
}
/**
* Leaves most 'suitable' for current source module elements
*
* @param sourceModule
* @param elements
* @param cache
* Model access cache if available
* @param monitor
* Progress monitor
* @return
*/
public static <T extends IModelElement> Collection<T> filterElements(
ISourceModule sourceModule, Collection<T> elements,
IModelAccessCache cache, IProgressMonitor monitor) {
if (elements == null) {
return null;
}
if (canUseFileNetworkFilter(elements)) {
return fileNetworkFilter(sourceModule, elements, cache, monitor);
}
return elements;
}
/**
* Returns the current method or function by the specified file and offset
*
* @param sourceModule
* The file where current namespace is requested
* @param offset
* The offset where current namespace is requested
* @return method element, or <code>null</code> if the scope not a method
* scope
*/
public static IMethod getCurrentMethod(ISourceModule sourceModule,
int offset) {
try {
IModelElement currentMethod = sourceModule.getElementAt(offset);
while (currentMethod != null) {
if (currentMethod instanceof IMethod) {
return (IMethod) currentMethod;
}
if (!(currentMethod instanceof IField)) {
break;
}
currentMethod = currentMethod.getParent();
}
} catch (ModelException e) {
PHPCorePlugin.log(e);
}
return null;
}
/**
* Returns the current namespace by the specified model element
*
* @param element
* Model element
* @return namespace element, or <code>null</code> if the scope is global
* under the specified cursor position
*/
public static IType getCurrentNamespace(IModelElement element) {
try {
IModelElement currentNs = element;
while (currentNs != null) {
if (currentNs instanceof IType
&& PHPFlags.isNamespace(((IType) currentNs).getFlags())) {
return (IType) currentNs;
}
currentNs = currentNs.getParent();
}
} catch (ModelException e) {
PHPCorePlugin.log(e);
}
return null;
}
/**
* Returns the current namespace by the specified file and offset
*
* @param sourceModule
* The file where current namespace is requested
* @param sourceModule
* The offset where current namespace is requested
* @return namespace element, or <code>null</code> if the scope is global
* under the specified cursor position
*/
public static IType getCurrentNamespace(ISourceModule sourceModule,
int offset) {
try {
IModelElement currentNs = sourceModule.getElementAt(offset);
while (currentNs instanceof IField) {
currentNs = sourceModule.getElementAt(((IField) currentNs)
.getSourceRange().getOffset() - 1);
}
while (currentNs != null) {
if (currentNs instanceof IType
&& PHPFlags.isNamespace(((IType) currentNs).getFlags())) {
return (IType) currentNs;
}
currentNs = currentNs.getParent();
}
} catch (ModelException e) {
PHPCorePlugin.log(e);
}
return null;
}
public static IType getCurrentNamespaceIfAny(ISourceModule sourceModule,
int offset) {
IType result = getCurrentNamespace(sourceModule, offset);
if (result == null) {
try {
IModelElement[] elements = sourceModule.getChildren();
for (IModelElement modelElement : elements) {
if (modelElement instanceof IType
&& PHPFlags.isNamespace(((IType) modelElement)
.getFlags())) {
result = (IType) modelElement;
}
if (modelElement instanceof SourceRefElement) {
SourceRefElement child = (SourceRefElement) modelElement;
ISourceRange range = child.getSourceRange();
int start = range.getOffset();
int end = start + range.getLength();
if (start <= offset && offset <= end) {
return result;
}
}
}
} catch (ModelException e) {
PHPCorePlugin.log(e);
}
}
return result;
}
/**
* if there are error in the php file,the parser can not be parse the ast
* correctly
*
* @param sourceModule
* @param offset
* @return
*/
public static IType getPossibleCurrentNamespace(ISourceModule sourceModule,
int offset) {
try {
IType result = getCurrentNamespace(sourceModule, offset);
if (result == null) {
IType[] types = sourceModule.getTypes();
if (types != null && types.length > 0
&& PHPFlags.isNamespace(types[0].getFlags())) {
for (int i = 0; i < types.length; i++) {
if (types[i].getSourceRange().getOffset() <= offset
&& PHPFlags.isNamespace(types[i].getFlags())) {
result = types[i];
} else {
return result;
}
}
}
}
return result;
} catch (ModelException e) {
PHPCorePlugin.log(e);
}
return null;
}
/**
* Returns the current class or interface by the specified file and offset
*
* @param sourceModule
* The file where current namespace is requested
* @param offset
* The offset where current namespace is requested
* @return type element, or <code>null</code> if the scope not a class or
* interface scope
*/
public static IType getCurrentType(IModelElement element) {
try {
while (element != null) {
if (element instanceof IType) {
if (!PHPFlags.isNamespace(((IType) element).getFlags())) {
return (IType) element;
}
break;
}
element = element.getParent();
}
} catch (ModelException e) {
PHPCorePlugin.log(e);
}
return null;
}
/**
* Returns the current class or interface by the specified file and offset
*
* @param sourceModule
* The file where current namespace is requested
* @param offset
* The offset where current namespace is requested
* @return type element, or <code>null</code> if the scope not a class or
* interface scope
*/
public static IType getCurrentType(ISourceModule sourceModule, int offset) {
try {
return getCurrentType(sourceModule.getElementAt(offset));
} catch (ModelException e) {
PHPCorePlugin.log(e);
}
return null;
}
/**
* Returns PHPDoc block associated with the given IField element
*
* @param field
* @return
*/
public static PHPDocBlock getDocBlock(IField field) {
if (field == null) {
return null;
}
try {
ISourceModule sourceModule = field.getSourceModule();
ModuleDeclaration moduleDeclaration = SourceParserUtil
.getModuleDeclaration(sourceModule);
ASTNode fieldDeclaration = PHPModelUtils.getNodeByField(
moduleDeclaration, field);
if (fieldDeclaration instanceof IPHPDocAwareDeclaration) {
return ((IPHPDocAwareDeclaration) fieldDeclaration).getPHPDoc();
} else if (fieldDeclaration == null) {
return DefineMethodUtils.getDefinePHPDocBlockByField(
moduleDeclaration, field);
}
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
Logger.logException(e);
}
}
return null;
}
/**
* Returns PHPDoc block associated with the given IMethod element
*
* @param method
* @return
*/
public static PHPDocBlock getDocBlock(IMethod method) {
if (method == null) {
return null;
}
try {
ISourceModule sourceModule = method.getSourceModule();
ModuleDeclaration moduleDeclaration = SourceParserUtil
.getModuleDeclaration(sourceModule);
MethodDeclaration methodDeclaration = PHPModelUtils
.getNodeByMethod(moduleDeclaration, method);
if (methodDeclaration instanceof IPHPDocAwareDeclaration) {
return ((IPHPDocAwareDeclaration) methodDeclaration)
.getPHPDoc();
}
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
Logger.logException(e);
}
}
return null;
}
/**
* Returns PHPDoc block associated with the given IType element
*
* @param type
* @return
*/
public static PHPDocBlock getDocBlock(IType type) {
if (type == null) {
return null;
}
try {
ISourceModule sourceModule = type.getSourceModule();
ModuleDeclaration moduleDeclaration = SourceParserUtil
.getModuleDeclaration(sourceModule);
TypeDeclaration typeDeclaration = PHPModelUtils.getNodeByClass(
moduleDeclaration, type);
if (typeDeclaration instanceof IPHPDocAwareDeclaration) {
return ((IPHPDocAwareDeclaration) typeDeclaration).getPHPDoc();
}
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
Logger.logException(e);
}
}
return null;
}
/**
* This method returns field corresponding to its name and the file where it
* was referenced. The field name may contain also the namespace part, like:
* A\B\C or \A\B\C
*
* @param fieldName
* Tye fully qualified field name
* @param sourceModule
* The file where the element is referenced
* @param offset
* The offset where the element is referenced
* @param monitor
* Progress monitor
* @return a list of relevant IField elements, or <code>null</code> in case
* there's no IField found
* @throws ModelException
*/
public static IField[] getFields(String fieldName,
ISourceModule sourceModule, int offset, IProgressMonitor monitor)
throws ModelException {
return getFields(fieldName, sourceModule, offset, null, monitor);
}
/**
* This method returns field corresponding to its name and the file where it
* was referenced. The field name may contain also the namespace part, like:
* A\B\C or \A\B\C
*
* @param fieldName
* Tye fully qualified field name
* @param sourceModule
* The file where the element is referenced
* @param offset
* The offset where the element is referenced
* @param cache
* Model access cache if available
* @param monitor
* Progress monitor
* @return a list of relevant IField elements, or <code>null</code> in case
* there's no IField found
* @throws ModelException
*/
public static IField[] getFields(String fieldName,
ISourceModule sourceModule, int offset, IModelAccessCache cache,
IProgressMonitor monitor) throws ModelException {
if (fieldName == null || fieldName.length() == 0) {
return PhpModelAccess.NULL_FIELDS;
}
if (!fieldName.startsWith("$")) { // variables are not supported by //$NON-NLS-1$
// namespaces in PHP 5.3
String namespace = extractNamespaceName(fieldName, sourceModule,
offset);
fieldName = extractElementName(fieldName);
if (namespace != null) {
if (namespace.length() > 0) {
IField[] fields = getNamespaceField(namespace, fieldName,
true, sourceModule, cache, monitor);
if (fields.length > 0) {
return fields;
}
return PhpModelAccess.NULL_FIELDS;
}
// it's a global reference: \C
} else {
// look for the element in current namespace:
IType currentNamespace = getCurrentNamespace(sourceModule,
offset);
if (currentNamespace != null) {
namespace = currentNamespace.getElementName();
IField[] fields = getNamespaceField(namespace, fieldName,
true, sourceModule, cache, monitor);
if (fields.length > 0) {
return fields;
}
// For functions and constants, PHP will fall back to global
// functions or constants if a namespaced function or
// constant does not exist:
IDLTKSearchScope scope = SearchEngine
.createSearchScope(sourceModule.getScriptProject());
fields = PhpModelAccess.getDefault().findFields(fieldName,
MatchRule.EXACT,
Modifiers.AccConstant | Modifiers.AccGlobal, 0,
scope, null);
Collection<IField> filteredElements = filterElements(
sourceModule, Arrays.asList(fields), cache, monitor);
return (IField[]) filteredElements
.toArray(new IField[filteredElements.size()]);
}
}
}
IDLTKSearchScope scope = SearchEngine.createSearchScope(sourceModule
.getScriptProject());
IField[] fields = PhpModelAccess.getDefault().findFields(fieldName,
MatchRule.EXACT, Modifiers.AccGlobal, 0, scope, null);
Collection<IField> filteredElements = null;
if (fields != null) {
filteredElements = filterElements(sourceModule,
Arrays.asList(fields), cache, monitor);
return (IField[]) filteredElements
.toArray(new IField[filteredElements.size()]);
}
return PhpModelAccess.NULL_FIELDS;
}
/**
* This method returns function corresponding to its name and the file where
* it was referenced. The function name may contain also the namespace part,
* like: A\B\foo() or \A\B\foo()
*
* @param functionName
* The fully qualified function name
* @param sourceModule
* The file where the element is referenced
* @param offset
* The offset where the element is referenced
* @param monitor
* Progress monitor
* @return a list of relevant IMethod elements, or <code>null</code> in case
* there's no IMethod found
* @throws ModelException
*/
public static IMethod[] getFunctions(String functionName,
ISourceModule sourceModule, int offset, IProgressMonitor monitor)
throws ModelException {
return getFunctions(functionName, sourceModule, offset, null, monitor);
}
/**
* This method returns function corresponding to its name and the file where
* it was referenced. The function name may contain also the namespace part,
* like: A\B\foo() or \A\B\foo()
*
* @param functionName
* The fully qualified function name
* @param sourceModule
* The file where the element is referenced
* @param offset
* The offset where the element is referenced
* @param cache
* Temporary model cache instance
* @param monitor
* Progress monitor
* @return a list of relevant IMethod elements, or <code>null</code> in case
* there's no IMethod found
* @throws ModelException
*/
public static IMethod[] getFunctions(String functionName,
ISourceModule sourceModule, int offset, IModelAccessCache cache,
IProgressMonitor monitor) throws ModelException {
if (functionName == null || functionName.length() == 0) {
return PhpModelAccess.NULL_METHODS;
}
String namespace = extractNamespaceName(functionName, sourceModule,
offset);
functionName = extractElementName(functionName);
if (namespace != null) {
if (namespace.length() > 0) {
IMethod[] functions = getNamespaceFunction(namespace,
functionName, true, sourceModule, cache, monitor);
if (functions.length > 0) {
return functions;
}
return PhpModelAccess.NULL_METHODS;
}
// it's a global reference: \foo()
} else {
// look for the element in current namespace:
IType currentNamespace = getCurrentNamespace(sourceModule, offset);
if (currentNamespace != null) {
namespace = currentNamespace.getElementName();
IMethod[] functions = getNamespaceFunction(namespace,
functionName, true, sourceModule, cache, monitor);
if (functions.length > 0) {
return functions;
}
// For functions and constants, PHP will fall back to global
// functions or constants if a namespaced function or constant
// does not exist:
return getGlobalFunctions(sourceModule, functionName, cache,
monitor);
}
}
return getGlobalFunctions(sourceModule, functionName, cache, monitor);
};
private static IMethod[] getGlobalFunctions(ISourceModule sourceModule,
String functionName, IModelAccessCache cache,
IProgressMonitor monitor) {
if (cache != null) {
Collection<IMethod> functions = cache.getGlobalFunctions(
sourceModule, functionName, monitor);
if (functions == null) {
return PhpModelAccess.NULL_METHODS;
}
return (IMethod[]) functions.toArray(new IMethod[functions.size()]);
}
IDLTKSearchScope scope = SearchEngine.createSearchScope(sourceModule
.getScriptProject());
IMethod[] functions = PhpModelAccess.getDefault().findMethods(
functionName, MatchRule.EXACT, Modifiers.AccGlobal, 0, scope,
null);
Collection<IMethod> filteredElements = filterElements(sourceModule,
Arrays.asList(functions), null, monitor);
return (IMethod[]) filteredElements
.toArray(new IMethod[filteredElements.size()]);
}
/**
* This method searches for all fields that where declared in the specified
* method (including global variables that where introduced to this method
* using 'global' keyword)
*
* @param method
* Method to look at
* @param prefix
* Field name
* @param exactName
* Whether the name is exact or it is prefix
* @param monitor
* Progress monitor
*/
public static IModelElement[] getMethodFields(final IMethod method,
final String prefix, final boolean exactName,
IProgressMonitor monitor) {
final List<IField> elements = new LinkedList<IField>();
final Set<String> processedVars = new HashSet<String>();
try {
getMethodFields(method, prefix, exactName, elements, processedVars);
// collect global variables
ModuleDeclaration rootNode = SourceParserUtil
.getModuleDeclaration(method.getSourceModule());
MethodDeclaration methodDeclaration = PHPModelUtils
.getNodeByMethod(rootNode, method);
if (methodDeclaration != null) {
methodDeclaration.traverse(new ASTVisitor() {
public boolean visit(Statement s) throws Exception {
if (s instanceof GlobalStatement) {
GlobalStatement globalStatement = (GlobalStatement) s;
for (Expression e : globalStatement.getVariables()) {
if (e instanceof VariableReference) {
VariableReference varReference = (VariableReference) e;
String varName = varReference.getName();
if (!processedVars.contains(varName)
&& (exactName
&& varName
.equalsIgnoreCase(prefix) || !exactName
&& varName
.toLowerCase()
.startsWith(
prefix.toLowerCase()))) {
elements.add(new FakeField(
(ModelElement) method, varName,
e.sourceStart(), e.sourceEnd()
- e.sourceStart()));
processedVars.add(varName);
}
}
}
}
return super.visit(s);
}
});
}
} catch (Exception e) {
PHPCorePlugin.log(e);
}
return elements.toArray(new IModelElement[elements.size()]);
}
public static void getMethodFields(final IMethod method,
final String prefix, final boolean exactName,
final List<IField> elements, final Set<String> processedVars)
throws ModelException {
IModelElement[] children = method.getChildren();
for (IModelElement child : children) {
if (child.getElementType() == IModelElement.FIELD) {
String elementName = child.getElementName();
if (exactName
&& elementName.equalsIgnoreCase(prefix)
|| !exactName
&& elementName.toLowerCase().startsWith(
prefix.toLowerCase())) {
IField field = (IField) child;
if (!isSameFileExisiting(elements, field)) {
elements.add((IField) child);
processedVars.add(elementName);
}
}
}
}
if (isNestedAnonymousMethod(method)) {
getMethodFields((IMethod) method.getParent().getParent(), prefix,
exactName, elements, processedVars);
}
}
public static boolean isNestedAnonymousMethod(final IMethod method) {
return PHPCoreConstants.ANONYMOUS.equals(method.getElementName())
&& method.getParent() instanceof IField
&& method.getParent().getParent() instanceof IMethod;
};
private static boolean isSameFileExisiting(List<IField> elements,
IField field) {
for (IField current : elements) {
if (isSameField(current, field)) {
return true;
}
}
return false;
}
public static boolean isSameField(IField current, IField field) {
if (!(field instanceof SourceField)) {
return false;
}
if (current == field) {
return true;
}
return current.getElementName().equals(field.getElementName())
&& current.getParent() != null
&& current.getParent().equals(field.getParent());
}
/**
* This method searches for all global fields that where declared in the
* specified file.
*
* @param sourceModule
* Source module to look at
* @param prefix
* Field name
* @param exactName
* Whether the name is exact or it is prefix
* @param monitor
* Progress monitor
*/
public static IField[] getFileFields(final ISourceModule sourceModule,
final String prefix, final boolean exactName,
IProgressMonitor monitor) {
final List<IField> elements = new LinkedList<IField>();
try {
IField[] sourceModuleFields = sourceModule.getFields();
for (IField field : sourceModuleFields) {
String elementName = field.getElementName();
if (exactName
&& elementName.equalsIgnoreCase(prefix)
|| !exactName
&& elementName.toLowerCase().startsWith(
prefix.toLowerCase())) {
elements.add(field);
}
}
} catch (Exception e) {
PHPCorePlugin.log(e);
}
return elements.toArray(new IField[elements.size()]);
}
/**
* This method returns field declared unders specified namespace
*
* @param namespace
* Namespace name
* @param prefix
* Field name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @param sourceModule
* Source module where the field is referenced
* @param monitor
* Progress monitor
* @return field declarated in the specified namespace, or null if there is
* none
* @throws ModelException
*/
public static IField[] getNamespaceField(String namespace, String prefix,
boolean exactName, ISourceModule sourceModule,
IProgressMonitor monitor) throws ModelException {
return getNamespaceField(namespace, prefix, exactName, sourceModule,
null, monitor);
}
/**
* This method returns field declared unders specified namespace
*
* @param namespace
* Namespace name
* @param prefix
* Field name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @param sourceModule
* Source module where the field is referenced
* @param cache
* Model access cache if available
* @param monitor
* Progress monitor
* @return field declarated in the specified namespace, or null if there is
* none
* @throws ModelException
*/
public static IField[] getNamespaceField(String namespace, String prefix,
boolean exactName, ISourceModule sourceModule,
IModelAccessCache cache, IProgressMonitor monitor)
throws ModelException {
IType[] namespaces = getNamespaces(sourceModule, namespace, cache,
monitor);
List<IField> result = new LinkedList<IField>();
for (IType ns : namespaces) {
result.addAll(Arrays.asList(PHPModelUtils.getTypeField(ns, prefix,
exactName)));
}
return (IField[]) result.toArray(new IField[result.size()]);
}
/**
* This method returns method declared unders specified namespace
*
* @param namespace
* Namespace name
* @param prefix
* Function name or prefix
* @param exactName
* Whether the type name is exact or it is prefix
* @param sourceModule
* Source module where the function is referenced
* @param monitor
* Progress monitor
* @throws ModelException
*/
public static IMethod[] getNamespaceFunction(String namespace,
String prefix, boolean exactName, ISourceModule sourceModule,
IProgressMonitor monitor) throws ModelException {
return getNamespaceFunction(namespace, prefix, exactName, sourceModule,
null, monitor);
}
/**
* This method returns method declared unders specified namespace
*
* @param namespace
* Namespace name
* @param prefix
* Function name or prefix
* @param exactName
* Whether the type name is exact or it is prefix
* @param sourceModule
* Source module where the function is referenced
* @param cache
* Model access cache if available
* @param monitor
* Progress monitor
* @throws ModelException
*/
public static IMethod[] getNamespaceFunction(String namespace,
String prefix, boolean exactName, ISourceModule sourceModule,
IModelAccessCache cache, IProgressMonitor monitor)
throws ModelException {
IType[] namespaces = getNamespaces(sourceModule, namespace, cache,
monitor);
List<IMethod> result = new LinkedList<IMethod>();
for (IType ns : namespaces) {
result.addAll(Arrays.asList(PHPModelUtils.getTypeMethod(ns, prefix,
exactName)));
}
return (IMethod[]) result.toArray(new IMethod[result.size()]);
}
/**
* Guess the namespace where the specified element is declared.
*
* @param elementName
* The name of the element, like: \A\B, A\B, namespace\B, \B,
* etc...
* @param sourceModule
* Source module where the element is referenced
* @param offset
* The offset in file where the element is referenced
* @param monitor
* @return model elements of found namespace, otherwise <code>null</code>
* (global namespace)
* @throws ModelException
*/
public static IType[] getNamespaceOf(String elementName,
ISourceModule sourceModule, int offset, IProgressMonitor monitor)
throws ModelException {
return getNamespaceOf(elementName, sourceModule, offset, null, monitor);
}
/**
* Guess the namespace where the specified element is declared.
*
* @param elementName
* The name of the element, like: \A\B, A\B, namespace\B, \B,
* etc...
* @param sourceModule
* Source module where the element is referenced
* @param offset
* The offset in file where the element is referenced
* @param cache
* Model access cache if available
* @param monitor
* @return model elements of found namespace, otherwise <code>null</code>
* (global namespace)
* @throws ModelException
*/
public static IType[] getNamespaceOf(String elementName,
ISourceModule sourceModule, int offset, IModelAccessCache cache,
IProgressMonitor monitor) throws ModelException {
String namespace = extractNamespaceName(elementName, sourceModule,
offset);
if (namespace != null && namespace.length() > 0) {
return getNamespaces(sourceModule, namespace, cache, monitor);
}
return PhpModelAccess.NULL_TYPES;
}
/**
* This method returns type declared unders specified namespace
*
* @param namespace
* Namespace name
* @param prefix
* Type name or prefix
* @param exactName
* Whether the type name is exact or it is prefix
* @param sourceModule
* Source module where the type is referenced
* @param monitor
* Progress monitor
* @return type declarated in the specified namespace, or null if there is
* none
* @throws ModelException
*/
public static IType[] getNamespaceType(String namespace, String prefix,
boolean exactName, ISourceModule sourceModule,
IProgressMonitor monitor, boolean isType) throws ModelException {
return getNamespaceType(namespace, prefix, exactName, sourceModule,
null, monitor, isType);
}
/**
* This method returns type declared unders specified namespace
*
* @param namespace
* Namespace name
* @param prefix
* Type name or prefix
* @param exactName
* Whether the type name is exact or it is prefix
* @param sourceModule
* Source module where the type is referenced
* @param cache
* Model access cache if available
* @param monitor
* Progress monitor
* @param isType
* @return type declarated in the specified namespace, or null if there is
* none
* @throws ModelException
*/
public static IType[] getNamespaceType(String namespace, String prefix,
boolean exactName, ISourceModule sourceModule,
IModelAccessCache cache, IProgressMonitor monitor, boolean isType)
throws ModelException {
IType[] namespaces = getNamespaces(sourceModule, namespace, cache,
monitor);
List<IType> result = new LinkedList<IType>();
for (IType ns : namespaces) {
result.addAll(Arrays.asList(PHPModelUtils.getTypeType(ns, prefix,
exactName, isType)));
}
return (IType[]) result.toArray(new IType[result.size()]);
}
private static IType[] getNamespaces(ISourceModule sourceModule,
String namespaceName, IModelAccessCache cache,
IProgressMonitor monitor) throws ModelException {
if (cache != null) {
Collection<IType> namespaces = cache.getNamespaces(sourceModule,
namespaceName, monitor);
if (namespaces == null) {
return PhpModelAccess.NULL_TYPES;
}
return (IType[]) namespaces.toArray(new IType[namespaces.size()]);
}
IDLTKSearchScope scope = SearchEngine.createSearchScope(sourceModule
.getScriptProject());
IType[] namespaces = PhpModelAccess.getDefault().findTypes(null,
namespaceName, MatchRule.EXACT, Modifiers.AccNameSpace, 0,
scope, monitor);
return namespaces;
}
public static TypeDeclaration getNodeByClass(ModuleDeclaration rootNode,
IType type) throws ModelException {
DeclarationSearcher visitor = new DeclarationSearcher(rootNode, type,
DeclarationType.CLASS);
try {
rootNode.traverse(visitor);
} catch (Exception e) {
if (DLTKCore.DEBUG) {
Logger.logException(e);
}
}
return (TypeDeclaration) visitor.getResult();
}
public static ASTNode getNodeByElement(ModuleDeclaration rootNode,
IModelElement element) throws ModelException {
switch (element.getElementType()) {
case IModelElement.TYPE:
return getNodeByClass(rootNode, (IType) element);
case IModelElement.METHOD:
return getNodeByMethod(rootNode, (IMethod) element);
case IModelElement.FIELD:
return getNodeByField(rootNode, (IField) element);
default:
throw new IllegalArgumentException("Unsupported element type: " //$NON-NLS-1$
+ element.getClass().getName());
}
}
public static ASTNode getNodeByField(ModuleDeclaration rootNode,
IField field) throws ModelException {
DeclarationSearcher visitor = new DeclarationSearcher(rootNode, field,
DeclarationType.FIELD);
try {
rootNode.traverse(visitor);
} catch (Exception e) {
if (DLTKCore.DEBUG) {
Logger.logException(e);
}
}
return (ASTNode) visitor.getResult();
}
public static MethodDeclaration getNodeByMethod(ModuleDeclaration rootNode,
IMethod method) throws ModelException {
DeclarationSearcher visitor = new DeclarationSearcher(rootNode, method,
DeclarationType.METHOD);
try {
rootNode.traverse(visitor);
} catch (Exception e) {
if (DLTKCore.DEBUG) {
Logger.logException(e);
}
}
return (MethodDeclaration) visitor.getResult();
}
/**
* Returns all super classes filtered using file hierarchy
*
* @throws ModelException
*/
public static IType[] getSuperClasses(IType type, ITypeHierarchy hierarchy)
throws ModelException {
if (hierarchy == null) {
if (type.getScriptProject() == null
|| !type.getScriptProject().exists()) {
return EMPTY_TYPES;
} else {
hierarchy = type.newSupertypeHierarchy(null);
}
}
Collection<IType> filtered = filterElements(type.getSourceModule(),
Arrays.asList(hierarchy.getAllSuperclasses(type)), null, null);
return (IType[]) filtered.toArray(new IType[filtered.size()]);
}
/**
* Finds field in the super class hierarchy
*
* @param type
* Class element
* @param prefix
* Field name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @throws CoreException
*/
public static IField[] getSuperTypeHierarchyField(IType type,
String prefix, boolean exactName, IProgressMonitor monitor)
throws CoreException {
return getSuperTypeHierarchyField(type, null, prefix, exactName,
monitor);
}
/**
* Finds field in the super class hierarchy
*
* @param type
* Class element
* @param hierarchy
* Cached type hierarchy (<code>null</code> to build a new one)
* @param prefix
* Field name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @throws CoreException
*/
public static IField[] getSuperTypeHierarchyField(IType type,
ITypeHierarchy hierarchy, String prefix, boolean exactName,
IProgressMonitor monitor) throws CoreException {
IType[] allSuperclasses = getSuperClasses(type, hierarchy);
return getTypesField(allSuperclasses, prefix, exactName);
}
/**
* Finds method in the super class hierarchy
*
* @param type
* Class element
* @param prefix
* Method name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @throws CoreException
*/
public static IMethod[] getSuperTypeHierarchyMethod(IType type,
String prefix, boolean exactName, IProgressMonitor monitor)
throws CoreException {
return getSuperTypeHierarchyMethod(type, null, prefix, exactName,
monitor);
}
/**
* Finds method in the super class hierarchy
*
* @param type
* Class element
* @param hierarchy
* Cached type hierarchy (<code>null</code> to build a new one)
* @param prefix
* Method name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @throws CoreException
*/
public static IMethod[] getSuperTypeHierarchyMethod(IType type,
ITypeHierarchy hierarchy, String prefix, boolean exactName,
IProgressMonitor monitor) throws CoreException {
IType[] allSuperclasses = getSuperClasses(type, hierarchy);
return getTypesMethod(allSuperclasses, prefix, exactName);
}
/**
* Returns the type field element by name
*
* @param type
* Type
* @param prefix
* Field name or prefix
* @param exactName
* Whether the name is exact name or prefix
* @throws ModelException
*/
public static IField[] getTypeField(IType type, String prefix,
boolean exactName) throws ModelException {
List<IField> result = new LinkedList<IField>();
if (type.exists()) {
Set<String> nameSet = new HashSet<String>();
IField[] fields = type.getFields();
for (IField field : fields) {
String elementName = field.getElementName();
if (elementName.startsWith("$")) { //$NON-NLS-1$
nameSet.add(elementName.substring(1));
}
if (elementName.startsWith("$") && !prefix.startsWith("$")) { //$NON-NLS-1$ //$NON-NLS-2$
elementName = elementName.substring(1);
}
if (exactName
&& elementName.equalsIgnoreCase(prefix)
|| !exactName
&& elementName.toLowerCase().startsWith(
prefix.toLowerCase())) {
result.add(field);
}
}
fields = TraitUtils.getTraitFields(type, nameSet);
for (IField field : fields) {
String elementName = field.getElementName();
if (elementName.startsWith("$") && !prefix.startsWith("$")) { //$NON-NLS-1$ //$NON-NLS-2$
elementName = elementName.substring(1);
}
if (exactName
&& elementName.equalsIgnoreCase(prefix)
|| !exactName
&& elementName.toLowerCase().startsWith(
prefix.toLowerCase())) {
result.add(field);
}
}
}
return (IField[]) result.toArray(new IField[result.size()]);
}
/**
* Finds field by name in the class hierarchy
*
* @param type
* Class element
* @param hierarchy
* Cached type hierarchy
* @param prefix
* Field name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @param monitor
* Progress monitor
* @throws CoreException
*/
public static IField[] getTypeHierarchyField(IType type,
ITypeHierarchy hierarchy, String prefix, boolean exactName,
IProgressMonitor monitor) throws CoreException {
if (prefix == null) {
throw new NullPointerException();
}
final List<IField> fields = new LinkedList<IField>();
fields.addAll(Arrays.asList(getTypeField(type, prefix, exactName)));
if (type.getSuperClasses() != null && type.getSuperClasses().length > 0) {
fields.addAll(Arrays.asList(getSuperTypeHierarchyField(type,
hierarchy, prefix, exactName, monitor)));
}
return fields.toArray(new IField[fields.size()]);
}
/**
* Finds field by name in the class hierarchy (including the class itself)
*
* @param type
* Class element
* @param prefix
* Field name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @param monitor
* Progress monitor
* @throws CoreException
*/
public static IField[] getTypeHierarchyField(IType type, String prefix,
boolean exactName, IProgressMonitor monitor) throws CoreException {
return getTypeHierarchyField(type, null, prefix, exactName, monitor);
}
/**
* Finds field documentation by field name in the class hierarchy
*
* @param type
* Class element
* @param name
* Field name
* @param exactName
* Whether the name is exact or it is prefix
* @param monitor
* Progress monitor
* @throws CoreException
*/
public static PHPDocBlock[] getTypeHierarchyFieldDoc(IType type,
String name, boolean exactName, IProgressMonitor monitor)
throws CoreException {
if (name == null) {
throw new NullPointerException();
}
final List<PHPDocBlock> docs = new LinkedList<PHPDocBlock>();
for (IField field : getTypeHierarchyField(type, name, exactName,
monitor)) {
PHPDocBlock docBlock = getDocBlock(field);
if (docBlock != null) {
docs.add(docBlock);
}
}
return docs.toArray(new PHPDocBlock[docs.size()]);
}
/**
* Finds method by name in the class hierarchy (including the class itself)
*
* @param type
* Class element
* @param hierarchy
* Cached type hierarchy
* @param prefix
* Method name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @param monitor
* Progress monitor
* @throws CoreException
*/
public static IMethod[] getTypeHierarchyMethod(IType type,
ITypeHierarchy hierarchy, String prefix, boolean exactName,
IProgressMonitor monitor) throws CoreException {
if (prefix == null) {
throw new NullPointerException();
}
final List<IMethod> methods = new LinkedList<IMethod>();
methods.addAll(Arrays.asList(getTypeMethod(type, prefix, exactName)));
if (type.getSuperClasses() != null && type.getSuperClasses().length > 0) {
methods.addAll(Arrays.asList(getSuperTypeHierarchyMethod(type,
hierarchy, prefix, exactName, monitor)));
}
return methods.toArray(new IMethod[methods.size()]);
}
/**
* Finds the first method by name in the class hierarchy (including the
* class itself)
*
* @param type
* Class element
* @param hierarchy
* Cached type hierarchy
* @param prefix
* Method name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @param monitor
* Progress monitor
* @throws CoreException
*/
public static IMethod[] getFirstTypeHierarchyMethod(IType type,
ITypeHierarchy hierarchy, String prefix, boolean exactName,
IProgressMonitor monitor) throws CoreException {
if (prefix == null) {
throw new NullPointerException();
}
final List<IMethod> methods = new LinkedList<IMethod>();
methods.addAll(Arrays.asList(getTypeMethod(type, prefix, exactName)));
if (type.getSuperClasses() != null && type.getSuperClasses().length > 0
&& methods.size() == 0) {
IType[] allSuperclasses = getSuperClasses(type, hierarchy);
for (IType superClass : allSuperclasses) {
IMethod[] method = getTypeMethod(superClass, prefix, exactName);
if (method != null && method.length > 0) {
methods.addAll(Arrays.asList(method));
break;
}
}
}
return methods.toArray(new IMethod[methods.size()]);
}
/**
* Finds method by name in the class hierarchy (including the class itself)
*
* @param type
* Class element
* @param prefix
* Method name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @param monitor
* Progress monitor
* @throws CoreException
*/
public static IMethod[] getTypeHierarchyMethod(IType type, String prefix,
boolean exactName, IProgressMonitor monitor) throws CoreException {
return getTypeHierarchyMethod(type, null, prefix, exactName, monitor);
}
public static PHPDocBlock[] getTypeHierarchyMethodDoc(IType type,
String prefix, boolean exactName, IProgressMonitor monitor)
throws CoreException {
return getTypeHierarchyMethodDoc(type, null, prefix, exactName, monitor);
}
/**
* Finds method documentation by method name in the class hierarchy
*
* @param type
* Class element
* @param prefix
* Method name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @param monitor
* Progress monitor
* @throws CoreException
*/
public static PHPDocBlock[] getTypeHierarchyMethodDoc(IType type,
ITypeHierarchy hierarchy, String prefix, boolean exactName,
IProgressMonitor monitor) throws CoreException {
if (prefix == null) {
throw new NullPointerException();
}
final List<PHPDocBlock> docs = new LinkedList<PHPDocBlock>();
for (IMethod method : getTypeHierarchyMethod(type, hierarchy, prefix,
exactName, monitor)) {
PHPDocBlock docBlock = getDocBlock(method);
if (docBlock != null) {
docs.add(docBlock);
}
}
return docs.toArray(new PHPDocBlock[docs.size()]);
}
/**
* Returns the type method element by name
*
* @param type
* Type
* @param prefix
* Method name or prefix
* @param exactName
* Whether the name is exact name or prefix
* @throws ModelException
*/
public static IMethod[] getTypeMethod(IType type, String prefix,
boolean exactName) throws ModelException {
List<IMethod> result = new LinkedList<IMethod>();
if (type.exists()) {
Set<String> nameSet = new HashSet<String>();
IMethod[] methods = type.getMethods();
for (IMethod method : methods) {
String elementName = method.getElementName();
nameSet.add(elementName);
if (exactName
&& elementName.equalsIgnoreCase(prefix)
|| !exactName
&& elementName.toLowerCase().startsWith(
prefix.toLowerCase())) {
result.add(method);
}
}
methods = TraitUtils.getTraitMethods(type, nameSet);
for (IMethod method : methods) {
String elementName = method.getElementName();
if (exactName
&& elementName.equalsIgnoreCase(prefix)
|| !exactName
&& elementName.toLowerCase().startsWith(
prefix.toLowerCase())) {
result.add(method);
}
}
}
return (IMethod[]) result.toArray(new IMethod[result.size()]);
}
/**
* This method returns type corresponding to its name and the file where it
* was referenced. The type name may contain also the namespace part, like:
* A\B\C or \A\B\C
*
* @param typeName
* Tye fully qualified type name
* @param sourceModule
* The file where the element is referenced
* @param offset
* The offset where the element is referenced
* @param monitor
* Progress monitor
* @return a list of relevant IType elements, or <code>null</code> in case
* there's no IType found
* @throws ModelException
*/
public static IType[] getTypes(String typeName, ISourceModule sourceModule,
int offset, IProgressMonitor monitor) throws ModelException {
return getTypes(typeName, sourceModule, offset, null, monitor);
}
/**
* This method returns type corresponding to its name and the file where it
* was referenced. The type name may contain also the namespace part, like:
* A\B\C or \A\B\C
*
* @param typeName
* Tye fully qualified type name
* @param sourceModule
* The file where the element is referenced
* @param offset
* The offset where the element is referenced
* @param cache
* Model access cache if available
* @param monitor
* Progress monitor
* @return a list of relevant IType elements, or <code>null</code> in case
* there's no IType found
* @throws ModelException
*/
public static IType[] getTypes(String typeName, ISourceModule sourceModule,
int offset, IModelAccessCache cache, IProgressMonitor monitor)
throws ModelException {
return getTypes(typeName, sourceModule, offset, cache, monitor, true);
}
public static IType[] getTypes(String typeName, ISourceModule sourceModule,
int offset, IModelAccessCache cache, IProgressMonitor monitor,
boolean isType) throws ModelException {
if (typeName == null || typeName.length() == 0) {
return PhpModelAccess.NULL_TYPES;
}
String namespace = extractNamespaceName(typeName, sourceModule, offset);
typeName = extractElementName(typeName);
if (namespace != null) {
if (namespace.length() > 0) {
typeName = getRealName(typeName, sourceModule, offset, typeName);
IType[] types = getNamespaceType(namespace, typeName, true,
sourceModule, cache, monitor, isType);
types = filterType(types, isType);
if (types.length > 0) {
return types;
}
return PhpModelAccess.NULL_TYPES;
}
// it's a global reference: \A
} else {
// look for the element in current namespace:
IType currentNamespace = getCurrentNamespace(sourceModule, offset);
if (currentNamespace != null) {
namespace = currentNamespace.getElementName();
IType[] types = getNamespaceType(namespace, typeName, true,
sourceModule, cache, monitor, isType);
types = filterType(types, isType);
if (types.length > 0) {
return types;
}
}
}
List<IType> result = new ArrayList<IType>();
Collection<IType> types;
if (cache == null) {
IDLTKSearchScope scope = SearchEngine
.createSearchScope(sourceModule.getScriptProject());
IType[] r;
if (isType) {
r = PhpModelAccess.getDefault().findTypes(typeName,
MatchRule.EXACT, 0, 0, scope, null);
} else {
r = PhpModelAccess.getDefault().findTraits(typeName,
MatchRule.EXACT, 0, 0, scope, null);
}
for (IType type : r) {
if (getCurrentNamespace(type) == null) {
result.add(type);
}
}
List<IType> tempList = new ArrayList<IType>(result.size());
tempList.addAll(result);
types = filterElements(sourceModule, tempList, null, monitor);
} else {
if (isType) {
types = cache.getTypes(sourceModule, typeName, null, monitor);
} else {
types = cache.getTraits(sourceModule, typeName, null, monitor);
}
if (types == null) {
return PhpModelAccess.NULL_TYPES;
}
}
result.clear();
for (IType type : types) {
if (getCurrentNamespace(type) == null) {
result.add(type);
}
}
return (IType[]) result.toArray(new IType[result.size()]);
}
private static IType[] filterType(IType[] types, boolean isType) {
// TODO Auto-generated method stub
if (isType) {
} else {
}
return types;
}
public static IType[] getTraits(String typeName,
ISourceModule sourceModule, int offset, IModelAccessCache cache,
IProgressMonitor monitor) throws ModelException {
return getTypes(typeName, sourceModule, offset, cache, monitor, false);
}
/**
* Finds field in the list of given types
*
* @param types
* List of types
* @param prefix
* Field name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @throws ModelException
*/
public static IField[] getTypesField(IType[] types, String prefix,
boolean exactName) throws ModelException {
List<IField> result = new LinkedList<IField>();
for (IType type : types) {
result.addAll(Arrays.asList(getTypeField(type, prefix, exactName)));
}
return (IField[]) result.toArray(new IField[result.size()]);
}
/**
* Finds method in the list of given types
*
* @param types
* List of types
* @param prefix
* Method name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @throws ModelException
*/
public static IMethod[] getTypesMethod(IType[] types, String prefix,
boolean exactName) throws ModelException {
List<IMethod> result = new LinkedList<IMethod>();
for (IType type : types) {
result.addAll(Arrays.asList(getTypeMethod(type, prefix, exactName)));
}
return (IMethod[]) result.toArray(new IMethod[result.size()]);
}
/**
* Returns the type inner type element by name
*
* @param type
* Type
* @param prefix
* Enclosed type name or prefix
* @param exactName
* Whether the name is exact or it is prefix
* @param isType
* @throws ModelException
*/
public static IType[] getTypeType(IType type, String prefix,
boolean exactName, boolean isType) throws ModelException {
List<IType> result = new LinkedList<IType>();
IType[] types = type.getTypes();
for (IType t : types) {
if (isType && PHPFlags.isTrait(t.getFlags()) || !isType
&& !PHPFlags.isTrait(t.getFlags())) {
continue;
}
String elementName = t.getElementName();
if (exactName
&& elementName.equalsIgnoreCase(prefix)
|| !exactName
&& elementName.toLowerCase().startsWith(
prefix.toLowerCase())) {
result.add(t);
}
}
return (IType[]) result.toArray(new IType[result.size()]);
}
public static IType[] getTypeType(IType type, String prefix,
boolean exactName) throws ModelException {
return getTypeType(type, prefix, exactName, true);
}
/**
* Returns methods that must be overridden in first non-abstract class in
* hierarchy.
*
* @param type
* Type to start the search from
* @param monitor
* Progress monitor
* @return unimplemented methods
* @throws ModelException
*/
public static IMethod[] getUnimplementedMethods(IType type,
IProgressMonitor monitor) throws ModelException {
return getUnimplementedMethods(type, null, monitor);
}
/**
* Returns methods that must be overridden in first non-abstract class in
* hierarchy.
*
* @param type
* Type to start the search from
* @param cache
* Temporary model cache instance
* @param monitor
* Progress monitor
* @return unimplemented methods
* @throws ModelException
*/
public static IMethod[] getUnimplementedMethods(IType type,
IModelAccessCache cache, IProgressMonitor monitor)
throws ModelException {
HashMap<String, IMethod> abstractMethods = new HashMap<String, IMethod>();
HashSet<String> nonAbstractMethods = new HashSet<String>();
internalGetUnimplementedMethods(type, nonAbstractMethods,
abstractMethods, new HashSet<String>(), cache, monitor, true);
for (String methodName : nonAbstractMethods) {
abstractMethods.remove(methodName);
}
Collection<IMethod> unimplementedMethods = abstractMethods.values();
return (IMethod[]) unimplementedMethods
.toArray(new IMethod[unimplementedMethods.size()]);
}
private static void internalGetUnimplementedMethods(IType type,
HashSet<String> nonAbstractMethods,
HashMap<String, IMethod> abstractMethods,
Set<String> processedTypes, IModelAccessCache cache,
IProgressMonitor monitor, boolean checkConstructor)
throws ModelException {
int typeFlags = type.getFlags();
IMethod[] methods = getTypeMethod(type, "", false); //$NON-NLS-1$
for (IMethod method : methods) {
String methodName = method.getElementName();
int methodFlags = method.getFlags();
boolean isAbstract = PHPFlags.isAbstract(methodFlags);
if (/* !PHPFlags.isInterface(typeFlags)&& */isConstructor(method)) {
if (checkConstructor) {
checkConstructor = false;
} else {
continue;
}
}
if (isAbstract || PHPFlags.isInterface(typeFlags)) {
if (!abstractMethods.containsKey(methodName)) {
abstractMethods.put(methodName, method);
}
} else if (!isAbstract) {
nonAbstractMethods.add(methodName);
}
}
String[] superClasses = type.getSuperClasses();
if (superClasses != null) {
for (String superClass : superClasses) {
if (!processedTypes.add(superClass)) {
continue;
}
Collection<IType> types = null;
if (cache == null) {
IDLTKSearchScope scope = SearchEngine
.createSearchScope(type.getScriptProject());
IType[] superTypes = PhpModelAccess.getDefault().findTypes(
superClass, MatchRule.EXACT, 0,
Modifiers.AccNameSpace, scope, null);
types = fileNetworkFilter(type.getSourceModule(),
Arrays.asList(superTypes), null, monitor);
} else {
String namespaceName = null;
int i = superClass
.lastIndexOf(NamespaceReference.NAMESPACE_SEPARATOR);
if (i != -1) {
namespaceName = superClass.substring(0, i);
superClass = superClass.substring(i + 1);
}
types = cache.getClassesOrInterfaces(
type.getSourceModule(), superClass, namespaceName,
monitor);
}
if (types != null) {
for (IType superType : types) {
internalGetUnimplementedMethods(superType,
nonAbstractMethods, abstractMethods,
processedTypes, cache, monitor,
checkConstructor);
}
}
}
}
}
public static boolean isConstructor(IMethod method) {
String methodName = method.getElementName();
if (methodName.equals("__construct") //$NON-NLS-1$
|| methodName
.equals(method.getDeclaringType().getElementName())) {
return true;
}
return false;
}
/**
*
* @param type
* the given type
* @return if the given type has static member
*/
public static boolean hasStaticOrConstMember(IType type) {
try {
if (PHPFlags.isNamespace(type.getFlags())) {
return false;
}
ITypeHierarchy hierarchy = type.newSupertypeHierarchy(null);
IModelElement[] members = PHPModelUtils.getTypeHierarchyField(type,
hierarchy, "", false, null); //$NON-NLS-1$
if (hasStaticOrConstMember(members)) {
return true;
}
members = PHPModelUtils.getTypeHierarchyMethod(type, hierarchy, "", //$NON-NLS-1$
false, null);
if (hasStaticOrConstMember(members)) {
return true;
}
} catch (ModelException e) {
PHPCorePlugin.log(e);
} catch (CoreException e) {
PHPCorePlugin.log(e);
}
return false;
}
public static boolean hasStaticOrConstMember(IModelElement[] elements)
throws ModelException {
for (int i = 0; i < elements.length; i++) {
IModelElement modelElement = elements[i];
if (modelElement instanceof IMember) {
IMember member = (IMember) modelElement;
int flags = member.getFlags();
if (Flags.isStatic(flags)
|| ((modelElement instanceof IField) && Flags
.isFinal(flags))) {
return true;
}
}
}
return false;
}
public static Map<String, UsePart> getAliasToNSMap(final String prefix,
ModuleDeclaration moduleDeclaration, final int offset,
IType namespace, final boolean exactMatch) {
final Map<String, UsePart> result = new HashMap<String, UsePart>();
try {
int start = 0;
if (namespace != null) {
start = namespace.getSourceRange().getOffset();
}
final int searchStart = start;
moduleDeclaration.traverse(new ASTVisitor() {
public boolean visit(Statement s) throws Exception {
if (s instanceof UseStatement) {
UseStatement useStatement = (UseStatement) s;
for (UsePart usePart : useStatement.getParts()) {
if (usePart.getAlias() != null
&& usePart.getAlias().getName() != null) {
String name = usePart.getAlias().getName();
if (CodeAssistUtils.startsWithIgnoreCase(name,
prefix)) {
result.put(name, usePart);
}
} else {
String name = usePart.getNamespace()
.getFullyQualifiedName();
int index = name
.lastIndexOf(NamespaceReference.NAMESPACE_SEPARATOR);
if (index >= 0) {
name = name.substring(index + 1);
}
if (exactMatch && name.equals(prefix)
|| !exactMatch
&& name.startsWith(prefix)) {
result.put(name, usePart);
}
}
}
}
return visitGeneral(s);
}
public boolean visitGeneral(ASTNode node) throws Exception {
if (node.sourceStart() > offset
|| node.sourceEnd() < searchStart) {
return false;
}
return super.visitGeneral(node);
}
});
} catch (Exception e) {
Logger.logException(e);
}
return result;
}
public static String getClassNameForNewStatement(
TextSequence newClassStatementText, PHPVersion phpVersion) {
if (phpVersion.isGreaterThan(PHPVersion.PHP5_3)) {
// TextSequence newClassStatementText =
// statementText.subTextSequence(
// functionNameStart + 1, propertyEndPosition - 1);
String newClassName = newClassStatementText.toString().trim();
if (newClassName.startsWith("new") && newClassName.endsWith(")")) { //$NON-NLS-1$ //$NON-NLS-2$
int newClassNameEnd = getFunctionNameEndOffset(
newClassStatementText,
newClassStatementText.length() - 1);
int newClassNameStart = PHPTextSequenceUtilities
.readIdentifierStartIndex(phpVersion,
newClassStatementText, newClassNameEnd, false);
if (newClassNameStart > 3
&& newClassNameStart < newClassNameEnd) {// should have
// blank
// chars
// after
// 'new'
newClassName = newClassStatementText.subSequence(
newClassNameStart, newClassNameEnd).toString();
return newClassName;
}
} else if (newClassName.startsWith("new")) { //$NON-NLS-1$
return newClassName.substring(3).trim();
}
}
return null;
}
/**
* this function searches the sequence from the right closing bracket ")"
* and finding the position of the left "(" the offset has to be the offset
* of the "("
*/
public static int getFunctionNameEndOffset(TextSequence statementText,
int offset) {
if (statementText.charAt(offset) != ')') {
return 0;
}
int currChar = offset;
int bracketsNum = 1;
char inStringMode = 0;
while (bracketsNum != 0 && currChar >= 0) {
currChar--;
// get the current char
final char charAt = statementText.charAt(currChar);
// if it is string close / open - update state
if (charAt == '\'' || charAt == '"') {
inStringMode = inStringMode == 0 ? charAt
: inStringMode == charAt ? 0 : inStringMode;
}
if (inStringMode != 0)
continue;
if (charAt == ')') {
bracketsNum++;
} else if (charAt == '(') {
bracketsNum--;
}
}
return currChar;
}
public static String getFullName(IType declaringType) {
try {
return getFullName(declaringType.getElementName(),
declaringType.getSourceModule(), declaringType
.getSourceRange().getOffset());
} catch (ModelException e) {
return declaringType.getElementName();
}
}
public static String getFullName(String typeName,
ISourceModule sourceModule, final int offset) {
String namespace = extractNamespaceName(typeName, sourceModule, offset);
typeName = extractElementName(typeName);
if (namespace != null) {
if (namespace.length() > 0) {
typeName = getRealName(typeName, sourceModule, offset, typeName);
typeName = namespace + NamespaceReference.NAMESPACE_SEPARATOR
+ typeName;
}
} else {
// look for the element in current namespace:
IType currentNamespace = getCurrentNamespace(sourceModule, offset);
if (currentNamespace != null) {
namespace = currentNamespace.getElementName();
typeName = namespace + NamespaceReference.NAMESPACE_SEPARATOR
+ typeName;
}
}
return typeName;
}
public static String getFullName(NamespaceName namespaceName) {
StringBuffer sb = new StringBuffer();
if (namespaceName.isGlobal()) {
sb.append(NamespaceReference.NAMESPACE_SEPARATOR);
}
List<Identifier> segments = namespaceName.segments();
for (Identifier identifier : segments) {
if (sb.length() == 0 && namespaceName.isGlobal()) {
sb.append(NamespaceReference.NAMESPACE_SEPARATOR);
} else if (sb.length() > 0) {
sb.append(NamespaceReference.NAMESPACE_SEPARATOR);
}
sb.append(identifier.getName());
}
return sb.toString();
}
public static String getLineSeparator(IProject project) {
String lineSeparator = null;
if (project != null) {
lineSeparator = Platform.getPreferencesService().getString(
Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null,
new IScopeContext[] { new ProjectScope(project) });
}
if (lineSeparator == null) {
lineSeparator = Platform.getPreferencesService().getString(
Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null,
new IScopeContext[] { InstanceScope.INSTANCE });
}
if (lineSeparator == null) {
lineSeparator = System.getProperty(Platform.PREF_LINE_SEPARATOR);
}
return lineSeparator;
}
public static boolean isInUseTraitStatement(ModuleDeclaration rootNode,
final int offset) {
final boolean[] found = new boolean[1];
found[0] = false;
try {
rootNode.traverse(new PHPASTVisitor() {
public boolean visit(TraitUseStatement s) throws Exception {
if (s.sourceStart() <= offset && s.sourceEnd() >= offset) {
found[0] = true;
}
return false;
}
@Override
public boolean visitGeneral(ASTNode node) throws Exception {
if (node.sourceEnd() < offset
|| node.sourceStart() > offset) {
return false;
}
return super.visitGeneral(node);
}
});
} catch (Exception e) {
e.printStackTrace();
}
return found[0];
}
/**
* Strips single or double quotes from the start and from the end of the
* given string
*
* @param name
* String
* @return
*/
public 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;
}
public static boolean isQuotesString(String name) {
int len = name.length();
if (len > 1
&& (name.charAt(0) == '\'' && name.charAt(len - 1) == '\'' || name
.charAt(0) == '"' && name.charAt(len - 1) == '"')) {
return true;
}
return false;
}
public static IModelElement[] getTypeInString(ISourceModule sourceModule,
IRegion wordRegion) {
IModelElement[] elements = null;
ModuleDeclaration parsedUnit = SourceParserUtil.getModuleDeclaration(
sourceModule, null);
ASTNode node = ASTUtils.findMinimalNode(parsedUnit,
wordRegion.getOffset(),
wordRegion.getOffset() + wordRegion.getLength());
if (node instanceof Scalar) {
Scalar scalar = (Scalar) node;
if (PHPModelUtils.isQuotesString(scalar.getValue())
&& scalar.getScalarType() == Scalar.TYPE_STRING) {
try {
elements = PHPModelUtils.getTypes(
PHPModelUtils.stripQuotes(scalar.getValue()),
sourceModule, scalar.sourceStart(), null, null);
} catch (Exception e) {
}
}
}
return elements;
}
}