/**
* Aptana Studio
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license-epl.html included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.editor.php.internal.indexer.language;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org2.eclipse.php.internal.core.PHPVersion;
import org2.eclipse.php.internal.core.documentModel.phpElementData.IPHPDocBlock;
import org2.eclipse.php.internal.core.documentModel.phpElementData.IPHPDocTag;
import org2.eclipse.php.internal.core.documentModel.phpElementData.PHPDocBlockImp;
import com.aptana.core.logging.IdeLog;
import com.aptana.core.util.CollectionsUtil;
import com.aptana.core.util.StringUtil;
import com.aptana.editor.php.PHPEditorPlugin;
import com.aptana.editor.php.epl.PHPEplPlugin;
import com.aptana.editor.php.indexer.IElementsIndex;
import com.aptana.editor.php.internal.parser.PHPParser;
import com.aptana.editor.php.internal.parser.nodes.IPHPParseNode;
import com.aptana.editor.php.internal.parser.nodes.PHPBaseParseNode;
import com.aptana.editor.php.internal.parser.nodes.PHPClassParseNode;
import com.aptana.editor.php.internal.parser.nodes.PHPConstantNode;
import com.aptana.editor.php.internal.parser.nodes.PHPFunctionParseNode;
import com.aptana.editor.php.internal.parser.nodes.PHPVariableParseNode;
import com.aptana.parsing.ast.IParseNode;
/**
* @author Pavel Petrochenko
*/
public final class PHPBuiltins
{
public static final String LANGUAGE_LIBRARY_PATH_BASE = "Resources/language/php"; //$NON-NLS-1$
public static final String PHP4_LANGUAGE_LIBRARY_PATH = LANGUAGE_LIBRARY_PATH_BASE + "4"; //$NON-NLS-1$
public static final String PHP5_LANGUAGE_LIBRARY_PATH = LANGUAGE_LIBRARY_PATH_BASE + "5"; //$NON-NLS-1$
public static final String PHP53_LANGUAGE_LIBRARY_PATH = LANGUAGE_LIBRARY_PATH_BASE + "5.3"; //$NON-NLS-1$
public static final String PHP54_LANGUAGE_LIBRARY_PATH = LANGUAGE_LIBRARY_PATH_BASE + "5.4"; //$NON-NLS-1$
private static final int INITIAL_CAPACITY = 5000;
private static final IPHPDocTag[] NO_TAGS = new IPHPDocTag[0];
private static final PHPBuiltins instance = new PHPBuiltins();
@SuppressWarnings("nls")
private static final Set<String> PHP4_RESTRICTED = CollectionsUtil.newSet("namespace", "using", "goto", "use");
private Object mutex = new Object();
private Map<PHPVersion, Set<String>> phpNames;
// Holds a function name map to the resource name that contains it
private Map<String, String> builtInFunctions = new HashMap<String, String>();
// Holds a Class/Constant name map to the resource name that contains it
private Map<String, String> builtInClasses = new HashMap<String, String>();
private Map<String, String> builtInConstants = new HashMap<String, String>();
private SortedSet<Object> builtins;
private boolean initializing;
private void addKeywords()
{
// additions after reviewing PHP manuals
addKeyword("cfunction", "cfunction");//$NON-NLS-1$ //$NON-NLS-2$
// reserved class and method names
addKeyword("stdClass", "stdClass");//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__construct", "__construct", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__destruct", "__destruct", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__call", "__call", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__callStatic", "__callStatic", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__get", "__get", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__set", "__set", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__isset", "__isset", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__unset", "__unset", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__sleep", "__sleep", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__wakeup", "__wakeup", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__toString", "__toString", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__invoke", "__invoke", PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__set_state", "__set_state", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
addMagicMethod("__clone", "__clone", PHPVersion.PHP5, PHPVersion.PHP5_3);//$NON-NLS-1$ //$NON-NLS-2$
// magic constants (PHP 5.x < 5.3)
addMagicConstant("__LINE__", "__LINE__", PHPVersion.PHP5, PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addMagicConstant("__FILE__", "__FILE__", PHPVersion.PHP5, PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addMagicConstant("__FUNCTION__", "__FUNCTION__", PHPVersion.PHP5, PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addMagicConstant("__CLASS__", "__CLASS__", PHPVersion.PHP5, PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addMagicConstant("__METHOD__", "__METHOD__", PHPVersion.PHP5, PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
// Super Globals
addSuperGlobal("$PHP_SELF", "$PHP_SELF");//$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$GLOBALS", "$GLOBALS");//$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$_SERVER", "$_SERVER");//$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$_GET", "$_GET"); //$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$_POST", "$_POST"); //$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$_COOKIE", "$_COOKIE"); //$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$_FILES", "$_FILES"); //$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$_ENV", "$_ENV"); //$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$_REQUEST", "$_REQUEST"); //$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$_SESSION", "$_SESSION"); //$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$HTTP_POST_VARS", "$HTTP_POST_VARS"); //$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$HTTP_GET_VARS", "$HTTP_GET_VARS"); //$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$HTTP_ENV_VARS", "$HTTP_ENV_VARS"); //$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$HTTP_SERVER_VARS", "$HTTP_SERVER_VARS"); //$NON-NLS-1$ //$NON-NLS-2$
addSuperGlobal("$HTTP_COOKIE_VARS", "$HTTP_COOKIE_VARS"); //$NON-NLS-1$ //$NON-NLS-2$
// ///////////////////////////////////
addKeyword("abstract", "abstract", PHPVersion.PHP5, PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("array", "array"); //$NON-NLS-1$ //$NON-NLS-2$
addBuiltin("KEYWORD", "ARRAY_CAST", "Array Cast"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addKeyword("as", "as"); //$NON-NLS-1$ //$NON-NLS-2$
addBuiltin("KEYWORD", "BOOL_CAST", "Boolean Cast"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addKeyword("break", "break"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("case", "case"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("catch", "catch"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("class", "class"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("clone", "clone"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("const", "const"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("continue", "continue"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("declare", "declare"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("default", "default"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("do", "do"); //$NON-NLS-1$ //$NON-NLS-2$
addBuiltin("KEYWORD", "DOUBLE_CAST", "Double Cast"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addKeyword("echo", "echo"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("else", "else"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("elseif", "elseif"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("empty", "empty"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("enddeclare", "enddeclare"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("endfor", "endfor"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("endforeach", "endforeach"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("endif", "endif"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("endswitch", "endswitch"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("endwhile", "endwhile"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("eval", "eval"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("exception", "exception"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("exit", "exit"); //$NON-NLS-1$ //$NON-NLS-2$
addBuiltin("die", "EXIT", "exit"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addKeyword("extends", "extends"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("final", "final", PHPVersion.PHP5, PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("for", "for"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("foreach", "foreach"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("function", "function"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("global", "global"); //$NON-NLS-1$ //$NON-NLS-2$
addBuiltin("__halt_compiler", "HALT_COMPILER", "Halt compiler"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addKeyword("if", "if"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("implements", "implements"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("include", "include"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("include_once", "include_once"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("instanceof", "instanceof"); //$NON-NLS-1$ //$NON-NLS-2$
addBuiltin("KEYWORD", "INT_CAST", "Integer Cast"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addKeyword("interface", "interface", PHPVersion.PHP5, PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("isset", "isset"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("list", "list"); //$NON-NLS-1$ //$NON-NLS-2$
addBuiltin("and", "LOGICAL_AND", "Logical and"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addBuiltin("or", "LOGICAL_OR", "Logical or"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addBuiltin("xor", "LOGICAL_XOR", "Logical xor"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addKeyword("new", "new"); //$NON-NLS-1$ //$NON-NLS-2$
addBuiltin("KEYWORD", "OBJECT_CAST", "Object Cast"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addKeyword("print", "print"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("private", "private", PHPVersion.PHP5, PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("protected", "protected", PHPVersion.PHP5, PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("public", "public", PHPVersion.PHP5, PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("require", "require"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("require_once", "require_once"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("return", "return"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("static", "static"); //$NON-NLS-1$ //$NON-NLS-2$
addBuiltin("KEYWORD", "STRING_CAST", "String Cast"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addKeyword("switch", "switch"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("throw", "throw"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("try", "try"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("unset", "unset"); //$NON-NLS-1$ //$NON-NLS-2$
addBuiltin("KEYWORD", "UNSET_CAST", "Unset Case"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
addKeyword("var", "var"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("while", "while"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("namespace", "namespace", PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("goto", "goto", PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("use", "use", PHPVersion.PHP5_3); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("trait", "trait", PHPVersion.PHP5_4); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("callable", "callable", PHPVersion.PHP5_4); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("insteadof", "insteadof", PHPVersion.PHP5_4); //$NON-NLS-1$ //$NON-NLS-2$
// not in Zend's lexer grammar
addKeyword("false", "false"); //$NON-NLS-1$ //$NON-NLS-2$
//addKeyword("from", "from"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("null", "null"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("old_function", "old_function"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("parent", "parent"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("php_user_filter", "php_user_filter"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("$this", "$this"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("self", "self"); //$NON-NLS-1$ //$NON-NLS-2$
addKeyword("true", "true"); //$NON-NLS-1$ //$NON-NLS-2$
// Note: Predefined constants are parsed from the built-in php scripts.
}
/**
* Adds built-in. By default, the type of node that will be added is IPHPParseNode.KEYWORD_NODE.
*
* @param string
* @param nodeName
* @param description
* @param phpVersions
* An optional array of supported php-versions. In case none is passed, the built-in will be added to all
* versions
* @see PHPBuiltins#addBuiltin(String, String, String, short, PHPVersion...)
*/
private void addBuiltin(String string, String nodeName, String description, PHPVersion... phpVersions)
{
addBuiltin(string, nodeName, description, IPHPParseNode.KEYWORD_NODE, phpVersions);
}
/**
* Adds built-in.
*
* @param string
* @param nodeName
* @param description
* @param nodeType
* @param phpVersions
* An optional array of supported php-versions. In case none is passed, the built-in will be added to all
* versions
*/
private void addBuiltin(String string, String nodeName, String description, short nodeType,
PHPVersion... phpVersions)
{
PHPBaseParseNode node = new PHPBaseParseNode(nodeType, 0, -1, -1, nodeName);
builtins.add(node);
node.setDocumentation(new PHPDocBlockImp(description, StringUtil.EMPTY, NO_TAGS, 0));
if (phpVersions != null && phpVersions.length > 0)
{
for (PHPVersion version : phpVersions)
{
phpNames.get(version).add(string);
}
}
else
{
// add to all versions
for (PHPVersion version : EnumSet.allOf(PHPVersion.class))
{
phpNames.get(version).add(string);
}
}
}
/**
* Adds keyword.
*
* @param string
* @param nodeName
*/
private void addKeyword(String string, String nodeName, PHPVersion... phpVersions)
{
addBuiltin(string, nodeName, MessageFormat.format(Messages.KEYWORD_LABEL, nodeName), phpVersions);
}
/**
* Adds a magic constant (see http://us.php.net/manual/en/language.constants.predefined.php).
*
* @param string
* @param nodeName
*/
private void addMagicConstant(String string, String nodeName, PHPVersion... phpVersions)
{
addBuiltin(string, nodeName, MessageFormat.format(Messages.MAGIC_CONSTANT_LABEL, nodeName), phpVersions);
}
/**
* Adds a magic method (see http://us.php.net/manual/en/language.oop5.magic.php).
*
* @param string
* @param nodeName
*/
private void addMagicMethod(String string, String nodeName, PHPVersion... phpVersions)
{
addBuiltin(string, nodeName, MessageFormat.format(Messages.MAGIC_METHOD_LABEL, nodeName), phpVersions);
}
/**
* Adds a predefined constant (see http://us.php.net/manual/en/reserved.constants.php).
*
* @param string
* @param nodeName
*/
@SuppressWarnings("unused")
private void addPredefinedConstant(String string, String nodeName, PHPVersion phpVersion)
{
addBuiltin(string, nodeName, MessageFormat.format(Messages.PREDEFINED_CONSTANT_LABEL, nodeName), phpVersion);
}
/**
* Adds keyword.
*
* @param string
* @param nodeName
*/
private void addSuperGlobal(String string, String nodeName)
{
// add the super-globals as constants.
addBuiltin(string, nodeName, MessageFormat.format(Messages.SUPERGLOBAL_LABEL, nodeName),
IPHPParseNode.CONST_NODE);
}
/**
* Returns the Built-in PHP index. This method will no block, and a Job will be started to collect the built-ins.
*
* @return builtins object
*/
public Collection<Object> getBuiltins()
{
if (builtins == null)
{
synchronized (mutex)
{
if (initializing)
{
return null;
}
}
Job parseBuiltins = new Job(Messages.PHPBuiltins_indexingLibraries)
{
protected IStatus run(IProgressMonitor monitor)
{
clean(monitor);
return Status.OK_STATUS;
}
};
parseBuiltins.setPriority(Job.BUILD);
parseBuiltins.schedule();
}
return builtins;
}
/**
* Returns <code>true</code> in case the given parse-node name exists in the built-in PHP name elements for the
* given version.
*
* @param version
* @param node
* @return <code>true</code> in case the given parse-node name exists in the built-ins; <code>false</code>
* otherwise.
*/
public boolean existsIn(PHPVersion version, IPHPParseNode node)
{
if (node == null || version == null)
{
return false;
}
if (version.equals(PHPVersion.PHP4) && node.getClass() == PHPBaseParseNode.class)
{
if (PHP4_RESTRICTED.contains(node.getNodeName()))
{
return false;
}
}
return phpNames.get(version).contains(node.getNodeName());
}
public boolean isBuiltinFunction(String name)
{
return builtInFunctions.containsKey(name);
}
public boolean isBuiltinClassOrConstant(String name)
{
return builtInClasses.containsKey(name) || builtInConstants.containsKey(name);
}
public boolean isBuiltinConstant(String name)
{
return builtInConstants.containsKey(name);
}
public boolean isBuiltinClass(String name)
{
return builtInClasses.containsKey(name);
}
/**
* Returns an input stream for the built-in resource that is associated to the given entry. With this method, we
* make sure that the appropriate resource is read for the entry (since we have multiple built-ins).
*
* @param entry
* The name/path of the PHP entry.
* @return A associated built-in stream; Null, if non is located.
* @throws IOException
* In case the steam could not be opened.
*/
public InputStream getBuiltinResourceStream(String entry) throws IOException
{
String path = null;
if (isBuiltinFunction(entry))
{
path = builtInFunctions.get(entry);
}
else if (isBuiltinClassOrConstant(entry))
{
path = builtInClasses.get(entry);
if (path == null)
{
path = builtInConstants.get(entry);
}
}
if (path != null)
{
try
{
URL url = new URL(path);
return url.openStream();
}
catch (MalformedURLException e)
{
IdeLog.logWarning(PHPEditorPlugin.getDefault(),
"Error retrieving the built-in resource", e, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$
}
}
return null;
}
/**
* Built-ins initialization entry point.
*
* @param monitor
*/
private void initBuiltins(IProgressMonitor monitor)
{
try
{
IdeLog.logInfo(PHPEditorPlugin.getDefault(),
"Indexing the PHP API libraries...", null, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$
this.builtins = new TreeSet<Object>(new Comparator<Object>()
{
public int compare(Object arg0, Object arg1)
{
PHPBaseParseNode node0 = (PHPBaseParseNode) arg0;
PHPBaseParseNode node1 = (PHPBaseParseNode) arg1;
if (node0.getClass() == node1.getClass())
{
return node0.getNodeName().toLowerCase().compareTo(node1.getNodeName().toLowerCase());
}
return node0.getNodeName().compareTo(node1.getNodeName());
}
});
Map<Object, Object> builtins = new HashMap<Object, Object>(INITIAL_CAPACITY);
long start = System.currentTimeMillis();
for (PHPVersion version : EnumSet.allOf(PHPVersion.class))
{
long timeMillis = System.currentTimeMillis();
monitor.setTaskName(MessageFormat.format(Messages.PHPBuiltins_languageSupportTaskName,
version.getAlias()));
initPHPBuiltins(version, builtins);
if (PHPEditorPlugin.INDEXER_DEBUG)
{
IdeLog.logInfo(PHPEditorPlugin.getDefault(),
MessageFormat.format("Parsed {0} built-ins ({1}ms)", version.getAlias(), //$NON-NLS-1$
(System.currentTimeMillis() - timeMillis)), null, PHPEditorPlugin.INDEXER_SCOPE);
timeMillis = System.currentTimeMillis();
}
}
this.builtins.addAll(builtins.values());
addKeywords();
IdeLog.logInfo(
PHPEditorPlugin.getDefault(),
MessageFormat.format("Loaded all PHP built-ins ({0}ms)", (System.currentTimeMillis() - start)), null, //$NON-NLS-1$
PHPEditorPlugin.INDEXER_SCOPE);
}
catch (Throwable t)
{
IdeLog.logError(PHPEditorPlugin.getDefault(), "Error loading the PHP Built-in API", t); //$NON-NLS-1$
}
}
/**
* Initialized the built-ins for a given {@link PHPVersion}.
*
* @param version
* A {@link PHPVersion}
* @param builtins
* A map that will be loaded with the detected built-ins.
*/
private void initPHPBuiltins(PHPVersion version, Map<Object, Object> builtins)
{
PHPParser parser = new PHPParser(version, false);
try
{
URL[] urls = getBuiltinsURLs(getLibraryPath(version));
for (URL url : urls)
{
try
{
IParseNode parseNode = parser.parse(url.openStream());
Set<String> names = phpNames.get(version);
for (IParseNode node : parseNode)
{
String name = node.getNameNode().getName().intern();
names.add(name);
if (node instanceof PHPFunctionParseNode)
{
builtInFunctions.put(name, url.toString().intern());
}
else
{
addBuiltinClassOrConstant(node, url);
}
// Since the constant nodes are inserted to the
// built-ins directly, don't deal with them here.
// (they were already dealt with on the
// addBuiltinClassOrConstant call above)
if (!(node instanceof PHPConstantNode))
{
builtins.put(name, node);
}
}
}
catch (Exception e)
{
IdeLog.logError(PHPEditorPlugin.getDefault(),
"Error loading the built-in PHP API for " + url.getFile(), e); //$NON-NLS-1$
}
}
}
catch (Exception e)
{
IdeLog.logError(PHPEditorPlugin.getDefault(), "Error loading the built-in PHP API.", e); //$NON-NLS-1$
}
}
/**
* Returns the library path for a given {@link PHPVersion}.
*
* @param version
* @return A library path.
*/
private String getLibraryPath(PHPVersion version)
{
switch (version)
{
case PHP4:
return PHP4_LANGUAGE_LIBRARY_PATH;
case PHP5:
return PHP5_LANGUAGE_LIBRARY_PATH;
case PHP5_3:
return PHP53_LANGUAGE_LIBRARY_PATH;
case PHP5_4:
return PHP54_LANGUAGE_LIBRARY_PATH;
}
IdeLog.logError(PHPEditorPlugin.getDefault(), "Unknows PHPVersion " + version); //$NON-NLS-1$
return null;
}
/**
* Adds a built-in class, variable or constant to the hash of built-ins. This method will also add the inner
* functions and variables inside a given class parse node.
*
* @param child
* A PHPClassParseNode or a PHPVariableParseNode (any other type is ignored)
* @param url
*/
private void addBuiltinClassOrConstant(IParseNode child, URL url)
{
if (child instanceof PHPClassParseNode)
{
builtInClasses.put(child.getNameNode().getName(), url.toString().intern());
IParseNode[] children = child.getChildren();
for (IParseNode node : children)
{
if (node instanceof PHPFunctionParseNode || node instanceof PHPVariableParseNode
|| node instanceof PHPConstantNode)
{
builtInClasses.put(child.getNameNode().getName() + IElementsIndex.DELIMITER
+ node.getNameNode().getName(), url.toString().intern());
}
}
}
else if (child instanceof PHPConstantNode || child.getNodeType() == IPHPParseNode.KEYWORD_NODE
|| child.getNodeType() == IPHPParseNode.CONST_NODE)
{
addAsKeyword(child, url);
}
}
private void addAsKeyword(IParseNode child, URL url)
{
// Convert this PHP constant to a PHP parse node with a Keyword
// type.
// Also, make sure that the documentation is providing some basics.
PHPBaseParseNode phpChild = (PHPBaseParseNode) child;
IPHPDocBlock documentation = phpChild.getDocumentation();
if (documentation == null || StringUtil.EMPTY.equals(documentation.getShortDescription()))
{
documentation = new PHPDocBlockImp(MessageFormat.format(Messages.PREDEFINED_CONSTANT_LABEL, child
.getNameNode().getName()), StringUtil.EMPTY, NO_TAGS, 0);
}
short type = (child.getNodeType() == IPHPParseNode.KEYWORD_NODE) ? IPHPParseNode.KEYWORD_NODE
: IPHPParseNode.CONST_NODE;
PHPBaseParseNode node = new PHPBaseParseNode(type, phpChild.getModifiers(), child.getStartingOffset(),
child.getEndingOffset(), phpChild.getNameNode().getName());
node.setDocumentation(documentation);
builtins.add(node);
String parentName = (child.getParent() != null) ? child.getParent().getNameNode().getName() : StringUtil.EMPTY;
if (parentName == null)
{
parentName = StringUtil.EMPTY;
}
else if (parentName.length() > 0)
{
parentName += IElementsIndex.DELIMITER;
}
builtInConstants.put(parentName + child.getNameNode().getName(), url.toString().intern());
}
/*
* Returns the resources URLs that are contained in the given root resource.
* @param libraryPath
* @return A URL array holding the resources for the children of the root library.
* @throws IOException
*/
@SuppressWarnings("rawtypes")
private static URL[] getBuiltinsURLs(String libraryPath)
{
List<URL> urls = new ArrayList<URL>();
PHPEplPlugin plugin = PHPEplPlugin.getDefault();
if (plugin != null)
{
try
{
Enumeration entries = plugin.getBundle().findEntries(libraryPath, "*.php", true); //$NON-NLS-1$
while (entries.hasMoreElements())
{
urls.add((URL) entries.nextElement());
}
return urls.toArray(new URL[urls.size()]);
}
catch (IllegalStateException ise)
{
// Ignore those, the bundle is probably shutting down.
}
}
return new URL[0];
}
/*
* Returns a file according to the URL protocol.
* @param url
* @return
* @throws IOException
*/
@SuppressWarnings("unused")
private static File getFile(URL url) throws IOException
{
if ("file".equals(url.getProtocol())) //$NON-NLS-1$
return new File(url.getPath());
if ("jar".equals(url.getProtocol())) { //$NON-NLS-1$
String path = url.getPath();
if (path.startsWith("file:")) { //$NON-NLS-1$
// strip off the file: and the !/
path = path.substring(5, path.length() - 2);
return new File(path);
}
}
throw new IOException("Unknown protocol"); //$NON-NLS-1$
}
private PHPBuiltins()
{
// initiate empty values
initNames();
}
/**
* @return instance of built ins
*/
public static PHPBuiltins getInstance()
{
return instance;
}
/**
* Clean and recreate the PHP built-ins.
*
* @param monitor
* A non null progress monitor.
*/
public synchronized void clean(IProgressMonitor monitor)
{
long start = System.currentTimeMillis();
initializing = true;
initNames();
this.builtInFunctions = new HashMap<String, String>();
this.builtInClasses = new HashMap<String, String>();
this.builtInConstants = new HashMap<String, String>();
if (monitor == null)
{
initializing = false;
throw new IllegalArgumentException("The progress monitor should not be null"); //$NON-NLS-1$
}
if (this.builtins == null)
{
initBuiltins(monitor);
}
initializing = false;
IdeLog.logInfo(PHPEditorPlugin.getDefault(), "Built-ins clean: " //$NON-NLS-1$
+ (System.currentTimeMillis() - start) + "ms", null, PHPEditorPlugin.INDEXER_SCOPE); //$NON-NLS-1$
}
private void initNames()
{
phpNames = new HashMap<PHPVersion, Set<String>>();
for (PHPVersion version : EnumSet.allOf(PHPVersion.class))
{
phpNames.put(version, new HashSet<String>());
}
}
/**
* Returns true if the PHP built-ins are being initialized.
*
* @return <code>true</code> if the built-ins are currently initialized. <code>false</code> otherwise.
*/
public synchronized boolean isInitializing()
{
return initializing;
}
}