/* * 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.ide.hierarchy.method; import com.intellij.ide.hierarchy.HierarchyBrowserManager; import com.intellij.ide.hierarchy.HierarchyNodeDescriptor; import com.intellij.ide.hierarchy.HierarchyTreeStructure; import com.intellij.openapi.project.Project; import com.intellij.plugins.haxe.ide.hierarchy.HaxeHierarchyUtils; import com.intellij.plugins.haxe.ide.index.HaxeInheritanceDefinitionsSearchExecutor; import com.intellij.plugins.haxe.lang.psi.HaxeAnonymousType; import com.intellij.plugins.haxe.lang.psi.HaxeClass; import com.intellij.plugins.haxe.lang.psi.HaxePsiModifier; import com.intellij.psi.*; import com.intellij.psi.search.searches.FunctionalExpressionSearch; import com.intellij.util.ArrayUtil; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Created by ebishton on 11/1/14. */ public class HaxeMethodHierarchyTreeStructure extends HierarchyTreeStructure { SmartPsiElementPointer<PsiMethod> myMethod; public HaxeMethodHierarchyTreeStructure(@NotNull Project project, @NotNull PsiMethod method) { super(project, new HaxeMethodHierarchyNodeDescriptor(project, null, method.getContainingClass(), true /*is_base*/, null)); ((HaxeMethodHierarchyNodeDescriptor)getBaseDescriptor()).setTreeStructure(this); myMethod = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(method); setBaseElement(myBaseDescriptor); //to set myRoot } // This is really the only thing we were overriding from MethodHierarchyTreeStructure. public static List<HaxeClass> getSubclasses(HaxeClass theClass) { final List<HaxeClass> subClasses = HaxeInheritanceDefinitionsSearchExecutor.getItemsByQName(theClass); return subClasses; } // // Most of the rest of this file was lifted from MethodHierarchyTreeStructure.java // because we couldn't override the private getSubclasses(), and because // buildChildren was final in that class. :/ // @Nullable public final PsiMethod getBaseMethod() { final PsiElement element = myMethod.getElement(); return element instanceof PsiMethod ? (PsiMethod)element : null; } @NotNull @Override protected Object[] buildChildren(@NotNull final HierarchyNodeDescriptor descriptor) { final HaxeClass theHaxeClass = ((HaxeMethodHierarchyNodeDescriptor) descriptor).getHaxeClass(); if (null == theHaxeClass) return ArrayUtil.EMPTY_OBJECT_ARRAY; if (theHaxeClass instanceof HaxeAnonymousType) return ArrayUtil.EMPTY_OBJECT_ARRAY; if (theHaxeClass.hasModifierProperty(HaxePsiModifier.FINAL)) return ArrayUtil.EMPTY_OBJECT_ARRAY; final Collection<HaxeClass> subclasses = getSubclasses(theHaxeClass); final List<HierarchyNodeDescriptor> descriptors = new ArrayList<HierarchyNodeDescriptor>(subclasses.size()); for (final PsiClass aClass : subclasses) { if (HierarchyBrowserManager.getInstance(myProject).getState().HIDE_CLASSES_WHERE_METHOD_NOT_IMPLEMENTED) { if (shouldHideClass(aClass)) { continue; } } final HaxeMethodHierarchyNodeDescriptor d = new HaxeMethodHierarchyNodeDescriptor(myProject, descriptor, aClass, false, this); descriptors.add(d); } final PsiMethod existingMethod = ((HaxeMethodHierarchyNodeDescriptor)descriptor).getMethod(theHaxeClass, false); if (existingMethod != null) { FunctionalExpressionSearch.search(existingMethod).forEach(new Processor<PsiFunctionalExpression>() { @Override public boolean process(PsiFunctionalExpression expression) { descriptors.add(new HaxeMethodHierarchyNodeDescriptor(myProject, descriptor, expression, false, HaxeMethodHierarchyTreeStructure.this)); return true; } }); } return descriptors.toArray(new HierarchyNodeDescriptor[descriptors.size()]); } private boolean shouldHideClass(final PsiClass psiClass) { if (getMethod(psiClass, false) != null || isSuperClassForBaseClass(psiClass)) { return false; } if (hasBaseClassMethod(psiClass) || isAbstract(psiClass)) { for (final PsiClass subclass : getSubclasses((HaxeClass)psiClass)) { if (!shouldHideClass(subclass)) { return false; } } return true; } return false; } private boolean isAbstract(final PsiModifierListOwner owner) { return owner.hasModifierProperty(PsiModifier.ABSTRACT); } private boolean hasBaseClassMethod(final PsiClass psiClass) { final PsiMethod baseClassMethod = getMethod(psiClass, true); return baseClassMethod != null && !isAbstract(baseClassMethod); } private PsiMethod getMethod(final PsiClass aClass, final boolean checkBases) { return HaxeHierarchyUtils.findBaseMethodInClass(getBaseMethod(), aClass, checkBases); } boolean isSuperClassForBaseClass(final PsiClass aClass) { final PsiMethod baseMethod = getBaseMethod(); if (baseMethod == null) { return false; } final PsiClass baseClass = baseMethod.getContainingClass(); if (baseClass == null) { return false; } // NB: parameters here are at CORRECT places!!! return baseClass.isInheritor(aClass, true); } }