| /******************************************************************************* |
| * Copyright (c) 2000, 2006 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.corext.callhierarchy; |
| |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.PlatformObject; |
| |
| import org.eclipse.ui.model.IWorkbenchAdapter; |
| |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IMember; |
| |
| |
| import org.eclipse.jdt.internal.ui.callhierarchy.MethodWrapperWorkbenchAdapter; |
| |
| /** |
| * This class represents the general parts of a method call (either to or from a |
| * method). |
| * |
| */ |
| public abstract class MethodWrapper extends PlatformObject { |
| private Map fElements = null; |
| |
| /* |
| * A cache of previously found methods. This cache should be searched |
| * before adding a "new" method object reference to the list of elements. |
| * This way previously found methods won't be searched again. |
| */ |
| private Map fMethodCache; |
| private MethodCall fMethodCall; |
| private MethodWrapper fParent; |
| private int fLevel; |
| |
| /** |
| * Constructor CallerElement. |
| */ |
| public MethodWrapper(MethodWrapper parent, MethodCall methodCall) { |
| Assert.isNotNull(methodCall); |
| |
| if (parent == null) { |
| setMethodCache(new HashMap()); |
| fLevel = 1; |
| } else { |
| setMethodCache(parent.getMethodCache()); |
| fLevel = parent.getLevel() + 1; |
| } |
| |
| this.fMethodCall = methodCall; |
| this.fParent = parent; |
| } |
| |
| public Object getAdapter(Class adapter) { |
| if (adapter == IJavaElement.class) { |
| return getMember(); |
| } else if (adapter == IWorkbenchAdapter.class){ |
| return new MethodWrapperWorkbenchAdapter(this); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * @return the child caller elements of this element |
| */ |
| public MethodWrapper[] getCalls(IProgressMonitor progressMonitor) { |
| if (fElements == null) { |
| doFindChildren(progressMonitor); |
| } |
| |
| MethodWrapper[] result = new MethodWrapper[fElements.size()]; |
| int i = 0; |
| |
| for (Iterator iter = fElements.keySet().iterator(); iter.hasNext();) { |
| MethodCall methodCall = getMethodCallFromMap(fElements, iter.next()); |
| result[i++] = createMethodWrapper(methodCall); |
| } |
| |
| return result; |
| } |
| |
| public int getLevel() { |
| return fLevel; |
| } |
| |
| public IMember getMember() { |
| return getMethodCall().getMember(); |
| } |
| |
| public MethodCall getMethodCall() { |
| return fMethodCall; |
| } |
| |
| public String getName() { |
| if (getMethodCall() != null) { |
| return getMethodCall().getMember().getElementName(); |
| } else { |
| return ""; //$NON-NLS-1$ |
| } |
| } |
| |
| public MethodWrapper getParent() { |
| return fParent; |
| } |
| |
| public boolean equals(Object oth) { |
| if (this == oth) { |
| return true; |
| } |
| |
| if (oth == null) { |
| return false; |
| } |
| |
| if (oth instanceof MethodWrapperWorkbenchAdapter) { |
| //Note: A MethodWrapper is equal to a referring MethodWrapperWorkbenchAdapter and vice versa (bug 101677). |
| oth= ((MethodWrapperWorkbenchAdapter) oth).getMethodWrapper(); |
| } |
| |
| if (oth.getClass() != getClass()) { |
| return false; |
| } |
| |
| MethodWrapper other = (MethodWrapper) oth; |
| |
| if (this.fParent == null) { |
| if (other.fParent != null) { |
| return false; |
| } |
| } else { |
| if (!this.fParent.equals(other.fParent)) { |
| return false; |
| } |
| } |
| |
| if (this.getMethodCall() == null) { |
| if (other.getMethodCall() != null) { |
| return false; |
| } |
| } else { |
| if (!this.getMethodCall().equals(other.getMethodCall())) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| public int hashCode() { |
| final int PRIME = 1000003; |
| int result = 0; |
| |
| if (fParent != null) { |
| result = (PRIME * result) + fParent.hashCode(); |
| } |
| |
| if (getMethodCall() != null) { |
| result = (PRIME * result) + getMethodCall().getMember().hashCode(); |
| } |
| |
| return result; |
| } |
| |
| private void setMethodCache(Map methodCache) { |
| fMethodCache = methodCache; |
| } |
| |
| protected abstract String getTaskName(); |
| |
| private void addCallToCache(MethodCall methodCall) { |
| Map cachedCalls = lookupMethod(this.getMethodCall()); |
| cachedCalls.put(methodCall.getKey(), methodCall); |
| } |
| |
| protected abstract MethodWrapper createMethodWrapper(MethodCall methodCall); |
| |
| private void doFindChildren(IProgressMonitor progressMonitor) { |
| Map existingResults = lookupMethod(getMethodCall()); |
| |
| if (existingResults != null) { |
| fElements = new HashMap(); |
| fElements.putAll(existingResults); |
| } else { |
| initCalls(); |
| |
| if (progressMonitor != null) { |
| progressMonitor.beginTask(getTaskName(), 100); |
| } |
| |
| try { |
| performSearch(progressMonitor); |
| } finally { |
| if (progressMonitor != null) { |
| progressMonitor.done(); |
| } |
| } |
| |
| // ModalContext.run(getRunnableWithProgress(), true, getProgressMonitor(), |
| // Display.getCurrent()); |
| } |
| } |
| |
| /** |
| * Determines if the method represents a recursion call (i.e. whether the |
| * method call is already in the cache.) |
| * |
| * @return True if the call is part of a recursion |
| */ |
| public boolean isRecursive() { |
| MethodWrapper current = getParent(); |
| |
| while (current != null) { |
| if (getMember().getHandleIdentifier().equals(current.getMember() |
| .getHandleIdentifier())) { |
| return true; |
| } |
| |
| current = current.getParent(); |
| } |
| |
| return false; |
| } |
| |
| /** |
| * This method finds the children of the current IMethod (either callers or |
| * callees, depending on the concrete subclass. |
| * @return The result of the search for children |
| */ |
| protected abstract Map findChildren(IProgressMonitor progressMonitor); |
| |
| private Map getMethodCache() { |
| return fMethodCache; |
| } |
| |
| private void initCalls() { |
| this.fElements = new HashMap(); |
| |
| initCacheForMethod(); |
| } |
| |
| /** |
| * Looks up a previously created search result in the "global" cache. |
| * @return the List of previously found search results |
| */ |
| private Map lookupMethod(MethodCall methodCall) { |
| return (Map) getMethodCache().get(methodCall.getKey()); |
| } |
| |
| private void performSearch(IProgressMonitor progressMonitor) { |
| fElements = findChildren(progressMonitor); |
| |
| for (Iterator iter = fElements.keySet().iterator(); iter.hasNext();) { |
| checkCanceled(progressMonitor); |
| |
| MethodCall methodCall = getMethodCallFromMap(fElements, iter.next()); |
| addCallToCache(methodCall); |
| } |
| } |
| |
| private MethodCall getMethodCallFromMap(Map elements, Object key) { |
| return (MethodCall) elements.get(key); |
| } |
| |
| private void initCacheForMethod() { |
| Map cachedCalls = new HashMap(); |
| getMethodCache().put(this.getMethodCall().getKey(), cachedCalls); |
| } |
| |
| /** |
| * Checks with the progress monitor to see whether the creation of the type hierarchy |
| * should be canceled. Should be regularly called |
| * so that the user can cancel. |
| * |
| * @exception OperationCanceledException if cancelling the operation has been requested |
| * @see IProgressMonitor#isCanceled |
| */ |
| protected void checkCanceled(IProgressMonitor progressMonitor) { |
| if (progressMonitor != null && progressMonitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } |
| } |
| |
| /** |
| * Allows a visitor to traverse the call hierarchy. The visiting is stopped when |
| * a recursive node is reached. |
| * |
| * @param visitor |
| */ |
| public void accept(CallHierarchyVisitor visitor, IProgressMonitor progressMonitor) { |
| if (getParent() != null && getParent().isRecursive()) { |
| return; |
| } |
| checkCanceled(progressMonitor); |
| |
| visitor.preVisit(this); |
| if (visitor.visit(this)) { |
| MethodWrapper[] methodWrappers= getCalls(progressMonitor); |
| for (int i= 0; i < methodWrappers.length; i++) { |
| methodWrappers[i].accept(visitor, progressMonitor); |
| } |
| } |
| visitor.postVisit(this); |
| |
| if (progressMonitor != null) { |
| progressMonitor.worked(1); |
| } |
| } |
| } |