1 /*******************************************************************************
2 * Copyright (c) 2000, 2012 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation
10 * (report 36180: Callers/Callees view)
11 *******************************************************************************/
12 package org.eclipse.jdt.internal.ui.callhierarchy;
14 import java.lang.reflect.InvocationTargetException;
16 import org.eclipse.swt.widgets.Display;
18 import org.eclipse.core.runtime.IProgressMonitor;
20 import org.eclipse.jface.operation.IRunnableContext;
21 import org.eclipse.jface.operation.IRunnableWithProgress;
22 import org.eclipse.jface.viewers.AbstractTreeViewer;
23 import org.eclipse.jface.viewers.ITreeContentProvider;
24 import org.eclipse.jface.viewers.Viewer;
26 import org.eclipse.ui.progress.DeferredTreeContentManager;
28 import org.eclipse.jdt.core.IMember;
29 import org.eclipse.jdt.core.IMethod;
30 import org.eclipse.jdt.core.IType;
31 import org.eclipse.jdt.core.JavaModelException;
33 import org.eclipse.jdt.internal.corext.callhierarchy.CallerMethodWrapper;
34 import org.eclipse.jdt.internal.corext.callhierarchy.MethodCall;
35 import org.eclipse.jdt.internal.corext.callhierarchy.MethodWrapper;
36 import org.eclipse.jdt.internal.corext.callhierarchy.RealCallers;
37 import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
38 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
39 import org.eclipse.jdt.internal.corext.util.JdtFlags;
41 import org.eclipse.jdt.ui.PreferenceConstants;
43 import org.eclipse.jdt.internal.ui.JavaPlugin;
44 import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
46 public class CallHierarchyContentProvider implements ITreeContentProvider {
49 * A named preference that holds the types whose methods are by default expanded with
50 * constructors in the Call Hierarchy.
52 * Value is of type <code>String</code>: semicolon separated list of fully qualified type names.
53 * <strong> It has been replaced in 3.6 by API:
54 * {@link PreferenceConstants#PREF_DEFAULT_EXPAND_WITH_CONSTRUCTORS_MEMBERS}</strong>
59 public static final String OLD_PREF_DEFAULT_EXPAND_WITH_CONSTRUCTORS= "CallHierarchy.defaultExpandWithConstructors"; //$NON-NLS-1$
61 private final static Object[] EMPTY_ARRAY= new Object[0];
63 private DeferredTreeContentManager fManager;
64 private CallHierarchyViewPart fPart;
66 private class MethodWrapperRunnable implements IRunnableWithProgress {
67 private MethodWrapper fMethodWrapper;
68 private MethodWrapper[] fCalls= null;
70 MethodWrapperRunnable(MethodWrapper methodWrapper) {
71 fMethodWrapper= methodWrapper;
74 public void run(IProgressMonitor pm) {
75 fCalls= fMethodWrapper.getCalls(pm);
78 MethodWrapper[] getCalls() {
82 return new MethodWrapper[0];
86 public CallHierarchyContentProvider(CallHierarchyViewPart part) {
92 * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
94 public Object[] getChildren(Object parentElement) {
95 if (parentElement instanceof TreeRoot) {
96 TreeRoot dummyRoot= (TreeRoot)parentElement;
97 return dummyRoot.getRoots();
99 } else if (parentElement instanceof RealCallers) {
100 MethodWrapper parentWrapper= ((RealCallers)parentElement).getParent();
101 RealCallers element= ((RealCallers)parentElement);
102 if (fManager != null) {
103 Object[] children= fManager.getChildren(new DeferredMethodWrapper(this, element));
104 if (children != null)
107 return fetchChildren(parentWrapper);
109 } else if (parentElement instanceof MethodWrapper) {
110 MethodWrapper methodWrapper= ((MethodWrapper)parentElement);
112 if (shouldStopTraversion(methodWrapper)) {
115 if (parentElement instanceof CallerMethodWrapper) {
116 CallerMethodWrapper caller= (CallerMethodWrapper)parentElement;
117 ensureDefaultExpandWithConstructors(caller);
118 if (caller.getExpandWithConstructors()) {
119 IType type= caller.getMember().getDeclaringType();
121 if (type.isAnonymous()) {
122 IMember anonymousClass= type;
123 MethodCall anonymousConstructor= new MethodCall(anonymousClass);
124 CallerMethodWrapper anonymousWrapper= (CallerMethodWrapper)caller.createMethodWrapper(anonymousConstructor);
125 return new Object[] { anonymousWrapper, new RealCallers(methodWrapper, caller.getMethodCall()) };
127 IMember[] constructors= JavaElementUtil.getAllConstructors(type);
128 if (constructors.length == 0) {
129 constructors= new IType[] { type }; // type stands for the default constructor
131 Object children[]= new Object[constructors.length + 1];
132 return caller.generated_2421808467164206615(methodWrapper, constructors, children);
134 } catch (JavaModelException e) {
141 if (fManager != null) {
142 Object[] children= fManager.getChildren(new DeferredMethodWrapper(this, methodWrapper));
143 if (children != null)
146 return fetchChildren(methodWrapper);
153 * Sets the default "expand with constructors" mode for the method wrapper. Does nothing if the
154 * mode has already been set.
157 * @param wrapper the caller method wrapper
160 static void ensureDefaultExpandWithConstructors(CallerMethodWrapper wrapper) {
162 if (!wrapper.isExpandWithConstructorsSet()) {
163 if (CallHierarchyContentProvider.canExpandWithConstructors(wrapper)) {
164 IMethod method= (IMethod)wrapper.getMember();
165 IType type= method.getDeclaringType();
167 boolean withConstructors= false;
169 boolean anonymousPref= PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.PREF_ANONYMOUS_EXPAND_WITH_CONSTRUCTORS);
170 if (anonymousPref && type.isAnonymous()) {
171 withConstructors= true;
172 } else if (isInTheDefaultExpandWithConstructorList(method)) {
173 withConstructors= true;
176 wrapper.setExpandWithConstructors(withConstructors);
177 } catch (JavaModelException e) {
178 // ignore: expand mode will be off
186 * Checks whether given caller method wrapper can be expanded with constructors.
188 * @param wrapper the caller method wrapper
189 * @return <code> true</code> if the wrapper can be expanded with constructors, <code>false</code> otherwise
192 static boolean canExpandWithConstructors(CallerMethodWrapper wrapper) {
193 IMember member= wrapper.getMember();
194 if (!(member instanceof IMethod))
196 IMethod method= (IMethod)member;
198 if (JdtFlags.isStatic(method) || method.isConstructor())
200 } catch (JavaModelException e) {
201 return false; // don't try to work with inexistent elements
207 * Checks if the method or its declaring type matches the pre-defined array of methods and types
208 * for default expand with constructors.
210 * @param method the wrapped method
211 * @return <code>true</code> if method or type matches the pre-defined list, <code>false</code>
216 static boolean isInTheDefaultExpandWithConstructorList(IMethod method) {
217 String serializedMembers= PreferenceConstants.getPreferenceStore().getString(PreferenceConstants.PREF_DEFAULT_EXPAND_WITH_CONSTRUCTORS_MEMBERS);
218 if (serializedMembers.length() == 0)
221 String[] defaultMemberPatterns= serializedMembers.split(";"); //$NON-NLS-1$
223 String methodName= method.getElementName();
224 IType declaringType= method.getDeclaringType();
225 String declaringTypeName= declaringType.getFullyQualifiedName('.');
226 String superClassName;
227 String[] superInterfaceNames;
229 superClassName= declaringType.getSuperclassName();
230 if (superClassName != null) {
231 superClassName= stripTypeArguments(superClassName);
233 superInterfaceNames= declaringType.getSuperInterfaceNames();
234 for (int i= 0; i < superInterfaceNames.length; i++) {
235 superInterfaceNames[i]= stripTypeArguments(superInterfaceNames[i]);
237 } catch (JavaModelException e) {
241 for (int i= 0; i < defaultMemberPatterns.length; i++) {
242 String defaultMemberPattern= defaultMemberPatterns[i];
243 int pos= defaultMemberPattern.lastIndexOf('.');
244 String defaultTypeName= defaultMemberPattern.substring(0, pos);
245 String defaultMethodName= defaultMemberPattern.substring(pos + 1);
247 if ("*".equals(defaultMethodName)) { //$NON-NLS-1$
248 if (declaringTypeName.equals(defaultTypeName)) {
252 if (!methodName.equals(defaultMethodName)) {
255 if (declaringTypeName.equals(defaultTypeName)) {
259 if (superClassName != null && JavaModelUtil.isMatchingName(superClassName, defaultTypeName)) {
262 for (int j= 0; j < superInterfaceNames.length; j++) {
263 String superInterfaceName= superInterfaceNames[j];
264 if (JavaModelUtil.isMatchingName(superInterfaceName, defaultTypeName)) {
273 * Strips type arguments from the given type name and returns only erased type name.
275 * @param typeName the type name
276 * @return the erased type name
280 private static String stripTypeArguments(String typeName) {
281 int pos= typeName.indexOf('<');
283 return typeName.substring(0, pos);
287 protected Object[] fetchChildren(final MethodWrapper methodWrapper) {
288 IRunnableContext context= JavaPlugin.getActiveWorkbenchWindow();
289 MethodWrapperRunnable runnable= new MethodWrapperRunnable(methodWrapper);
291 context.run(true, true, runnable);
292 } catch (InvocationTargetException e) {
293 ExceptionHandler.handle(e, CallHierarchyMessages.CallHierarchyContentProvider_searchError_title, CallHierarchyMessages.CallHierarchyContentProvider_searchError_message);
295 } catch (InterruptedException e) {
296 final CallerMethodWrapper element= (CallerMethodWrapper)methodWrapper;
297 if (!isExpandWithConstructors(element)) {
298 Display.getDefault().asyncExec(new Runnable() {
300 collapseAndRefresh(element);
306 return runnable.getCalls();
311 * Returns whether the given element is an "Expand witch Constructors" node.
313 * @param element a method wrapped
314 * @return <code>true</code> iff the element is an "Expand witch Constructors" node
317 static boolean isExpandWithConstructors(MethodWrapper element) {
318 return element instanceof CallerMethodWrapper && ((CallerMethodWrapper)element).getExpandWithConstructors();
322 * Collapses and refreshes the given element when search has been canceled.
324 * @param element the element on which search has been canceled and which has to be collapsed
327 protected void collapseAndRefresh(MethodWrapper element) {
328 CallHierarchyViewer viewer= fPart.getViewer();
330 /* Problem case: The user expands the RealCallers node and then unchecks "Expand with Constructors"
331 * while the search for the real callers is still in progress.
333 * In this scenario, the RealCallers is not even part of the current tree any more, since the
334 * ExpandWithConstructorsAction already toggled the flag and refreshed the tree.
336 * But since setExpandedState(element, false) walks up the getParent() chain of the given element,
337 * this causes the parent's children to be created, which would wrongly start a deferred search.
339 * The fix is to do nothing when the RealCaller's parent is expandWithConstructors.
341 boolean elementStays= true;
342 if (element instanceof RealCallers) {
343 elementStays= isExpandWithConstructors(element.getParent());
345 element.generated_5934007691874227139(viewer, elementStays);
349 * Returns the call hierarchy view part.
351 * @return the call hierarchy view part
354 public CallHierarchyViewPart getViewPart() {
358 public boolean shouldStopTraversion(MethodWrapper methodWrapper) {
359 return methodWrapper.generated_1632587395956144884();
363 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
365 public Object[] getElements(Object inputElement) {
366 return getChildren(inputElement);
370 * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
372 public Object getParent(Object element) {
373 if (element instanceof MethodWrapper) {
374 return ((MethodWrapper) element).getParent();
381 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
383 public void dispose() {
384 // Nothing to dispose
388 * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
390 public boolean hasChildren(Object element) {
391 if (element == TreeRoot.EMPTY_ROOT || element == TreeTermination.SEARCH_CANCELED) {
395 // Only certain members can have subelements, so there's no need to fool the
396 // user into believing that there is more
397 if (element instanceof MethodWrapper) {
398 MethodWrapper methodWrapper= (MethodWrapper) element;
399 return methodWrapper.generated_9121535494207793525(this);
400 } else if (element instanceof TreeRoot) {
402 } else if (element instanceof DeferredMethodWrapper) {
403 // Err on the safe side by returning true even though
404 // we don't know for sure that there are children.
408 return false; // the "Update ..." placeholder has no children
412 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
413 * java.lang.Object, java.lang.Object)
415 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
416 if (oldInput instanceof TreeRoot) {
417 MethodWrapper[] roots = ((TreeRoot) oldInput).getRoots();
420 if (viewer instanceof AbstractTreeViewer) {
421 fManager = new DeferredTreeContentManager((AbstractTreeViewer) viewer, fPart.getSite());
426 * Cancel all current jobs.
427 * @param wrappers the parents to cancel jobs for
429 void cancelJobs(MethodWrapper[] wrappers) {
430 if (fManager != null && wrappers != null) {
431 for (int i= 0; i < wrappers.length; i++) {
432 MethodWrapper wrapper= wrappers[i];
433 fManager.cancel(wrapper);
436 fPart.setCancelEnabled(false);
444 public void doneFetching() {
446 fPart.setCancelEnabled(false);
453 public void startFetching() {
455 fPart.setCancelEnabled(true);