/******************************************************************************* * Copyright (c) 2009, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Zend Technologies * Dawid PakuĊ‚a [469267] *******************************************************************************/ package org.eclipse.php.internal.core.typeinference; import java.util.*; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.declarations.ModuleDeclaration; import org.eclipse.dltk.core.*; import org.eclipse.dltk.evaluation.types.*; import org.eclipse.dltk.internal.core.ScriptProject; import org.eclipse.dltk.ti.IContext; import org.eclipse.dltk.ti.ISourceModuleContext; import org.eclipse.dltk.ti.goals.ExpressionTypeGoal; import org.eclipse.dltk.ti.types.IEvaluatedType; import org.eclipse.php.core.compiler.PHPFlags; import org.eclipse.php.internal.core.compiler.ast.parser.ASTUtils; import org.eclipse.php.internal.core.typeinference.context.IModelCacheContext; import org.eclipse.php.internal.core.typeinference.evaluators.PHPTraitType; public class PHPTypeInferenceUtils { public static IEvaluatedType combineMultiType(Collection<IEvaluatedType> evaluatedTypes) { MultiTypeType multiTypeType = new MultiTypeType(); for (IEvaluatedType type : evaluatedTypes) { if (type == null) { type = PHPSimpleTypes.NULL; } multiTypeType.addType(type); } return multiTypeType; } private static Collection<IEvaluatedType> resolveAmbiguousTypes(Collection<IEvaluatedType> evaluatedTypes) { List<IEvaluatedType> resolved = new LinkedList<IEvaluatedType>(); for (IEvaluatedType type : evaluatedTypes) { if (type instanceof AmbiguousType) { AmbiguousType ambType = (AmbiguousType) type; resolved.addAll(resolveAmbiguousTypes(Arrays.asList(ambType.getPossibleTypes()))); } else { resolved.add(type); } } return resolved; } public static IEvaluatedType combineTypes(Collection<IEvaluatedType> evaluatedTypes) { Set<IEvaluatedType> types = new LinkedHashSet<IEvaluatedType>(resolveAmbiguousTypes(evaluatedTypes)); if (types.contains(null)) { types.remove(null); types.add(PHPSimpleTypes.NULL); } if (types.size() == 0) { return UnknownType.INSTANCE; } if (types.size() == 1) { return types.iterator().next(); } return new AmbiguousType(types.toArray(new IEvaluatedType[types.size()])); } public static IEvaluatedType resolveExpression(ISourceModule sourceModule, ASTNode expression) { ModuleDeclaration moduleDeclaration = SourceParserUtil.getModuleDeclaration(sourceModule); IContext context = ASTUtils.findContext(sourceModule, moduleDeclaration, expression); return resolveExpression(sourceModule, moduleDeclaration, context, expression); } public static IEvaluatedType resolveExpression(ISourceModule sourceModule, ModuleDeclaration moduleDeclaration, IContext context, ASTNode expression) { return resolveExpression(new PHPTypeInferencer(), sourceModule, moduleDeclaration, context, expression); } public static IEvaluatedType resolveExpression(IPHPTypeInferencer inferencer, ISourceModule sourceModule, ModuleDeclaration moduleDeclaration, IContext context, ASTNode expression) { if (context != null) { return inferencer.evaluateType(new ExpressionTypeGoal(context, expression)); } return null; } /** * Converts IEvaluatedType to IType, if found. This method filters elements * using file network dependencies. * * @param evaluatedType * Evaluated type * @param context * @return model elements or <code>null</code> in case no element could be * found */ public static IType[] getModelElements(IEvaluatedType evaluatedType, ISourceModuleContext context) { return PHPTypeInferenceUtils.getModelElements(evaluatedType, context, 0, context instanceof IModelCacheContext ? ((IModelCacheContext) context).getCache() : null); } /** * Converts IEvaluatedType to IType, if found. This method filters elements * using file network dependencies. * * @param evaluatedType * Evaluated type * @param context * @param cache * @return model elements or <code>null</code> in case no element could be * found */ public static IType[] getModelElements(IEvaluatedType evaluatedType, ISourceModuleContext context, IModelAccessCache cache) { return PHPTypeInferenceUtils.getModelElements(evaluatedType, context, 0, cache); } /** * Converts IEvaluatedType to IType, if found. This method filters elements * using file network dependencies. * * @param evaluatedType * Evaluated type * @param context * @param offset * @return model elements or <code>null</code> in case no element could be * found */ public static IType[] getModelElements(IEvaluatedType evaluatedType, ISourceModuleContext context, int offset) { return internalGetModelElements(evaluatedType, context, offset, context instanceof IModelCacheContext ? ((IModelCacheContext) context).getCache() : null); } /** * Converts IEvaluatedType to IType, if found. This method filters elements * using file network dependencies. * * @param evaluatedType * Evaluated type * @param context * @param offset * @param cache * @return model elements or <code>null</code> in case no element could be * found */ public static IType[] getModelElements(IEvaluatedType evaluatedType, ISourceModuleContext context, int offset, IModelAccessCache cache) { return internalGetModelElements(evaluatedType, context, offset, cache); } private static IType[] internalGetModelElements(IEvaluatedType evaluatedType, ISourceModuleContext context, int offset, IModelAccessCache cache) { ISourceModule sourceModule = context.getSourceModule(); if (isSimple(evaluatedType)) { return null; } if (evaluatedType instanceof AnonymousClassInstanceType) { IType type = ((AnonymousClassInstanceType) evaluatedType).getType(); if (type != null) { return new IType[] { type }; } return null; } if (evaluatedType instanceof ModelClassType) { return new IType[] { ((ModelClassType) evaluatedType).getTypeDeclaration() }; } if (evaluatedType instanceof PHPClassType) { IScriptProject scriptProject = sourceModule.getScriptProject(); if (!ScriptProject.hasScriptNature(scriptProject.getProject())) { List<IType> result = new LinkedList<IType>(); try { IType[] types = sourceModule.getTypes(); for (IType t : types) { if (t.getElementName().equalsIgnoreCase(evaluatedType.getTypeName())) { Class<?> expressionClass = evaluatedType.getClass(); if ((expressionClass == PHPTraitType.class) && PHPFlags.isTrait(t.getFlags()) || (expressionClass == PHPClassType.class) && PHPFlags.isClass(t.getFlags())) { result.add(t); } break; } } } catch (ModelException e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } return result.toArray(new IType[result.size()]); } else { try { Class<?> expressionClass = evaluatedType.getClass(); if (expressionClass == PHPTraitType.class) { PHPTraitType phpTraitType = (PHPTraitType) evaluatedType; return PHPModelUtils.getTypes(evaluatedType.getTypeName(), sourceModule, offset, cache, null, false, phpTraitType.isGlobal()); } else { PHPClassType phpClassType = (PHPClassType) evaluatedType; return PHPModelUtils.getTypes(evaluatedType.getTypeName(), sourceModule, offset, cache, null, true, phpClassType.isGlobal()); } } catch (ModelException e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } } } else if (evaluatedType instanceof AmbiguousType) { List<IType> tmpList = new LinkedList<IType>(); IEvaluatedType[] possibleTypes = ((AmbiguousType) evaluatedType).getPossibleTypes(); for (IEvaluatedType possibleType : possibleTypes) { IType[] tmpArray = internalGetModelElements(possibleType, context, offset, cache); if (tmpArray != null) { tmpList.addAll(Arrays.asList(tmpArray)); } } // the elements are filtered already return tmpList.toArray(new IType[tmpList.size()]); } else if (evaluatedType instanceof MultiTypeType) { MultiTypeType multiTypeType = (MultiTypeType) evaluatedType; List<IType> result = new ArrayList<IType>(); for (IEvaluatedType tmpType : multiTypeType.getTypes()) { IType[] tmpTypes = internalGetModelElements(tmpType, context, offset, cache); if (tmpTypes != null) { result.addAll(Arrays.asList(tmpTypes)); } return result.toArray(new IType[result.size()]); } } return null; } /** * Check if IEvaluatedType is Simpletype * * array, mixed, object[] etc... * * @param object * @return * @since 3.3 */ public static boolean isSimple(Object object) { if (object == null || object instanceof SimpleType) { return true; } else if (PHPSimpleTypes.RESOURCE == object || PHPSimpleTypes.OBJECT == object || PHPSimpleTypes.MIXED == object || PHPSimpleTypes.CALLABLE == object) { return true; } else if (object instanceof MultiTypeType) { for (IEvaluatedType type : ((MultiTypeType) object).getTypes()) { if (!isSimple(type)) { return false; } } return true; } else if (object instanceof AmbiguousType) { for (IEvaluatedType type : ((AmbiguousType) object).getPossibleTypes()) { if (!isSimple(type)) { return false; } } return true; } return false; } }