package jetbrains.mps.debug.api;
/*Generated by MPS */
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.components.StoragePathMacros;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.components.PersistentStateComponent;
import org.jdom.Element;
import jetbrains.mps.logging.Logger;
import org.apache.log4j.LogManager;
import java.util.Map;
import org.jetbrains.mps.openapi.model.SModelReference;
import java.util.Set;
import jetbrains.mps.debug.api.breakpoints.ILocationBreakpoint;
import java.util.HashMap;
import jetbrains.mps.debug.api.breakpoints.IBreakpoint;
import java.util.HashSet;
import java.util.List;
import java.util.ArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SNodeReference;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import jetbrains.mps.internal.collections.runtime.IVisitor;
import java.util.ListIterator;
import java.util.Collections;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.Nullable;
@State(name = "BreakpointManager", storages = @Storage(value = StoragePathMacros.WORKSPACE_FILE)
)
public class BreakpointManagerComponent implements ProjectComponent, PersistentStateComponent<Element> {
private static final Logger LOG = Logger.wrap(LogManager.getLogger(BreakpointManagerComponent.class));
private static final String BREAKPOINTS_LIST_ELEMENT = "breakpointsList";
private static final BreakpointManagerComponent.DummyIO DUMMY_IO = new BreakpointManagerComponent.DummyIO();
/**
* Map implementation shall tolerate null keys (HashMap does).
*/
private final Map<SModelReference, Set<ILocationBreakpoint>> myRootsToBreakpointsMap = new HashMap<SModelReference, Set<ILocationBreakpoint>>();
private boolean myBreakpointsForRootInitialized = false;
private final Set<IBreakpoint> myBreakpoints = new HashSet<IBreakpoint>();
private final List<Element> myUnreadBreakpoints = new ArrayList<Element>();
private BreakpointManagerComponent.IBreakpointsIO myBreakpointsIO = DUMMY_IO;
private final List<BreakpointManagerComponent.IBreakpointManagerListener> myListeners = new ArrayList<BreakpointManagerComponent.IBreakpointManagerListener>();
public BreakpointManagerComponent() {
}
@NotNull
@Override
public String getComponentName() {
return "Breakpoint Manager";
}
@Override
public void projectOpened() {
}
@Override
public void projectClosed() {
}
@Override
public void initComponent() {
}
@Override
public void disposeComponent() {
myBreakpointsIO = null;
// dispose
}
public void setBreakpointsIO(BreakpointManagerComponent.IBreakpointsIO io) {
myBreakpointsIO = io;
reReadState();
}
public void addBreakpoint(@NotNull final IBreakpoint breakpoint) {
synchronized (myBreakpoints) {
if (breakpoint instanceof ILocationBreakpoint && myBreakpointsForRootInitialized) {
addLocationBreakpoint((ILocationBreakpoint) breakpoint);
}
breakpoint.setCreationTime(System.currentTimeMillis());
myBreakpoints.add(breakpoint);
breakpoint.addToRunningSessions();
}
fireBreakpointAdded(breakpoint);
}
private void addLocationBreakpoint(ILocationBreakpoint breakpoint) {
SNodeReference node = breakpoint.getLocation().getNodePointer();
Set<ILocationBreakpoint> breakpointsForModel = myRootsToBreakpointsMap.get(node.getModelReference());
if (breakpointsForModel == null) {
myRootsToBreakpointsMap.put(node.getModelReference(), breakpointsForModel = new HashSet<ILocationBreakpoint>());
}
// check the following assumption: one breakpoint for one node
for (ILocationBreakpoint bp : breakpointsForModel) {
if (bp.getLocation().equals(breakpoint.getLocation())) {
LOG.error("Trying to add a second breakpoint for node", bp.getLocation().getPresentation());
break;
}
}
breakpointsForModel.add(breakpoint);
}
public void removeBreakpoint(@NotNull final IBreakpoint breakpoint) {
synchronized (myBreakpoints) {
if (breakpoint instanceof ILocationBreakpoint) {
removeLocationBreakpoint((ILocationBreakpoint) breakpoint);
}
myBreakpoints.remove(breakpoint);
breakpoint.removeFromRunningSessions();
}
fireBreakpointRemoved(breakpoint);
}
private void removeLocationBreakpoint(ILocationBreakpoint breakpoint) {
SNodeReference node = breakpoint.getLocation().getNodePointer();
Set<ILocationBreakpoint> breakpointsForModel = myRootsToBreakpointsMap.get(node.getModelReference());
if (breakpointsForModel != null) {
breakpointsForModel.remove(breakpoint);
}
}
private void clear() {
synchronized (myBreakpoints) {
myRootsToBreakpointsMap.clear();
myBreakpoints.clear();
myUnreadBreakpoints.clear();
myBreakpointsForRootInitialized = false;
}
}
@Override
public void loadState(Element state) {
Set<IBreakpoint> newBreakpoints = SetSequence.fromSet(new HashSet<IBreakpoint>());
Set<IBreakpoint> oldBreakpoints = SetSequence.fromSet(new HashSet<IBreakpoint>());
loadStateInternal(state, oldBreakpoints, newBreakpoints);
SetSequence.fromSet(oldBreakpoints).subtract(SetSequence.fromSet(newBreakpoints)).visitAll(new IVisitor<IBreakpoint>() {
public void visit(IBreakpoint it) {
fireBreakpointRemoved(it);
}
});
SetSequence.fromSet(newBreakpoints).subtract(SetSequence.fromSet(oldBreakpoints)).visitAll(new IVisitor<IBreakpoint>() {
public void visit(IBreakpoint it) {
fireBreakpointAdded(it);
}
});
}
private void loadStateInternal(Element state, Set<IBreakpoint> oldBreakpoints, Set<IBreakpoint> newBreakpoints) {
synchronized (myBreakpoints) {
SetSequence.fromSet(oldBreakpoints).addSequence(SetSequence.fromSet(myBreakpoints));
clear();
List breakpointsElement = state.getChildren();
for (ListIterator it = breakpointsElement.listIterator(); it.hasNext();) {
Element breakpointElement = (Element) it.next();
try {
IBreakpoint breakpoint = myBreakpointsIO.readBreakpoint(breakpointElement);
if (breakpoint != null) {
myBreakpoints.add(breakpoint);
} else {
myUnreadBreakpoints.add(breakpointElement);
}
} catch (Throwable t) {
LOG.error("Error while loading breakpoint from " + breakpointElement, t);
}
}
SetSequence.fromSet(newBreakpoints).addSequence(SetSequence.fromSet(myBreakpoints));
}
}
@Override
public Element getState() {
Element rootElement = new Element(BREAKPOINTS_LIST_ELEMENT);
synchronized (myBreakpoints) {
for (IBreakpoint breakpoint : myBreakpoints) {
try {
Element element = myBreakpointsIO.writeBreakpoint(breakpoint);
if (element != null) {
rootElement.addContent(element);
}
} catch (Throwable t) {
LOG.error("Error while saving breakpoint " + breakpoint.getPresentation(), t);
}
}
}
for (Element el : myUnreadBreakpoints) {
rootElement.addContent((Element) el.clone());
}
return rootElement;
}
public void reReadState() {
Set<IBreakpoint> newBreakpoints = SetSequence.fromSet(new HashSet<IBreakpoint>());
Set<IBreakpoint> oldBreakpoints = SetSequence.fromSet(new HashSet<IBreakpoint>());
synchronized (myBreakpoints) {
loadStateInternal(getState(), oldBreakpoints, newBreakpoints);
}
SetSequence.fromSet(oldBreakpoints).subtract(SetSequence.fromSet(newBreakpoints)).visitAll(new IVisitor<IBreakpoint>() {
public void visit(IBreakpoint it) {
fireBreakpointRemoved(it);
}
});
SetSequence.fromSet(newBreakpoints).subtract(SetSequence.fromSet(oldBreakpoints)).visitAll(new IVisitor<IBreakpoint>() {
public void visit(IBreakpoint it) {
fireBreakpointAdded(it);
}
});
}
public Set<IBreakpoint> getAllIBreakpoints() {
synchronized (myBreakpoints) {
return new HashSet<IBreakpoint>(myBreakpoints);
}
}
public void addChangeListener(BreakpointManagerComponent.IBreakpointManagerListener listener) {
synchronized (myListeners) {
myListeners.add(listener);
}
}
public void removeChangeListener(BreakpointManagerComponent.IBreakpointManagerListener listener) {
synchronized (myListeners) {
myListeners.remove(listener);
}
}
private List<BreakpointManagerComponent.IBreakpointManagerListener> getListeners() {
synchronized (myListeners) {
return new ArrayList<BreakpointManagerComponent.IBreakpointManagerListener>(myListeners);
}
}
private void fireBreakpointRemoved(IBreakpoint breakpoint) {
List<BreakpointManagerComponent.IBreakpointManagerListener> listeners = getListeners();
for (BreakpointManagerComponent.IBreakpointManagerListener listener : listeners) {
listener.breakpointRemoved(breakpoint);
}
}
private void fireBreakpointAdded(IBreakpoint breakpoint) {
List<BreakpointManagerComponent.IBreakpointManagerListener> listeners = getListeners();
for (BreakpointManagerComponent.IBreakpointManagerListener listener : listeners) {
listener.breakpointAdded(breakpoint);
}
}
/**
* Tell subset of breakpoints 'close' to supplied ancor node.
* Here, 'close' means they are at a node from the same model, and perhaps are from descendants.
*
* IMPORTANT: contract of the method has been changed. It used to return breakpoints within given root, now the set is wider and
* gives breakpoints from the same model. Sticking to root doesn't bring any noticeable benefit (we need to match breakpoints anyway), but
* brings a lot of complications as we need to go from node reference to node to containing root.
*
* @param rootPointer narrows scope where to look for breakpoints, e.g. root node
* @return breakpoints 'close' to the specified anchor, or empty set if none found
*/
public Set<ILocationBreakpoint> getBreakpoints(final SNodeReference rootPointer) {
synchronized (myBreakpoints) {
if (!(myBreakpointsForRootInitialized)) {
myBreakpointsForRootInitialized = true;
for (IBreakpoint breakpoint : myBreakpoints) {
if (breakpoint instanceof ILocationBreakpoint) {
addLocationBreakpoint((ILocationBreakpoint) breakpoint);
}
}
}
Set<ILocationBreakpoint> rv = myRootsToBreakpointsMap.get(rootPointer.getModelReference());
return (rv == null ? Collections.emptySet() : rv);
}
}
public static BreakpointManagerComponent getInstance(@NotNull Project project) {
return project.getComponent(BreakpointManagerComponent.class);
}
public interface IBreakpointManagerListener {
void breakpointAdded(@NotNull IBreakpoint breakpoint);
void breakpointRemoved(@NotNull IBreakpoint breakpoint);
}
public static abstract class BreakpointManagerListener implements BreakpointManagerComponent.IBreakpointManagerListener {
public BreakpointManagerListener() {
}
@Override
public void breakpointAdded(@NotNull IBreakpoint breakpoints) {
breakpointsChanged();
}
@Override
public void breakpointRemoved(@NotNull IBreakpoint breakpoint) {
breakpointsChanged();
}
public abstract void breakpointsChanged();
}
public interface IBreakpointsIO {
@Nullable
IBreakpoint readBreakpoint(@NotNull Element element);
@Nullable
Element writeBreakpoint(@NotNull IBreakpoint breakpoint);
}
public static class DummyIO implements BreakpointManagerComponent.IBreakpointsIO {
public DummyIO() {
}
@Override
public IBreakpoint readBreakpoint(@NotNull Element element) {
return null;
}
@Override
public Element writeBreakpoint(@NotNull IBreakpoint breakpoint) {
return null;
}
}
}