/******************************************************************************* * Copyright (c) 2008, 2012 Wind River Systems, Inc. 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.internal.cdt.ui.breakpoints; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.eclipse.cdt.debug.core.model.ICBreakpoint; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.ILaunchesListener2; import org.eclipse.debug.internal.ui.SWTFactory; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.ITreeModelViewer; 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.model.provisional.PresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy; import org.eclipse.debug.ui.IDetailPane; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.tcf.debug.ui.ITCFDebugUIConstants; import org.eclipse.tcf.internal.cdt.ui.ImageCache; import org.eclipse.tcf.internal.debug.model.ITCFConstants; import org.eclipse.tcf.internal.debug.model.TCFLaunch; import org.eclipse.tcf.internal.debug.ui.model.TCFChildrenContextQuery; import org.eclipse.tcf.internal.debug.ui.model.TCFModel; import org.eclipse.tcf.internal.debug.ui.model.TCFModelManager; import org.eclipse.tcf.internal.debug.ui.model.TCFNode; import org.eclipse.tcf.internal.debug.ui.model.TCFNodeExecContext; import org.eclipse.tcf.protocol.Protocol; import org.eclipse.tcf.services.IRunControl; import org.eclipse.tcf.util.TCFDataCache; import org.eclipse.ui.IWorkbenchPartSite; /** * This detail pane uses a tree viewer to show which contexts a given breakpoint * can potentially trigger. */ @SuppressWarnings("restriction") public class TCFBreakpointScopeDetailPane implements IDetailPane { public static final String ID = "org.eclipse.tcf.debug.DetailPaneFactory"; public static final String NAME = "TCF Detail Pane"; public static final String DESC = "TCF Detail Pane"; private Composite fComposite; private Label fFilterName; private TreeModelViewer fTreeViewer; public static class ScopeDetailInputObject extends PlatformObject implements IElementContentProvider, IModelProxyFactory { private final ContextQueryElement fContextQueryElement; public ScopeDetailInputObject(ContextQueryElement query) { fContextQueryElement = query; } @Override public boolean equals(Object other) { if (other instanceof ScopeDetailInputObject) { return fContextQueryElement.equals( ((ScopeDetailInputObject)other).fContextQueryElement ); } return false; } @Override public int hashCode() { return fContextQueryElement.hashCode(); } public void update(IChildrenCountUpdate[] updates) { for (IChildrenCountUpdate update : updates) { update.setChildCount(1); update.done(); } } public void update(IChildrenUpdate[] updates) { for (IChildrenUpdate update : updates) { if (update.getOffset() == 0) { update.setChild(fContextQueryElement, 0); update.done(); } } } public void update(IHasChildrenUpdate[] updates) { for (IHasChildrenUpdate update : updates) { update.setHasChilren(true); update.done(); } } public IModelProxy createModelProxy(Object element, IPresentationContext context) { return new AbstractModelProxy() { @Override public void initialize(ITreeModelViewer viewer) { super.initialize(viewer); ModelDelta delta = new ModelDelta(this, IModelDelta.NO_CHANGE); delta.addNode(fContextQueryElement, 0, IModelDelta.INSTALL); fireModelChanged(delta); } }; } } public static class ContextQueryElement extends PlatformObject implements IElementContentProvider, IElementLabelProvider, IModelProxyFactory { private final String fQuery; private final Set<String> fScope; public ContextQueryElement(String query, Set<String> contexts) { fQuery = query; fScope = contexts; } @Override public boolean equals(Object other) { if (other instanceof ContextQueryElement) { ContextQueryElement element = (ContextQueryElement)other; return ((fQuery == null && element.fQuery == null) || (fQuery != null && fQuery.equals(element.fQuery))) && ((fScope == null && element.fScope == null) || (fScope != null && fScope.equals(element.fScope))); } return false; } @Override public int hashCode() { return (fQuery != null ? fQuery.hashCode() : 0) + (fScope != null ? fScope.hashCode() : 0); } public void update(IChildrenCountUpdate[] updates) { for (IViewerUpdate update : updates) { getFilteredLaunches(update); } } public void update(IChildrenUpdate[] updates) { for (IViewerUpdate update : updates) { getFilteredLaunches(update); } } public void update(IHasChildrenUpdate[] updates) { for (IViewerUpdate update : updates) { getFilteredLaunches(update); } } public void update(ILabelUpdate[] updates) { for (ILabelUpdate update : updates) { getQueryFilteredContexts(update); } } private List<TCFLaunch> getTCFLaunches() { List<TCFLaunch> tcfLaunches = new ArrayList<TCFLaunch>(); for (ILaunch launch : DebugPlugin.getDefault().getLaunchManager().getLaunches()) { if (launch instanceof TCFLaunch) { tcfLaunches.add((TCFLaunch)launch); } } return tcfLaunches; } private void getFilteredLaunches (final IViewerUpdate update) { Protocol.invokeLater( new Runnable() { public void run() { final List<TCFLaunch> filteredLaunches = new ArrayList<TCFLaunch>(); TCFModelManager modelManager = TCFModelManager.getModelManager(); for (TCFLaunch launch : getTCFLaunches()) { TCFModel model = modelManager.getModel(launch); if (model != null && model.getRootNode() != null) { TCFChildrenContextQuery.Descendants des = TCFChildrenContextQuery.getDescendants( model.getRootNode(), fQuery, fScope, this); if (des == null) return; if (des.map != null && des.map.size() > 0) filteredLaunches.add(launch); } } done(filteredLaunches, update); } }); } private void getQueryFilteredContexts (final ILabelUpdate update) { Protocol.invokeLater( new Runnable() { public void run() { Map<String,String> node_names = new HashMap<String,String>(); TCFModelManager model_manager = TCFModelManager.getModelManager(); Set<String> descendants = new HashSet<String>(); for (TCFModel model : model_manager.getModels()) { if (!model.getLaunch().isConnected()) continue; TCFChildrenContextQuery.Descendants des = TCFChildrenContextQuery.getDescendants( model.getRootNode(), fQuery, fScope, this); if (des == null) return; if (des.map != null) descendants.addAll(des.map.keySet()); if (fScope == null) continue; String launch_name = model.getLaunch().getLaunchConfiguration().getName(); for (String id : fScope) { /* 'id' format is <launch name>/<context ID> */ if (node_names.containsKey(id)) continue; int i = id.indexOf('/'); if (i < 0) continue; if (!launch_name.equals(id.substring(0, i))) continue; TCFNode node = model.getNode(id.substring(i + 1)); if (node instanceof TCFNodeExecContext) { TCFDataCache<IRunControl.RunControlContext> ctx_cache = ((TCFNodeExecContext)node).getRunContext(); if (!ctx_cache.validate(this)) return; IRunControl.RunControlContext ctx_data = ctx_cache.getData(); if (ctx_data != null) { String name = ctx_data.getName(); if (name != null) node_names.put(id, name); } } } } StringBuffer label = new StringBuffer(); label.append("("); label.append(descendants.size()); label.append(") "); if (fQuery != null) { label.append("Filter: "); label.append(fQuery); if (fScope != null) { label.append(", "); } } if (fScope != null) { int cnt = 0; label.append("Contexts: "); label.append('['); for (String id : fScope) { if (cnt > 0) label.append(','); String name = node_names.get(id); label.append(name != null ? name : id); cnt++; } label.append(']'); } update.setLabel(label.toString(), 0); update.setImageDescriptor(ImageCache.getImageDescriptor(ImageCache.IMG_BREAKPOINT_SCOPE), 0); update.done(); } }); } private void done(List<TCFLaunch> launches, IViewerUpdate update) { if (update instanceof IHasChildrenUpdate) { ((IHasChildrenUpdate)update).setHasChilren(!launches.isEmpty()); } else if (update instanceof IChildrenCountUpdate) { ((IChildrenCountUpdate)update).setChildCount(launches.size()); } else if (update instanceof IChildrenUpdate) { IChildrenUpdate childrenUpdate = (IChildrenUpdate)update; int updateStart = childrenUpdate.getOffset(); int updateEnd = childrenUpdate.getOffset() + childrenUpdate.getLength(); for (int i = updateStart; i < updateEnd && i < launches.size(); i++) { childrenUpdate.setChild(launches.get(i), i); } } update.done(); } public IModelProxy createModelProxy(Object element, IPresentationContext context) { return new QueryInputObjectProxy(); } } public static class QueryInputObjectProxy extends AbstractModelProxy implements ILaunchesListener2 { private ILaunchManager fLaunchManager; /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.AbstractModelProxy#init(org.eclipse.debug.internal.ui.viewers.IPresentationContext) */ public synchronized void init(IPresentationContext context) { super.init(context); fLaunchManager = DebugPlugin.getDefault().getLaunchManager(); fLaunchManager.addLaunchListener(this); } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy#installed(org.eclipse.jface.viewers.Viewer) */ public void installed(Viewer viewer) { // expand existing launches ILaunch[] launches = fLaunchManager.getLaunches(); if (launches.length > 0) { launchesAdded(launches); } } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.AbstractModelProxy#dispose() */ public synchronized void dispose() { super.dispose(); if (fLaunchManager != null) { fLaunchManager.removeLaunchListener(this); fLaunchManager = null; } } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchesListener2#launchesTerminated(org.eclipse.debug.core.ILaunch[]) */ public void launchesTerminated(ILaunch[] launches) { } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchesListener#launchesRemoved(org.eclipse.debug.core.ILaunch[]) */ public void launchesRemoved(ILaunch[] launches) { fireDelta(launches, IModelDelta.REMOVED); } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchesListener#launchesAdded(org.eclipse.debug.core.ILaunch[]) */ public void launchesAdded(ILaunch[] launches) { fireDelta(launches, IModelDelta.ADDED | IModelDelta.INSTALL); } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchesListener#launchesChanged(org.eclipse.debug.core.ILaunch[]) */ public void launchesChanged(ILaunch[] launches) { } /** * Convenience method for firing a delta * @param launches the launches to set in the delta * @param launchFlags the flags for the delta */ protected void fireDelta(ILaunch[] launches, int launchFlags) { ModelDelta delta = new ModelDelta(fLaunchManager, IModelDelta.NO_CHANGE); for (int i = 0; i < launches.length; i++) { if (launches[i] instanceof TCFLaunch) { delta.addNode(launches[i], launchFlags); } } fireModelChanged(delta); } } public Control createControl(Composite parent) { fComposite = SWTFactory.createComposite(parent, 1, 1, GridData.FILL_BOTH); fFilterName = SWTFactory.createLabel(fComposite, "Scope", 1); fTreeViewer = new TreeModelViewer(fComposite, SWT.VIRTUAL, new PresentationContext(ITCFDebugUIConstants.ID_CONTEXT_QUERY_VIEW)); Control control = fTreeViewer.getControl(); GridData treeLayoutData = new GridData(GridData.FILL_BOTH); treeLayoutData.horizontalIndent = 10; control.setLayoutData(treeLayoutData); GridData gd = new GridData(GridData.FILL_BOTH); control.setLayoutData(gd); return fComposite; } public void display(IStructuredSelection selection) { if (fTreeViewer == null) return; TCFBreakpointScopeExtension extension = getTCFBreakpointScopeExtension((ICBreakpoint)selection.getFirstElement()); if (extension != null) { String filter = extension.getPropertiesFilter(); if (filter != null && filter.trim().isEmpty()) { filter = null; } String[] contexts = extension.getThreadFilters(); if (filter != null || contexts != null) { fFilterName.setText("Scope"); Set<String> contextsSet = contexts != null ? new TreeSet<String>(Arrays.asList(contexts)) : null; fTreeViewer.setInput( new ScopeDetailInputObject( new ContextQueryElement(filter, contextsSet)) ); fTreeViewer.getPresentationContext().setProperty(ITCFDebugUIConstants.PROP_CONTEXT_QUERY, filter); fTreeViewer.getPresentationContext().setProperty(ITCFDebugUIConstants.PROP_FILTER_CONTEXTS, contextsSet); fTreeViewer.refresh(); return; } } fFilterName.setText("No scope specified."); fTreeViewer.setInput(null); fTreeViewer.getPresentationContext().setProperty(ITCFDebugUIConstants.PROP_CONTEXT_QUERY, null); fTreeViewer.getPresentationContext().setProperty(ITCFDebugUIConstants.PROP_FILTER_CONTEXTS, null); } private TCFBreakpointScopeExtension getTCFBreakpointScopeExtension(ICBreakpoint bp) { if (bp == null) return null; try { return (TCFBreakpointScopeExtension) bp.getExtension( ITCFConstants.ID_TCF_DEBUG_MODEL, TCFBreakpointScopeExtension.class); } catch (CoreException e) {} return null; } public void dispose() { if (fTreeViewer != null) { fTreeViewer.getControl().dispose(); fTreeViewer = null; } if (fFilterName != null) { fFilterName.dispose(); fFilterName = null; } if (fComposite != null) { fComposite.dispose(); fComposite = null; } } public String getDescription() { return DESC; } public String getID() { return ID; } public String getName() { return NAME; } public void init(IWorkbenchPartSite part_site) { } public boolean setFocus() { if (fTreeViewer == null) return false; fTreeViewer.getControl().setFocus(); return true; } }