/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.debug.ui.propertypages; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IThread; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.jdt.debug.core.IJavaDebugTarget; import org.eclipse.jdt.debug.core.IJavaThread; import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; import org.eclipse.jface.viewers.AbstractTreeViewer; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.ITreeContentProvider; 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.Label; /** * */ public class ThreadFilterEditor { private JavaBreakpointAdvancedPage fPage; private CheckboxTreeViewer fThreadViewer; private ThreadFilterContentProvider fContentProvider; private CheckHandler fCheckHandler; private static String MAIN= "main"; //$NON-NLS-1$ public ThreadFilterEditor(Composite parent, JavaBreakpointAdvancedPage page) { fPage= page; fContentProvider= new ThreadFilterContentProvider(); fCheckHandler= new CheckHandler(); createThreadViewer(parent); } private void createThreadViewer(Composite parent) { Label label= new Label(parent, SWT.NONE); label.setText(PropertyPageMessages.ThreadFilterEditor_1); label.setFont(parent.getFont()); label.setLayoutData(new GridData()); GridData data= new GridData(GridData.FILL_BOTH); data.heightHint= 100; fThreadViewer= new CheckboxTreeViewer(parent, SWT.BORDER); fThreadViewer.addCheckStateListener(fCheckHandler); fThreadViewer.getTree().setLayoutData(data); fThreadViewer.getTree().setFont(parent.getFont()); fThreadViewer.setContentProvider(fContentProvider); fThreadViewer.setLabelProvider(DebugUITools.newDebugModelPresentation()); fThreadViewer.setInput(DebugPlugin.getDefault().getLaunchManager()); setInitialCheckedState(); } protected void doStore() { IDebugTarget[] targets= getDebugTargets(); IJavaDebugTarget target; IThread[] threads; IJavaThread thread; for (int i= 0, numTargets= targets.length; i < numTargets; i++) { target = targets[i].getAdapter(IJavaDebugTarget.class); if (target != null) { try { if (fThreadViewer.getChecked(target)) { threads= target.getThreads(); for (int j=0, numThreads= threads.length; j < numThreads; j++) { thread= (IJavaThread)threads[j]; if (fThreadViewer.getChecked(thread)) { // thread selected for filtering fPage.getBreakpoint().setThreadFilter(thread); break; // Can only set one filtered thread. } } } else { fPage.getBreakpoint().removeThreadFilter(target); } } catch (CoreException e) { JDIDebugUIPlugin.log(e); } } } } /** * Sets the initial checked state of the tree viewer. * The initial state should reflect the current state * of the breakpoint. If the breakpoint has a thread * filter in a given thread, that thread should be * checked. */ protected void setInitialCheckedState() { try { IDebugTarget[] targets= getDebugTargets(); for (int i= 0, numTargets= targets.length; i < numTargets; i++) { IJavaDebugTarget target = targets[i].getAdapter(IJavaDebugTarget.class); if (target != null) { IJavaThread filteredThread= fPage.getBreakpoint().getThreadFilter(target); if (filteredThread != null) { fCheckHandler.checkThread(filteredThread, true); } } } } catch (CoreException e) { JDIDebugUIPlugin.log(e); } } /** * Returns the debug targets that appear in the tree */ protected IDebugTarget[] getDebugTargets() { Object input= fThreadViewer.getInput(); if (!(input instanceof ILaunchManager)) { return new IJavaDebugTarget[0]; } ILaunchManager launchManager= (ILaunchManager)input; return launchManager.getDebugTargets(); } class CheckHandler implements ICheckStateListener { @Override public void checkStateChanged(CheckStateChangedEvent event) { Object element= event.getElement(); if (element instanceof IDebugTarget) { checkTarget((IDebugTarget)element, event.getChecked()); } else if (element instanceof IThread) { checkThread((IThread)element, event.getChecked()); } verifyCheckedState(); } /** * Check or uncheck a debug target in the tree viewer. * When a debug target is checked, attempt to * check one of the target's threads by default. * When a debug target is unchecked, uncheck all * its threads. */ protected void checkTarget(IDebugTarget target, boolean checked) { fThreadViewer.setChecked(target, checked); if (checked) { fThreadViewer.expandToLevel(target, AbstractTreeViewer.ALL_LEVELS); IThread[] threads; try { threads= target.getThreads(); } catch (DebugException exception) { JDIDebugUIPlugin.log(exception); return; } IThread thread; boolean checkedThread= false; // Try to check the "main" thread by default for (int i= 0, numThreads= threads.length; i < numThreads; i++) { thread= threads[i]; String name= null; try { name= thread.getName(); } catch (DebugException exception) { JDIDebugUIPlugin.log(exception); } if (MAIN.equals(name)) { checkedThread= fThreadViewer.setChecked(thread, true); } } // If the main thread couldn't be checked, check the first // available thread if (!checkedThread) { for (int i= 0, numThreads= threads.length; i < numThreads; i++) { if (fThreadViewer.setChecked(threads[i], true)) { break; } } } } else { // Unchecked IThread[] threads; try { threads= target.getThreads(); } catch (DebugException exception) { JDIDebugUIPlugin.log(exception); return; } for (int i= 0, numThreads= threads.length; i < numThreads; i++) { fThreadViewer.setChecked(threads[i], false); } } } /** * Check or uncheck a thread. * When a thread is checked, make sure its debug * target is also checked. * When a thread is unchecked, uncheck its debug * target. */ protected void checkThread(IThread thread, boolean checked) { fThreadViewer.setChecked(thread, checked); IDebugTarget target= (thread).getDebugTarget(); if (checked) { // When a thread is checked, make sure the target // is checked and all other threads are // unchecked (simulate radio button behavior) if (!fThreadViewer.getChecked(target)) { fThreadViewer.setChecked(target, true); } IThread[] threads; try { threads= target.getThreads(); } catch (DebugException exception) { JDIDebugUIPlugin.log(exception); return; } for (int i= 0, numThreads= threads.length; i < numThreads; i++) { if (threads[i] != thread) { // Uncheck all threads other than the selected thread fThreadViewer.setChecked(threads[i], false); } } } else { // When a thread is unchecked, uncheck the target fThreadViewer.setChecked(target, false); } } /** * Verify the state of the tree viewer. * If the user selects a debug target, they must select * a thread. */ protected void verifyCheckedState() { IDebugTarget[] targets= getDebugTargets(); IDebugTarget target; IThread[] threads; boolean checkedThread; for (int i= 0, numTargets= targets.length; i < numTargets; i++) { target= targets[i]; if (!fThreadViewer.getChecked(target)) { continue; } try { threads= target.getThreads(); } catch (DebugException exception) { JDIDebugUIPlugin.log(exception); continue; } checkedThread= false; for (int j= 0, numThreads= threads.length; j < numThreads; j++) { if (fThreadViewer.getChecked(threads[j])) { checkedThread= true; break; } } if (checkedThread) { fPage.setErrorMessage(null); } else { fPage.setErrorMessage(PropertyPageMessages.ThreadFilterEditor_2); } } } } class ThreadFilterContentProvider implements ITreeContentProvider { /** * @see ITreeContentProvider#getChildren(Object) */ @Override public Object[] getChildren(Object parent) { if (parent instanceof IDebugTarget) { IJavaDebugTarget target = ((IDebugTarget)parent).getAdapter(IJavaDebugTarget.class); if (target != null) { try { return ((IJavaDebugTarget)parent).getThreads(); } catch (DebugException e) { JDIDebugUIPlugin.log(e); } } } if (parent instanceof ILaunchManager) { List<IJavaDebugTarget> children= new ArrayList<>(); ILaunch[] launches= ((ILaunchManager) parent).getLaunches(); IDebugTarget[] targets; IJavaDebugTarget target; for (int i= 0, numLaunches= launches.length; i < numLaunches; i++) { targets= launches[i].getDebugTargets(); for (int j= 0, numTargets= targets.length; j < numTargets; j++) { target= targets[j].getAdapter(IJavaDebugTarget.class); if (target != null && !target.isDisconnected() && !target.isTerminated()) { children.add(target); } } } return children.toArray(); } return new Object[0]; } /** * @see ITreeContentProvider#getParent(Object) */ @Override public Object getParent(Object element) { if (element instanceof IThread) { return ((IThread)element).getDebugTarget(); } if (element instanceof IDebugTarget) { return ((IDebugElement)element).getLaunch(); } if (element instanceof ILaunch) { return DebugPlugin.getDefault().getLaunchManager(); } return null; } /** * @see ITreeContentProvider#hasChildren(Object) */ @Override public boolean hasChildren(Object element) { if (element instanceof IStackFrame) { return false; } if (element instanceof IDebugElement) { return getChildren(element).length > 0; } if (element instanceof ILaunch) { return true; } if (element instanceof ILaunchManager) { return ((ILaunchManager) element).getLaunches().length > 0; } return false; } /** * @see IStructuredContentProvider#getElements(Object) */ @Override public Object[] getElements(Object inputElement) { return getChildren(inputElement); } /** * @see IContentProvider#dispose() */ @Override public void dispose() { } /** * @see IContentProvider#inputChanged(Viewer, Object, Object) */ @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } } }