/***************************************************************** * Copyright (c) 2009, 2013 Texas Instruments 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: * Patrick Chuong (Texas Instruments) - Initial API and implementation (Bug 238956) * IBM Corporation - ongoing enhancements and bug fixing * Wind River Systems - ongoing enhancements and bug fixing * Wind River System (Randy Rohrbach) - non standard model breakpoint filtering (Bug 333517) *****************************************************************/ package org.eclipse.debug.internal.ui.model.elements; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.core.resources.IMarkerDelta; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IBreakpointManager; import org.eclipse.debug.core.IBreakpointsListener; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IThread; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.breakpoints.provisional.IBreakpointOrganizer; import org.eclipse.debug.internal.ui.breakpoints.provisional.IBreakpointUIConstants; import org.eclipse.debug.internal.ui.elements.adapters.DefaultBreakpointsViewInput; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.debug.internal.ui.viewers.update.BreakpointManagerProxy; import org.eclipse.debug.internal.ui.views.breakpoints.BreakpointContainer; import org.eclipse.debug.internal.ui.views.breakpoints.ElementComparator; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.debug.ui.contexts.DebugContextEvent; import org.eclipse.debug.ui.contexts.IDebugContextListener; import org.eclipse.debug.ui.contexts.IDebugContextService; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.ui.IWorkbenchWindow; /** * This class provides breakpoint content for the breakpoint manager. * * @since 3.6 */ public class BreakpointManagerContentProvider extends ElementContentProvider implements IBreakpointsListener { /** * Breakpoint input data. Contains all input specific data. * * @since 3.6 */ private class InputData { /** * Breakpoint manager input */ final private DefaultBreakpointsViewInput fInput; /** * Model proxy of the input */ final private List<BreakpointManagerProxy> fProxies = new ArrayList<BreakpointManagerProxy>(1); /** * Element comparator, use to compare the ordering of elements for the model * <br/> Note: We assume that the comparator does not change. */ private ElementComparator fComparator; /** * The breakpoint root container.<br/> * Note: The final qualifier guarantees that fContainer will be * initialized before the class is accessed on other threads. */ final private BreakpointContainer fContainer; /** * Known current breakpoint organizers. */ private IBreakpointOrganizer[] fOrganizers; private IStructuredSelection fDebugContext = StructuredSelection.EMPTY; private IPropertyChangeListener fOrganizersListener = new IPropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent event) { // For any property changes in breakpoint organizers, refresh the containers. updateContainers(); } }; private IPropertyChangeListener fPresentationContextListener = new IPropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent event) { presentationPropertyChanged(event); } }; private IDebugContextListener fDebugContextListener = new IDebugContextListener() { @Override public void debugContextChanged(DebugContextEvent event) { InputData.this.debugContextChanged(event); } }; /** * Constructor * * @param input the breakpoint manager input */ InputData(DefaultBreakpointsViewInput input) { fInput = input; fComparator = (ElementComparator) input.getContext().getProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_ELEMENT_COMPARATOR); fOrganizers = (IBreakpointOrganizer[]) input.getContext().getProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_ORGANIZERS); // Create the initial container. ModelDelta initialDelta = new ModelDelta(fInput, 0, IModelDelta.NO_CHANGE, -1); IBreakpoint[] breakpoints = filterBreakpoints( fInput, getSelectionFilter(fInput, getDebugContext()), fBpManager.getBreakpoints()); fContainer = createRootContainer(initialDelta, fInput, fOrganizers, breakpoints); registerOrganizersListener(null, fOrganizers); input.getContext().addPropertyChangeListener(fPresentationContextListener); IWorkbenchWindow window = fInput.getContext().getWindow(); if (window != null) { IDebugContextService debugContextService = DebugUITools.getDebugContextManager().getContextService(window); ISelection debugContext = debugContextService.getActiveContext(); if (debugContext instanceof IStructuredSelection) { synchronized(this) { fDebugContext = (IStructuredSelection)debugContext; } } debugContextService.addDebugContextListener(fDebugContextListener); } } void dispose() { // Unregister listener to breakpoint organizers. IBreakpointOrganizer[] organizers; synchronized(this) { organizers = fOrganizers; fOrganizers = null; } registerOrganizersListener(organizers, null); // Unregister listener to presentation context. fInput.getContext().removePropertyChangeListener(fPresentationContextListener); // Unregister listener to debug context in window. IWorkbenchWindow window = fInput.getContext().getWindow(); if (window != null) { IDebugContextService debugContextService = DebugUITools.getDebugContextManager().getContextService(window); debugContextService.removeDebugContextListener(fDebugContextListener); } } void proxyInstalled(BreakpointManagerProxy proxy) { ModelDelta rootDelta = null; synchronized(this) { fProxies.add(proxy); // Generate an install delta rootDelta = new ModelDelta(fInput, 0, IModelDelta.NO_CHANGE, -1); buildInstallDelta(rootDelta, fContainer); if (DebugUIPlugin.DEBUG_BREAKPOINT_DELTAS) { DebugUIPlugin.trace("PROXY INSTALLED (" + proxy + ")\n"); //$NON-NLS-1$ //$NON-NLS-2$ } proxy.postModelChanged(rootDelta, false); } } synchronized void proxyDisposed(BreakpointManagerProxy proxy) { fProxies.remove(proxy); if (DebugUIPlugin.DEBUG_BREAKPOINT_DELTAS) { DebugUIPlugin.trace("PROXY DISPOSED (" + proxy + ")\n"); //$NON-NLS-1$ //$NON-NLS-2$ } } synchronized BreakpointManagerProxy[] getProxies() { return fProxies.toArray(new BreakpointManagerProxy[fProxies.size()]); } /** * Change the breakpoint organizers for the root container. * * @param organizers the new organizers. */ void setOrganizers(IBreakpointOrganizer[] organizers) { IBreakpointOrganizer[] oldOrganizers = null; synchronized(this) { oldOrganizers = fOrganizers; fOrganizers = organizers; } registerOrganizersListener(oldOrganizers, organizers); updateContainers(); } private void registerOrganizersListener(IBreakpointOrganizer[] oldOrganizers, IBreakpointOrganizer[] newOrganizers) { if (oldOrganizers != null) { for (int i = 0; i < oldOrganizers.length; i++) { oldOrganizers[i].removePropertyChangeListener(fOrganizersListener); } } if (newOrganizers != null) { for (int i = 0; i < newOrganizers.length; i++) { newOrganizers[i].addPropertyChangeListener(fOrganizersListener); } } } void updateContainers() { IBreakpoint[] breakpoints = filterBreakpoints( fInput, getSelectionFilter(fInput, getDebugContext()), fBpManager.getBreakpoints()); synchronized(this) { ModelDelta delta = new ModelDelta(fInput, IModelDelta.NO_CHANGE); // create a reference container, use for deleting elements and adding elements ModelDelta dummyDelta = new ModelDelta(null, IModelDelta.NO_CHANGE); BreakpointContainer refContainer = createRootContainer(dummyDelta, fInput, fOrganizers, breakpoints); // delete the removed elements deleteRemovedElements(fContainer, refContainer, delta); // adjust the old organizer with the reference organizer BreakpointContainer.copyOrganizers(fContainer, refContainer); // insert the added elements IBreakpoint newBreakpoint = insertAddedElements(fContainer, refContainer, delta); delta.setChildCount(fContainer.getChildren().length); // select the new breakpoint if (newBreakpoint != null) { appendModelDeltaToElement(delta, newBreakpoint, IModelDelta.SELECT); } if (DebugUIPlugin.DEBUG_BREAKPOINT_DELTAS) { DebugUIPlugin.trace("POST BREAKPOINT DELTA (setOrganizers)\n"); //$NON-NLS-1$ } postModelChanged(delta, false); } } void sortContainers() { IBreakpoint[] breakpoints = filterBreakpoints(fInput, getSelectionFilter(fInput, getDebugContext()), fBpManager.getBreakpoints()); synchronized (this) { ModelDelta delta = new ModelDelta(fInput, IModelDelta.NO_CHANGE); // create a reference container, use for deleting elements and // adding elements ModelDelta dummyDelta = new ModelDelta(null, IModelDelta.NO_CHANGE); BreakpointContainer refContainer = createRootContainer(dummyDelta, fInput, fOrganizers, breakpoints); // delete all elements deleteAllElements(fContainer, delta); // adjust the old organizer with the reference organizer BreakpointContainer.copyOrganizers(fContainer, refContainer); // insert all elements IBreakpoint newBreakpoint = insertAddedElements(fContainer, refContainer, delta); delta.setChildCount(fContainer.getChildren().length); // select the new breakpoint if (newBreakpoint != null) { appendModelDeltaToElement(delta, newBreakpoint, IModelDelta.SELECT); } if (DebugUIPlugin.DEBUG_BREAKPOINT_DELTAS) { DebugUIPlugin.trace("POST BREAKPOINT DELTA (setOrganizers)\n"); //$NON-NLS-1$ } postModelChanged(delta, false); } } private synchronized IStructuredSelection getDebugContext() { return fDebugContext; } /** * Handles the property changed events in presentation contexts. * Sub-classes may override to perform additional handling. * @param event the event */ private void presentationPropertyChanged(PropertyChangeEvent event) { if (IBreakpointUIConstants.PROP_BREAKPOINTS_ELEMENT_COMPARATOR_SORT.equals(event.getProperty())) { sortContainers(); } else if (IPresentationContext.PROPERTY_DISPOSED.equals(event.getProperty())) { contextDisposed(fInput.getContext()); } if (IBreakpointUIConstants.PROP_BREAKPOINTS_ORGANIZERS.equals(event.getProperty())) { IBreakpointOrganizer[] organizers = (IBreakpointOrganizer[])event.getNewValue(); setOrganizers(organizers); } else if ( IBreakpointUIConstants.PROP_BREAKPOINTS_FILTER_SELECTION.equals(event.getProperty()) ) { IStructuredSelection selection = null; if (Boolean.TRUE.equals(event.getNewValue()) ) { selection = getDebugContext(); } setFilterSelection(selection); } else if ( IBreakpointUIConstants.PROP_BREAKPOINTS_TRACK_SELECTION.equals(event.getProperty()) ) { IStructuredSelection selection = null; if (Boolean.TRUE.equals(event.getNewValue()) ) { selection = getDebugContext(); } trackSelection(selection); } } private void debugContextChanged(DebugContextEvent event) { IStructuredSelection newContext; if (event.getContext() instanceof IStructuredSelection) { newContext = (IStructuredSelection)event.getContext(); } else { newContext = StructuredSelection.EMPTY; } synchronized(this) { fDebugContext = newContext; } if (Boolean.TRUE.equals(fInput.getContext().getProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_FILTER_SELECTION)) ) { setFilterSelection(newContext); } if (Boolean.TRUE.equals(fInput.getContext().getProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_TRACK_SELECTION)) ) { trackSelection(newContext); } } private void setFilterSelection(IStructuredSelection ss) { ModelDelta delta = new ModelDelta(fInput, IModelDelta.NO_CHANGE); boolean changed = false; // calculate supported breakpoints outside of the synchronized section. IBreakpoint[] allBreakpoints = fBpManager.getBreakpoints(); boolean[] supportedBreakpoints = new boolean[allBreakpoints.length]; for (int i = 0; i < allBreakpoints.length; ++i) { supportedBreakpoints[i] = supportsBreakpoint(ss, allBreakpoints[i]); } synchronized(this) { Set<IBreakpoint> existingBreakpoints = new HashSet<IBreakpoint>(Arrays.asList(fContainer.getBreakpoints())); // Bug 310879 // Process breakpoints in two passes: first remove breakpoints, then add new ones. // This way the breakpoint counts and indexes will be consistent in the delta. for (int i = 0; i < allBreakpoints.length; ++i) { if (!supportedBreakpoints[i] && existingBreakpoints.contains(allBreakpoints[i])) { fContainer.removeBreakpoint(allBreakpoints[i], delta); changed = true; } } for (int i = 0; i < allBreakpoints.length; ++i) { if (supportedBreakpoints[i] && !existingBreakpoints.contains(allBreakpoints[i])) { fContainer.addBreakpoint(allBreakpoints[i], delta); changed = true; } } if (changed) { if (DebugUIPlugin.DEBUG_BREAKPOINT_DELTAS) { DebugUIPlugin.trace("POST BREAKPOINT DELTA (setFilterSelection)\n"); //$NON-NLS-1$ } postModelChanged(delta, false); } } } private void trackSelection(IStructuredSelection selection) { if (selection == null || selection.size() != 1) { return; } Iterator<?> iter = selection.iterator(); Object firstElement = iter.next(); if (firstElement == null || iter.hasNext()) { return; } IThread thread = null; if (firstElement instanceof IStackFrame) { thread = ((IStackFrame) firstElement).getThread(); } else if (firstElement instanceof IThread) { thread = (IThread) firstElement; } else { return; } IBreakpoint[] breakpoints = thread.getBreakpoints(); Set<IBreakpoint> bpsSet = new HashSet<IBreakpoint>(breakpoints.length * 4 / 3); for (int i = 0; i< breakpoints.length; i++) { bpsSet.add(breakpoints[i]); } ModelDelta delta = new ModelDelta(fInput, IModelDelta.NO_CHANGE); synchronized (this) { if (buildTrackSelectionDelta(delta, fContainer, bpsSet)) { if (DebugUIPlugin.DEBUG_BREAKPOINT_DELTAS) { DebugUIPlugin.trace("POST BREAKPOINT DELTA (trackSelection)\n"); //$NON-NLS-1$ } BreakpointManagerProxy[] proxies = getProxies(); for (int i = 0; i < proxies.length; i++) { proxies[i].postModelChanged(delta, true); } } } } /** * Recursive function to build the model delta to select a breakpoint * corresponding to the active debug context selection. * * @param delta Delta node to build on * @param container Container element to build delta for. * @param breakpoints Breakpoint set to be selected. * @return whether we found a breakpoint to select */ private boolean buildTrackSelectionDelta(ModelDelta delta, BreakpointContainer container, Set<IBreakpoint> breakpoints) { Object[] children = container.getChildren(); delta.setChildCount(children.length); for (int i = 0; i < children.length; i++) { ModelDelta childDelta = delta.addNode(children[i], i, IModelDelta.NO_CHANGE); if (children[i] instanceof BreakpointContainer) { BreakpointContainer childContainer = (BreakpointContainer)children[i]; boolean containsBP = false; IBreakpoint[] containerBPs = childContainer.getBreakpoints(); for (int j = 0; j < containerBPs.length; j++) { if (breakpoints.contains(containerBPs[j])) { containsBP = true; break; } } if (containsBP && buildTrackSelectionDelta(childDelta, childContainer, breakpoints) ) { return true; } } else if (children[i] instanceof IBreakpoint && breakpoints.contains(children[i])) { childDelta.setFlags(IModelDelta.SELECT | IModelDelta.EXPAND); return true; } } return false; } /** * Helper method to add breakpoints to the given input. * * @param breakpoints the breakpoints */ void breakpointsAdded(IBreakpoint[] breakpoints) { IBreakpoint[] filteredBreakpoints = filterBreakpoints( fInput, getSelectionFilter(fInput, getDebugContext()), breakpoints); if (filteredBreakpoints.length > 0) { synchronized (this) { ModelDelta delta = new ModelDelta(fInput, 0, IModelDelta.NO_CHANGE, -1); for (int i = 0; i < filteredBreakpoints.length; ++i) { // Avoid adding breakpoints which were already removed. If breakpoints // are added and removed very fast, the Breakpoint manager can issue // breakpoint added events after breakpoint removed events! This means // that such breakpoints would never be removed from the view. // (Bug 289526) if (DebugPlugin.getDefault().getBreakpointManager().getBreakpoint(filteredBreakpoints[i].getMarker()) != null) { fContainer.addBreakpoint(filteredBreakpoints[i], delta); } } delta.setChildCount(fContainer.getChildren().length); // select the breakpoint if (filteredBreakpoints.length > 0) { appendModelDeltaToElement(delta, filteredBreakpoints[0], IModelDelta.SELECT); } if (DebugUIPlugin.DEBUG_BREAKPOINT_DELTAS) { DebugUIPlugin.trace("POST BREAKPOINT DELTA (breakpointsAddedInput)\n"); //$NON-NLS-1$ } postModelChanged(delta, false); } } } /** * Helper method to remove breakpoints from a given input. * * @param breakpoints the breakpoints */ void breakpointsRemoved(IBreakpoint[] breakpoints) { synchronized (this) { boolean removed = false; ModelDelta delta = new ModelDelta(fInput, IModelDelta.NO_CHANGE); for (int i = 0; i < breakpoints.length; ++i) { removed = fContainer.removeBreakpoint(breakpoints[i], delta) || removed; } if (removed) { if (DebugUIPlugin.DEBUG_BREAKPOINT_DELTAS) { DebugUIPlugin.trace("POST BREAKPOINT DELTA (breakpointsRemovedInput)\n"); //$NON-NLS-1$ } postModelChanged(delta, false); } } } void breakpointsChanged(IBreakpoint[] breakpoints) { IBreakpoint[] filteredBreakpoints = filterBreakpoints( fInput, getSelectionFilter(fInput, getDebugContext()), breakpoints); synchronized (this) { ModelDelta delta = new ModelDelta(fInput, IModelDelta.NO_CHANGE); // If the change caused a breakpoint to be added (installed) or remove (un-installed) update accordingly. List<IBreakpoint> removed = new ArrayList<IBreakpoint>(); List<IBreakpoint> added = new ArrayList<IBreakpoint>(); List<IBreakpoint> filteredAsList = Arrays.asList(filteredBreakpoints); for (int i = 0; i < breakpoints.length; i++) { IBreakpoint bp = breakpoints[i]; boolean oldContainedBp = fContainer.contains(bp); boolean newContained = filteredAsList.contains(bp); if (oldContainedBp && !newContained) { removed.add(bp); } else if (!oldContainedBp && newContained) { added.add(bp); } } if (!added.isEmpty()) { breakpointsAdded(added.toArray(new IBreakpoint[added.size()])); } if (!removed.isEmpty()) { breakpointsRemoved(removed.toArray(new IBreakpoint[removed.size()])); } for (int i = 0; i < filteredBreakpoints.length; ++i) { appendModelDelta(fContainer, delta, IModelDelta.STATE | IModelDelta.CONTENT, filteredBreakpoints[i]); // content flag triggers detail refresh } if (DebugUIPlugin.DEBUG_BREAKPOINT_DELTAS) { DebugUIPlugin.trace("POST BREAKPOINT DELTA (breakpointsChanged)\n"); //$NON-NLS-1$ } postModelChanged(delta, false); } } /** * Recursive function to build the model delta to install breakpoint * model proxies for all breakpoints and breakpoint containers. * * @param delta Delta node to build on * @param container Container element to build delta for. */ private void buildInstallDelta(ModelDelta delta, BreakpointContainer container) { Object[] children = container.getChildren(); delta.setChildCount(children.length); for (int i = 0; i < children.length; i++) { ModelDelta childDelta = delta.addNode(children[i], i, IModelDelta.NO_CHANGE); if (children[i] instanceof BreakpointContainer) { childDelta.setFlags(IModelDelta.INSTALL); buildInstallDelta(childDelta, (BreakpointContainer)children[i]); } else if (children[i] instanceof IBreakpoint) { childDelta.setFlags(IModelDelta.INSTALL); } } } /** * Insert elements from the reference container to an existing container. * * @param container the existing container to insert the new elements. * @param refContainer the reference container to compare elements that are added. * @param containerDelta the delta of the existing container. * @return the breakpoint that was inserted */ private IBreakpoint insertAddedElements(BreakpointContainer container, BreakpointContainer refContainer, ModelDelta containerDelta) { IBreakpoint newBreakpoint = null; Object[] children = container.getChildren(); Object[] refChildren = refContainer.getChildren(); for (int i = 0; i < refChildren.length; ++i) { Object element = getElement(children, refChildren[i]); // if a child of refContainer doesn't exist in container, than insert it to container // - if the reference child is a container, than copy the reference child container to container // - otherwise (Breakpoint), add the breakpoint to container if (element == null) { if (refChildren[i] instanceof BreakpointContainer) { BreakpointContainer.addChildContainer(container, (BreakpointContainer) refChildren[i], containerDelta); } else if(refChildren[i] instanceof IBreakpoint) { BreakpointContainer.addBreakpoint(container, (IBreakpoint) refChildren[i], containerDelta); if (newBreakpoint == null) { newBreakpoint = (IBreakpoint) refChildren[i]; } } // if a child exist in container, than recursively search into container. And also update the organizer of // of container to the one in the refContainer's child. } else if (element instanceof BreakpointContainer) { ModelDelta childDelta = containerDelta.addNode(element, container.getChildIndex(element), IModelDelta.INSTALL, -1); BreakpointContainer.copyOrganizers((BreakpointContainer) element, (BreakpointContainer) refChildren[i]); newBreakpoint = insertAddedElements((BreakpointContainer) element, (BreakpointContainer) refChildren[i], childDelta); childDelta.setChildCount(((BreakpointContainer) element).getChildren().length); } } return newBreakpoint; } /** * Delete elements from existing container that doesn't exist in the reference container. * * @param container the existing container to delete the removed elements. * @param refContainer the reference container to compare elements that are removed. * @param containerDelta the delta of the existing container. */ private void deleteRemovedElements(BreakpointContainer container, BreakpointContainer refContainer, ModelDelta containerDelta) { Object[] children = container.getChildren(); Object[] refChildren = refContainer.getChildren(); // if a child of container doesn't exist in refContainer, than remove it from container for (int i = 0; i < children.length; ++i) { Object element = getElement(refChildren, children[i]); if (element == null) { if (children[i] instanceof BreakpointContainer) { BreakpointContainer.removeAll((BreakpointContainer) children[i], containerDelta); } else { BreakpointContainer.removeBreakpoint(container, (IBreakpoint) children[i], containerDelta); } } else if (element instanceof BreakpointContainer){ ModelDelta childDelta = containerDelta.addNode(children[i], IModelDelta.STATE); deleteRemovedElements((BreakpointContainer) children[i], (BreakpointContainer) element, childDelta); } } } private void deleteAllElements(BreakpointContainer container, ModelDelta containerDelta) { Object[] children = container.getChildren(); // Object[] refChildren = refContainer.getChildren(); // if a child of container doesn't exist in refContainer, than // remove it from container for (int i = 0; i < children.length; ++i) { if (children[i] instanceof BreakpointContainer) { BreakpointContainer.removeAll((BreakpointContainer) children[i], containerDelta); } else { BreakpointContainer.removeBreakpoint(container, (IBreakpoint) children[i], containerDelta); } } } /** * Get the element that is in the collection. * * @param collection the collection of elements. * @param element the element to search. * @return if element exist in collection, than it is returned, otherwise <code>null</code> is returned. * @see #insertAddedElements * @see #deleteRemovedElements */ private Object getElement(Object[] collection, Object element) { for (int i = 0; i < collection.length; ++i) { if (collection[i] instanceof BreakpointContainer && element instanceof BreakpointContainer) { if (collection[i].equals(element)) { return collection[i]; } } else { if (collection[i].equals(element)) { return collection[i]; } } } return null; } /** * Create a root container. * * @param rootDelta the root delta. * @param input the view input. * @param organizers the breakpoint organizers. * @param breakpoints the breakpoints to add to the container * @return the new root container */ private BreakpointContainer createRootContainer( ModelDelta rootDelta, DefaultBreakpointsViewInput input, IBreakpointOrganizer[] organizers, IBreakpoint[] breakpoints) { BreakpointContainer container = new BreakpointContainer(organizers, fComparator); container.initDefaultContainers(rootDelta); for (int i = 0; i < breakpoints.length; ++i) { container.addBreakpoint(breakpoints[i], rootDelta); } return container; } /** * Fire model change event for the input. * * @param delta the model delta. * @param select if the viewer selection should change */ synchronized private void postModelChanged(final IModelDelta delta, boolean select) { for (int i = 0; fProxies != null && i < fProxies.size(); i++) { fProxies.get(i).postModelChanged(delta, select); } } } private class InputDataMap<K, V> extends LinkedHashMap<K, V> { private static final long serialVersionUID = 1L; public InputDataMap() { super(1, (float)0.75, true); } @Override protected boolean removeEldestEntry(java.util.Map.Entry<K, V> arg0) { InputData data = (InputData)arg0.getValue(); if (size() > getMaxInputsCache() && data.fProxies.isEmpty()) { data.dispose(); return true; } return false; } } /** * Scheduling rule to make sure that breakpoint manager listener updates * are process serially. */ private ISchedulingRule fBreakpointsListenerSchedulingRule = new ISchedulingRule() { @Override public boolean isConflicting(ISchedulingRule rule) { return rule == this; } @Override public boolean contains(ISchedulingRule rule) { return rule == this; } }; /** * A map of input to info data cache */ final private Map<DefaultBreakpointsViewInput, InputData> fInputToData = Collections.synchronizedMap(new InputDataMap<DefaultBreakpointsViewInput, InputData>()); /** * Flag indicating whether the content provider is currently a breakpoints listener. */ private boolean fIsBreakpointListener = false; /** * The breakpoint manager. */ final private IBreakpointManager fBpManager = DebugPlugin.getDefault().getBreakpointManager(); /** * Sub-classes may override this method to filter the breakpoints. * * @param input the breakpoint manager input. * @param selectionFilter the selection to use as filter * @param breakpoints the list of breakpoint to filter. * @return the filtered list of breakpoint based on the input. */ protected IBreakpoint[] filterBreakpoints(DefaultBreakpointsViewInput input, IStructuredSelection selectionFilter, IBreakpoint[] breakpoints) { if (selectionFilter != null && !selectionFilter.isEmpty()) { List<IDebugTarget> targets = getDebugTargets(selectionFilter); ArrayList<IBreakpoint> retVal = new ArrayList<IBreakpoint>(); if (targets != null) { for (int i = 0; i < breakpoints.length; ++i) { if (supportsBreakpoint(targets, breakpoints[i])) { retVal.add(breakpoints[i]); } } } return retVal.toArray(new IBreakpoint[retVal.size()]); } else { return breakpoints; } } /** * Sub-classes may override this to determine whether the breakpoint is supported by the selection. * * @param ss the selection of the debug elements. * @param breakpoint the breakpoint. * @return true if supported. */ protected boolean supportsBreakpoint(IStructuredSelection ss, IBreakpoint breakpoint) { return supportsBreakpoint(getDebugTargets(ss), breakpoint); } /** * Returns true if the breakpoint contains in one of the targets. * * @param targets a list of <code>IDebugTarget</code> objects. * @param breakpoint the breakpoint. * @return true if breakpoint contains in the list of targets. */ protected boolean supportsBreakpoint(List<IDebugTarget> targets, IBreakpoint breakpoint) { boolean exist = targets.size() == 0 ? true : false; for (int i = 0; !exist && i < targets.size(); ++i) { IDebugTarget target = targets.get(i); exist |= target.supportsBreakpoint(breakpoint); } return exist; } /** * Returns the list of IDebugTarget for the selection. * * @param ss the selection. * @return list of IDebugTarget object. */ protected List<IDebugTarget> getDebugTargets(IStructuredSelection ss) { List<IDebugTarget> debugTargets = new ArrayList<IDebugTarget>(2); if (ss != null) { Iterator<?> i = ss.iterator(); while (i.hasNext()) { Object next = i.next(); if (next instanceof IDebugElement) { debugTargets.add(((IDebugElement)next).getDebugTarget()); } else if (next instanceof ILaunch) { IDebugTarget[] targets = ((ILaunch)next).getDebugTargets(); for (int j = 0; j < targets.length; j++) { debugTargets.add(targets[j]); } } else if (next instanceof IProcess) { IDebugTarget target = ((IProcess)next).getAdapter(IDebugTarget.class); if (target != null) { debugTargets.add(target); } } else if (next instanceof IAdaptable) { // Allow non-standard debug model element return an IDebugTarget // element that could be used for implementing breakpoint filtering. // Bug 333517. IDebugTarget target = ((IAdaptable)next).getAdapter(IDebugTarget.class); if (target != null) { debugTargets.add(target); } } } } return debugTargets; } /** * Maximum number of breakpoint manager input objects that this provider * will cache data for. This method is called once upon class creation * when setting up the data cache. Sub-classes may override to provide * a custom setting. * * @return Maximum data cache size */ protected int getMaxInputsCache() { return 2; } /** * Handles the event when a presentation context is disposed. * Sub-classes may override to perform additional cleanup. * * @param context Presentation context that was disposed. */ protected void contextDisposed(IPresentationContext context) { List<InputData> removed = new ArrayList<InputData>(1); synchronized (fInputToData) { for (Iterator<Entry<DefaultBreakpointsViewInput, InputData>> itr = fInputToData.entrySet().iterator(); itr.hasNext();) { Map.Entry<DefaultBreakpointsViewInput, InputData> entry = itr.next(); IPresentationContext entryContext = entry.getKey().getContext(); if (context.equals(entryContext)) { removed.add(entry.getValue()); itr.remove(); } } } // Dispose the removed input data for (int i = 0; i < removed.size(); i++) { removed.get(i).dispose(); } } /** * Register the breakpoint manager input with this content provider. * * @param input the breakpoint manager input to register. * @param proxy the model proxy of the input. */ public void registerModelProxy(DefaultBreakpointsViewInput input, BreakpointManagerProxy proxy) { synchronized(this) { if (!fIsBreakpointListener) { fBpManager.addBreakpointListener(this); fIsBreakpointListener = true; } } InputData inputData = getInputData(input); if (inputData != null) { inputData.proxyInstalled(proxy); } } /** * Unregister the breakpoint manager input with this content provider. * * @param input the breakpoint manager input to unregister. * @param proxy the manager proxy */ public void unregisterModelProxy(DefaultBreakpointsViewInput input, BreakpointManagerProxy proxy) { InputData inputData = fInputToData.get(input); if (inputData != null) { inputData.proxyDisposed(proxy); if (fInputToData.isEmpty()) { synchronized(this) { if (fIsBreakpointListener) { fBpManager.removeBreakpointListener(this); fIsBreakpointListener = false; } } } } } private InputData getInputData(DefaultBreakpointsViewInput input) { if (Boolean.TRUE.equals(input.getContext().getProperty(IPresentationContext.PROPERTY_DISPOSED)) ) { return null; } InputData data = null; synchronized (fInputToData) { data = fInputToData.get(input); if (data == null) { data = new InputData(input); fInputToData.put(input, data); } } return data; } /** * Returns the selection filter for the input. * * @param input the selection. * @param debugContext the current context * @return the filtered selection or <code>null</code> */ protected IStructuredSelection getSelectionFilter(Object input, IStructuredSelection debugContext) { if (input instanceof DefaultBreakpointsViewInput) { IPresentationContext presentation = ((DefaultBreakpointsViewInput)input).getContext(); if ( Boolean.TRUE.equals(presentation.getProperty(IBreakpointUIConstants.PROP_BREAKPOINTS_FILTER_SELECTION)) ) { return debugContext; } } return null; } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#supportsContextId(java.lang.String) */ @Override protected boolean supportsContextId(String id) { return id.equals(IDebugUIConstants.ID_BREAKPOINT_VIEW); } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate) */ @Override protected int getChildCount(Object element, IPresentationContext context, IViewerUpdate monitor) throws CoreException { Object input = monitor.getViewerInput(); if (input instanceof DefaultBreakpointsViewInput) { DefaultBreakpointsViewInput bpManagerInput = (DefaultBreakpointsViewInput)input; InputData inputData = getInputData(bpManagerInput); if (inputData != null) { return inputData.fContainer.getChildren().length; } } return 0; } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext, org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate) */ @Override protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context, IViewerUpdate monitor) throws CoreException { Object input = monitor.getViewerInput(); if (input instanceof DefaultBreakpointsViewInput) { DefaultBreakpointsViewInput bpManagerInput = (DefaultBreakpointsViewInput)input; InputData inputData = getInputData(bpManagerInput); if (inputData != null) { Object[] children = inputData.fContainer.getChildren(); return getElements(children, index, length); } } return EMPTY; } /* * (non-Javadoc) * @see org.eclipse.debug.core.IBreakpointsListener#breakpointsAdded(org.eclipse.debug.core.model.IBreakpoint[]) */ @Override public void breakpointsAdded(final IBreakpoint[] breakpoints) { new Job("Breakpoints View Update Job") { //$NON-NLS-1$ { setSystem(true); setRule(fBreakpointsListenerSchedulingRule); } @Override protected IStatus run(IProgressMonitor monitor) { InputData[] datas = fInputToData.values().toArray(new InputData[0]); for (int i = 0; i < datas.length; i++) { datas[i].breakpointsAdded(breakpoints); } return Status.OK_STATUS; } }.schedule(); } /* * (non-Javadoc) * @see org.eclipse.debug.core.IBreakpointsListener#breakpointsRemoved(org.eclipse.debug.core.model.IBreakpoint[], org.eclipse.core.resources.IMarkerDelta[]) */ @Override public void breakpointsRemoved(final IBreakpoint[] breakpoints, IMarkerDelta[] deltas) { new Job("Breakpoints View Update Job") { //$NON-NLS-1$ { setSystem(true); setRule(fBreakpointsListenerSchedulingRule); } @Override protected IStatus run(IProgressMonitor monitor) { InputData[] datas = fInputToData.values().toArray(new InputData[0]); for (int i = 0; i < datas.length; i++) { datas[i].breakpointsRemoved(breakpoints); } return Status.OK_STATUS; } }.schedule(); } /* * (non-Javadoc) * @see org.eclipse.debug.core.IBreakpointsListener#breakpointsChanged(org.eclipse.debug.core.model.IBreakpoint[], org.eclipse.core.resources.IMarkerDelta[]) */ @Override public void breakpointsChanged(final IBreakpoint[] breakpoints, IMarkerDelta[] deltas) { new Job("Breakpoints View Update Job") { //$NON-NLS-1$ { setSystem(true); setRule(fBreakpointsListenerSchedulingRule); } @Override protected IStatus run(IProgressMonitor monitor) { InputData[] datas = fInputToData.values().toArray(new InputData[0]); for (int i = 0; i < datas.length; i++) { datas[i].breakpointsChanged(breakpoints); } return Status.OK_STATUS; } }.schedule(); } /** * Appends the model delta flags to child containers that contains the breakpoint. * * @param parent the parent container. * @param parentDelta the parent model delta. * @param flags the model delta flags. * @param breakpoint the breakpoint to search in the children containers. */ private void appendModelDelta(BreakpointContainer parent, ModelDelta parentDelta, int flags, IBreakpoint breakpoint) { BreakpointContainer[] containers = parent.getContainers(); if (parent.contains(breakpoint)) { if ((containers.length != 0)) { for (int i = 0; i < containers.length; ++i) { ModelDelta nodeDelta = parentDelta.addNode(containers[i], IModelDelta.STATE); appendModelDelta(containers[i], nodeDelta, flags, breakpoint); } } else { parentDelta.addNode(breakpoint, flags); } } } /** * Appends the model delta to the first found element in the model delta tree. * * @param parentDelta the parent delta * @param element the element to search * @param flags the delta flags */ private void appendModelDeltaToElement(IModelDelta parentDelta, Object element, int flags) { if (element.equals(parentDelta.getElement())) { ((ModelDelta) parentDelta).setFlags(parentDelta.getFlags() | flags); return; } IModelDelta[] childDeltas = parentDelta.getChildDeltas(); for (int i = 0; i < childDeltas.length; ++i) { if (element.equals(childDeltas[i].getElement())) { ((ModelDelta) childDeltas[i]).setFlags(childDeltas[i].getFlags() | flags); return; } appendModelDeltaToElement(childDeltas[i], element, flags); } } }