/*******************************************************************************
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* 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
*******************************************************************************/
package gov.redhawk.ide.dcd.internal.ui.editor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.databinding.EMFDataBindingContext;
import org.eclipse.emf.databinding.edit.EMFEditObservables;
import org.eclipse.emf.ecore.provider.EcoreItemProviderAdapterFactory;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.DecoratingLabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.progress.WorkbenchJob;
import gov.redhawk.ide.dcd.internal.ui.ComponentPlacementContentProvider;
import gov.redhawk.ide.dcd.internal.ui.editor.provider.DcdItemProviderAdapterFactoryAdapter;
import gov.redhawk.ide.dcd.internal.ui.editor.provider.DevicesSectionComponentPlacementItemProvider;
import gov.redhawk.ide.dcd.ui.wizard.ScaNodeProjectDevicesWizardPage;
import gov.redhawk.ide.sdr.LoadState;
import gov.redhawk.ide.sdr.SdrRoot;
import gov.redhawk.ide.sdr.ui.SdrUiPlugin;
import gov.redhawk.model.sca.util.ModelUtil;
import gov.redhawk.sca.ui.parts.FormFilteredTree;
import gov.redhawk.ui.actions.SortAction;
import gov.redhawk.ui.editor.TreeSection;
import gov.redhawk.ui.parts.TreePart;
import gov.redhawk.ui.util.SCAEditorUtil;
import mil.jpeojtrs.sca.dcd.DcdComponentInstantiation;
import mil.jpeojtrs.sca.dcd.DcdComponentPlacement;
import mil.jpeojtrs.sca.dcd.DcdFactory;
import mil.jpeojtrs.sca.dcd.DcdPackage;
import mil.jpeojtrs.sca.dcd.DcdPartitioning;
import mil.jpeojtrs.sca.dcd.DeviceConfiguration;
import mil.jpeojtrs.sca.dcd.provider.DcdItemProviderAdapterFactory;
import mil.jpeojtrs.sca.partitioning.ComponentFile;
import mil.jpeojtrs.sca.partitioning.ComponentFileRef;
import mil.jpeojtrs.sca.partitioning.ComponentFiles;
import mil.jpeojtrs.sca.partitioning.ComponentPlacement;
import mil.jpeojtrs.sca.partitioning.PartitioningFactory;
import mil.jpeojtrs.sca.partitioning.PartitioningPackage;
import mil.jpeojtrs.sca.spd.SoftPkg;
/**
* @since 1.1
*
*/
public class DevicesSection extends TreeSection implements IPropertyChangeListener {
private static final int BUTTON_REMOVE = 1;
private static final int BUTTON_ADD = 0;
protected static final long SDR_REFRESH_DELAY = 500;
private FormFilteredTree fFilteredTree;
private TreeViewer fExtensionTree;
private SortAction fSortAction;
private ComposedAdapterFactory adapterFactory;
private Resource dcdResource;
private SoftPkg[] devices;
private boolean disposed;
private boolean editable;
private DataBindingContext context;
private DcdComponentPlacement lastComp;
private final WorkbenchJob refreshViewerJob = new WorkbenchJob("Refresh") {
@Override
public IStatus runInUIThread(final IProgressMonitor monitor) {
DevicesSection.this.fExtensionTree.refresh();
return Status.OK_STATUS;
}
};
private final EContentAdapter refreshAdapter = new EContentAdapter() {
@Override
public void notifyChanged(final Notification notification) {
super.notifyChanged(notification);
final Object feature = notification.getFeature();
if (feature == DcdPackage.Literals.DEVICE_CONFIGURATION__PARTITIONING || feature == PartitioningPackage.Literals.PARTITIONING__COMPONENT_PLACEMENT
|| feature == DcdPackage.Literals.DCD_COMPONENT_PLACEMENT__COMPOSITE_PART_OF_DEVICE
|| feature == DcdPackage.Literals.COMPOSITE_PART_OF_DEVICE__REF_ID) {
DevicesSection.this.refreshViewerJob.schedule(10); // SUPPRESS CHECKSTYLE MagicNumber
}
}
};
/**
* The Constructor.
*
* @param page the page
* @param parent the parent
*/
public DevicesSection(final DevicesPage page, final Composite parent) {
super(page, parent, Section.DESCRIPTION, new String[] { "Add...", "Remove" });
this.fHandleDefaultButton = false;
}
/**
* {@inheritDoc}
*/
@Override
protected void selectionChanged(final IStructuredSelection selection) {
getPage().setSelection(selection);
updateButtons(selection);
}
/**
* {@inheritDoc}
*/
@Override
public DevicesPage getPage() {
return (DevicesPage) super.getPage();
}
/**
* {@inheritDoc}
*/
@Override
protected void createClient(final Section section, final FormToolkit toolkit) {
final Composite container = createClientContainer(section, 2, toolkit);
final TreePart treePart = getTreePart();
createViewerPartControl(container, SWT.MULTI, 2, toolkit);
this.fExtensionTree = treePart.getTreeViewer();
this.fExtensionTree.setContentProvider(new ComponentPlacementContentProvider());
this.fExtensionTree.setLabelProvider(new DecoratingLabelProvider(new AdapterFactoryLabelProvider(getAdapterFactory()), PlatformUI.getWorkbench()
.getDecoratorManager().getLabelDecorator()) {
@Override
public String getText(final Object element) {
if (element instanceof DcdComponentPlacement) {
return ((DcdComponentPlacement) element).getComponentInstantiation().get(0).getUsageName();
}
return super.getText(element);
}
});
this.fExtensionTree.setFilters(createComponentPlacementViewerFilter());
toolkit.paintBordersFor(container);
section.setClient(container);
section.setDescription("Select devices to include in this node within the following section.");
// See Bug # 160554: Set text before text client
section.setText("All Devices");
initialize();
createSectionToolbar(section, toolkit);
// Create the adapted listener for the filter entry field
final Text filterText = this.fFilteredTree.getFilterControl();
if (filterText != null) {
filterText.addModifyListener(new ModifyListener() {
@Override
public void modifyText(final ModifyEvent e) {
final StructuredViewer viewer = getStructuredViewerPart().getViewer();
final IStructuredSelection ssel = (IStructuredSelection) viewer.getSelection();
updateButtons((ssel.size() != 1) ? null : ssel); // SUPPRESS CHECKSTYLE AvoidInline
}
});
}
}
private ViewerFilter[] createComponentPlacementViewerFilter() {
return new ViewerFilter[] { new ViewerFilter() {
@Override
public boolean select(final Viewer viewer, final Object parentElement, final Object element) {
return element instanceof ComponentPlacement;
}
} };
}
/**
* {@inheritDoc}
*/
@Override
public boolean setFormInput(final Object object) {
if (object != null) {
this.fExtensionTree.setSelection(new StructuredSelection(object), true);
return true;
} else {
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
// Explicitly call the dispose method on the extensions tree
if (this.fFilteredTree != null) {
this.fFilteredTree.dispose();
}
this.adapterFactory.dispose();
this.refreshAdapter.unsetTarget(this.dcdResource);
super.dispose();
}
/**
* {@inheritDoc}
*/
@Override
protected TreeViewer createTreeViewer(final Composite parent, final int style) {
this.fFilteredTree = new FormFilteredTree(parent, style, new PatternFilter());
parent.setData("filtered", Boolean.TRUE); //$NON-NLS-1$
return this.fFilteredTree.getViewer();
}
/**
* Gets the adapter factory.
*
* @return the adapter factory
*/
private AdapterFactory getAdapterFactory() {
if (this.adapterFactory == null) {
this.adapterFactory = new ComposedAdapterFactory();
final DcdItemProviderAdapterFactoryAdapter dcdAdapter = new DcdItemProviderAdapterFactoryAdapter();
dcdAdapter.setComponentPlacementAdapter(new DevicesSectionComponentPlacementItemProvider(dcdAdapter));
this.adapterFactory.addAdapterFactory(dcdAdapter);
this.adapterFactory.addAdapterFactory(new DcdItemProviderAdapterFactory());
this.adapterFactory.addAdapterFactory(new ResourceItemProviderAdapterFactory());
this.adapterFactory.addAdapterFactory(new EcoreItemProviderAdapterFactory());
this.adapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());
}
return this.adapterFactory;
}
/**
* Initialize.
*
* @param model the model
*/
private void initialize() {
selectFirstElement();
final TreePart treePart = getTreePart();
treePart.setButtonEnabled(DevicesSection.BUTTON_ADD, true);
treePart.setButtonEnabled(DevicesSection.BUTTON_REMOVE, false);
}
/**
* Select first element.
*/
private void selectFirstElement() {
final Tree tree = this.fExtensionTree.getTree();
final TreeItem[] items = tree.getItems();
if (items.length == 0) {
return;
}
final TreeItem firstItem = items[0];
final Object obj = firstItem.getData();
this.fExtensionTree.setSelection(new StructuredSelection(obj));
}
/**
* Select the desired element
*/
private void selectElement(final DcdComponentPlacement placement) {
final Tree tree = this.fExtensionTree.getTree();
for (final TreeItem item : tree.getItems()) {
final Object obj = item.getData();
if (obj.equals(placement)) {
this.fExtensionTree.setSelection(new StructuredSelection(obj));
break;
}
}
}
/**
* Creates the section toolbar.
*
* @param section the section
* @param toolkit the toolkit
*/
private void createSectionToolbar(final Section section, final FormToolkit toolkit) {
final ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
final ToolBar toolbar = toolBarManager.createControl(section);
final Cursor handCursor = new Cursor(Display.getCurrent(), SWT.CURSOR_HAND);
toolbar.setCursor(handCursor);
// Cursor needs to be explicitly disposed
toolbar.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(final DisposeEvent e) {
if (!handCursor.isDisposed()) {
handCursor.dispose();
}
}
});
// Add sort action to the tool bar
this.fSortAction = new SortAction(this.fExtensionTree, "Sort the properties alphabetically.", null, null, this);
toolBarManager.add(this.fSortAction);
toolBarManager.update(true);
section.setTextClient(toolbar);
}
/**
* Update buttons.
*
* @param item the item
*/
private void updateButtons(final Object item) {
final boolean sorted = this.fSortAction != null && this.fSortAction.isChecked();
if (sorted) {
return;
}
final boolean filtered = this.fFilteredTree.isFiltered();
boolean addEnabled = true;
boolean removeEnabled = false;
if (item != null && this.editable) {
removeEnabled = true;
}
if (filtered || !this.editable) {
// Fix for bug 194529 and bug 194828
addEnabled = false;
}
getTreePart().setButtonEnabled(DevicesSection.BUTTON_ADD, addEnabled);
getTreePart().setButtonEnabled(DevicesSection.BUTTON_REMOVE, removeEnabled);
}
/**
* {@inheritDoc}
*/
@Override
protected void buttonSelected(final int index) {
switch (index) {
case BUTTON_ADD:
handleNew();
break;
case BUTTON_REMOVE:
handleDelete();
break;
default:
break;
}
}
/**
* Handle delete.
*/
private void handleDelete() {
final IStructuredSelection selections = ((IStructuredSelection) getTreePart().getViewer().getSelection());
if (selections != null) {
List< ? > selectionList = selections.toList();
for (Object selection : selectionList) {
execute(RemoveCommand.create(getEditingDomain(), getDeviceConfiguration().getPartitioning(),
PartitioningPackage.Literals.PARTITIONING__COMPONENT_PLACEMENT, selection));
}
}
this.refresh(this.dcdResource);
}
/**
* Handle new.
*/
private void handleNew() {
final SdrRoot sdrRoot = SdrUiPlugin.getDefault().getTargetSdrRoot();
if (sdrRoot.getState() != LoadState.LOADED) {
final IRunnableWithProgress waitForLoad = new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
monitor.beginTask("Waiting for SDR Root to load", IProgressMonitor.UNKNOWN);
sdrRoot.load(monitor);
}
};
try {
new ProgressMonitorDialog(getPage().getEditorSite().getWorkbenchWindow().getShell()).run(true, false, waitForLoad);
} catch (final InvocationTargetException e) {
return;
} catch (final InterruptedException e) {
return;
}
}
final ScaNodeProjectDevicesWizardPage devWizardPage = new ScaNodeProjectDevicesWizardPage("Select Devices to Add");
final Wizard wiz = new Wizard() {
@Override
public boolean performFinish() {
final ScaNodeProjectDevicesWizardPage page = (ScaNodeProjectDevicesWizardPage) this.getPages()[0];
if (page != null) {
DevicesSection.this.devices = page.getNodeDevices();
}
return true;
}
};
final WizardDialog dialog = new WizardDialog(getPage().getSite().getShell(), wiz);
wiz.addPage(devWizardPage);
if (dialog.open() == Window.OK) {
final DeviceConfiguration dcd = getDeviceConfiguration();
if (this.devices.length == 0 || dcd == null) {
return;
}
final CompoundCommand command = new CompoundCommand("Add Devices");
// First see if we need to add a componentfile for this
ComponentFiles files = dcd.getComponentFiles();
if (files == null) {
files = PartitioningFactory.eINSTANCE.createComponentFiles();
command.append(SetCommand.create(getEditingDomain(), dcd, DcdPackage.Literals.DEVICE_CONFIGURATION__COMPONENT_FILES, files));
}
for (final SoftPkg device : this.devices) {
ComponentFile file = null;
for (final ComponentFile f : files.getComponentFile()) {
if (f == null) {
continue;
}
final SoftPkg fSpd = f.getSoftPkg();
if (fSpd != null && device.getId().equals(fSpd.getId())) {
file = f;
break;
}
}
if (file == null) {
file = DcdFactory.eINSTANCE.createComponentFile();
file.setSoftPkg(device);
command.append(AddCommand.create(getEditingDomain(), files, PartitioningPackage.Literals.COMPONENT_FILES__COMPONENT_FILE,
file));
}
DcdPartitioning partitioning = dcd.getPartitioning();
if (partitioning == null) {
partitioning = DcdFactory.eINSTANCE.createDcdPartitioning();
command.append(SetCommand.create(getEditingDomain(), dcd, DcdPackage.Literals.DEVICE_CONFIGURATION__PARTITIONING, partitioning));
}
final DcdComponentPlacement placement = DcdFactory.eINSTANCE.createDcdComponentPlacement();
final ComponentFileRef cfp = PartitioningFactory.eINSTANCE.createComponentFileRef();
cfp.setRefid(file.getId());
placement.setComponentFileRef(cfp);
command.append(AddCommand.create(getEditingDomain(), dcd.getPartitioning(), PartitioningPackage.Literals.PARTITIONING__COMPONENT_PLACEMENT,
placement));
final DcdComponentInstantiation instantiation = DcdFactory.eINSTANCE.createDcdComponentInstantiation();
final String uniqueName = DeviceConfiguration.Util.createDeviceUsageName(dcd, device.getName());
instantiation.setId(dcd.getName() + ":" + uniqueName);
instantiation.setUsageName(uniqueName);
command.append(SetCommand.create(getEditingDomain(), placement, PartitioningPackage.Literals.COMPONENT_PLACEMENT__COMPONENT_INSTANTIATION,
Arrays.asList((new DcdComponentInstantiation[] { instantiation }))));
this.lastComp = placement;
}
execute(command);
refresh(this.dcdResource);
selectElement(this.lastComp);
}
}
private DeviceConfiguration getDeviceConfiguration() {
return ModelUtil.getDeviceConfiguration(this.dcdResource);
}
/**
* Execute.
*
* @param command the command
*/
private void execute(final Command command) {
getEditingDomain().getCommandStack().execute(command);
}
/**
* Gets the selection.
*
* @return the selection
*/
private Object getSelection() {
return ((IStructuredSelection) getTreePart().getViewer().getSelection()).getFirstElement();
}
/**
* Gets the selection.
*
* @return the selection
*/
public SoftPkg getSelectedDevice() {
final DeviceConfiguration dcd = getDeviceConfiguration();
final ComponentPlacement< ? > place = (ComponentPlacement< ? >) getSelection();
final String targetId = place.getComponentFileRef().getRefid();
if (dcd.getComponentFiles() != null) {
for (final ComponentFile file : dcd.getComponentFiles().getComponentFile()) {
if (file.getId().equals(targetId)) {
return file.getSoftPkg();
}
}
}
return null;
}
/**
* Gets the editing domain.
*
* @return the editing domain
*/
private EditingDomain getEditingDomain() {
return getPage().getEditor().getEditingDomain();
}
/**
* {@inheritDoc}
*/
@Override
public void propertyChange(final PropertyChangeEvent event) {
if (this.fSortAction.equals(event.getSource()) && IAction.RESULT.equals(event.getProperty())) {
final StructuredViewer viewer = getStructuredViewerPart().getViewer();
final IStructuredSelection ssel = (IStructuredSelection) viewer.getSelection();
updateButtons(ssel);
}
}
/**
* Fire selection.
*/
protected void fireSelection() {
final ISelection selection = this.fExtensionTree.getSelection();
if (selection.isEmpty()) {
selectFirstElement();
} else {
this.fExtensionTree.setSelection(selection);
}
}
/**
* {@inheritDoc}
*/
@Override
public void refresh(final Resource resource) {
if (isDisposed()) {
return;
}
this.dcdResource = resource;
if (this.dcdResource == null) {
return;
}
if (this.fExtensionTree != null) {
if (this.context != null) {
this.context.dispose();
}
this.context = new EMFDataBindingContext();
final DeviceConfiguration dcd = getDeviceConfiguration();
this.context.bindValue(ViewersObservables.observeInput(this.fExtensionTree),
EMFEditObservables.observeValue(getEditingDomain(), dcd, DcdPackage.Literals.DEVICE_CONFIGURATION__PARTITIONING));
if (!this.dcdResource.eAdapters().contains(this.refreshAdapter)) {
this.refreshAdapter.setTarget(this.dcdResource);
}
}
this.fireSelection();
this.setEditable();
}
private boolean isDisposed() {
return this.disposed;
}
private void setEditable() {
this.editable = SCAEditorUtil.isEditableResource(getPage(), this.dcdResource);
this.getTreePart().setButtonEnabled(DevicesSection.BUTTON_ADD, this.editable);
this.getTreePart().setButtonEnabled(DevicesSection.BUTTON_REMOVE, this.editable);
}
}