package org.python.pydev.debug.referrers;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IViewSite;
import org.python.pydev.core.log.Log;
import org.python.pydev.debug.model.AbstractDebugTarget;
import org.python.pydev.debug.model.IVariableLocator;
import org.python.pydev.debug.model.XMLUtils;
import org.python.pydev.debug.model.XMLUtils.XMLToReferrersInfo;
import org.python.pydev.debug.model.remote.AbstractDebuggerCommand;
import org.python.pydev.debug.model.remote.ICommandResponseListener;
import org.python.pydev.debug.model.remote.RunCustomOperationCommand;
import org.python.pydev.debug.views.BaseDebugView;
import org.python.pydev.debug.views.ILaunchAndDebugListener;
import org.python.pydev.shared_ui.utils.UIUtils;
public class ReferrersView extends BaseDebugView {
private static final String REFERRERS_VIEW_ID = "org.python.pydev.views.ReferrersView";
/**
* May only be called in the UI thread. If the view is not visible, shows it if the
* preference to do that is set to true.
*
* Note that it may return null if the preference to show it is false and the view is not currently shown.
*/
public static ReferrersView getView(boolean forceVisible) {
return (ReferrersView) UIUtils.getView(REFERRERS_VIEW_ID, forceVisible);
}
@Override
protected ITreeContentProvider createContentProvider() {
return new ReferrersViewContentProvider();
}
private static final class ReferrerCommandResponseListener implements ICommandResponseListener {
private final IVariableLocator locator;
private final AbstractDebugTarget debugTarget;
private ReferrersView referrersView;
private ReferrerCommandResponseListener(ReferrersView referrersView, IVariableLocator locator,
AbstractDebugTarget debugTarget) {
this.locator = locator;
this.debugTarget = debugTarget;
this.referrersView = referrersView;
}
@Override
public void commandComplete(AbstractDebuggerCommand cmd) {
try {
if (cmd instanceof RunCustomOperationCommand) {
RunCustomOperationCommand c = (RunCustomOperationCommand) cmd;
String responsePayload = c.getResponsePayload();
if (responsePayload != null) {
XMLToReferrersInfo xmlToReferrers = XMLUtils.XMLToReferrers(debugTarget, locator,
responsePayload);
if (xmlToReferrers != null) {
referrersView.addReferrersInfo(xmlToReferrers);
}
} else {
Log.log("Command to get referrers did not return proper value.");
}
}
} finally {
this.finish();
}
}
private void finish() {
boolean removedNow;
synchronized (referrersView.listenersLock) {
removedNow = referrersView.listeners.remove(this);
}
if (removedNow) {
referrersView.endProgress();
}
}
}
@Override
protected ILaunchAndDebugListener createListener() {
return new ILaunchAndDebugListener() {
@Override
public void launchRemoved(ILaunch launch) {
IDebugTarget debugTarget = launch.getDebugTarget();
if (debugTarget instanceof AbstractDebugTarget) {
remove((AbstractDebugTarget) debugTarget);
}
}
@Override
public void launchChanged(ILaunch launch) {
if (launch.isTerminated()) {
this.launchRemoved(launch);
}
}
@Override
public void launchAdded(ILaunch launch) {
}
@Override
public void handleDebugEvents(DebugEvent[] events) {
for (DebugEvent debugEvent : events) {
if (debugEvent.getSource() instanceof AbstractDebugTarget) {
if (debugEvent.getKind() == DebugEvent.TERMINATE) {
AbstractDebugTarget debugTarget = (AbstractDebugTarget) debugEvent.getSource();
remove(debugTarget);
}
}
}
}
private void remove(AbstractDebugTarget debugTarget) {
if (debugTarget.isTerminated()) {
synchronized (xmlToReferrersLock) {
Iterator<XMLToReferrersInfo> iterator = xmlToReferrers.iterator();
while (iterator.hasNext()) {
XMLToReferrersInfo next = iterator.next();
if (next.target == debugTarget) {
iterator.remove();
}
}
}
updateTreeJob.schedule();
}
}
};
}
public ReferrersView() {
}
@Override
protected void configureToolBar(IViewSite viewSite) {
IActionBars actionBars = viewSite.getActionBars();
IToolBarManager toolBar = actionBars.getToolBarManager();
//IMenuManager menuManager = actionBars.getMenuManager(); -- not adding anything to the menu for now.
toolBar.add(new ClearCurrentReferrers(this));
}
private final Set<ReferrerCommandResponseListener> listeners = new HashSet<>();
protected final Object listenersLock = new Object();
@Override
public void clear() {
super.clear();
//Any registered pending command should be stopped now!
synchronized (listenersLock) {
for (ReferrerCommandResponseListener referrerCommandResponseListener : listeners) {
referrerCommandResponseListener.finish();
}
listeners.clear();
synchronized (xmlToReferrersLock) {
this.xmlToReferrers.clear();
}
}
}
public void showReferrersFor(final AbstractDebugTarget debugTarget, final IVariableLocator locator) {
RunCustomOperationCommand cmd = new RunCustomOperationCommand(debugTarget, locator,
"from _pydevd_bundle.pydevd_referrers import get_referrer_info",
"get_referrer_info");
ReferrerCommandResponseListener listener = new ReferrerCommandResponseListener(this, locator, debugTarget);
synchronized (listenersLock) {
startProgress();
listeners.add(listener);
}
cmd.setCompletionListener(listener);
debugTarget.postCommand(cmd);
}
// Information to add to the tree and updating it ------------------------------------------------------------------
protected final List<XMLToReferrersInfo> xmlToReferrers = new ArrayList<>();
protected final Object xmlToReferrersLock = new Object();
@Override
protected void onSetTreeInput() {
XMLToReferrersInfo[] array;
int size = xmlToReferrers.size();
synchronized (xmlToReferrersLock) {
array = xmlToReferrers.toArray(new XMLToReferrersInfo[size]);
}
viewer.setInput(array);
}
protected void addReferrersInfo(XMLToReferrersInfo xmlToReferrers) {
synchronized (xmlToReferrersLock) {
this.xmlToReferrers.add(xmlToReferrers);
}
updateTreeJob.schedule();
}
@Override
protected void makeLastVisibleInTree(Object input) {
if (input instanceof XMLToReferrersInfo[]) {
XMLToReferrersInfo[] xmlToReferrersInfos = (XMLToReferrersInfo[]) input;
if (xmlToReferrersInfos.length > 0) {
//i.e.: scroll to the last added element.
XMLToReferrersInfo element = xmlToReferrersInfos[xmlToReferrersInfos.length - 1];
if (element.forVar != null) {
viewer.reveal(element.forVar);
}
}
}
}
}