| /***************************************************************** |
| * Copyright (c) 2009, 2013 Texas Instruments and others |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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<>(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<>(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<>(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<>(); |
| List<IBreakpoint> added = new ArrayList<>(); |
| 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<>(); |
| 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.isEmpty() ? 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<>(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<>(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; |
| } |
| |
| @Override |
| protected boolean supportsContextId(String id) { |
| return id.equals(IDebugUIConstants.ID_BREAKPOINT_VIEW); |
| } |
| |
| @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; |
| } |
| |
| @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; |
| } |
| |
| @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(); |
| } |
| |
| @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(); |
| } |
| |
| @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); |
| } |
| } |
| } |