/*
* Copyright 2000-2013 JetBrains s.r.o.
* Copyright 2014-2014 AS3Boyan
* Copyright 2014-2014 Elias Ku
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.plugins.haxe.util;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.plugins.haxe.HaxeComponentType;
import com.intellij.plugins.haxe.ide.index.HaxeComponentFileNameIndex;
import com.intellij.plugins.haxe.lang.psi.*;
import com.intellij.plugins.haxe.lang.psi.impl.AbstractHaxeTypeDefImpl;
import com.intellij.plugins.haxe.lang.psi.impl.HaxeParameterListPsiMixinImpl;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashSet;
import org.apache.log4j.Logger;
import org.apache.log4j.Level;
import org.apache.xmlbeans.impl.common.ResolverUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author: Fedor.Korotkov
*/
public class HaxeResolveUtil {
private static final HaxeDebugLogger LOG = HaxeDebugLogger.getLogger();
@Nullable
public static HaxeReference getLeftReference(@Nullable final PsiElement node) {
if (node == null) return null;
for (PsiElement sibling = UsefulPsiTreeUtil.getPrevSiblingSkipWhiteSpaces(node, true);
sibling != null;
sibling = UsefulPsiTreeUtil.getPrevSiblingSkipWhiteSpaces(sibling, true))
{
if (".".equals(sibling.getText())) continue;
HaxeExpression tmpExpression = null;
if (sibling instanceof HaxeExpression) {
tmpExpression = (HaxeExpression) sibling;
}
if (tmpExpression != null && tmpExpression instanceof HaxeParenthesizedExpression) {
tmpExpression = ((HaxeParenthesizedExpression) tmpExpression).getExpression();
}
HaxeReference theHaxeRef = null;
if (tmpExpression != null) {
if (tmpExpression instanceof HaxeAssignExpression) {
if (2 == ((HaxeAssignExpression) tmpExpression).getExpressionList().size()) {
final HaxeExpression rhsHaxeExpr = ((HaxeAssignExpression) tmpExpression).getExpressionList().get(1);
final HaxeReference rhsHaxeReference = (HaxeReference) rhsHaxeExpr.getReference();
if ((rhsHaxeReference != null) && (rhsHaxeReference.resolveHaxeClass().getHaxeClass() != null)) {
theHaxeRef = rhsHaxeReference;
}
else {
final HaxeExpression lhsHaxeExpr = ((HaxeAssignExpression) tmpExpression).getExpressionList().get(0);
final HaxeReference lhsHaxeReference = (HaxeReference) lhsHaxeExpr.getReference();
if ((lhsHaxeReference != null) && (lhsHaxeReference.resolveHaxeClass().getHaxeClass() != null)) {
theHaxeRef = lhsHaxeReference;
}
}
}
}
else
if (tmpExpression instanceof HaxeReferenceExpression) {
theHaxeRef = (HaxeReference) tmpExpression; // TODO: FIX: not correct for identifiers - HaxeReferenceExpresssion.getIdentifier() -- but, that's not a reference?!
}
else {
theHaxeRef = (HaxeReference) tmpExpression.getReference();
}
}
if (theHaxeRef != null && theHaxeRef != sibling) {
sibling = theHaxeRef;
}
return ((sibling instanceof HaxeReference) && (sibling != node)) ? (HaxeReference) sibling : null;
}
return null;
}
@NotNull
public static Pair<String, String> splitQName(@NotNull String qName) {
final int dotIndex = qName.lastIndexOf('.');
final String packageName = dotIndex == -1 ? "" : qName.substring(0, dotIndex);
final String className = dotIndex == -1 ? qName : qName.substring(dotIndex + 1);
return Pair.create(packageName, className);
}
@NotNull
public static String joinQName(@Nullable String packageName, @Nullable String className) {
String result = "";
if (packageName != null && !packageName.isEmpty()) {
result = packageName;
if (className != null) {
result += ".";
}
}
if (className != null) {
result += className;
}
return result;
}
@NotNull
@NonNls
public static String getPackageName(@Nullable final PsiFile file) {
final HaxePackageStatement packageStatement = PsiTreeUtil.getChildOfType(file, HaxePackageStatement.class);
return getPackageName(packageStatement);
}
@NotNull
@NonNls
public static String getPackageName(@Nullable HaxePackageStatement packageStatement) {
HaxeReferenceExpression referenceExpression = packageStatement != null ? packageStatement.getReferenceExpression() : null;
if (referenceExpression != null) {
return referenceExpression.getText();
}
return "";
}
@Nullable
public static HaxeClass findClassByQName(final @Nullable String qName, final @Nullable PsiElement context) {
if (context == null || qName == null) {
return null;
}
final PsiManager psiManager = context.getManager();
final GlobalSearchScope scope = getScopeForElement(context);
return findClassByQName(qName, psiManager, scope);
}
@NotNull
public static GlobalSearchScope getScopeForElement(@NotNull PsiElement context) {
final Project project = context.getProject();
if (ApplicationManager.getApplication().isUnitTestMode()) {
return GlobalSearchScope.allScope(project);
}
final Module module = ModuleUtilCore.findModuleForPsiElement(context);
return module != null ? GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module) : GlobalSearchScope.allScope(project);
}
@Nullable
public static HaxeClass findClassByQName(String qName, PsiManager psiManager, GlobalSearchScope scope) {
final List<VirtualFile> classFiles = HaxeComponentFileNameIndex.
getFilesNameByQName(qName, scope);
final Pair<String, String> qNamePair = splitQName(qName);
for (VirtualFile classFile : classFiles) {
final HaxeClass componentPsiElement = findComponentDeclaration(psiManager.findFile(classFile), qNamePair.getSecond());
if (componentPsiElement != null) {
return componentPsiElement;
}
}
return null;
}
@NotNull
public static List<HaxeClass> findComponentDeclarations(@Nullable PsiFile file) {
if (file == null) {
return Collections.emptyList();
}
final HaxeClass[] components = PsiTreeUtil.getChildrenOfType(file, HaxeClass.class);
if (components == null) {
return Collections.emptyList();
}
return Arrays.asList(components);
}
@Nullable
public static HaxeClass findComponentDeclaration(@Nullable PsiFile file, @NotNull String componentName) {
final List<HaxeClass> declarations = findComponentDeclarations(file);
for (HaxeClass haxeClass : declarations) {
final HaxeComponentName identifier = haxeClass.getComponentName();
if (identifier != null && componentName.equals(identifier.getText())) {
return haxeClass;
}
}
return null;
}
@NotNull
public static List<HaxeType> findExtendsList(@Nullable HaxeInheritList extendsList) {
List<? extends HaxeInherit> ext = null == extendsList ? null : extendsList.getExtendsDeclarationList();
return findExtendsImplementsListImpl(ext);
}
public static List<HaxeType> getImplementsList(@Nullable HaxeInheritList extendsList) {
List<? extends HaxeInherit> ext = null == extendsList ? null : extendsList.getImplementsDeclarationList();
return findExtendsImplementsListImpl(ext);
}
@NotNull
private static List<HaxeType> findExtendsImplementsListImpl(@Nullable List<? extends HaxeInherit> extendsList) {
if (extendsList == null) {
return Collections.emptyList();
}
final List<HaxeType> result = new ArrayList<HaxeType>();
for (HaxeInherit inherit : extendsList) {
final List<HaxeType> inheritTypes = inherit.getTypeList();
result.addAll(inheritTypes);
}
return result;
}
public static List<HaxeNamedComponent> filterNamedComponentsByType(List<HaxeNamedComponent> result, final HaxeComponentType type) {
return ContainerUtil.filter(result, new Condition<HaxeNamedComponent>() {
@Override
public boolean value(HaxeNamedComponent component) {
return HaxeComponentType.typeOf(component) == type;
}
});
}
@Nullable
public static HaxeNamedComponent findNamedSubComponent(@Nullable HaxeClass haxeClass, @NotNull final String name) {
if (haxeClass == null) {
return null;
}
final HaxeNamedComponent result = haxeClass.findHaxeMethodByName(name);
return result != null ? result : haxeClass.findHaxeFieldByName(name);
}
@NotNull
public static List<HaxeNamedComponent> findNamedSubComponents(@NotNull HaxeClass... rootHaxeClasses) {
return findNamedSubComponents(true, rootHaxeClasses);
}
@NotNull
public static List<HaxeNamedComponent> findNamedSubComponents(boolean unique, @NotNull HaxeClass... rootHaxeClasses) {
final List<HaxeNamedComponent> unfilteredResult = new ArrayList<HaxeNamedComponent>();
final LinkedList<HaxeClass> classes = new LinkedList<HaxeClass>();
final HashSet<HaxeClass> processed = new HashSet<HaxeClass>();
classes.addAll(Arrays.asList(rootHaxeClasses));
while (!classes.isEmpty()) {
final HaxeClass haxeClass = classes.pollFirst();
for (HaxeNamedComponent namedComponent : getNamedSubComponents(haxeClass)) {
if (namedComponent.getName() != null) {
unfilteredResult.add(namedComponent);
}
}
List<HaxeType> baseTypes = new ArrayList<HaxeType>();
baseTypes.addAll(haxeClass.getHaxeExtendsList());
baseTypes.addAll(haxeClass.getHaxeImplementsList());
List<HaxeClass> baseClasses = tyrResolveClassesByQName(baseTypes);
for(HaxeClass baseClass : baseClasses) {
if(processed.add(baseClass)) {
classes.add(baseClass);
}
}
}
if (!unique) {
return unfilteredResult;
}
return new ArrayList<HaxeNamedComponent>(namedComponentToMap(unfilteredResult).values());
}
public static Map<String, HaxeNamedComponent> namedComponentToMap(List<HaxeNamedComponent> unfilteredResult) {
final Map<String, HaxeNamedComponent> result = new HashMap<String, HaxeNamedComponent>();
for (HaxeNamedComponent haxeNamedComponent : unfilteredResult) {
// need order
if (result.containsKey(haxeNamedComponent.getName())) continue;
result.put(haxeNamedComponent.getName(), haxeNamedComponent);
}
return result;
}
@NotNull
public static List<HaxeNamedComponent> getNamedSubComponentsInOrder(HaxeClass haxeClass) {
final List<HaxeNamedComponent> result = getNamedSubComponents(haxeClass);
Collections.sort(result, new Comparator<HaxeNamedComponent>() {
@Override
public int compare(HaxeNamedComponent o1, HaxeNamedComponent o2) {
return o1.getTextOffset() - o2.getTextOffset();
}
});
return result;
}
public static List<HaxeNamedComponent> getNamedSubComponents(HaxeClass haxeClass) {
PsiElement body = null;
final HaxeComponentType type = HaxeComponentType.typeOf(haxeClass);
if (type == HaxeComponentType.CLASS) {
body = PsiTreeUtil.getChildOfAnyType(haxeClass, HaxeClassBody.class, HaxeExternClassDeclarationBody.class);
}
else if (type == HaxeComponentType.INTERFACE) {
body = PsiTreeUtil.getChildOfType(haxeClass, HaxeInterfaceBody.class);
}
else if (type == HaxeComponentType.ENUM) {
body = PsiTreeUtil.getChildOfType(haxeClass, HaxeEnumBody.class);
}
else if (haxeClass instanceof HaxeTypedefDeclaration) {
final HaxeTypeOrAnonymous typeOrAnonymous = ((HaxeTypedefDeclaration)haxeClass).getTypeOrAnonymous();
if (typeOrAnonymous != null && typeOrAnonymous.getAnonymousType() != null) {
HaxeAnonymousType anonymous = typeOrAnonymous.getAnonymousType();
if(anonymous != null) {
return getNamedSubComponents(anonymous);
}
}
else if (typeOrAnonymous != null) {
final HaxeClass typeClass = getHaxeClassResolveResult(typeOrAnonymous.getType()).getHaxeClass();
assert typeClass != haxeClass;
return getNamedSubComponents(typeClass);
}
}
final List<HaxeNamedComponent> result = new ArrayList<HaxeNamedComponent>();
if (haxeClass instanceof HaxeAnonymousType) {
final HaxeAnonymousTypeFieldList typeFieldList = ((HaxeAnonymousType)haxeClass).getAnonymousTypeBody().getAnonymousTypeFieldList();
if (typeFieldList != null) {
result.addAll(typeFieldList.getAnonymousTypeFieldList());
}
body = ((HaxeAnonymousType)haxeClass).getAnonymousTypeBody().getInterfaceBody();
}
if (body == null) {
return result;
}
final HaxeNamedComponent[] namedComponents = PsiTreeUtil.getChildrenOfType(body, HaxeNamedComponent.class);
if (namedComponents != null) {
for (HaxeNamedComponent namedComponent : namedComponents) {
// Variable declarations are named components, but don't have the
// same Psi structure as most. So, go find the actual sub-element(s)
// that are named (that themselves have COMPONENT_NAME sub-elements).
if (namedComponent instanceof HaxeVarDeclaration) {
HaxeVarDeclaration varDeclaration = (HaxeVarDeclaration)namedComponent;
result.add(varDeclaration.getVarDeclarationPart());
}
else {
result.add(namedComponent);
}
}
}
return result;
}
public static List<HaxeVarDeclaration> getClassVarDeclarations(HaxeClass haxeClass) {
PsiElement body = null;
final HaxeComponentType type = HaxeComponentType.typeOf(haxeClass);
if (type == HaxeComponentType.CLASS) {
body = PsiTreeUtil.getChildOfAnyType(haxeClass, HaxeClassBody.class, HaxeExternClassDeclarationBody.class);
}
final List<HaxeVarDeclaration> result = new ArrayList<HaxeVarDeclaration>();
if (body == null) {
return result;
}
final HaxeVarDeclaration[] variables = PsiTreeUtil.getChildrenOfType(body, HaxeVarDeclaration.class);
if (variables == null) {
return result;
}
Collections.addAll(result, variables);
return result;
}
@Nullable
public static List<HaxeType> getFunctionParameters(HaxeNamedComponent component) {
if (HaxeComponentType.typeOf(component) != HaxeComponentType.METHOD) {
return null;
}
final HaxeParameterListPsiMixinImpl parameterList = PsiTreeUtil.getChildOfType(component, HaxeParameterListPsiMixinImpl.class);
if (parameterList == null || parameterList.getParametersCount() == 0) {
return Collections.emptyList();
}
return ContainerUtil.map(parameterList.getParametersAsList(), new Function<HaxeParameter, HaxeType>() {
@Override
public HaxeType fun(HaxeParameter parameter) {
final HaxeTypeTag typeTag = parameter.getTypeTag();
if (null == typeTag) return null;
final HaxeTypeOrAnonymous typeOrAnonymous = typeTag.getTypeOrAnonymous();
return (null == typeOrAnonymous) ? null : typeOrAnonymous.getType();
}
});
}
@NotNull
public static HaxeClassResolveResult getHaxeClassResolveResult(@Nullable PsiElement element) {
return getHaxeClassResolveResult(element, new HaxeGenericSpecialization());
}
@NotNull
public static HaxeClassResolveResult getHaxeClassResolveResult(@Nullable PsiElement element,
@NotNull HaxeGenericSpecialization specialization) {
if (element == null || element instanceof PsiPackage) {
return HaxeClassResolveResult.create(null);
}
if (element instanceof HaxeComponentName) {
return getHaxeClassResolveResult(element.getParent(), specialization);
}
if (element instanceof AbstractHaxeTypeDefImpl) {
final AbstractHaxeTypeDefImpl typeDef = (AbstractHaxeTypeDefImpl)element;
return typeDef.getTargetClass(specialization);
}
if (element instanceof HaxeClass) {
final HaxeClass haxeClass = (HaxeClass)element;
return HaxeClassResolveResult.create(haxeClass);
}
if (element instanceof HaxeForStatement) {
final HaxeIterable iterable = ((HaxeForStatement)element).getIterable();
assert iterable != null;
final HaxeExpression expression = iterable.getExpression();
if (expression instanceof HaxeReference) {
final HaxeClassResolveResult resolveResult = ((HaxeReference)expression).resolveHaxeClass();
final HaxeClass resolveResultHaxeClass = resolveResult.getHaxeClass();
// try next
HaxeClassResolveResult result =
getHaxeClassResolveResult(resolveResultHaxeClass == null ? null : resolveResultHaxeClass.findHaxeMethodByName("next"),
resolveResult.getSpecialization());
if (result.getHaxeClass() != null) {
return result;
}
// try iterator
result = getHaxeClassResolveResult(resolveResultHaxeClass == null ? null : resolveResultHaxeClass.findHaxeMethodByName("iterator"),
resolveResult.getSpecialization());
return result.getSpecialization().containsKey(null, "T")
? result.getSpecialization().get(null, "T")
: HaxeClassResolveResult.EMPTY;
}
return HaxeClassResolveResult.EMPTY;
}
HaxeClassResolveResult result = tryResolveClassByTypeTag(element, specialization);
if (result.getHaxeClass() != null) {
return result;
}
result = HaxeAbstractEnumUtil.resolveFieldType(element);
if(result != null) {
return result;
}
if (specialization.containsKey(null, element.getText())) {
return specialization.get(null, element.getText());
}
final HaxeVarInit varInit = PsiTreeUtil.getChildOfType(element, HaxeVarInit.class);
final HaxeExpression initExpression = varInit == null ? null : varInit.getExpression();
if (initExpression instanceof HaxeReference) {
result = ((HaxeReference)initExpression).resolveHaxeClass();
result.specialize(initExpression);
return result;
}
return getHaxeClassResolveResult(initExpression);
}
@NotNull
public static HaxeClassResolveResult tryResolveClassByTypeTag(PsiElement element,
HaxeGenericSpecialization specialization) {
final HaxeTypeTag typeTag = PsiTreeUtil.getChildOfType(element, HaxeTypeTag.class);
final HaxeTypeOrAnonymous typeOrAnonymous = (typeTag != null) ? typeTag.getTypeOrAnonymous() : null;
final HaxeType type = (typeOrAnonymous != null) ? typeOrAnonymous.getType() :
((element instanceof HaxeType) ? (HaxeType)element : null);
HaxeClass haxeClass = type == null ? null : tryResolveClassByQName(type);
if (haxeClass == null && type != null && specialization.containsKey(element, type.getText())) {
return specialization.get(element, type.getText());
}
if(haxeClass instanceof HaxeTypedefDeclaration) {
HaxeClassResolveResult temp = HaxeClassResolveResult.create(haxeClass, specialization);
temp.specializeByParameters(type.getTypeParam());
specialization = temp.getSpecialization();
}
HaxeClassResolveResult result = getHaxeClassResolveResult(haxeClass, specialization.getInnerSpecialization(element));
if (result.getHaxeClass() != null) {
result.specializeByParameters(type == null ? null : type.getTypeParam());
return result;
}
if (typeTag != null) {
return tryResolveFunctionType(typeTag.getFunctionType(), specialization);
}
return HaxeClassResolveResult.EMPTY;
}
private static HaxeClassResolveResult tryResolveFunctionType(@Nullable HaxeFunctionType functionType,
HaxeGenericSpecialization specialization) {
if (functionType == null) {
return HaxeClassResolveResult.EMPTY;
}
final HaxeTypeOrAnonymous returnTypeOrAnonymous =
functionType.getTypeOrAnonymousList().get(functionType.getTypeOrAnonymousList().size() - 1);
final HaxeClassResolveResult result = tryResolveClassByTypeTag(returnTypeOrAnonymous.getType(), specialization);
functionType = functionType.getFunctionType();
while (functionType != null) {
// todo: anonymous types :(
final List<HaxeTypeOrAnonymous> typeList = functionType.getTypeOrAnonymousList();
Collections.reverse(typeList);
for (HaxeTypeOrAnonymous typeOrAnonymous : typeList) {
result.getFunctionTypes().add(tryResolveClassByTypeTag(typeOrAnonymous.getType(), specialization));
}
functionType = functionType.getFunctionType();
}
Collections.reverse(result.getFunctionTypes());
return result;
}
@NotNull
public static List<HaxeClass> tyrResolveClassesByQName(@NotNull List<HaxeType> types) {
final List<HaxeClass> result = new ArrayList<HaxeClass>();
for (HaxeType haxeType : types) {
final HaxeClass haxeClass = tryResolveClassByQName(haxeType);
if (haxeClass != null) {
result.add(haxeClass);
}
}
return result;
}
@Nullable
public static HaxeClass tryResolveClassByQName(@Nullable PsiElement type) {
if (type == null || type.getContext() == null) {
return null;
}
final String name = getQName(type);
HaxeClass result = name == null ? tryResolveClassByQNameWhenGetQNameFail(type) : findClassByQName(name, type.getContext());
result = result != null ? result : tryFindHelper(type);
result = result != null ? result : findClassByQNameInSuperPackages(type);
return result;
}
private static String tryResolveFullyQualifiedHaxeReferenceExpression(PsiElement type) {
if (type instanceof HaxeReferenceExpression) {
HaxeReferenceExpression topmostParentOfType = PsiTreeUtil.getTopmostParentOfType(type, HaxeReferenceExpression.class);
if (topmostParentOfType == null) {
topmostParentOfType = (HaxeReferenceExpression)type;
}
HaxeClass haxeClass = findClassByQName(topmostParentOfType.getText(), topmostParentOfType.getContext());
if (haxeClass != null) {
return topmostParentOfType.getText();
}
PsiElement parent = type.getParent();
HaxeClass classByQName = findClassByQName(parent.getText(), parent.getContext());
if (classByQName != null) {
return parent.getText();
}
}
return null;
}
@Nullable
private static HaxeClass findClassByQNameInSuperPackages(PsiElement type) {
HaxePackageStatement packageStatement = PsiTreeUtil.getChildOfType(type.getContainingFile(), HaxePackageStatement.class);
String packageName = getPackageName(packageStatement);
String[] packages = packageName.split("\\.");
String typeName = (type instanceof HaxeType ? ((HaxeType)type).getReferenceExpression() : type).getText();
for (int i = packages.length - 1; i >= 0; --i) {
StringBuilder qNameBuilder = new StringBuilder();
for (int j = 0; j <= i; ++j) {
if (!packages[j].isEmpty()) {
qNameBuilder.append(packages[j]).append('.');
}
}
qNameBuilder.append(typeName);
HaxeClass haxeClass = findClassByQName(qNameBuilder.toString(), type);
if (haxeClass != null) {
return haxeClass;
}
}
return null;
}
@Nullable
private static HaxeClass tryFindHelper(PsiElement element) {
// issue #435: don't use getText(), find "Ref" instead of "Ref<String>"
String className = element.getText();
if (element instanceof HaxeType) {
className = ((HaxeType)element).getReferenceExpression().getText();
}
final HaxeClass ownerClass = findClassByQName(UsefulPsiTreeUtil.findHelperOwnerQName(element, className), element);
return ownerClass == null ? null : findComponentDeclaration(ownerClass.getContainingFile(), className);
}
@Nullable
private static String getQName(@NotNull PsiElement type) {
HaxeImportStatementWithInSupport importStatementWithInSupport = PsiTreeUtil.getParentOfType(type,
HaxeImportStatementWithInSupport.class,
false);
if (importStatementWithInSupport != null) {
return importStatementWithInSupport.getReferenceExpression().getText();
}
HaxeUsingStatement usingStatement = PsiTreeUtil.getParentOfType(type, HaxeUsingStatement.class, false);
if (usingStatement != null) {
HaxeReferenceExpression expression = usingStatement.getReferenceExpression();
return expression == null? null : expression.getText();
}
return null;
}
@Nullable
private static HaxeClass tryResolveClassByQNameWhenGetQNameFail(@NotNull PsiElement type) {
if (type instanceof HaxeType) {
type = ((HaxeType)type).getReferenceExpression();
}
String name = type.getText();
HaxeClass result = null;
//1. try searchInSamePackage, ex if type is Bar, be referenced in foo.Foo then we will find class foo.Bar
//note if there are 2 class: Bar & foo.Bar then we need resolve foo.Bar instead of Bar.
if (name != null && name.indexOf('.') == -1) {
final PsiFile psiFile = type.getContainingFile();
final PsiElement[] fileChildren = psiFile.getChildren();
String nameWithPackage = getQName(fileChildren, name, true);
result = findClassByQName(nameWithPackage, type.getContext());
}
else {
name = tryResolveFullyQualifiedHaxeReferenceExpression(type);
result = findClassByQName(name, type.getContext());
}
//2. try without searchInSamePackage,
// ex if type is Int, be referenced in foo.Foo then we will find class has qName = Int, not foo.Int
// (if foo.Int exist then the prev step have aready resolved & returned it)
result = result != null ? result : findClassByQName(name, type.getContext());
return result;
}
public static String getQName(PsiElement[] fileChildren, final String result, boolean searchInSamePackage) {
final HaxeImportStatementRegular importStatement = (HaxeImportStatementRegular)ContainerUtil.find(fileChildren, new Condition<PsiElement>() {
@Override
public boolean value(PsiElement element) {
return element instanceof HaxeImportStatementRegular &&
UsefulPsiTreeUtil.importStatementForClassName((HaxeImportStatementRegular)element, result);
}
});
final HaxeImportStatementWithInSupport importStatementWithInSupport = (HaxeImportStatementWithInSupport)ContainerUtil.find(fileChildren, new Condition<PsiElement>() {
@Override
public boolean value(PsiElement element) {
return element instanceof HaxeImportStatementWithInSupport &&
UsefulPsiTreeUtil.importInStatementForClassName(((HaxeImportStatementWithInSupport)element), result);
}
});
final List<PsiElement> importStatementWithWildcardList = ContainerUtil.findAll(fileChildren, new Condition<PsiElement>() {
@Override
public boolean value(PsiElement element) {
return element instanceof HaxeImportStatementWithWildcard;
}
});
final HaxeImportStatementWithWildcard importStatementWithWildcard = (HaxeImportStatementWithWildcard)ContainerUtil.find(importStatementWithWildcardList, new Condition<PsiElement>() {
@Override
public boolean value(PsiElement element) {
return element instanceof HaxeImportStatementWithWildcard &&
(UsefulPsiTreeUtil.importStatementWithWildcardForClassName((HaxeImportStatementWithWildcard)element, result) ||
UsefulPsiTreeUtil.importStatementWithWildcardTypeForClassName(
(HaxeImportStatementWithWildcard)element, result));
}
});
/*String qName = null;
for (PsiElement element : importStatementWithWildcardList) {
HaxeImportStatementWithWildcard importStatementWithWildcard1 = (HaxeImportStatementWithWildcard)element;
List<HaxeNamedComponent> namedSubComponents = UsefulPsiTreeUtil
.getImportStatementWithWildcardTypeNamedSubComponents(importStatementWithWildcard1, element.getContainingFile());
for (HaxeNamedComponent namedComponent : namedSubComponents) {
if (namedComponent.getName().equals(result)) {
//namedComponent.getComponentName()
//namedComponent.getText()
qName = UsefulPsiTreeUtil.getQNameForImportStatementWithWildcardType(importStatementWithWildcard1) + "." + result;
break;
}
}
}*/
/*
final HaxeImportStatementWithWildcard importStatementWithWildcardType = (HaxeImportStatementWithWildcard)ContainerUtil.find(importStatementWithWildcardList, new Condition<PsiElement>() {
@Override
public boolean value(PsiElement element) {
if (element instanceof HaxeImportStatementWithWildcard) {
HaxeImportStatementWithWildcard importStatementWithWildcard1 = (HaxeImportStatementWithWildcard)element;
List<HaxeNamedComponent> namedSubComponents = UsefulPsiTreeUtil
.getImportStatementWithWildcardTypeNamedSubComponents(importStatementWithWildcard1, element.getContainingFile());
for (HaxeNamedComponent namedComponent : namedSubComponents) {
if (namedComponent.getName().equals(result)) {
qName = UsefulPsiTreeUtil.getQNameForImportStatementWithWildcardType(importStatementWithWildcard1) + "." + result;
return true;
}
}
}
return false;
}
});
*/
final HaxeExpression importStatementExpression = importStatement == null ? null : importStatement.getReferenceExpression();
final HaxeExpression importStatementWithInExpression = importStatementWithInSupport == null ? null : importStatementWithInSupport.getReferenceExpression();
final String packageName = getPackageName((HaxePackageStatement)ContainerUtil.find(fileChildren, new Condition<PsiElement>() {
@Override
public boolean value(PsiElement element) {
return element instanceof HaxePackageStatement;
}
}));
final HaxeClass classForType = (HaxeClass)ContainerUtil.find(fileChildren, new Condition<PsiElement>() {
@Override
public boolean value(PsiElement element) {
return element instanceof HaxeClass && result.equals(((HaxeClass)element).getName());
}
});
if (classForType != null) {
return classForType.getQualifiedName();
}
else if (importStatement != null && importStatementExpression != null) {
return importStatementExpression.getText();
}
else if (importStatementWithInSupport != null && importStatementWithInExpression != null) {
return importStatementWithInExpression.getText();
}
else if (importStatementWithWildcard != null) {
String text = importStatementWithWildcard.getReferenceExpression().getText();
if (text.endsWith("." + result + ".*")) {
return text.substring(0, text.length() - 2);
}
else {
return UsefulPsiTreeUtil.getPackageStatementForImportStatementWithWildcard(importStatementWithWildcard) + "." + result;
}
}
else if (searchInSamePackage && !packageName.isEmpty()) {
return packageName + "." + result;
}
return result;
}
@Nullable
public static PsiComment findDocumentation(HaxeNamedComponent element) {
final PsiElement candidate = UsefulPsiTreeUtil.getPrevSiblingSkipWhiteSpaces(element, true);
if (candidate instanceof PsiComment) {
return (PsiComment)candidate;
}
return null;
}
public static Set<IElementType> getDeclarationTypes(@Nullable HaxeDeclarationAttribute[] attributeList) {
return attributeList == null ? Collections.<IElementType>emptySet() : getDeclarationTypes(Arrays.asList(attributeList));
}
public static Set<IElementType> getDeclarationTypes(@Nullable List<HaxeDeclarationAttribute> attributeList) {
if (attributeList == null || attributeList.isEmpty()) {
return Collections.emptySet();
}
final Set<IElementType> resultSet = new THashSet<IElementType>();
for (HaxeDeclarationAttribute attribute : attributeList) {
PsiElement result = attribute.getFirstChild();
final HaxeAccess access = attribute.getAccess();
if (access != null) {
result = access.getFirstChild();
}
if (result instanceof LeafPsiElement) {
resultSet.add(((LeafPsiElement)result).getElementType());
}
}
return resultSet;
}
@NotNull
public static List<HaxeClass> findUsingClasses(PsiFile file) {
final HaxeUsingStatement[] usingStatements = PsiTreeUtil.getChildrenOfType(file, HaxeUsingStatement.class);
if (usingStatements == null) {
return Collections.emptyList();
}
final List<HaxeClass> result = new ArrayList<HaxeClass>();
for (HaxeUsingStatement usingStatement : usingStatements) {
final HaxeExpression usingStatementExpression = usingStatement.getReferenceExpression();
if (usingStatementExpression == null) continue;
PsiManager manager = file.getManager();
if(manager != null) {
GlobalSearchScope scope = getScopeForElement(file);
final List<VirtualFile> classFiles = HaxeComponentFileNameIndex.getFilesNameByQName(usingStatementExpression.getText(), scope);
for(VirtualFile vf : classFiles) {
PsiFile pf = manager.findFile(vf);
if(pf != null) {
List<HaxeClass> classes = findComponentDeclarations(pf);
for(HaxeClass cls : classes) {
if(cls instanceof HaxeTypedefDeclaration) {
HaxeTypeOrAnonymous toa = ((HaxeTypedefDeclaration)cls).getTypeOrAnonymous();
if(toa != null) {
HaxeType t = toa.getType();
if(t != null) {
HaxeClass typeClass = t.getReferenceExpression().resolveHaxeClass().getHaxeClass();
if(typeClass != null) {
result.add(typeClass);
}
}
}
}
else {
result.add(cls);
}
}
}
}
}
//final HaxeClass haxeClass = findClassByQName(usingStatementExpression.getText(), file);
//if (haxeClass != null) {
// result.add(haxeClass);
//}
}
return result;
}
@NotNull
public static List<HaxeComponentName> getComponentNames(List<? extends HaxeNamedComponent> components) {
return ContainerUtil.map(components, new Function<HaxeNamedComponent, HaxeComponentName>() {
@Override
public HaxeComponentName fun(HaxeNamedComponent component) {
return component.getComponentName();
}
});
}
@NotNull
public static HaxeClassResolveResult findFirstParameterClass(HaxeNamedComponent haxeNamedComponent) {
final HaxeParameterListPsiMixinImpl parameterList = PsiTreeUtil.getChildOfType(haxeNamedComponent, HaxeParameterListPsiMixinImpl.class);
if (parameterList == null) {
return HaxeClassResolveResult.EMPTY;
}
final List<HaxeParameter> parameters = parameterList.getParametersAsList();
if (!parameters.isEmpty()) {
final HaxeParameter parameter = parameters.iterator().next();
return getHaxeClassResolveResult(parameter, HaxeGenericSpecialization.EMPTY);
}
return HaxeClassResolveResult.EMPTY;
}
public static HaxeParameterListPsiMixinImpl toHaxePsiParameterList(HaxeParameterList haxeParameterList) {
return new HaxeParameterListPsiMixinImpl(haxeParameterList.getNode());
}
public static HashSet<HaxeClass> getBaseClassesSet(@NotNull HaxeClass clazz) {
return getBaseClassesSet(clazz, new HashSet<HaxeClass>());
}
@NotNull
public static HashSet<HaxeClass> getBaseClassesSet(@NotNull HaxeClass clazz, @NotNull HashSet<HaxeClass> outClasses) {
List<HaxeType> types = new ArrayList<HaxeType>();
types.addAll(clazz.getHaxeExtendsList());
types.addAll(clazz.getHaxeImplementsList());
for(HaxeType baseType : types) {
final HaxeClass baseClass = HaxeResolveUtil.tryResolveClassByQName(baseType);
if(baseClass != null && outClasses.add(baseClass)) {
getBaseClassesSet(baseClass, outClasses);
}
}
return outClasses;
}
}