]> git.uio.no Git - ifi-stolz-refaktor.git/blobdiff - case-study/jdt-after/ui/org/eclipse/jdt/internal/ui/callhierarchy/CallHierarchyContentProvider.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / ui / org / eclipse / jdt / internal / ui / callhierarchy / CallHierarchyContentProvider.java
diff --git a/case-study/jdt-after/ui/org/eclipse/jdt/internal/ui/callhierarchy/CallHierarchyContentProvider.java b/case-study/jdt-after/ui/org/eclipse/jdt/internal/ui/callhierarchy/CallHierarchyContentProvider.java
new file mode 100644 (file)
index 0000000..402e537
--- /dev/null
@@ -0,0 +1,458 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation
+ *                     (report 36180: Callers/Callees view)
+ *******************************************************************************/
+package org.eclipse.jdt.internal.ui.callhierarchy;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.swt.widgets.Display;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+import org.eclipse.ui.progress.DeferredTreeContentManager;
+
+import org.eclipse.jdt.core.IMember;
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+
+import org.eclipse.jdt.internal.corext.callhierarchy.CallerMethodWrapper;
+import org.eclipse.jdt.internal.corext.callhierarchy.MethodCall;
+import org.eclipse.jdt.internal.corext.callhierarchy.MethodWrapper;
+import org.eclipse.jdt.internal.corext.callhierarchy.RealCallers;
+import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.internal.corext.util.JdtFlags;
+
+import org.eclipse.jdt.ui.PreferenceConstants;
+
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
+
+public class CallHierarchyContentProvider implements ITreeContentProvider {
+
+       /**
+        * A named preference that holds the types whose methods are by default expanded with
+        * constructors in the Call Hierarchy.
+        * <p>
+        * Value is of type <code>String</code>: semicolon separated list of fully qualified type names.
+        * <strong> It has been replaced in 3.6 by API:
+        * {@link PreferenceConstants#PREF_DEFAULT_EXPAND_WITH_CONSTRUCTORS_MEMBERS}</strong>
+        * </p>
+        * 
+        * @since 3.5
+        */
+       public static final String OLD_PREF_DEFAULT_EXPAND_WITH_CONSTRUCTORS= "CallHierarchy.defaultExpandWithConstructors"; //$NON-NLS-1$
+
+       private final static Object[] EMPTY_ARRAY= new Object[0];
+
+    private DeferredTreeContentManager fManager;
+    private CallHierarchyViewPart fPart;
+
+    private class MethodWrapperRunnable implements IRunnableWithProgress {
+        private MethodWrapper fMethodWrapper;
+        private MethodWrapper[] fCalls= null;
+
+        MethodWrapperRunnable(MethodWrapper methodWrapper) {
+            fMethodWrapper= methodWrapper;
+        }
+
+        public void run(IProgressMonitor pm) {
+               fCalls= fMethodWrapper.getCalls(pm);
+        }
+
+        MethodWrapper[] getCalls() {
+            if (fCalls != null) {
+                return fCalls;
+            }
+            return new MethodWrapper[0];
+        }
+    }
+
+    public CallHierarchyContentProvider(CallHierarchyViewPart part) {
+        super();
+        fPart= part;
+    }
+
+    /**
+     * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
+     */
+       public Object[] getChildren(Object parentElement) {
+               if (parentElement instanceof TreeRoot) {
+                       TreeRoot dummyRoot= (TreeRoot)parentElement;
+                       return dummyRoot.getRoots();
+
+               } else if (parentElement instanceof RealCallers) {
+                       MethodWrapper parentWrapper= ((RealCallers)parentElement).getParent();
+                       RealCallers element= ((RealCallers)parentElement);
+                       if (fManager != null) {
+                               Object[] children= fManager.getChildren(new DeferredMethodWrapper(this, element));
+                               if (children != null)
+                                       return children;
+                       }
+                       return fetchChildren(parentWrapper);
+
+               } else if (parentElement instanceof MethodWrapper) {
+                       MethodWrapper methodWrapper= ((MethodWrapper)parentElement);
+
+                       if (shouldStopTraversion(methodWrapper)) {
+                               return EMPTY_ARRAY;
+                       } else {
+                               if (parentElement instanceof CallerMethodWrapper) {
+                                       CallerMethodWrapper caller= (CallerMethodWrapper)parentElement;
+                                       ensureDefaultExpandWithConstructors(caller);
+                                       if (caller.getExpandWithConstructors()) {
+                                               IType type= caller.getMember().getDeclaringType();
+                                               try {
+                                                       if (type.isAnonymous()) {
+                                                               IMember anonymousClass= type;
+                                                               MethodCall anonymousConstructor= new MethodCall(anonymousClass);
+                                                               CallerMethodWrapper anonymousWrapper= (CallerMethodWrapper)caller.createMethodWrapper(anonymousConstructor);
+                                                               return new Object[] { anonymousWrapper, new RealCallers(methodWrapper, caller.getMethodCall()) };
+                                                       } else {
+                                                               IMember[] constructors= JavaElementUtil.getAllConstructors(type);
+                                                               if (constructors.length == 0) {
+                                                                       constructors= new IType[] { type }; // type stands for the default constructor
+                                                               }
+                                                               Object children[]= new Object[constructors.length + 1];
+                                                               return caller.generated_2421808467164206615(methodWrapper, constructors, children);
+                                                       }
+                                               } catch (JavaModelException e) {
+                                                       JavaPlugin.log(e);
+                                                       return null;
+                                               }
+
+                                       }
+                               }
+                               if (fManager != null) {
+                                       Object[] children= fManager.getChildren(new DeferredMethodWrapper(this, methodWrapper));
+                                       if (children != null)
+                                               return children;
+                               }
+                               return fetchChildren(methodWrapper);
+                       }
+               }
+        return EMPTY_ARRAY;
+    }
+
+       /**
+        * Sets the default "expand with constructors" mode for the method wrapper. Does nothing if the
+        * mode has already been set.
+        * 
+        * 
+        * @param wrapper the caller method wrapper
+        * @since 3.5
+        */
+       static void ensureDefaultExpandWithConstructors(CallerMethodWrapper wrapper) {
+
+               if (!wrapper.isExpandWithConstructorsSet()) {
+                       if (CallHierarchyContentProvider.canExpandWithConstructors(wrapper)) {
+                               IMethod method= (IMethod)wrapper.getMember();
+                               IType type= method.getDeclaringType();
+                               try {
+                                       boolean withConstructors= false;
+                                       if (type != null) {
+                                               boolean anonymousPref= PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.PREF_ANONYMOUS_EXPAND_WITH_CONSTRUCTORS);
+                                               if (anonymousPref && type.isAnonymous()) {
+                                                       withConstructors= true;
+                                               } else if (isInTheDefaultExpandWithConstructorList(method)) {
+                                                       withConstructors= true;
+                                               }
+                                       }
+                                       wrapper.setExpandWithConstructors(withConstructors);
+                               } catch (JavaModelException e) {
+                                       // ignore: expand mode will be off
+                               }
+                       }
+               }
+
+       }
+
+       /**
+        * Checks whether given caller method wrapper can be expanded with constructors.
+        * 
+        * @param wrapper the caller method wrapper
+        * @return <code> true</code> if the wrapper can be expanded with constructors, <code>false</code> otherwise
+        * @since 3.5
+        */
+       static boolean canExpandWithConstructors(CallerMethodWrapper wrapper) {
+               IMember member= wrapper.getMember();
+               if (!(member instanceof IMethod))
+                       return false;
+               IMethod method= (IMethod)member;
+               try {
+                       if (JdtFlags.isStatic(method) || method.isConstructor())
+                               return false;
+               } catch (JavaModelException e) {
+                       return false; // don't try to work with inexistent elements
+               }
+               return true;
+       }
+       
+       /**
+        * Checks if the method or its declaring type matches the pre-defined array of methods and types
+        * for default expand with constructors.
+        * 
+        * @param method the wrapped method
+        * @return <code>true</code> if method or type matches the pre-defined list, <code>false</code>
+        *         otherwise
+        * 
+        * @since 3.5
+        */
+       static boolean isInTheDefaultExpandWithConstructorList(IMethod method) {
+               String serializedMembers= PreferenceConstants.getPreferenceStore().getString(PreferenceConstants.PREF_DEFAULT_EXPAND_WITH_CONSTRUCTORS_MEMBERS);
+               if (serializedMembers.length() == 0)
+                       return false;
+               
+               String[] defaultMemberPatterns= serializedMembers.split(";"); //$NON-NLS-1$
+               
+               String methodName= method.getElementName();
+               IType declaringType= method.getDeclaringType();
+               String declaringTypeName= declaringType.getFullyQualifiedName('.');
+               String superClassName;
+               String[] superInterfaceNames;
+               try {
+                       superClassName= declaringType.getSuperclassName();
+                       if (superClassName != null) {
+                               superClassName= stripTypeArguments(superClassName);
+                       }
+                       superInterfaceNames= declaringType.getSuperInterfaceNames();
+                       for (int i= 0; i < superInterfaceNames.length; i++) {
+                               superInterfaceNames[i]= stripTypeArguments(superInterfaceNames[i]);
+                       }
+               } catch (JavaModelException e) {
+                       return false;
+               }
+               
+               for (int i= 0; i < defaultMemberPatterns.length; i++) {
+                       String defaultMemberPattern= defaultMemberPatterns[i];
+                       int pos= defaultMemberPattern.lastIndexOf('.');
+                       String defaultTypeName= defaultMemberPattern.substring(0, pos);
+                       String defaultMethodName= defaultMemberPattern.substring(pos + 1);
+                       
+                       if ("*".equals(defaultMethodName)) { //$NON-NLS-1$
+                               if (declaringTypeName.equals(defaultTypeName)) {
+                                       return true;
+                               }
+                       } else {
+                               if (!methodName.equals(defaultMethodName)) {
+                                       continue;
+                               }
+                               if (declaringTypeName.equals(defaultTypeName)) {
+                                       return true;
+                               }
+                       }
+                       if (superClassName != null && JavaModelUtil.isMatchingName(superClassName, defaultTypeName)) {
+                               return true;
+                       }
+                       for (int j= 0; j < superInterfaceNames.length; j++) {
+                               String superInterfaceName= superInterfaceNames[j];
+                               if (JavaModelUtil.isMatchingName(superInterfaceName, defaultTypeName)) {
+                                       return true;
+                               }
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Strips type arguments from the given type name and returns only erased type name.
+        * 
+        * @param typeName the type name
+        * @return the erased type name
+        * 
+        * @since 3.6
+        */
+       private static String stripTypeArguments(String typeName) {
+               int pos= typeName.indexOf('<');
+               if (pos != -1)
+                       return typeName.substring(0, pos);
+               return typeName;
+       }
+
+       protected Object[] fetchChildren(final MethodWrapper methodWrapper) {
+        IRunnableContext context= JavaPlugin.getActiveWorkbenchWindow();
+        MethodWrapperRunnable runnable= new MethodWrapperRunnable(methodWrapper);
+        try {
+            context.run(true, true, runnable);
+        } catch (InvocationTargetException e) {
+            ExceptionHandler.handle(e, CallHierarchyMessages.CallHierarchyContentProvider_searchError_title, CallHierarchyMessages.CallHierarchyContentProvider_searchError_message);
+            return EMPTY_ARRAY;
+        } catch (InterruptedException e) {
+               final CallerMethodWrapper element= (CallerMethodWrapper)methodWrapper;
+               if (!isExpandWithConstructors(element)) {
+                       Display.getDefault().asyncExec(new Runnable() {
+                               public void run() {
+                                       collapseAndRefresh(element);
+                               }
+                       });
+               }
+        }
+
+        return runnable.getCalls();
+    }
+
+
+    /**
+     * Returns whether the given element is an "Expand witch Constructors" node.
+     * 
+        * @param element a method wrapped
+        * @return <code>true</code> iff the element is an "Expand witch Constructors" node
+        * @since 3.5
+        */
+       static boolean isExpandWithConstructors(MethodWrapper element) {
+               return element instanceof CallerMethodWrapper && ((CallerMethodWrapper)element).getExpandWithConstructors();
+       }
+
+       /**
+     * Collapses and refreshes the given element when search has been canceled.
+     * 
+     * @param element the element on which search has been canceled and which has to be collapsed
+     * @since 3.5
+     */
+    protected void collapseAndRefresh(MethodWrapper element) {
+       CallHierarchyViewer viewer= fPart.getViewer();
+       
+               /* Problem case: The user expands the RealCallers node and then unchecks "Expand with Constructors"
+                * while the search for the real callers is still in progress.
+                * 
+                * In this scenario, the RealCallers is not even part of the current tree any more, since the
+                * ExpandWithConstructorsAction already toggled the flag and refreshed the tree.
+        * 
+        * But since setExpandedState(element, false) walks up the getParent() chain of the given element,
+        * this causes the parent's children to be created, which would wrongly start a deferred search.
+        * 
+        * The fix is to do nothing when the RealCaller's parent is expandWithConstructors.
+                */
+       boolean elementStays= true;
+       if (element instanceof RealCallers) {
+               elementStays= isExpandWithConstructors(element.getParent());
+       }
+               element.generated_5934007691874227139(viewer, elementStays);
+    }
+
+       /**
+     * Returns the call hierarchy view part.
+        * 
+        * @return the call hierarchy view part
+        * @since 3.5
+        */
+    public CallHierarchyViewPart getViewPart() {
+       return fPart;
+    }
+
+       public boolean shouldStopTraversion(MethodWrapper methodWrapper) {
+        return methodWrapper.generated_1632587395956144884();
+    }
+
+       /**
+     * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
+     */
+    public Object[] getElements(Object inputElement) {
+        return getChildren(inputElement);
+    }
+
+    /**
+     * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
+     */
+    public Object getParent(Object element) {
+        if (element instanceof MethodWrapper) {
+            return ((MethodWrapper) element).getParent();
+        }
+
+        return null;
+    }
+
+    /**
+     * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+     */
+    public void dispose() {
+        // Nothing to dispose
+    }
+
+    /**
+        * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
+        */
+       public boolean hasChildren(Object element) {
+               if (element == TreeRoot.EMPTY_ROOT || element == TreeTermination.SEARCH_CANCELED) {
+                       return false;
+               }
+
+               // Only certain members can have subelements, so there's no need to fool the
+               // user into believing that there is more
+               if (element instanceof MethodWrapper) {
+                       MethodWrapper methodWrapper= (MethodWrapper) element;
+                       return methodWrapper.generated_9121535494207793525(this);
+               } else if (element instanceof TreeRoot) {
+                       return true;
+               } else if (element instanceof DeferredMethodWrapper) {
+                       // Err on the safe side by returning true even though
+                       // we don't know for sure that there are children.
+                       return true;
+               }
+
+               return false; // the "Update ..." placeholder has no children
+       }
+
+       /**
+        * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
+        *      java.lang.Object, java.lang.Object)
+        */
+    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+       if (oldInput instanceof TreeRoot) {
+               MethodWrapper[] roots = ((TreeRoot) oldInput).getRoots();
+                       cancelJobs(roots);
+       }
+        if (viewer instanceof AbstractTreeViewer) {
+            fManager = new DeferredTreeContentManager((AbstractTreeViewer) viewer, fPart.getSite());
+        }
+    }
+
+    /**
+     * Cancel all current jobs.
+     * @param wrappers the parents to cancel jobs for
+     */
+    void cancelJobs(MethodWrapper[] wrappers) {
+        if (fManager != null && wrappers != null) {
+               for (int i= 0; i < wrappers.length; i++) {
+                               MethodWrapper wrapper= wrappers[i];
+                               fManager.cancel(wrapper);
+                       }
+            if (fPart != null) {
+                fPart.setCancelEnabled(false);
+            }
+        }
+    }
+
+    /**
+     *
+     */
+    public void doneFetching() {
+        if (fPart != null) {
+            fPart.setCancelEnabled(false);
+        }
+    }
+
+    /**
+     *
+     */
+    public void startFetching() {
+        if (fPart != null) {
+            fPart.setCancelEnabled(true);
+        }
+    }
+}