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; } } }