/**
* Aptana Studio
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions).
* Please see the license.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;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org2.eclipse.php.core.compiler.PHPFlags;
import com.aptana.editor.php.indexer.IElementEntry;
import com.aptana.editor.php.indexer.IElementsIndex;
import com.aptana.editor.php.indexer.IPHPIndexConstants;
import com.aptana.editor.php.internal.contentAssist.PHPContentAssistProcessor;
/**
* PHPTypeProcessor
*
* @author Denis Denisenko
*/
public final class PHPTypeProcessor
{
/**
* Maximum recursion depth.
*/
private static final int MAX_REC_DEPTH = 10;
/**
* Processes encoded types and returns the decoded type names list.
*
* @param encodedTypes
* - types to process.
* @param indexer
* - indexer to use for processing.
* @return encoded types and returns the decoded type names list.
*/
public static Set<String> processTypes(Set<Object> encodedTypes, IElementsIndex indexer)
{
Set<String> result = new HashSet<String>();
processTypes(encodedTypes, result, indexer, 0);
return result;
}
/**
* Processes encoded types and returns the decoded types list.
*
* @param encodedTypes
* - types to process.
* @param indexer
* - indexer to use for processing.
* @param depth
* - current depth.
* @return
*/
private static Set<String> processTypes(Set<Object> encodedTypes, IElementsIndex indexer, int depth)
{
Set<String> result = new HashSet<String>();
processTypes(encodedTypes, result, indexer, depth);
return result;
}
/**
* Filters the set of types and returns the custom types only.
*
* @param types
* - set of types to choose custom types from.
* @return set of custom types.
*/
public static Set<String> getCustomTypes(Set<String> types)
{
Set<String> result = new HashSet<String>();
for (String type : types)
{
if (!isBuiltInType(type))
{
result.add(type);
}
}
return result;
}
/**
* Gets whether type is built-in.
*
* @param type
* - type name.
* @return true if built-in, false otherwise.
*/
public static boolean isBuiltInType(String type)
{
if (IPHPIndexConstants.BOOLEAN_TYPE.equals(type))
{
return true;
}
else if (IPHPIndexConstants.STRING_TYPE.equals(type))
{
return true;
}
else if (IPHPIndexConstants.INTEGER_TYPE.equals(type))
{
return true;
}
else if (IPHPIndexConstants.REAL_TYPE.equals(type))
{
return true;
}
else if (IPHPIndexConstants.SYSTEM_TYPE.equals(type))
{
return true;
}
return false;
}
/**
* Processes types.
*
* @param encodedTypes
* - encoded types.
* @param result
* - result to fill.
* @param indexer
* - indexer.
* @param depth
* - current depth.
*/
private static void processTypes(Set<Object> encodedTypes, Set<String> result, IElementsIndex indexer, int depth)
{
if (depth >= MAX_REC_DEPTH)
{
return;
}
for (Object type : encodedTypes)
{
if (type != null)
{
if (type instanceof String)
{
String typeStr = type.toString();
if (typeStr.startsWith(PHPContentAssistProcessor.GLOBAL_NAMESPACE))
{
typeStr = typeStr.substring(1);
}
result.add(typeStr);
}
else if (type instanceof VariablePathReference)
{
VariablePathReference ref = (VariablePathReference) type;
Set<Object> dispatcherTypes = ref.getDispatcherTypes();
procesPathReference(dispatcherTypes, ref.getPath(), result, indexer, depth + 1, false);
}
else if (type instanceof FunctionPathReference)
{
FunctionPathReference ref = (FunctionPathReference) type;
List<IElementEntry> entries = indexer.getEntries(IPHPIndexConstants.FUNCTION_CATEGORY, ref
.getFunctionEntryPath());
for (IElementEntry entry : entries)
{
Set<Object> dispatcherTypes = getEntryDispatcherTypes(entry);
if (dispatcherTypes != null && !dispatcherTypes.isEmpty())
{
procesPathReference(dispatcherTypes, ref.getPath(), result, indexer, depth + 1, false);
}
}
}
else if (type instanceof StaticPathReference)
{
StaticPathReference ref = (StaticPathReference) type;
Set<Object> dispatcherTypes = ref.getDispatcherTypes();
procesPathReference(dispatcherTypes, ref.getPath(), result, indexer, depth + 1, false);
}
}
}
}
/**
* Processes variable call path reference.
*
* @param ref
* - reference.
* @param result
* - result to fill.
* @param indexer
* - indexer.
* @param depth
* - current recursion depth.
* @param dispatcherTypes
* - dispatcher types.
* @param path
* - call path to process.
* @param staticsOnly
* - whether to accept static entries only.
*/
private static void procesPathReference(Set<Object> dispatcherTypes, CallPath path, Set<String> result,
IElementsIndex indexer, int depth, boolean staticsOnly)
{
if (depth >= MAX_REC_DEPTH)
{
return;
}
if (dispatcherTypes == null || dispatcherTypes.isEmpty())
{
return;
}
Set<Object> encodedRefTypes = dispatcherTypes;
Set<String> decodedRefTypes = processTypes(encodedRefTypes, indexer, depth + 1);
Set<String> customRefTypes = getCustomTypes(decodedRefTypes);
if (customRefTypes.isEmpty())
{
return;
}
if (path == null || path.getSize() == 0)
{
result.addAll(customRefTypes);
return;
}
CallPath.Entry firstPathEntry = path.getEntries().get(0);
// reference for all possible dispatcher types
for (String type : customRefTypes)
{
String entryName = firstPathEntry.getName();
String entryPath = type + IElementsIndex.DELIMITER + entryName;
int category = -1;
if (firstPathEntry instanceof CallPath.VariableEntry)
{
category = IPHPIndexConstants.VAR_CATEGORY;
}
else if (firstPathEntry instanceof CallPath.MethodEntry)
{
category = IPHPIndexConstants.FUNCTION_CATEGORY;
}
List<IElementEntry> entries = indexer.getEntries(category, entryPath);
// processing all entries got by the type and the first path entry
for (IElementEntry entry : entries)
{
// skipping non-static entries if "statics only" flag is on
if (staticsOnly && !isStaticEntry(entry))
{
continue;
}
Set<Object> entryTypes = getEntryDispatcherTypes(entry);
if (entryTypes == null || entryTypes.isEmpty())
{
continue;
}
CallPath subPath = path.subPath(1);
// if we have more of path to process, do it recursively
if (subPath.getSize() != 0)
{
procesPathReference(entryTypes, subPath, result, indexer, depth + 1, false);
}
// in other case reporting the field types found
else
{
processTypes(entryTypes, result, indexer, depth + 1);
}
}
}
}
/**
* Gets entry dispatcher types: for variable that are variable types, for method that are return types.
*
* @return types set or null
*/
private static Set<Object> getEntryDispatcherTypes(IElementEntry entry)
{
Object val = entry.getValue();
if (val instanceof VariablePHPEntryValue)
{
VariablePHPEntryValue entryValue = (VariablePHPEntryValue) val;
return entryValue.getTypes();
}
else if (val instanceof FunctionPHPEntryValue)
{
FunctionPHPEntryValue entryValue = (FunctionPHPEntryValue) val;
return entryValue.getReturnTypes();
}
return null;
}
/**
* Checks whether entry is static.
*
* @param entry
* - entry.
* @return true if entry is static, false otherwise.
*/
private static boolean isStaticEntry(IElementEntry entry)
{
Object entryValue = entry.getValue();
if (entryValue instanceof AbstractPHPEntryValue)
{
int modifiers = ((AbstractPHPEntryValue) entryValue).getModifiers();
return PHPFlags.isStatic(modifiers);
}
return false;
}
/**
* PHPTypeProcessor private constructor.
*/
private PHPTypeProcessor()
{
}
}