diff options
author | Pawel Piech | 2009-01-08 21:08:05 +0000 |
---|---|---|
committer | Pawel Piech | 2009-01-08 21:08:05 +0000 |
commit | 5b471873662a87a77cfd854c98fca9c9948aa878 (patch) | |
tree | 3da8dcf3820bf3c2d4ebd17e4e8f3043e9773676 /dsf/org.eclipse.cdt.examples.dsf/src | |
parent | 47093064424981f85335cfdaf25368c54cd840f8 (diff) | |
parent | c1e6da229b8ffcea160498f034bfa6bc8ff6f230 (diff) | |
download | org.eclipse.cdt-5b471873662a87a77cfd854c98fca9c9948aa878.tar.gz org.eclipse.cdt-5b471873662a87a77cfd854c98fca9c9948aa878.tar.xz org.eclipse.cdt-5b471873662a87a77cfd854c98fca9c9948aa878.zip |
Migrated DSF and DSF-GDB to the CDT project.
Diffstat (limited to 'dsf/org.eclipse.cdt.examples.dsf/src')
23 files changed, 3021 insertions, 0 deletions
diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/DsfExamplesPlugin.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/DsfExamplesPlugin.java new file mode 100644 index 00000000000..8799f748c88 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/DsfExamplesPlugin.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + */ +public class DsfExamplesPlugin extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.cdt.examples.dsf"; //$NON-NLS-1$ + + public static final String IMG_LAYOUT_TOGGLE = "icons/layout.gif"; //$NON-NLS-1$ + public static final String IMG_ALARM = "icons/alarm.gif"; //$NON-NLS-1$ + public static final String IMG_ALARM_TRIGGERED = "icons/alarm_triggered.gif"; //$NON-NLS-1$ + public static final String IMG_TIMER = "icons/timer.gif"; //$NON-NLS-1$ + public static final String IMG_REMOVE = "icons/remove.gif"; //$NON-NLS-1$ + + // The shared instance + private static DsfExamplesPlugin fgPlugin; + + private static BundleContext fgBundleContext; + + /** + * The constructor + */ + public DsfExamplesPlugin() { + fgPlugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + @Override + public void start(BundleContext context) throws Exception { + fgBundleContext = context; + super.start(context); + getImageRegistry().put(IMG_ALARM, imageDescriptorFromPlugin(PLUGIN_ID, IMG_ALARM)); + getImageRegistry().put(IMG_ALARM_TRIGGERED, imageDescriptorFromPlugin(PLUGIN_ID, IMG_ALARM_TRIGGERED)); + getImageRegistry().put(IMG_TIMER, imageDescriptorFromPlugin(PLUGIN_ID, IMG_TIMER)); + getImageRegistry().put(IMG_REMOVE, imageDescriptorFromPlugin(PLUGIN_ID, IMG_REMOVE)); + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + fgPlugin = null; + fgBundleContext = null; + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static DsfExamplesPlugin getDefault() { + return fgPlugin; + } + + public static BundleContext getBundleContext() { + return fgBundleContext; + } + + /** + * Returns an image descriptor for the image file at the given + * plug-in relative path + * + * @param path the path + * @return the image descriptor + */ + public static ImageDescriptor getImageDescriptor(String path) { + return imageDescriptorFromPlugin(PLUGIN_ID, path); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserAction.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserAction.java new file mode 100644 index 00000000000..2e686f5c8d1 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserAction.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.filebrowser; + +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowActionDelegate; +import org.eclipse.ui.actions.ActionDelegate; + +/** + * Action that opens the File Browser example dialog. + */ +public class FileBrowserAction extends ActionDelegate + implements IWorkbenchWindowActionDelegate +{ + private IWorkbenchWindow fWindow; + + @Override + public void run(IAction action) { + if (fWindow != null) { + // Create the dialog and open it. + Dialog dialog = new FileBrowserDialog(fWindow.getShell()); + dialog.open(); + } + } + + public void init(IWorkbenchWindow window) { + fWindow = window; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserDialog.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserDialog.java new file mode 100644 index 00000000000..807913c0216 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserDialog.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.filebrowser; + +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +/** + * File Browser example dialog. It hold a tree viewer that displays + * file system contents and a text box for entering a file path to be + * shown in the tree. + */ +@SuppressWarnings("restriction") +public class FileBrowserDialog extends Dialog { + + /** + * Tree viewer for showing the filesystem contents. + */ + private TreeModelViewer fViewer; + + /** + * The model adapter for the tree viewer. + */ + private FileBrowserModelAdapter fModelAdapter; + + /** + * Flag used to disable text-box changed events, when the text + * box is updated due to selection change in tree. + */ + private boolean fDisableTextChangeNotifications = false; + + public FileBrowserDialog(Shell parent) { + super(parent); + setShellStyle(getShellStyle() | SWT.RESIZE); + } + + @Override + protected Control createDialogArea(Composite parent) { + Composite area = (Composite) super.createDialogArea(parent); + IPresentationContext presentationContext = new PresentationContext("org.eclipse.cdt.examples.dsf.filebrowser"); //$NON-NLS-1$ + + fViewer = new TreeModelViewer(area, SWT.VIRTUAL, presentationContext); + fViewer.getControl().setLayoutData(new GridData(GridData.FILL_BOTH)); + + fModelAdapter = new FileBrowserModelAdapter(presentationContext); + fViewer.setInput(fModelAdapter.getVMProvider().getViewerInputObject()); + + final Text text = new Text(area, SWT.SINGLE | SWT.BORDER); + text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + fViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + /* + * Update the file name in the text control, to match the + * selection in the tree. Do this only if the user is not + * actively typing in the text field (test if text has focus). + */ + if (!text.isFocusControl() && + event.getSelection() instanceof IStructuredSelection && + ((IStructuredSelection)event.getSelection()).getFirstElement() instanceof FileVMContext) + { + FileVMContext fileVmc = (FileVMContext)((IStructuredSelection)event.getSelection()).getFirstElement(); + + fDisableTextChangeNotifications = true; + text.setText(fileVmc.getFile().getAbsolutePath()); + fDisableTextChangeNotifications = false; + } + } + }); + + text.addModifyListener(new ModifyListener() { + public void modifyText(ModifyEvent e) { + if (!fDisableTextChangeNotifications) { + fModelAdapter.getVMProvider().selectionTextChanged(text.getText()); + } + } + }); + + return area; + } + + @Override + public boolean close() { + if (super.close()) { + fModelAdapter.dispose(); + fModelAdapter = null; + return true; + } + return false; + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserModelAdapter.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserModelAdapter.java new file mode 100644 index 00000000000..497b31f45c1 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserModelAdapter.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.filebrowser; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * This is the adapter that implements the flexible hierarchy viewer interfaces + * for providing content, labels, and event proxy-ing for the viewer. This + * adapter is registered with the DSF Session object, and is returned by the + * IDMContext.getAdapter() and IVMContext.getAdapter() methods, + * which both call {@link DsfSession#getModelAdapter(Class)}. + * <p> + * The adapter implementation for this exercise is hard-coded to provide + * contents for only one view. In turn the view contents are determined using + * the configurable ViewModelProvider. For demonstration purposes, this model + * adapter has two different layout configurations that can be used. These + * layout configurations can be set by calling the {@link #setViewLayout} method. + * <p> + * This class is primarily accessed by the flexible hierarchy viewer from a + * non-executor thread. So the class is thread-safe, except for a view methods + * which must be called on the executor thread. + * + * @see AbstractDMVMProvider + */ +@SuppressWarnings("restriction") +@ThreadSafe +public class FileBrowserModelAdapter extends AbstractVMAdapter +{ + FileBrowserVMProvider fViewModelProvider; + + @Override + protected IVMProvider createViewModelProvider(IPresentationContext context) { + /* + * In this example there is only one viewer, so there is only one + * VMProvider. + */ + return fViewModelProvider; + } + + public FileBrowserModelAdapter(IPresentationContext presentationContext) { + super(); + fViewModelProvider = new FileBrowserVMProvider(this, presentationContext); + } + + FileBrowserVMProvider getVMProvider() { + return fViewModelProvider; + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserVMProvider.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserVMProvider.java new file mode 100644 index 00000000000..ef3d098bb9c --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileBrowserVMProvider.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.filebrowser; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.RootVMNode; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * + */ +@SuppressWarnings("restriction") +public class FileBrowserVMProvider extends AbstractVMProvider +{ + /** + * The object to be set to the viewer that shows contents supplied by this provider. + * @see org.eclipse.jface.viewers.TreeViewer#setInput(Object) + */ + private final IAdaptable fViewerInputObject = + new IAdaptable() { + /** + * The input object provides the viewer access to the viewer model adapter. + */ + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + if ( adapter.isInstance(getVMAdapter()) ) { + return getVMAdapter(); + } + return null; + } + + @Override + public String toString() { + return "File Browser Viewer Input"; //$NON-NLS-1$ + } + }; + + /** + * Constructor creates and configures the layout nodes to display file + * system contents. + * @param adapter The viewer model adapter that this provider is registered with. + * @param presentationContext The presentation context that this provider is + * generating contents for. + */ + public FileBrowserVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext) { + super(adapter, presentationContext); + + IRootVMNode root = new RootVMNode(this); + IVMNode fileSystemRoots = new FilesystemRootsVMNode(this); + addChildNodes(root, new IVMNode[] { fileSystemRoots }); + IVMNode files = new FileVMNode(this); + addChildNodes(fileSystemRoots, new IVMNode[] { files }); + addChildNodes(files, new IVMNode[] { files }); + setRootNode(root); + } + + /** + * Returns the input object to be set to the viewer that shows contents + * supplied by this provider. + */ + public Object getViewerInputObject() { + return fViewerInputObject; + } + + /** + * Event handler for file selection text changes in the dialog. + * @param text New text entered in file selection text box. + */ + void selectionTextChanged(final String text) { + if (isDisposed()) return; + + // We're in the UI thread. Re-dispach to VM Adapter executor thread + // and then call root layout node. + try { + getExecutor().execute(new Runnable() { + public void run() { + if (isDisposed()) return; + handleEvent(text); + }}); + } catch (RejectedExecutionException e) { + // Ignore. This exception could be thrown if the provider is being + // shut down. + } + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMContext.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMContext.java new file mode 100644 index 00000000000..93b8da64e9e --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMContext.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.filebrowser; + +import java.io.File; + +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; + +class FileVMContext extends AbstractVMContext { + private File fFile; + FileVMContext(IVMNode layoutNode, File file) { + super(layoutNode); + fFile = file; + } + + File getFile() { return fFile; } + + @Override + public boolean equals(Object obj) { + return obj instanceof FileVMContext && ((FileVMContext)obj).getFile().equals(fFile); + } + + @Override + public int hashCode() { + return fFile.hashCode(); + } + + @Override + public String toString() { + return fFile.toString(); + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMNode.java new file mode 100644 index 00000000000..e137dad245f --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FileVMNode.java @@ -0,0 +1,314 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.filebrowser; + +import java.io.File; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +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.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.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; + + +/** + * File view model node which returns file elements that are found in the directory + * specified by the parent element. The child nodes of this node are fixed to + * reference this element, and therefore this node will recursively populate + * the contents of the tree reflecting the underlying filesystem directories. + * <br> + * Note: this node does NOT sub-class the {@link org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMNode} + */ +@SuppressWarnings("restriction") +class FileVMNode + implements IElementLabelProvider, IVMNode +{ + /** + * Reference to the viewer model provider. It's mainly used to access the + * viewer model adapter and its executor. + */ + private final FileBrowserVMProvider fProvider; + + public FileVMNode(FileBrowserVMProvider provider) { + fProvider = provider; + } + + @Override + public String toString() { + return "FileVMNode"; + } + + + public void dispose() { + // All resources garbage collected. + } + + public void setChildNodes(IVMNode[] childNodes) { + throw new UnsupportedOperationException("This node does not support children."); //$NON-NLS-1$ + } + + /** + * List of child nodes containing only a reference to this. + */ + private final IVMNode[] fChildNodes = { this }; + + public IVMNode[] getChildNodes() { + return fChildNodes; + } + + public void update(final IHasChildrenUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (IHasChildrenUpdate update : updates) { + /* + * Do not retrieve directory contents just to mark the plus + * sign in the tree. If it's a directory, just assume that + * it has children. + */ + FileVMContext vmc = (FileVMContext)update.getElement(); + update.setHasChilren(vmc.getFile().isDirectory()); + update.done(); + } + + return Status.OK_STATUS; + } + }.schedule(); + } + + public void update(final IChildrenCountUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (IChildrenCountUpdate update : updates) { + update.setChildCount(getFiles(update).length); + update.done(); + } + return Status.OK_STATUS; + } + }.schedule(); + } + + public void update(final IChildrenUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (IChildrenUpdate update : updates) { + File[] files = getFiles(update); + int offset = update.getOffset() != -1 ? update.getOffset() : 0; + int length = update.getLength() != -1 ? update.getLength() : files.length; + for (int i = offset; (i < files.length) && (i < (offset + length)); i++) { + update.setChild(new FileVMContext(FileVMNode.this, files[i]), i); + } + update.done(); + } + return Status.OK_STATUS; + } + }.schedule(); + } + + public void update(final ILabelUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (ILabelUpdate update : updates) { + update.setLabel(getLabel((FileVMContext)update.getElement()), 0); + update.done(); + } + + return Status.OK_STATUS; + } + }.schedule(); + } + + private static final File[] EMPTY_FILE_LIST = new File[0]; + + /** + * Retrieves the list of files for this node. The list of files is based + * on the parent element in the tree, which must be of type FileVMC. + * + * @param update Update object containing the path (and the parent element) + * in the tree viewer. + * @return List of files contained in the directory specified in the + * update object. An empty list if the parent element is not a directory. + * @throws ClassCastException If the parent element contained in the update + * is NOT of type FileVMC. + */ + private File[] getFiles(IViewerUpdate update) { + FileVMContext vmc = (FileVMContext)update.getElement(); + File[] files = vmc.getFile().listFiles(); + return files != null ? files : EMPTY_FILE_LIST; + } + + /** + * Returs the text label to show in the tree for given element. + */ + private String getLabel(FileVMContext vmc) { + return vmc.getFile().getName(); + } + + public void getContextsForEvent(VMDelta parentDelta, Object event, DataRequestMonitor<IVMContext[]> rm) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "", null)); //$NON-NLS-1$ + rm.done(); + } + + public int getDeltaFlags(Object e) { + /* + * @see buildDelta() + */ + int retVal = IModelDelta.NO_CHANGE; + if (e instanceof String) { + retVal |= IModelDelta.SELECT | IModelDelta.EXPAND; + } + + return retVal; + } + + public void buildDelta(final Object event, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { + /* + * The FileLayoutNode is recursive, with itself as the only child. In this + * method the delta is calculated for a full path VMContext elements, and the + * implementation of this method is not recursive. + */ + if (event instanceof String) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + /* + * Requirements for a selection event to be issued is that the file exist, and + * that the parentDelta contain a FileVMC of a parent directory as its element. + * + * The test for first the former requirement could be performed inside getDeltaFlags() + * but getDeltaFlags() is synchronous, so it is better to perform this test here using + * a background thread (job). + * + * The latter is requirement is needed because this node does not have the algorithm + * calculate the complete list of root nodes. That algorithm is implemented inside the + * {@link FileSystemRootsLayoutNode#updateElements} method. + */ + + final File eventFile = new File((String)event); + File parentFile = null; + if (parentDelta.getElement() instanceof FileVMContext) { + parentFile = ((FileVMContext)parentDelta.getElement()).getFile(); + } + + // The file has to exist in order for us to be able to select + // it in the tree. + if (eventFile.exists() && parentFile != null) { + // Create a list containing all files in path + List<File> filePath = new LinkedList<File>(); + for (File file = eventFile; file != null && !file.equals(parentFile); file = file.getParentFile()) { + filePath.add(0, file); + } + + if (filePath.size() != 0) { + // Build the delta for all files in path. + ModelDelta delta = parentDelta; + File[] allFilesInDirectory = parentFile.listFiles(); + for (File pathSegment : filePath) { + // All files in path should be directories, and should therefore + // have a valid list of elements. + assert allFilesInDirectory != null; + + File[] pathSegmentDirectoryFiles = pathSegment.listFiles(); + delta = delta.addNode( + new FileVMContext(FileVMNode.this, pathSegment), + nodeOffset + Arrays.asList(allFilesInDirectory).indexOf(pathSegment), + IModelDelta.NO_CHANGE, + pathSegmentDirectoryFiles != null ? pathSegmentDirectoryFiles.length : 0); + allFilesInDirectory = pathSegmentDirectoryFiles; + } + + // The last file in path gets the EXPAND | SELECT flags. + delta.setFlags(delta.getFlags() | IModelDelta.SELECT | IModelDelta.EXPAND); + } + } + + // Invoke the request monitor. + + requestMonitor.done(); + + return Status.OK_STATUS; + } + }.schedule(); + } else { + requestMonitor.done(); + } + } + + /** + * Override the behavior which checks for delta flags of all the child nodes, + * because we would get stuck in a recursive loop. Instead call only the child + * nodes which are not us. + */ + protected Map<IVMNode, Integer> getChildNodesWithDeltas(Object e) { + Map<IVMNode, Integer> nodes = new HashMap<IVMNode, Integer>(); + for (final IVMNode childNode : getChildNodes()) { + int delta = childNode.getDeltaFlags(e); + if (delta != IModelDelta.NO_CHANGE) { + nodes.put(childNode, delta); + } + } + return nodes; + } + + public IVMProvider getVMProvider() { + return fProvider; + } + + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FilesystemRootsVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FilesystemRootsVMNode.java new file mode 100644 index 00000000000..a2eb938da95 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/FilesystemRootsVMNode.java @@ -0,0 +1,197 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.filebrowser; + +import java.io.File; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +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.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.ModelDelta; + + +/** + * Viewer model node that populates the filesystem root elements. + */ +@SuppressWarnings("restriction") +class FilesystemRootsVMNode extends AbstractVMNode + implements IElementLabelProvider +{ + public FilesystemRootsVMNode(AbstractVMProvider provider) { + super(provider); + } + + @Override + public String toString() { + return "FilesystemRootsVMNode"; + } + + public void update(final IChildrenUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + File[] files = File.listRoots(); + for (IChildrenUpdate update : updates) { + int offset = update.getOffset() != -1 ? update.getOffset() : 0; + int length = update.getLength() != -1 ? update.getLength() : files.length; + for (int i = offset; (i < files.length) && (i < (offset + length)); i++) { + update.setChild(new FileVMContext(FilesystemRootsVMNode.this, files[i]), i); + } + update.done(); + } + return Status.OK_STATUS; + } + }.schedule(); + } + + public void update(final IHasChildrenUpdate[] updates) { + for (IHasChildrenUpdate update : updates) { + /* + * Assume that all filesystem roots have children. If user attempts + * to expand an empty directory, the plus sign will be removed + * from the element. + */ + update.setHasChilren(true); + update.done(); + } + } + + public void update(final IChildrenCountUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (IChildrenCountUpdate update : updates) { + if (!checkUpdate(update)) continue; + update.setChildCount(File.listRoots().length); + update.done(); + } + return Status.OK_STATUS; + } + }.schedule(); + } + + public void update(final ILabelUpdate[] updates) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + for (ILabelUpdate update : updates) { + update.setLabel(getLabel((FileVMContext)update.getElement()), 0); + update.done(); + } + + return Status.OK_STATUS; + } + }.schedule(); + } + + + /** + * Returs the text label to show in the tree for given element. Filesystem + * roots return an empty string for call to File.getName(), use the abolute path + * string instead. + */ + private String getLabel(FileVMContext vmc) { + return vmc.getFile().getAbsolutePath(); + } + + public int getDeltaFlags(Object e) { + /* + * @see buildDelta() + */ + int retVal = IModelDelta.NO_CHANGE; + if (e instanceof String) { + retVal |= IModelDelta.SELECT | IModelDelta.EXPAND; + } + + return retVal; + } + + public void buildDelta(final Object event, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor requestMonitor) { + if (event instanceof String) { + new Job("") { //$NON-NLS-1$ + { + setSystem(true); + setPriority(INTERACTIVE); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + final File eventFile = new File((String)event); + + if (eventFile.exists()) { + // Create a list containing all files in path of the file from the event + List<File> filePath = new LinkedList<File>(); + for (File file = eventFile; file != null; file = file.getParentFile()) { + filePath.add(0, file); + } + File eventRoot = filePath.get(0); + + // Get the index of the file in list of filesystem roots. + File[] roots = File.listRoots(); + + int index = 0; + for (; index < roots.length; index++) { + if (eventRoot.equals(roots[index])) break; + } + + // Check if the specified file is not one of the roots. + if (index < roots.length) { + ModelDelta delta = parentDelta.addNode( + new FileVMContext(FilesystemRootsVMNode.this, eventRoot), + index, IModelDelta.NO_CHANGE); + + if (eventFile.equals(eventRoot)) { + // The event is for the root node. Select it and extend parent node. + delta.setFlags(delta.getFlags() | IModelDelta.SELECT | IModelDelta.EXPAND); + } + } + } + + // Invoke the request monitor. + requestMonitor.done(); + + return Status.OK_STATUS; + } + }.schedule(); + } else { + requestMonitor.done(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/package.html b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/package.html new file mode 100644 index 00000000000..865be837a58 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/filebrowser/package.html @@ -0,0 +1,127 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> + <meta content="text/html; charset=ISO-8859-1" + http-equiv="content-type"> + <title>DSF Filesystem Browser Example</title> +</head> +<body> +<h2>DSF Filesystem Browser Example</h2> +<h3>Goals</h3> +This example demonstrates an implementation of a viewer model with a +layout node that has itself as a child. Such layout nodes are +needed to represents elements which themselves have a natural tree +structures. This example uses filesystem folders as the +tree-structured data, which is retrieved directly from the java.io.File +class. This example also demonstrates a viewer model +implementation which does not retrieve data using DSF services and +associated data model interfaces. <br> +<h3><span style="font-weight: bold;">Design</span></h3> +<span style="text-decoration: underline;">Model Adapter Hookup</span><br> +A flexible-hierarchy tree viewer {@link +org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer} +is created within a model dialog. Corresponding {@link +FileBrowserModelAdapter} and {@link FileBrowserVMProvider} classes are +instanciated, and the root element object created by +FileBrowserVMProvider is set as input to the tree viewer. From +there FileBrowserModelAdapter is returned as the {@link +IElementContentProvier} and {@link IModelProxyFactory} for all elements +in the tree.<br> +<br> +<p><span style="text-decoration: underline;">Layout Nodes</span><br> +There are three layout nodes:<br> +</p> +<ul> + <li>{@link FileBrowserVMProvider.VMRootLayoutNode} is just a root +node, which generates the input element for the viewer.</li> + <li>{@link FilesystemRootsLayoutNode} retrieves the roots of the +filesystem hierarchy ("C:\", "D:\", etc on Windows). <br> + </li> + <li>{@link FileLayoutNode} is a child of <span + style="font-family: monospace;">FilesystemRootsLayoutNode</span> and +it recursively retrieves all folders and files under the given parent +file element. This layout node does not allow any children nodes +to be added to it, and it returns only itself as a child node (through +a call to <span style="font-family: monospace;">IVMLayoutNode.getChildLayoutNodes</span>).<br> + </li> +</ul> +Both <span style="font-family: monospace;">FilesystemRootsLayoutNode</span> +and <span style="font-family: monospace;">FileLayoutNode</span> create +elements of the same type: {@link FileVMContext}. Additionally, +when populating elements in the tree, the <span + style="font-family: monospace;">FileLayoutNode</span> requires that a <span + style="font-family: monospace;">FileVMContext</span> element be the +parent element in order to be able to retrieve its children. <br> +<span style="font-family: monospace;"></span> +<p><span style="font-family: monospace;"></span></p> +<span style="text-decoration: underline;">Event Handling/Generating +Model Deltas</span><br> +The view model responds to events generated by a text box in the +dialog, where the user can type in a filesystem path. If the +entered path resolves to a file on the filesystem, the view model +generates a delta to select and reveal the given file in the +tree. The two file layout nodes handle generating the delta in +different ways:<br> +<ul> + <li><span style="font-family: monospace;">FilesystemRootsLayoutNode</span> +is a standard layout node. <br> + </li> + <ol> + <li>In the event handler implementation {@link +org.eclipse.cdt.dsf.ui.viewermodel.IVMLayoutNode#buildDelta}, the user +specified file-path is compared to the list of file-system roots. + <br> + </li> + <li>If the user file-path contains one of the filesystem roots, a +new delta node is added for that root and the child layout node is +called to continue the delta processing. <br> + </li> + <li>If the user file-path points to one of the filesystem roots, +the <span style="font-family: monospace;">IModelDelta.SELECT</span> +and <span style="font-family: monospace;">IModelDelta.EXPAND</span> +flags are also added to the delta so that the root will be selected in +the viewer.<br> + </li> + </ol> + <li><span style="font-family: monospace;">FileLayoutNode</span> is +the special case, because it is a recusrive node. This node does +not call any child nodes to process the delta, instead it calculates +the delta for all file elements in user file-path, starting at the +parent element. <br> + </li> + <ol> + <li>First the parent <span style="font-family: monospace;">FileVMContext</span> +element is retrieved from the delta. <br> + </li> + <li>Then the user file-path is broken down into {@link +java.io.File} objects representing each segment in the path, starting +at the parent file element retrieved in step 1.</li> + <li>Then a delta node is added for each segment of the calculated +path. <br> + </li> + <li><span style="font-family: monospace;">IModelDelta.SELECT</span> +and <span style="font-family: monospace;">IModelDelta.EXPAND</span> +flags are added to the last delta.<br> + </li> + </ol> +</ul> +<h3>How to use</h3> +<ol> + <li>Make sure that the DSF examples menu is visible in the perspective</li> + <ul> + <li>Go to Windows -> Customize Perspective...</li> + <li>Select Commands tab</li> + <li>Check the "DSF Examples" in the "Available command groups" +table.</li> + </ul> + <li>Open the dialog by selecting DSF Examples->Open File Browser +Dialog menu item.</li> + <li>Expand the items in the tree to see filesystem contents.</li> + <li>Select elements in the tree, to fill in text box with selected +file's path.</li> + <li>Type in a file path in text box and have the tree expand to the +specified element.<br> + </li> +</ol> +</body> +</html> diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmService.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmService.java new file mode 100644 index 00000000000..3823930faf7 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmService.java @@ -0,0 +1,252 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerDMContext; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerTickDMEvent; +import org.osgi.framework.BundleContext; + +/** + * The alarm service tracks triggers and alarms. Triggers have a specified + * value and can be created and removed independently. Alarms are created + * for a specific timer and a trigger, and can indicate whether an alarm is + * triggered. + * <p> + * This service depends on the TimerService, so the TimerService has to be + * initialized before this service is initialized. + * </p> + */ +public class AlarmService extends AbstractDsfService +{ + /** Event indicating that the list of triggers is changed. */ + @Immutable + public static class TriggersChangedEvent {} + + /** Context representing an alarm tracked by this service. */ + @Immutable + public static class TriggerDMContext extends AbstractDMContext { + /** Alarm number, also index into alarm map */ + final int fNumber; + + private TriggerDMContext(String sessionId, int number) { + super(sessionId, new IDMContext[0]); + fNumber = number; + } + + @Override + public boolean equals(Object other) { + return baseEquals(other) && + ((TriggerDMContext)other).fNumber == fNumber; + } + + public int getTriggerNumber() { + return fNumber; + } + + @Override + public int hashCode() { + return baseHashCode() + fNumber; + } + + @Override + public String toString() { + return baseToString() + ".trigger[" + fNumber + "]"; + } + } + + /** + * Context representing the "triggered" status of an alarm with respect to + * a specific timer. + */ + @Immutable + public static class AlarmDMContext extends AbstractDMContext { + // An alarm requires both a timer and alarm context, both of which + // become parents of the alarm context. + // Note: beyond the parent contexts this context does not contain + // any other data, because no other data is needed. + private AlarmDMContext(String sessionId, + TimerDMContext timerCtx, TriggerDMContext alarmCtx) + { + super(sessionId, new IDMContext[] { timerCtx, alarmCtx }); + } + + @Override + public boolean equals(Object other) { return baseEquals(other); } + + @Override + public int hashCode() { return baseHashCode(); } + + @Override + public String toString() { + return baseToString() + ":alarm"; //$NON-NLS-1$ + } + } + + /** + * Event indicating that an alarm has been triggered by a timer. + */ + public class AlarmTriggeredDMEvent extends AbstractDMEvent<AlarmDMContext> { + public AlarmTriggeredDMEvent(AlarmDMContext context) { + super(context); + } + } + + private int fTriggerNumberCounter = 1; + private Map<TriggerDMContext, Integer> fTriggers = + new LinkedHashMap<TriggerDMContext, Integer>(); + + AlarmService(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return DsfExamplesPlugin.getDefault().getBundle().getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + protected void handleSuccess() { + // After super-class is finished initializing + // perform TimerService initialization. + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(RequestMonitor requestMonitor) { + // Add this class as a listener for service events, in order to receive + // TimerTickEvent events. + getSession().addServiceEventListener(this, null); + + // Register service + register(new String[]{AlarmService.class.getName()}, new Hashtable<String,String>()); + + requestMonitor.done(); + } + + @Override + public void shutdown(RequestMonitor requestMonitor) { + getSession().removeServiceEventListener(this); + unregister(); + super.shutdown(requestMonitor); + } + + public boolean isValid() { return true; } + + @DsfServiceEventHandler + public void eventDispatched(TimerTickDMEvent event) { + final TimerDMContext timerContext = event.getDMContext(); + + int timerValue = getServicesTracker().getService(TimerService.class). + getTimerValue(event.getDMContext()); + + // If a timer triggers an alarm, this service needs to issue an alarm + // triggered event. + checkAlarmsForTimer(timerContext, timerValue); + } + + private void checkAlarmsForTimer(TimerDMContext timerContext, int timerValue) { + // Check the existing alarms for whether they are triggered by given + // timer. + for (Map.Entry<TriggerDMContext, Integer> entry : fTriggers.entrySet()) { + if (timerValue == entry.getValue()) { + // Generate the AlarmTriggeredEvent + AlarmDMContext alarmCtx = new AlarmDMContext( + getSession().getId(), timerContext, entry.getKey()); + getSession().dispatchEvent( + new AlarmTriggeredDMEvent(alarmCtx), getProperties()); + } + } + } + + + /** Returns the list of triggers. */ + public TriggerDMContext[] getTriggers() { + return fTriggers.keySet().toArray(new TriggerDMContext[fTriggers.size()]); + } + + /** Returns the trigger value. */ + public int getTriggerValue(TriggerDMContext alarmCtx) { + Integer value = fTriggers.get(alarmCtx); + if (value != null) { + return value; + } else { + return -1; + } + } + + /** Returns the alarm context for given timer and trigger contexts. */ + public AlarmDMContext getAlarm(TriggerDMContext alarmCtx, TimerDMContext timerCtx) { + return new AlarmDMContext(getSession().getId(), timerCtx, alarmCtx); + } + + /** Returns true if the given alarm is triggered */ + public boolean isAlarmTriggered(AlarmDMContext alarmCtx) { + // Extract the timer and trigger contexts. They should always be part + // of the alarm. + TimerService.TimerDMContext timerCtx = DMContexts.getAncestorOfType( + alarmCtx, TimerService.TimerDMContext.class); + TriggerDMContext triggerCtx = DMContexts.getAncestorOfType( + alarmCtx, TriggerDMContext.class); + + assert triggerCtx != null && timerCtx != null; + + // Find the trigger and check whether the timers value has surpassed it. + if (fTriggers.containsKey(triggerCtx)) { + int timerValue = getServicesTracker().getService(TimerService.class). + getTimerValue(timerCtx); + + return timerValue >= fTriggers.get(triggerCtx); + } + + return false; + } + + /** Creates a new alarm object with given value. */ + public TriggerDMContext createTrigger(int value) { + TriggerDMContext triggerCtx = + new TriggerDMContext(getSession().getId(), fTriggerNumberCounter++); + fTriggers.put(triggerCtx, value); + getSession().dispatchEvent(new TriggersChangedEvent(), getProperties()); + return triggerCtx; + } + + /** Removes given alarm from service. */ + public void deleteTrigger(TriggerDMContext alarmCtx) { + fTriggers.remove(alarmCtx); + getSession().dispatchEvent(new TriggersChangedEvent(), getProperties()); + } + + /** Changes the value of the given trigger. */ + public void setTriggerValue(TriggerDMContext ctx, int newValue) { + if (fTriggers.containsKey(ctx)) { + fTriggers.put(ctx, newValue); + } + getSession().dispatchEvent(new TriggersChangedEvent(), getProperties()); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmsVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmsVMNode.java new file mode 100644 index 00000000000..be7b22f3b07 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/AlarmsVMNode.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.AlarmService.AlarmDMContext; +import org.eclipse.cdt.examples.dsf.timers.AlarmService.TriggerDMContext; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerDMContext; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; + +/** + * View model node that determines whether an "alarm triggered" indicator is + * shown in the tree. This indicator is only shown if a given alarm is + * triggered for a given timer. + * + * @see AlarmDMContext + */ +@SuppressWarnings("restriction") +class AlarmsVMNode extends AbstractDMVMNode + implements IElementLabelProvider +{ + public AlarmsVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session, AlarmDMContext.class); + } + + @Override + public String toString() { + return "AlarmsVMNode(" + getSession().getId() + ")"; + } + + + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + // Check that the service is available and find the trigger and timer contexts. + // If not found, fail. + AlarmService alarmService = getServicesTracker().getService(AlarmService.class, null); + TriggerDMContext alarmDmc = findDmcInPath( + update.getViewerInput(), update.getElementPath(), TriggerDMContext.class); + TimerDMContext timerDmc = findDmcInPath( + update.getViewerInput(), update.getElementPath(), TimerDMContext.class); + if (alarmService == null || alarmDmc == null || timerDmc == null) { + update.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, "Required elements not found in path")); + update.done(); + return; + } + + // Get the alarm context then check the triggered value. + final AlarmDMContext alarmStatusDmc = alarmService.getAlarm(alarmDmc, timerDmc); + boolean triggered = alarmService.isAlarmTriggered(alarmStatusDmc); + + // Only return the alarm in list of elements if it is triggered. + if (triggered) { + update.setChild(createVMContext(alarmStatusDmc), 0); + } + update.done(); + } + + public void update(ILabelUpdate[] updates) { + for (ILabelUpdate update : updates) { + update.setLabel("ALARM TRIGGERED", 0); + update.setImageDescriptor( + DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor( + DsfExamplesPlugin.IMG_ALARM_TRIGGERED), + 0); + update.done(); + } + } + + + public int getDeltaFlags(Object e) { + if (e instanceof AlarmService.AlarmTriggeredDMEvent) { + return IModelDelta.ADDED | IModelDelta.SELECT | IModelDelta.EXPAND; + } + return IModelDelta.NO_CHANGE; + } + + public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor requestMonitor) { + // The alarm element is added when and selected upon a triggered event. + // Parent element is also expanded allow the alarm to be selected. + if (e instanceof AlarmService.AlarmTriggeredDMEvent) { + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.EXPAND); + parentDelta.addNode( + createVMContext( ((AlarmService.AlarmTriggeredDMEvent)e).getDMContext() ), + 0, + IModelDelta.ADDED | IModelDelta.SELECT); + } + requestMonitor.done(); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesShutdownSequence.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesShutdownSequence.java new file mode 100644 index 00000000000..cb8276c9a13 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesShutdownSequence.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Sequence that stops the services in the timers session. + */ +public class ServicesShutdownSequence extends Sequence { + + // Session to that the services are running in. + final private DsfSession fSession; + + // DSF Services is created as the first step of the sequence. It + // cannot be created by the constructor because it can only be called + // in the session thread. + DsfServicesTracker fTracker; + + public ServicesShutdownSequence(DsfSession session) { + super(session.getExecutor()); + fSession = session; + } + + Step[] fSteps = new Step[] { + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + fTracker = new DsfServicesTracker(DsfExamplesPlugin.getBundleContext(), fSession.getId()); + requestMonitor.done(); + } + + @Override + public void rollBack(RequestMonitor requestMonitor) { + // Dispose the tracker in case shutdown sequence is aborted + // and is rolled back. + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(AlarmService.class, requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + shutdownService(TimerService.class, requestMonitor); + } + }, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + // Dispose the tracker after the services are shut down. + fTracker.dispose(); + fTracker = null; + requestMonitor.done(); + } + } + }; + + @Override + public Step[] getSteps() { return fSteps; } + + // A convenience method that shuts down given service. Only service class + // is used to identify the service. + private <V extends IDsfService> void shutdownService(Class<V> clazz, RequestMonitor requestMonitor) { + IDsfService service = fTracker.getService(clazz); + if (service != null) { + service.shutdown(requestMonitor); + } + else { + requestMonitor.setStatus(new Status( + IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, + IDsfStatusConstants.INTERNAL_ERROR, + "Service '" + clazz.getName() + "' not found.", null)); + requestMonitor.done(); + } + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesStartupSequence.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesStartupSequence.java new file mode 100644 index 00000000000..c137bc36369 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/ServicesStartupSequence.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Sequence; +import org.eclipse.cdt.dsf.service.DsfSession; + +/** + * Startup sequence for the timers session. With only two services, this is + * a very simple sequence. + */ +public class ServicesStartupSequence extends Sequence { + + final private DsfSession fSession; + + // The reference to the services are saved to use in the last step. + private TimerService fTimerService = null; + private AlarmService fAlarmService = null; + + + public ServicesStartupSequence(DsfSession session) { + super(session.getExecutor()); + fSession = session; + } + + Step[] fSteps = new Step[] { + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + fTimerService = new TimerService(fSession); + fTimerService.initialize(requestMonitor); + }}, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + fAlarmService = new AlarmService(fSession); + fAlarmService.initialize(requestMonitor); + }}, + new Step() { + @Override + public void execute(RequestMonitor requestMonitor) { + // Create the first timer and trigger. + fTimerService.startTimer(); + fAlarmService.createTrigger(5); + requestMonitor.done(); + }} + }; + + @Override + public Step[] getSteps() { return fSteps; } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimerService.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimerService.java new file mode 100644 index 00000000000..6b05f0a1c5e --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimerService.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.eclipse.cdt.dsf.concurrent.Immutable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.osgi.framework.BundleContext; + +/** + * Timer service tracks a set of timers, which are created per user request. + * The timers are represented using a Data Model context object, which + * implements {@link IDMContext}. Each timers value, which can be retrieved + * by calling {@link #getTimerValue(TimerDMContext)}, is incremented every + * second. When a timer value is incremented the TimerService issues a + * {@link TimerTickDMEvent}. + */ +public class TimerService extends AbstractDsfService +{ + /** Event indicating that the list of timers is changed. */ + @Immutable + public static class TimersChangedEvent {} + + /** Data Model context representing a timer. */ + @Immutable + public static class TimerDMContext extends AbstractDMContext { + final int fNumber; + + public TimerDMContext(String sessionId, int timer) { + super(sessionId, new IDMContext[0]); + fNumber = timer; + } + + /** Returns the sequential creation number of this timer. */ + public int getTimerNumber() { + return fNumber; + } + + // Timer context objects are created as needed and not cached, so the + // equals method implementation is critical. + @Override + public boolean equals(Object other) { + return baseEquals(other) && + ((TimerDMContext)other).fNumber == fNumber; + } + + @Override + public int hashCode() { return baseHashCode() + fNumber; } + + @Override + public String toString() { + return baseToString() + ".timer[" + fNumber + "]"; + } + } + + /** + * Event indicating that a timer's value has incremented. The context in + * the event points to the timer that has changed. + */ + public class TimerTickDMEvent extends AbstractDMEvent<TimerDMContext> { + public TimerTickDMEvent(TimerDMContext context) { + super(context); + } + } + + /** Counter for generating timer numbers */ + private int fTimerNumberCounter = 1; + + // Use a linked hash in order to be able to return an ordered list of timers. + private Map<TimerDMContext, Integer> fTimers = + new LinkedHashMap<TimerDMContext, Integer>(); + + private Map<TimerDMContext, Future<?>> fTimerFutures = + new HashMap<TimerDMContext, Future<?>>(); + + + TimerService(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return DsfExamplesPlugin.getDefault().getBundle().getBundleContext(); + } + + @Override + public void initialize(final RequestMonitor requestMonitor) { + super.initialize( + new RequestMonitor(getExecutor(), requestMonitor) { + @Override + public void handleSuccess() { + // After super-class is finished initializing + // perform TimerService initialization. + doInitialize(requestMonitor); + }}); + } + + private void doInitialize(RequestMonitor requestMonitor) { + // Register service + register( new String[]{ TimerService.class.getName() }, + new Hashtable<String,String>() ); + requestMonitor.done(); + } + + @Override + public void shutdown(RequestMonitor requestMonitor) { + // Cancel timer futures to avoid firing more events. + for (Future<?> future : fTimerFutures.values()) { + future.cancel(false); + } + unregister(); + super.shutdown(requestMonitor); + } + + /** Retrieves the list of timer contexts. */ + public TimerDMContext[] getTimers() { + return fTimers.keySet().toArray(new TimerDMContext[fTimers.size()]); + } + + /** Retrieves the timer value for the given context. */ + public int getTimerValue(TimerDMContext context) { + Integer value = fTimers.get(context); + if (value != null) { + return value; + } + return -1; + } + + /** Creates a new timer and returns its context. */ + public TimerDMContext startTimer() { + // Create a new timer context and add it to the internal list. + final TimerDMContext newTimer = + new TimerDMContext(getSession().getId(), fTimerNumberCounter++); + fTimers.put(newTimer, 0); + + // Create a new runnable that will execute every second and increment + // the timer value. The returned future is the handle that allows + // for canceling the scheduling of the runnable. + Future<?> timerFuture = getExecutor().scheduleAtFixedRate( + new Runnable() { + public void run() { + fTimers.put(newTimer, fTimers.get(newTimer) + 1); + getSession().dispatchEvent(new TimerTickDMEvent(newTimer), getProperties()); + } + @Override + public String toString() { return "Scheduled timer runnable for timer " + newTimer; } //$NON-NLS-1$ + }, + 1, 1, TimeUnit.SECONDS); + fTimerFutures.put(newTimer, timerFuture); + + // Issue an event to allow clients to update the list of timers. + getSession().dispatchEvent(new TimersChangedEvent(), getProperties()); + return newTimer; + } + + /** Removes given timer from list of timers. */ + public void killTimer(TimerDMContext timerContext) { + if (fTimers.containsKey(timerContext)) { + fTimers.remove(timerContext); + fTimerFutures.remove(timerContext).cancel(false); + } + getSession().dispatchEvent(new TimersChangedEvent(), getProperties()); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersRootVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersRootVMNode.java new file mode 100644 index 00000000000..c804f6d2476 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersRootVMNode.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.RootDMVMNode; +import org.eclipse.cdt.examples.dsf.timers.TimersVMProvider.TimersViewLayoutChanged; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; + +/** + * + */ +@SuppressWarnings("restriction") +public class TimersRootVMNode extends RootDMVMNode { + + public TimersRootVMNode(AbstractVMProvider provider) { + super(provider); + } + + @Override + public boolean isDeltaEvent(Object rootObject, Object e) { + if (e instanceof TimersViewLayoutChanged) { + return true; + } + return super.isDeltaEvent(rootObject, e); + } + + @Override + public int getDeltaFlags(Object e) { + if (e instanceof TimersViewLayoutChanged) { + return IModelDelta.CONTENT; + } + + return IModelDelta.NO_CHANGE; + } + + @Override + public void createRootDelta(Object rootObject, Object event, final DataRequestMonitor<VMDelta> rm) { + rm.setData(new VMDelta(rootObject, 0, IModelDelta.NO_CHANGE)); + int flags = IModelDelta.NO_CHANGE; + if (event instanceof TimersViewLayoutChanged) { + flags |= IModelDelta.CONTENT; + } + rm.setData( new VMDelta(rootObject, 0, flags) ); + rm.done(); + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMAdapter.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMAdapter.java new file mode 100644 index 00000000000..5fae6c32c75 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMAdapter.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMAdapter; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * This is the adapter that implements the flexible hierarchy viewer interfaces + * for providing content, labels, and event processing for the viewer. This + * adapter is registered with the DSF Session object, and is returned by the + * IDMContext.getAdapter() and IVMContext.getAdapter() methods, + * which both call {@link DsfSession#getModelAdapter(Class)}. + */ +@SuppressWarnings("restriction") +@ThreadSafe +public class TimersVMAdapter extends AbstractDMVMAdapter +{ + @Override + protected IVMProvider createViewModelProvider(IPresentationContext context) { + if ( TimersView.ID_VIEW_TIMERS.equals(context.getId()) ) { + return new TimersVMProvider(this, context, getSession()); + } + return null; + } + + public TimersVMAdapter(DsfSession session, IPresentationContext presentationContext) { + super(session); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMNode.java new file mode 100644 index 00000000000..bba6d1f1ed3 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMNode.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import java.text.MessageFormat; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IElementPropertiesProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelAttribute; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelColumnInfo; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelImage; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertyBasedLabelProvider; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerDMContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +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.IViewerUpdate; + + +/** + * View model node that defines how timer DMContexts are displayed in the view. Timers + * change with every tick of the timer, so the label has to be repained + * upon timer tick events. + * @see TimerDMContext + */ +@SuppressWarnings("restriction") +class TimersVMNode extends AbstractDMVMNode + implements IElementLabelProvider, IElementPropertiesProvider +{ + private static final String PROP_TIMER_NUMBER = "alarmNumber"; + private static final String PROP_TIMER_VALUE = "alarmTriggerValue"; + + // Create and configure the label provider. + private static final PropertyBasedLabelProvider fgLabelProvider; + static { + fgLabelProvider = new PropertyBasedLabelProvider(); + + LabelColumnInfo idCol = new LabelColumnInfo( + new LabelAttribute[] { + new LabelText(new MessageFormat("Timer #{0}"), + new String[] { PROP_TIMER_NUMBER }), + new LabelImage(DsfExamplesPlugin.getDefault().getImageRegistry(). + getDescriptor(DsfExamplesPlugin.IMG_ALARM)) + }); + fgLabelProvider.setColumnInfo(TimersViewColumnPresentation.COL_ID, idCol); + + LabelColumnInfo valueCol = new LabelColumnInfo( + new LabelAttribute[] { + new LabelText(new MessageFormat("{0}"), + new String[] { PROP_TIMER_VALUE }) + }); + fgLabelProvider.setColumnInfo(TimersViewColumnPresentation.COL_VALUE, + valueCol); + + } + + + public TimersVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session, TimerDMContext.class); + } + + @Override + public String toString() { + return "TimersVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + public void update(ILabelUpdate[] updates) { + fgLabelProvider.update(updates); + } + + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + TimerService timerService = getServicesTracker().getService(TimerService.class, null); + if ( timerService == null ) { + handleFailedUpdate(update); + return; + } + + // Retrieve the timer DMContexts, create the corresponding VMCs array, and + // set them as result. + TimerDMContext[] timers = timerService.getTimers(); + fillUpdateWithVMCs(update, timers); + update.done(); + } + + + public void update(final IPropertiesUpdate[] updates) { + // Switch to the session thread before processing the updates. + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + for (IPropertiesUpdate update : updates) { + updatePropertiesInSessionThread(update); + } + }}); + } catch (RejectedExecutionException e) { + for (IViewerUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + @ConfinedToDsfExecutor("getSession#getExecutor") + private void updatePropertiesInSessionThread(final IPropertiesUpdate update) { + // Find the timer context in the element being updated + TimerDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(), TimerDMContext.class); + TimerService timerService = getServicesTracker().getService(TimerService.class, null); + + // If either update or service are not valid, fail the update and exit. + if ( dmc == null || timerService == null) { + handleFailedUpdate(update); + return; + } + + int value = timerService.getTimerValue(dmc); + + if (value == -1) { + handleFailedUpdate(update); + return; + } + + update.setProperty(PROP_TIMER_NUMBER, dmc.getTimerNumber()); + update.setProperty(PROP_TIMER_VALUE, value); + update.done(); + } + + public int getDeltaFlags(Object e) { + // This node generates delta if the timers have changed, or if the + // label has changed. + if (e instanceof TimerService.TimerTickDMEvent) { + return IModelDelta.STATE; + } else if (e instanceof TimerService.TimersChangedEvent) { + return IModelDelta.CONTENT; + } + return IModelDelta.NO_CHANGE; + } + + public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor requestMonitor) { + if (e instanceof TimerService.TimerTickDMEvent) { + // Add delta indicating that the given timer has changed. + parentDelta.addNode( createVMContext(((TimerService.TimerTickDMEvent)e).getDMContext()), IModelDelta.STATE ); + } else if (e instanceof TimerService.TimersChangedEvent) { + // The list of timers has changed, which means that the parent + // node needs to refresh its contents, which in turn will re-fetch the + // elements from this node. + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + requestMonitor.done(); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMProvider.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMProvider.java new file mode 100644 index 00000000000..e44c122d7db --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersVMProvider.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.examples.dsf.timers.AlarmService.TriggersChangedEvent; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimersChangedEvent; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; + +/** + * The View Model provider for the Timers view. This provider allows for + * switching between two different view layouts: + * <ol> + * <li>Timers -> Triggers -> Alarms</li> + * <li>Triggers -> Timers -> Alarms</li> + * </ol> + * A special event is sent when the layout is changed in order to generate + * a proper delta to refresh the view. + */ +@SuppressWarnings("restriction") +public class TimersVMProvider extends AbstractDMVMProvider { + + /** Event indicating that the timers view layout has changed */ + public static class TimersViewLayoutChanged {} + + /** Enumeration of possible layouts for the timers view model */ + public enum ViewLayout { TRIGGERS_AT_TOP, TIMERS_AT_TOP } + + public TimersVMProvider(AbstractVMAdapter adapter, IPresentationContext presentationContext, DsfSession session) { + super(adapter, presentationContext, session); + // Set the initial view layout. + setViewLayout(ViewLayout.TIMERS_AT_TOP); + } + + /** + * Configures a new layout for the timers view model. + * @param layout New layout to use. + */ + public void setViewLayout(ViewLayout layout) { + clearNodes(); + if (layout == ViewLayout.TRIGGERS_AT_TOP) { + IRootVMNode root = new TimersRootVMNode(this); + IVMNode triggersNode = new TriggersVMNode(this, getSession()); + addChildNodes(root, new IVMNode[] { triggersNode }); + IVMNode timersNode = new TimersVMNode(this, getSession()); + addChildNodes(triggersNode, new IVMNode[] { timersNode }); + IVMNode alarmNode = new AlarmsVMNode(this, getSession()); + addChildNodes(timersNode, new IVMNode[] { alarmNode }); + setRootNode(root); + } else if (layout == ViewLayout.TIMERS_AT_TOP) { + IRootVMNode root = new TimersRootVMNode(this); + IVMNode timersNode = new TimersVMNode(this, getSession()); + addChildNodes(root, new IVMNode[] { timersNode }); + IVMNode triggersNode = new TriggersVMNode(this, getSession()); + addChildNodes(timersNode, new IVMNode[] { triggersNode }); + IVMNode alarmNode = new AlarmsVMNode(this, getSession()); + addChildNodes(triggersNode, new IVMNode[] { alarmNode }); + setRootNode(root); + } + + handleEvent(new TimersViewLayoutChanged()); + } + + @Override + public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { + return new TimersViewColumnPresentation(); + } + + @Override + public String getColumnPresentationId(IPresentationContext context, Object element) { + return TimersViewColumnPresentation.ID; + } + + // Add a handler for the triggers and timers changed events. The + // AbstractDMVMProvider superclass automatically registers this provider + // for all IDMEvent events, however these two events do not implement + // IDMEvent + @DsfServiceEventHandler + public void eventDispatched(final TriggersChangedEvent event) { + if (isDisposed()) return; + + try { + getExecutor().execute(new Runnable() { + public void run() { + if (isDisposed()) return; + handleEvent(event); + } + }); + } catch (RejectedExecutionException e) {} + } + + @DsfServiceEventHandler + public void eventDispatched(final TimersChangedEvent event) { + if (isDisposed()) return; + + try { + getExecutor().execute(new Runnable() { + public void run() { + if (isDisposed()) return; + handleEvent(event); + } + }); + } catch (RejectedExecutionException e) {} + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersView.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersView.java new file mode 100644 index 00000000000..d9c4a59dac8 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersView.java @@ -0,0 +1,325 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import java.util.concurrent.ExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.service.DsfServicesTracker; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.TimerService.TimerDMContext; +import org.eclipse.cdt.examples.dsf.timers.TimersVMProvider.ViewLayout; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxyFactory; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.action.Separator; +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.InputDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.part.ViewPart; + + +/** + * Example view which displays data from timers and alarms services. It starts + * a new DSF session and configures the services for it. Then it configures + * a data model provider to process the service data and display it in a + * flexible-hierarchy asynchronous viewer. + */ +@SuppressWarnings("restriction") +public class TimersView extends ViewPart { + + /** Timers view ID */ + public static final String ID_VIEW_TIMERS = "org.eclipse.cdt.examples.dsf.TimersView"; + + /** Asynchronous tree viewer from the platform debug.ui plugin. */ + private TreeModelViewer fViewer; + + /** Presentation context of the timers viewer */ + private PresentationContext fPresentationContext; + + /** DSF executor to use for a new session with timers and alarms services */ + private DsfExecutor fExecutor; + + /** DSF session */ + private DsfSession fSession; + + /** DSF services tracker used by actions in the viewer. */ + private DsfServicesTracker fServices; + + /** Adapter used to provide view model for flexible-hierarchy viewer */ + private TimersVMAdapter fTimersVMAdapter; + + /** Action which toggles the layout in the viewer */ + private Action fToggleLayoutAction; + + /** Action that adds a new timer */ + private Action fAddTimerAction; + + /** Action that adds a new trigger */ + private Action fAddTriggerAction; + + /** Action that removes the selected trigger or timer */ + private Action fRemoveAction; + + public TimersView() {} + + /** + * This is a call-back that will allow us to create the viewer and + * initialize it. For this view, it creates the DSF session, along + * with its services. Then it creates the viewer model adapter and + * registers it with the session. + */ + @Override + public void createPartControl(Composite parent) { + // Create the Flexible Hierarchy viewer. Also create a presentation + // context which will be given to the content/label provider adapters + // to distinguish this view from other flexible-hierarchy views. + fPresentationContext = new PresentationContext(ID_VIEW_TIMERS); + fViewer = new TreeModelViewer( + parent, SWT.VIRTUAL | SWT.FULL_SELECTION, fPresentationContext); + + // Create the executor, which will be used exclusively with this view, + // as well as a session and a services tracker for managing references + // to services. + fExecutor = new DefaultDsfExecutor(); + fSession = DsfSession.startSession(fExecutor, "Timers(DSF Example)"); + fServices = new DsfServicesTracker( + DsfExamplesPlugin.getBundleContext(), fSession.getId()); + + // Start the services using a sequence. The sequence runs in the + // session executor thread, therefore the thread calling this method + // has to block using Future.get() until the sequence it completes. + // The Future.get() will throw an exception if the sequence fails. + ServicesStartupSequence startupSeq = new ServicesStartupSequence(fSession); + fSession.getExecutor().execute(startupSeq); + try { + startupSeq.get(); + } catch (InterruptedException e) { assert false; + } catch (ExecutionException e) { assert false; + } + + // Create the flexible hierarchy content/label adapter. Then register + // it with the session. + fTimersVMAdapter = new TimersVMAdapter(fSession, fPresentationContext); + fSession.registerModelAdapter(IElementContentProvider.class, fTimersVMAdapter); + fSession.registerModelAdapter(IModelProxyFactory.class, fTimersVMAdapter); + fSession.registerModelAdapter(IColumnPresentationFactory.class, fTimersVMAdapter); + + // Create the input object for the view. This object needs to return + // the VM adapter through the IAdaptable interface when queried for the + // flexible hierarchy adapters. + final IAdaptable viewerInputObject = + new IAdaptable() { + /** + * The input object provides the viewer access to the viewer model adapter. + */ + @SuppressWarnings("unchecked") + public Object getAdapter(Class adapter) { + if ( adapter.isInstance(fTimersVMAdapter) ) { + return fTimersVMAdapter; + } + return null; + } + + @Override + public String toString() { + return "Timers View Root"; //$NON-NLS-1$ + } + }; + fViewer.setInput(viewerInputObject); + + makeActions(); + contributeToActionBars(); + } + + @Override + public void dispose() { + try { + // First dispose the view model, which is the client of services. + // This operation needs to be performed in the session executor + // thread. Block using Future.get() until this call completes. + fSession.getExecutor().submit(new Runnable() { + public void run() { + fSession.unregisterModelAdapter(IElementContentProvider.class); + fSession.unregisterModelAdapter(IModelProxyFactory.class); + fSession.unregisterModelAdapter(IColumnPresentationFactory.class); + }}).get(); + + // Dispose the VM adapter. + fTimersVMAdapter.dispose(); + fTimersVMAdapter = null; + + // Next invoke the shutdown sequence for the services. Sequence + // class also implements Future.get()... + ServicesShutdownSequence shutdownSeq = + new ServicesShutdownSequence(fSession); + fSession.getExecutor().execute(shutdownSeq); + try { + shutdownSeq.get(); + } catch (InterruptedException e) { assert false; + } catch (ExecutionException e) { assert false; + } + + // Finally end the session and the executor. + fSession.getExecutor().submit(new Runnable() { + public void run() { + DsfSession.endSession(fSession); + fSession = null; + fExecutor.shutdown(); + fExecutor = null; + }}).get(); + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + super.dispose(); + } + + private void contributeToActionBars() { + IActionBars bars = getViewSite().getActionBars(); + fillLocalToolBar(bars.getToolBarManager()); + } + + private void fillLocalToolBar(IToolBarManager manager) { + manager.add(fToggleLayoutAction); + manager.add(fAddTimerAction); + manager.add(fAddTriggerAction); + manager.add(fRemoveAction); + manager.add(new Separator()); + } + + private void makeActions() { + fToggleLayoutAction = new Action("Toggle Layout", IAction.AS_CHECK_BOX) { //$NON-NLS-1$ + @Override + public void run() { + // Get the toggle state of the action while on UI thread. + final ViewLayout layout = isChecked() ? ViewLayout.TRIGGERS_AT_TOP : ViewLayout.TIMERS_AT_TOP; + + IVMProvider provider = fTimersVMAdapter.getVMProvider(fPresentationContext); + ((TimersVMProvider)provider).setViewLayout(layout); + } + }; + fToggleLayoutAction.setToolTipText("Toggle Layout"); //$NON-NLS-1$ + fToggleLayoutAction.setImageDescriptor(DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor( + DsfExamplesPlugin.IMG_LAYOUT_TOGGLE)); + + fAddTimerAction = new Action("Add New Timer") { + @Override + public void run() { + fExecutor.execute(new Runnable() { + public void run() { + // Only need to create the new timer, the events will + // cause the view to refresh. + fServices.getService(TimerService.class).startTimer(); + } + }); + } + }; + fAddTimerAction.setToolTipText("Add a new timer"); + fAddTimerAction.setImageDescriptor( + getImage(DsfExamplesPlugin.IMG_TIMER)); + + fAddTriggerAction = new Action("Add New Trigger") { + @Override + public void run() { + // Ask user for the new trigger value. + InputDialog inputDialog = new InputDialog( + getSite().getShell(), + "New Trigger", + "Please enter trigger value", + "", + new IInputValidator() { + public String isValid(String input) { + try { + int i= Integer.parseInt(input); + if (i <= 0) + return "Please enter a positive integer"; + + } catch (NumberFormatException x) { + return "Please enter a positive integer"; + } + return null; + } + } + ); + if (inputDialog.open() != Window.OK) return; + int tmpTriggerValue = -1; + try { + tmpTriggerValue = Integer.parseInt(inputDialog.getValue()); + } catch (NumberFormatException x) { assert false; } + final int triggerValue = tmpTriggerValue; + fExecutor.execute(new Runnable() { + public void run() { + // Create the new trigger + fServices.getService(AlarmService.class). + createTrigger(triggerValue); + } + }); + } + }; + fAddTriggerAction.setToolTipText("Add a new trigger"); + fAddTriggerAction.setImageDescriptor( + getImage(DsfExamplesPlugin.IMG_ALARM)); + + fRemoveAction = new Action("Remove") { + @Override + public void run() { + final Object selectedElement = + ((IStructuredSelection)fViewer.getSelection()).getFirstElement(); + if (!(selectedElement instanceof IDMVMContext)) return; + final IDMContext selectedCtx = + ((IDMVMContext)selectedElement).getDMContext(); + // Based on the context from the selection, call the + // appropriate service to remove the item. + if (selectedCtx instanceof TimerDMContext) { + fExecutor.execute(new Runnable() { public void run() { + fServices.getService(TimerService.class).killTimer( + ((TimerDMContext)selectedCtx)); + }}); + } else if (selectedCtx instanceof AlarmService.TriggerDMContext) { + fExecutor.execute(new Runnable() { public void run() { + fServices.getService(AlarmService.class).deleteTrigger( + (AlarmService.TriggerDMContext)selectedCtx); + }}); + } + } + }; + fRemoveAction.setToolTipText("Remove selected item"); + fRemoveAction.setImageDescriptor( getImage(DsfExamplesPlugin.IMG_REMOVE) ); + } + + private ImageDescriptor getImage(String key) { + return DsfExamplesPlugin.getDefault().getImageRegistry().getDescriptor(key); + } + + /** + * Passing the focus request to the viewer's control. + */ + @Override + public void setFocus() { + fViewer.getControl().setFocus(); + } +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersViewColumnPresentation.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersViewColumnPresentation.java new file mode 100644 index 00000000000..a43778a525a --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TimersViewColumnPresentation.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; + +/** + * + */ +@SuppressWarnings("restriction") +public class TimersViewColumnPresentation implements IColumnPresentation { + + public static final String ID = DsfExamplesPlugin.PLUGIN_ID + ".TIMER_COLUMN_PRESENTATION_ID"; //$NON-NLS-1$ + public static final String COL_ID = ID + ".COL_ID"; //$NON-NLS-1$ + public static final String COL_VALUE = ID + ".COL_VALUE"; //$NON-NLS-1$ + + public void init(IPresentationContext context) {} + + public void dispose() {} + + public String[] getAvailableColumns() { + return new String[] { COL_ID, COL_VALUE }; + } + + public String getHeader(String id) { + if (COL_ID.equals(id)) { + return "ID"; //$NON-NLS-1$ + } else if (COL_VALUE.equals(id)) { + return "Value"; //$NON-NLS-1$ + } + return null; + } + + public String getId() { + return ID; + } + + public ImageDescriptor getImageDescriptor(String id) { + return null; + } + + public String[] getInitialColumns() { + return getAvailableColumns(); + } + + public boolean isOptional() { + return true; + } + +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggerCellModifier.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggerCellModifier.java new file mode 100644 index 00000000000..b7de00a0ca4 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggerCellModifier.java @@ -0,0 +1,258 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.concurrent.ThreadSafe; +import org.eclipse.cdt.dsf.concurrent.ThreadSafeAndProhibitedFromDsfExecutor; +import org.eclipse.cdt.dsf.service.DsfServices; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.AlarmService.TriggerDMContext; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ICellModifier; +import org.eclipse.swt.widgets.Shell; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.util.tracker.ServiceTracker; + +/** + * Cell modifier used to edit the trigger value. + */ +@ThreadSafeAndProhibitedFromDsfExecutor("fSession.getExecutor()") +public class TriggerCellModifier implements ICellModifier { + + private final DsfSession fSession; + + // Need to use the OSGi service tracker (instead of DsfServiceTracker), + // because it's being accessed on multiple threads. + @ThreadSafe + private ServiceTracker fServiceTracker; + + /** + * Constructor for the modifier requires a valid session in order to + * initialize the service tracker. + * @param session DSF session this modifier will use. + */ + public TriggerCellModifier(DsfSession session) { + fSession = session; + } + + public boolean canModify(Object element, String property) { + return TimersViewColumnPresentation.COL_VALUE.equals(property) && + getAlarmDMC(element) != null; + } + + public Object getValue(Object element, String property) { + if (!TimersViewColumnPresentation.COL_VALUE.equals(property)) return ""; + + // Get the context and the session. If element is not an trigger + // context or if the session is stale then bail out. + TriggerDMContext triggerCtx = getAlarmDMC(element); + if (triggerCtx == null) return ""; + DsfSession session = DsfSession.getSession(triggerCtx.getSessionId()); + if (session == null) return ""; + + // Create the query to request the value from service. + GetValueQuery query = new GetValueQuery(triggerCtx); + try { + session.getExecutor().execute(query); + } catch (RejectedExecutionException e) { + return ""; + } + try { + return query.get().toString(); + } catch (InterruptedException e) { + assert false; + return ""; + } catch (ExecutionException e) { + return ""; + } + } + + + public void modify(Object element, String property, Object value) { + if (!TimersViewColumnPresentation.COL_VALUE.equals(property)) return; + + TriggerDMContext dmc = getAlarmDMC(element); + if (dmc == null) return; + DsfSession session = DsfSession.getSession(dmc.getSessionId()); + if (session == null) return; + + // Shell is used in displaying error dialogs. + Shell shell = getShell(); + if (shell == null) return; + + Integer intValue = null; + if (value instanceof String) { + try { + intValue = new Integer(((String)value).trim()); + } catch (NumberFormatException e) { + MessageDialog.openError(shell, "Invalid Value", + "Please enter a positive integer"); + return; + } + if (intValue.intValue() <= 0) { + MessageDialog.openError(shell, "Invalid Value", + "Please enter a positive integer"); + return; + } + } + + // Create the query to write the value to the service. + SetValueQuery query = new SetValueQuery(dmc, intValue); + + try { + session.getExecutor().execute(query); + } catch (RejectedExecutionException e) { + // View must be shutting down, no need to show error dialog. + } + try { + // Return value is irrelevant, any error would come through with an exception. + query.get().toString(); + } catch (InterruptedException e) { + assert false; + } catch (ExecutionException e) { + // View must be shutting down, no need to show error dialog. + } + } + + /** + * Need to dispose the cell modifier property because of the service + * tracker. + */ + @ThreadSafe + public synchronized void dispose() { + if (fServiceTracker != null) { + fServiceTracker.close(); + } + } + + private Shell getShell() { + if (DsfExamplesPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow() != null) { + return DsfExamplesPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(); + } + return null; + } + + private TriggerDMContext getAlarmDMC(Object element) { + if (element instanceof IAdaptable) { + return (TriggerDMContext)((IAdaptable)element).getAdapter(TriggerDMContext.class); + } + return null; + } + + @ThreadSafe + private synchronized AlarmService getService(TriggerDMContext dmc) { + // Create and initialize the service tracker if needed. + String serviceId = DsfServices.createServiceFilter( AlarmService.class, fSession.getId() ); + if (fServiceTracker == null) { + try { + fServiceTracker = new ServiceTracker( + DsfExamplesPlugin.getBundleContext(), + DsfExamplesPlugin.getBundleContext().createFilter(serviceId), + null); + fServiceTracker.open(); + } catch (InvalidSyntaxException e) { + return null; + } + } + // Get the service. + return (AlarmService)fServiceTracker.getService(); + } + + + private class GetValueQuery extends Query<Integer> { + final TriggerDMContext fDmc; + + private GetValueQuery(TriggerDMContext dmc) { + super(); + fDmc = dmc; + } + + @Override + protected void execute(final DataRequestMonitor<Integer> rm) { + // Guard against the session being disposed. If session is disposed + // it could mean that the executor is shut-down, which in turn + // could mean that we can't execute the "done" argument. + // In that case, cancel to notify waiting thread. + final DsfSession session = DsfSession.getSession(fDmc.getSessionId()); + if (session == null) { + cancel(false); + return; + } + + AlarmService service = getService(fDmc); + if (service == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "Service not available", null)); + rm.done(); + return; + } + + int value = service.getTriggerValue(fDmc); + if (value == -1) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, + "Invalid context", null)); + rm.done(); + return; + } + + rm.setData(value); + rm.done(); + } + } + + private class SetValueQuery extends Query<Object> { + + TriggerDMContext fDmc; + int fValue; + + SetValueQuery(TriggerDMContext dmc, int value) { + super(); + fDmc = dmc; + fValue = value; + } + + @Override + protected void execute(final DataRequestMonitor<Object> rm) { + // Guard against terminated session + final DsfSession session = DsfSession.getSession(fDmc.getSessionId()); + if (session == null) { + cancel(false); + return; + } + + // Guard against a disposed service + AlarmService service = getService(fDmc); + if (service == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfExamplesPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, + "Service not available", null)); + rm.done(); + return; + } + + // Finally set the value and return. + service.setTriggerValue(fDmc, fValue); + + // Return value is irrelevant. + rm.setData(new Object()); + rm.done(); + } + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggersVMNode.java b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggersVMNode.java new file mode 100644 index 00000000000..4b6cef7d88a --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/TriggersVMNode.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 Wind River Systems 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.cdt.examples.dsf.timers; + +import java.text.MessageFormat; +import java.util.concurrent.RejectedExecutionException; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IElementPropertiesProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelAttribute; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelColumnInfo; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelImage; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertyBasedLabelProvider; +import org.eclipse.cdt.examples.dsf.DsfExamplesPlugin; +import org.eclipse.cdt.examples.dsf.timers.AlarmService.TriggerDMContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +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.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ICellModifier; +import org.eclipse.jface.viewers.TextCellEditor; +import org.eclipse.swt.widgets.Composite; + + +/** + * View model node that defines how alarm DMContexts are displayed in the view. Alarm + * nodes are fairly static, once they are created their label doesn't change. + * @see TriggerDMContext + */ +@SuppressWarnings("restriction") +class TriggersVMNode extends AbstractDMVMNode + implements IElementEditor, IElementPropertiesProvider, IElementLabelProvider +{ + private static final String PROP_TRIGGER_NUMBER = "alarmNumber"; + private static final String PROP_TRIGGER_VALUE = "alarmTriggerValue"; + + // Create and configure the label provider. + private static final PropertyBasedLabelProvider fgLabelProvider; + static { + fgLabelProvider = new PropertyBasedLabelProvider(); + + LabelColumnInfo idCol = new LabelColumnInfo( + new LabelAttribute[] { + new LabelText(new MessageFormat("Trigger #{0}"), + new String[] { PROP_TRIGGER_NUMBER }), + new LabelImage(DsfExamplesPlugin.getDefault().getImageRegistry(). + getDescriptor(DsfExamplesPlugin.IMG_ALARM)) + }); + fgLabelProvider.setColumnInfo(TimersViewColumnPresentation.COL_ID, idCol); + + LabelColumnInfo valueCol = new LabelColumnInfo( + new LabelAttribute[] { + new LabelText(new MessageFormat("{0}"), + new String[] { PROP_TRIGGER_VALUE }) + }); + fgLabelProvider.setColumnInfo(TimersViewColumnPresentation.COL_VALUE, + valueCol); + } + + private TriggerCellModifier fAlarmCellModifier; + + public TriggersVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session, TriggerDMContext.class); + } + + @Override + public String toString() { + return "TriggersVMNode(" + getSession().getId() + ")"; + } + + @Override + protected void updateElementsInSessionThread(final IChildrenUpdate update) { + AlarmService alarmService = getServicesTracker().getService(AlarmService.class, null); + if ( alarmService == null ) { + handleFailedUpdate(update); + return; + } + + TriggerDMContext[] triggers = alarmService.getTriggers(); + fillUpdateWithVMCs(update, triggers); + update.done(); + } + + public void update(ILabelUpdate[] updates) { + fgLabelProvider.update(updates); + } + + public void update(final IPropertiesUpdate[] updates) { + // Switch to the session thread before processing the updates. + try { + getSession().getExecutor().execute(new DsfRunnable() { + public void run() { + for (IPropertiesUpdate update : updates) { + updatePropertiesInSessionThread(update); + } + }}); + } catch (RejectedExecutionException e) { + for (IViewerUpdate update : updates) { + handleFailedUpdate(update); + } + } + } + + @ConfinedToDsfExecutor("getSession#getExecutor") + private void updatePropertiesInSessionThread(final IPropertiesUpdate update) { + // Find the trigger context in the element being updated + TriggerDMContext triggerCtx = findDmcInPath( + update.getViewerInput(), update.getElementPath(), TriggerDMContext.class); + AlarmService alarmService = getServicesTracker().getService(AlarmService.class, null); + + // If either update or service are not valid, fail the update and return. + if ( triggerCtx == null || alarmService == null) { + handleFailedUpdate(update); + return; + } + + // Calculate and set the update properties. + int value = alarmService.getTriggerValue(triggerCtx); + + if (value == -1) { + handleFailedUpdate(update); + return; + } + + update.setProperty(PROP_TRIGGER_NUMBER, triggerCtx.getTriggerNumber()); + update.setProperty(PROP_TRIGGER_VALUE, value); + update.done(); + } + + public CellEditor getCellEditor(IPresentationContext context, String columnId, + Object element, Composite parent) + { + // Create a cell editor to modify the trigger value. + if (TimersViewColumnPresentation.COL_VALUE.equals(columnId)) { + return new TextCellEditor(parent); + } + return null; + } + + // Note: this method is synchronized because IElementEditor.getCellModifier can be called + // on any thread, even though in practice it should be only called on the UI thread. + public ICellModifier getCellModifier(IPresentationContext context, + Object element) + { + // Create the cell modifier if needed. + if (fAlarmCellModifier == null) { + fAlarmCellModifier = new TriggerCellModifier(getSession()); + } + return fAlarmCellModifier; + } + + public int getDeltaFlags(Object e) { + // Since the label for triggers doesn't change, this node will generate + // delta info only if the list of alarms is changed. + if (e instanceof AlarmService.TriggersChangedEvent) { + return IModelDelta.CONTENT; + } + return IModelDelta.NO_CHANGE; + } + + + public void buildDelta(Object event, VMDelta parentDelta, int nodeOffset, + RequestMonitor requestMonitor) + { + if (event instanceof AlarmService.TriggersChangedEvent) { + // The list of alarms has changed, which means that the parent + // node needs to refresh its contents, which in turn will re-fetch the + // elements from this node. + parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT); + } + requestMonitor.done(); + } + + @Override + public void dispose() { + if (fAlarmCellModifier != null) { + fAlarmCellModifier.dispose(); + } + super.dispose(); + } +} diff --git a/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/doc-files/package-1.png b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/doc-files/package-1.png Binary files differnew file mode 100644 index 00000000000..0e7c8683d61 --- /dev/null +++ b/dsf/org.eclipse.cdt.examples.dsf/src/org/eclipse/cdt/examples/dsf/timers/doc-files/package-1.png |