]> git.uio.no Git - ifi-stolz-refaktor.git/blob - case-study/jdt-after/ui/org/eclipse/jdt/internal/ui/callhierarchy/CallHierarchyContentProvider.java
402e5374c5f08dd340e418e1a6c342667b3b107c
[ifi-stolz-refaktor.git] / case-study / jdt-after / ui / org / eclipse / jdt / internal / ui / callhierarchy / CallHierarchyContentProvider.java
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
7  *
8  * Contributors:
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;
13
14 import java.lang.reflect.InvocationTargetException;
15
16 import org.eclipse.swt.widgets.Display;
17
18 import org.eclipse.core.runtime.IProgressMonitor;
19
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;
25
26 import org.eclipse.ui.progress.DeferredTreeContentManager;
27
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;
32
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;
40
41 import org.eclipse.jdt.ui.PreferenceConstants;
42
43 import org.eclipse.jdt.internal.ui.JavaPlugin;
44 import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
45
46 public class CallHierarchyContentProvider implements ITreeContentProvider {
47
48         /**
49          * A named preference that holds the types whose methods are by default expanded with
50          * constructors in the Call Hierarchy.
51          * <p>
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>
55          * </p>
56          * 
57          * @since 3.5
58          */
59         public static final String OLD_PREF_DEFAULT_EXPAND_WITH_CONSTRUCTORS= "CallHierarchy.defaultExpandWithConstructors"; //$NON-NLS-1$
60
61         private final static Object[] EMPTY_ARRAY= new Object[0];
62
63     private DeferredTreeContentManager fManager;
64     private CallHierarchyViewPart fPart;
65
66     private class MethodWrapperRunnable implements IRunnableWithProgress {
67         private MethodWrapper fMethodWrapper;
68         private MethodWrapper[] fCalls= null;
69
70         MethodWrapperRunnable(MethodWrapper methodWrapper) {
71             fMethodWrapper= methodWrapper;
72         }
73
74         public void run(IProgressMonitor pm) {
75                 fCalls= fMethodWrapper.getCalls(pm);
76         }
77
78         MethodWrapper[] getCalls() {
79             if (fCalls != null) {
80                 return fCalls;
81             }
82             return new MethodWrapper[0];
83         }
84     }
85
86     public CallHierarchyContentProvider(CallHierarchyViewPart part) {
87         super();
88         fPart= part;
89     }
90
91     /**
92      * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
93      */
94         public Object[] getChildren(Object parentElement) {
95                 if (parentElement instanceof TreeRoot) {
96                         TreeRoot dummyRoot= (TreeRoot)parentElement;
97                         return dummyRoot.getRoots();
98
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)
105                                         return children;
106                         }
107                         return fetchChildren(parentWrapper);
108
109                 } else if (parentElement instanceof MethodWrapper) {
110                         MethodWrapper methodWrapper= ((MethodWrapper)parentElement);
111
112                         if (shouldStopTraversion(methodWrapper)) {
113                                 return EMPTY_ARRAY;
114                         } else {
115                                 if (parentElement instanceof CallerMethodWrapper) {
116                                         CallerMethodWrapper caller= (CallerMethodWrapper)parentElement;
117                                         ensureDefaultExpandWithConstructors(caller);
118                                         if (caller.getExpandWithConstructors()) {
119                                                 IType type= caller.getMember().getDeclaringType();
120                                                 try {
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()) };
126                                                         } else {
127                                                                 IMember[] constructors= JavaElementUtil.getAllConstructors(type);
128                                                                 if (constructors.length == 0) {
129                                                                         constructors= new IType[] { type }; // type stands for the default constructor
130                                                                 }
131                                                                 Object children[]= new Object[constructors.length + 1];
132                                                                 return caller.generated_2421808467164206615(methodWrapper, constructors, children);
133                                                         }
134                                                 } catch (JavaModelException e) {
135                                                         JavaPlugin.log(e);
136                                                         return null;
137                                                 }
138
139                                         }
140                                 }
141                                 if (fManager != null) {
142                                         Object[] children= fManager.getChildren(new DeferredMethodWrapper(this, methodWrapper));
143                                         if (children != null)
144                                                 return children;
145                                 }
146                                 return fetchChildren(methodWrapper);
147                         }
148                 }
149         return EMPTY_ARRAY;
150     }
151
152         /**
153          * Sets the default "expand with constructors" mode for the method wrapper. Does nothing if the
154          * mode has already been set.
155          * 
156          * 
157          * @param wrapper the caller method wrapper
158          * @since 3.5
159          */
160         static void ensureDefaultExpandWithConstructors(CallerMethodWrapper wrapper) {
161
162                 if (!wrapper.isExpandWithConstructorsSet()) {
163                         if (CallHierarchyContentProvider.canExpandWithConstructors(wrapper)) {
164                                 IMethod method= (IMethod)wrapper.getMember();
165                                 IType type= method.getDeclaringType();
166                                 try {
167                                         boolean withConstructors= false;
168                                         if (type != null) {
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;
174                                                 }
175                                         }
176                                         wrapper.setExpandWithConstructors(withConstructors);
177                                 } catch (JavaModelException e) {
178                                         // ignore: expand mode will be off
179                                 }
180                         }
181                 }
182
183         }
184
185         /**
186          * Checks whether given caller method wrapper can be expanded with constructors.
187          * 
188          * @param wrapper the caller method wrapper
189          * @return <code> true</code> if the wrapper can be expanded with constructors, <code>false</code> otherwise
190          * @since 3.5
191          */
192         static boolean canExpandWithConstructors(CallerMethodWrapper wrapper) {
193                 IMember member= wrapper.getMember();
194                 if (!(member instanceof IMethod))
195                         return false;
196                 IMethod method= (IMethod)member;
197                 try {
198                         if (JdtFlags.isStatic(method) || method.isConstructor())
199                                 return false;
200                 } catch (JavaModelException e) {
201                         return false; // don't try to work with inexistent elements
202                 }
203                 return true;
204         }
205         
206         /**
207          * Checks if the method or its declaring type matches the pre-defined array of methods and types
208          * for default expand with constructors.
209          * 
210          * @param method the wrapped method
211          * @return <code>true</code> if method or type matches the pre-defined list, <code>false</code>
212          *         otherwise
213          * 
214          * @since 3.5
215          */
216         static boolean isInTheDefaultExpandWithConstructorList(IMethod method) {
217                 String serializedMembers= PreferenceConstants.getPreferenceStore().getString(PreferenceConstants.PREF_DEFAULT_EXPAND_WITH_CONSTRUCTORS_MEMBERS);
218                 if (serializedMembers.length() == 0)
219                         return false;
220                 
221                 String[] defaultMemberPatterns= serializedMembers.split(";"); //$NON-NLS-1$
222                 
223                 String methodName= method.getElementName();
224                 IType declaringType= method.getDeclaringType();
225                 String declaringTypeName= declaringType.getFullyQualifiedName('.');
226                 String superClassName;
227                 String[] superInterfaceNames;
228                 try {
229                         superClassName= declaringType.getSuperclassName();
230                         if (superClassName != null) {
231                                 superClassName= stripTypeArguments(superClassName);
232                         }
233                         superInterfaceNames= declaringType.getSuperInterfaceNames();
234                         for (int i= 0; i < superInterfaceNames.length; i++) {
235                                 superInterfaceNames[i]= stripTypeArguments(superInterfaceNames[i]);
236                         }
237                 } catch (JavaModelException e) {
238                         return false;
239                 }
240                 
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);
246                         
247                         if ("*".equals(defaultMethodName)) { //$NON-NLS-1$
248                                 if (declaringTypeName.equals(defaultTypeName)) {
249                                         return true;
250                                 }
251                         } else {
252                                 if (!methodName.equals(defaultMethodName)) {
253                                         continue;
254                                 }
255                                 if (declaringTypeName.equals(defaultTypeName)) {
256                                         return true;
257                                 }
258                         }
259                         if (superClassName != null && JavaModelUtil.isMatchingName(superClassName, defaultTypeName)) {
260                                 return true;
261                         }
262                         for (int j= 0; j < superInterfaceNames.length; j++) {
263                                 String superInterfaceName= superInterfaceNames[j];
264                                 if (JavaModelUtil.isMatchingName(superInterfaceName, defaultTypeName)) {
265                                         return true;
266                                 }
267                         }
268                 }
269                 return false;
270         }
271
272         /**
273          * Strips type arguments from the given type name and returns only erased type name.
274          * 
275          * @param typeName the type name
276          * @return the erased type name
277          * 
278          * @since 3.6
279          */
280         private static String stripTypeArguments(String typeName) {
281                 int pos= typeName.indexOf('<');
282                 if (pos != -1)
283                         return typeName.substring(0, pos);
284                 return typeName;
285         }
286
287         protected Object[] fetchChildren(final MethodWrapper methodWrapper) {
288         IRunnableContext context= JavaPlugin.getActiveWorkbenchWindow();
289         MethodWrapperRunnable runnable= new MethodWrapperRunnable(methodWrapper);
290         try {
291             context.run(true, true, runnable);
292         } catch (InvocationTargetException e) {
293             ExceptionHandler.handle(e, CallHierarchyMessages.CallHierarchyContentProvider_searchError_title, CallHierarchyMessages.CallHierarchyContentProvider_searchError_message);
294             return EMPTY_ARRAY;
295         } catch (InterruptedException e) {
296                 final CallerMethodWrapper element= (CallerMethodWrapper)methodWrapper;
297                 if (!isExpandWithConstructors(element)) {
298                         Display.getDefault().asyncExec(new Runnable() {
299                                 public void run() {
300                                         collapseAndRefresh(element);
301                                 }
302                         });
303                 }
304         }
305
306         return runnable.getCalls();
307     }
308
309
310     /**
311      * Returns whether the given element is an "Expand witch Constructors" node.
312      * 
313          * @param element a method wrapped
314          * @return <code>true</code> iff the element is an "Expand witch Constructors" node
315          * @since 3.5
316          */
317         static boolean isExpandWithConstructors(MethodWrapper element) {
318                 return element instanceof CallerMethodWrapper && ((CallerMethodWrapper)element).getExpandWithConstructors();
319         }
320
321         /**
322      * Collapses and refreshes the given element when search has been canceled.
323      * 
324      * @param element the element on which search has been canceled and which has to be collapsed
325      * @since 3.5
326      */
327     protected void collapseAndRefresh(MethodWrapper element) {
328         CallHierarchyViewer viewer= fPart.getViewer();
329         
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.
332                  * 
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.
335          * 
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.
338          * 
339          * The fix is to do nothing when the RealCaller's parent is expandWithConstructors.
340                  */
341         boolean elementStays= true;
342         if (element instanceof RealCallers) {
343                 elementStays= isExpandWithConstructors(element.getParent());
344         }
345                 element.generated_5934007691874227139(viewer, elementStays);
346     }
347
348         /**
349      * Returns the call hierarchy view part.
350          * 
351          * @return the call hierarchy view part
352          * @since 3.5
353          */
354     public CallHierarchyViewPart getViewPart() {
355         return fPart;
356     }
357
358         public boolean shouldStopTraversion(MethodWrapper methodWrapper) {
359         return methodWrapper.generated_1632587395956144884();
360     }
361
362         /**
363      * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
364      */
365     public Object[] getElements(Object inputElement) {
366         return getChildren(inputElement);
367     }
368
369     /**
370      * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
371      */
372     public Object getParent(Object element) {
373         if (element instanceof MethodWrapper) {
374             return ((MethodWrapper) element).getParent();
375         }
376
377         return null;
378     }
379
380     /**
381      * @see org.eclipse.jface.viewers.IContentProvider#dispose()
382      */
383     public void dispose() {
384         // Nothing to dispose
385     }
386
387     /**
388          * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
389          */
390         public boolean hasChildren(Object element) {
391                 if (element == TreeRoot.EMPTY_ROOT || element == TreeTermination.SEARCH_CANCELED) {
392                         return false;
393                 }
394
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) {
401                         return true;
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.
405                         return true;
406                 }
407
408                 return false; // the "Update ..." placeholder has no children
409         }
410
411         /**
412          * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
413          *      java.lang.Object, java.lang.Object)
414          */
415     public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
416         if (oldInput instanceof TreeRoot) {
417                 MethodWrapper[] roots = ((TreeRoot) oldInput).getRoots();
418                         cancelJobs(roots);
419         }
420         if (viewer instanceof AbstractTreeViewer) {
421             fManager = new DeferredTreeContentManager((AbstractTreeViewer) viewer, fPart.getSite());
422         }
423     }
424
425     /**
426      * Cancel all current jobs.
427      * @param wrappers the parents to cancel jobs for
428      */
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);
434                         }
435             if (fPart != null) {
436                 fPart.setCancelEnabled(false);
437             }
438         }
439     }
440
441     /**
442      *
443      */
444     public void doneFetching() {
445         if (fPart != null) {
446             fPart.setCancelEnabled(false);
447         }
448     }
449
450     /**
451      *
452      */
453     public void startFetching() {
454         if (fPart != null) {
455             fPart.setCancelEnabled(true);
456         }
457     }
458 }