/* * Copyright 2000-2013 JetBrains s.r.o. * Copyright 2014-2015 AS3Boyan * Copyright 2014-2015 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.call; import com.intellij.ide.hierarchy.HierarchyNodeDescriptor; import com.intellij.ide.hierarchy.HierarchyTreeStructure; import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor; import com.intellij.openapi.project.Project; import com.intellij.plugins.haxe.ide.hierarchy.HaxeHierarchyTimeoutHandler; import com.intellij.plugins.haxe.lang.psi.HaxeCallExpression; import com.intellij.plugins.haxe.lang.psi.HaxeMethod; import com.intellij.plugins.haxe.lang.psi.HaxeNewExpression; import com.intellij.plugins.haxe.lang.psi.HaxeReference; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.Processor; import com.intellij.util.Query; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; /** * Created by ebishton on 1/12/15. */ public class HaxeCalleeMethodsTreeStructure extends HierarchyTreeStructure { private final String myScopeType; /** * Should be called in read action */ public HaxeCalleeMethodsTreeStructure(final Project project, final PsiMethod method, final String scopeType) { super(project, new CallHierarchyNodeDescriptor(project, null, method, true, false)); myScopeType = scopeType; } @NotNull protected final Object[] buildChildren(@NotNull final HierarchyNodeDescriptor descriptor) { final HaxeHierarchyTimeoutHandler timeoutHandler = new HaxeHierarchyTimeoutHandler(); try { final PsiMember enclosingElement = ((CallHierarchyNodeDescriptor)descriptor).getEnclosingElement(); if (!(enclosingElement instanceof PsiMethod)) { return ArrayUtil.EMPTY_OBJECT_ARRAY; } final PsiMethod method = (PsiMethod)enclosingElement; final ArrayList<PsiMethod> methods = new ArrayList<PsiMethod>(); final PsiCodeBlock body = method.getBody(); if (body != null) { visitor(body, methods); } final PsiMethod baseMethod = (PsiMethod)((CallHierarchyNodeDescriptor)getBaseDescriptor()).getTargetElement(); final PsiClass baseClass = baseMethod.getContainingClass(); final HashMap<PsiMethod, CallHierarchyNodeDescriptor> methodToDescriptorMap = new HashMap<PsiMethod, CallHierarchyNodeDescriptor>(); final ArrayList<CallHierarchyNodeDescriptor> result = new ArrayList<CallHierarchyNodeDescriptor>(); for (final PsiMethod calledMethod : methods) { if (timeoutHandler.checkAndCancelIfNecessary()) { break; } if (!isInScope(baseClass, calledMethod, myScopeType)) continue; CallHierarchyNodeDescriptor d = methodToDescriptorMap.get(calledMethod); if (d == null) { d = new CallHierarchyNodeDescriptor(myProject, descriptor, calledMethod, false, false); methodToDescriptorMap.put(calledMethod, d); result.add(d); } else { d.incrementUsageCount(); } } // also add overriding methods as children if (!timeoutHandler.isCanceled()) { Query<PsiMethod> query = HaxeMethodsSearch.search(method, timeoutHandler); query.forEach(new Processor<PsiMethod>() { @Override public boolean process(PsiMethod overridingMethod) { if (isInScope(baseClass, overridingMethod, myScopeType)) { final CallHierarchyNodeDescriptor node = new CallHierarchyNodeDescriptor(myProject, descriptor, overridingMethod, false, false); if (!result.contains(node)) result.add(node); } return timeoutHandler.checkAndCancelIfNecessary(); } }); } return ArrayUtil.toObjectArray(result); } finally { // This is in a finally clause because a cancellation would otherwise throw // right past us. timeoutHandler.stop(); // Clean up. if (timeoutHandler.isCanceled()) { timeoutHandler.postCanceledDialog(myProject); } } } private static void visitor(final PsiElement element, final ArrayList<PsiMethod> methods) { final PsiElement[] children = element.getChildren(); for (final PsiElement child : children) { visitor(child, methods); if (child instanceof HaxeCallExpression) { //final PsiReference ref = ((HaxeCallExpression)child).getReference(); // TODO: Fix. This code shouldn't reach down into the expression this way. // Expressions should NOT be derived from references, though they are. This is implemented via the BNF. final PsiReference ref = PsiTreeUtil.findChildOfType(child, HaxeReference.class); final PsiElement resolved = ref.resolve(); if (null != resolved && resolved instanceof HaxeMethod) { methods.add((HaxeMethod)resolved); } } else if (child instanceof HaxeNewExpression) { final HaxeReference ref = (HaxeNewExpression)child.getReference(); final PsiElement resolved = ref.resolve(); if (null != resolved && resolved instanceof HaxeMethod) { methods.add((HaxeMethod)resolved); } } } } }