/*******************************************************************************
* 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.codegen.ui;
import gov.redhawk.common.ui.editor.FormLayoutFactory;
import gov.redhawk.common.ui.parts.FormEntry;
import gov.redhawk.ide.codegen.CodegenPackage;
import gov.redhawk.ide.codegen.ICodeGeneratorDescriptor;
import gov.redhawk.ide.codegen.ITemplateDesc;
import gov.redhawk.ide.codegen.ImplementationSettings;
import gov.redhawk.ide.codegen.PortRepToGeneratorMap;
import gov.redhawk.ide.codegen.RedhawkCodegenActivator;
import gov.redhawk.ide.codegen.ui.internal.GeneratorDialog;
import gov.redhawk.ide.codegen.ui.internal.PortGeneratorComposite;
import gov.redhawk.ui.editor.EMFTableViewerElementSelector;
import gov.redhawk.ui.parts.FormEntryBindingFactory;
import gov.redhawk.ui.util.EMFEmptyStringToNullUpdateValueStrategy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import mil.jpeojtrs.sca.scd.Ports;
import mil.jpeojtrs.sca.scd.Provides;
import mil.jpeojtrs.sca.scd.Uses;
import mil.jpeojtrs.sca.spd.Descriptor;
import mil.jpeojtrs.sca.spd.Implementation;
import mil.jpeojtrs.sca.spd.SoftPkg;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.conversion.Converter;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.databinding.EMFDataBindingContext;
import org.eclipse.emf.databinding.EMFUpdateValueStrategy;
import org.eclipse.emf.databinding.edit.EMFEditObservables;
import org.eclipse.emf.ecore.EObject;
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.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.widgets.FormToolkit;
/**
* @since 6.0
*/
public abstract class BaseGeneratorPropertiesComposite extends Composite implements ICodegenComposite {
private static final int NUM_COLUMNS = 3;
private final FormToolkit toolkit;
private final int style;
private FormEntry outputDirEntry;
private ComboViewer generatorViewer;
private ComboViewer templateViewer;
private DataBindingContext context = new EMFDataBindingContext();
private ImplementationSettings implSettings;
private ITemplateDesc selectedTemplate;
private EditingDomain domain;
private PortGeneratorComposite portMapComposite;
private Implementation impl;
private EMFTableViewerElementSelector portMapSelector;
/**
* @param parent
* @param style
* @param toolkit
*/
public BaseGeneratorPropertiesComposite(final Composite parent, final int style, final FormToolkit toolkit) {
super(parent, style);
this.style = style;
this.toolkit = toolkit;
setLayout(FormLayoutFactory.createSectionClientGridLayout(false, BaseGeneratorPropertiesComposite.NUM_COLUMNS));
}
protected void initialize() {
createGeneratorEntry();
createTemplateEntry();
createOutputDirEntry();
createPropertiesArea();
createExtraArea(this, this.style, this.toolkit);
this.toolkit.paintBordersFor(this);
}
protected FormToolkit getToolkit() {
return this.toolkit;
}
protected ImplementationSettings getImplSettings() {
return this.implSettings;
}
protected EditingDomain getEditingDomain() {
return this.domain;
}
public DataBindingContext getContext() {
return this.context;
}
/**
* Creates the generator entry.
*
* @param client the client
* @param toolkit the toolkit
* @param actionBars the action bars
*/
private void createGeneratorEntry() {
final Label label = this.toolkit.createLabel(this, "Generator:");
label.setForeground(this.toolkit.getColors().getColor(IFormColors.TITLE));
this.generatorViewer = new ComboViewer(this, SWT.SINGLE | SWT.READ_ONLY | SWT.DROP_DOWN);
this.generatorViewer.getControl().addListener(SWT.MouseVerticalWheel, new Listener() {
@Override
public void handleEvent(Event event) {
event.doit = false;
}
});
this.generatorViewer.setContentProvider(new ArrayContentProvider());
this.generatorViewer.setLabelProvider(new LabelProvider() {
@Override
public String getText(final Object element) {
final ICodeGeneratorDescriptor desc = (ICodeGeneratorDescriptor) element;
return desc.getName();
}
});
this.generatorViewer.getControl().setLayoutData(GridDataFactory.fillDefaults().span(2, 1).grab(true, false).create());
this.generatorViewer.setInput(RedhawkCodegenActivator.getCodeGeneratorsRegistry().getCodegens());
this.generatorViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent event) {
final ICodeGeneratorDescriptor desc = (ICodeGeneratorDescriptor) ((IStructuredSelection) event.getSelection()).getFirstElement();
if (desc == null) {
BaseGeneratorPropertiesComposite.this.templateViewer.setInput(Collections.EMPTY_LIST);
} else {
ITemplateDesc[] templates = RedhawkCodegenActivator.getCodeGeneratorTemplatesRegistry().findTemplatesByCodegen(desc.getId());
BaseGeneratorPropertiesComposite.this.templateViewer.setInput(templates);
}
}
});
}
/**
* Creates the output dir entry.
*
* @param client the client
* @param toolkit the toolkit
* @param actionBars the action bars
*/
private void createOutputDirEntry() {
this.outputDirEntry = new FormEntry(this, this.toolkit, "Output Dir:", SWT.SINGLE, "Browse...", false);
this.outputDirEntry.getText().setToolTipText("Directory where generated code will be created.");
}
/**
* Creates the template entry.
*
* @param client the client
* @param toolkit the toolkit
* @param actionBars the action bars
*/
private void createTemplateEntry() {
final Label label = this.toolkit.createLabel(this, "Template:");
label.setForeground(this.toolkit.getColors().getColor(IFormColors.TITLE));
label.setLayoutData(GridDataFactory.fillDefaults().align(SWT.LEFT, SWT.TOP).create());
this.templateViewer = new ComboViewer(this, SWT.READ_ONLY | SWT.SINGLE | SWT.DROP_DOWN);
this.templateViewer.getControl().addListener(SWT.MouseVerticalWheel, new Listener() {
@Override
public void handleEvent(Event event) {
event.doit = false;
}
});
this.templateViewer.getControl().setLayoutData(GridDataFactory.fillDefaults().span(2, 1).grab(true, false).create());
this.templateViewer.getControl().setToolTipText("Template for the code generator");
this.templateViewer.setContentProvider(new ArrayContentProvider());
this.templateViewer.setLabelProvider(new LabelProvider() {
@Override
public String getText(final Object element) {
final ITemplateDesc desc = (ITemplateDesc) element;
return desc.getName();
}
});
this.templateViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent event) {
final ITemplateDesc desc = (ITemplateDesc) ((IStructuredSelection) event.getSelection()).getFirstElement();
final EditingDomain dom = BaseGeneratorPropertiesComposite.this.domain;
if ((dom != null) && (desc != null) && (desc != BaseGeneratorPropertiesComposite.this.selectedTemplate)) {
// Save the selected Template and update the ImplementationSettings
BaseGeneratorPropertiesComposite.this.selectedTemplate = desc;
// Check if the template has actually changed
// - this gets called when selecting implementations in the
// implementation list and we don't want change properties
// - this gets called when you actually click the dropdown,
// here we actually want to change the properties
if (!desc.getId().equals(BaseGeneratorPropertiesComposite.this.implSettings.getTemplate())) {
final Command command = SetCommand.create(dom, BaseGeneratorPropertiesComposite.this.implSettings,
CodegenPackage.Literals.IMPLEMENTATION_SETTINGS__TEMPLATE, desc.getId());
dom.getCommandStack().execute(command);
// Change the enablement of the port map if the new
// template supports it
BaseGeneratorPropertiesComposite.this.portMapComposite.setEnabled(BaseGeneratorPropertiesComposite.this.getEnablePortMap());
// Update the properties display and rebind
templateSelected(desc);
}
}
}
});
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
if ((this.implSettings != null) && this.implSettings.eAdapters().contains(this.portMapSelector)) {
this.implSettings.eAdapters().remove(this.portMapSelector);
}
this.context.dispose();
super.dispose();
}
/**
* This method is used to add extra Generator Settings information for
* subclasses.
*
* @param parent the parent composite
* @param style this composite's style
* @param toolkit the toolkit to use
*/
protected void createExtraArea(final Composite parent, final int style, final FormToolkit toolkit) {
this.portMapComposite = new PortGeneratorComposite(parent, SWT.NONE, toolkit);
this.portMapComposite.setLayoutData(GridDataFactory.fillDefaults().span(BaseGeneratorPropertiesComposite.NUM_COLUMNS, 1).grab(true, false).create());
this.portMapComposite.getAddPropertyButton().addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
handleAddPortMapping();
}
});
this.portMapComposite.getEditPropertyButton().addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
handleEditPortMapping();
}
});
this.portMapComposite.getRemovePropertyButton().addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
handleRemovePortMapping();
}
});
this.portMapComposite.getPortMapViewer().addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent event) {
final boolean enabled = BaseGeneratorPropertiesComposite.this.portMapComposite.isEnabled();
BaseGeneratorPropertiesComposite.this.portMapComposite.getRemovePropertyButton().setEnabled(enabled && !event.getSelection().isEmpty());
BaseGeneratorPropertiesComposite.this.portMapComposite.getEditPropertyButton().setEnabled(enabled && !event.getSelection().isEmpty());
BaseGeneratorPropertiesComposite.this.portMapComposite.getAddPropertyButton().setEnabled(
enabled && BaseGeneratorPropertiesComposite.this.getUnmappedRepIds(null).size() > 0);
}
});
}
/**
* This returns the outputDirEntry field
*
* @return the outputDirEntry field
* @since 7.0
*/
@Override
public FormEntry getOutputDirEntry() {
return this.outputDirEntry;
}
/**
* This returns the selected generator.
*
* @return the currently selected code generator descriptor
*/
public ICodeGeneratorDescriptor getSelectedGenerator() {
return (ICodeGeneratorDescriptor) ((IStructuredSelection) this.generatorViewer.getSelection()).getFirstElement();
}
/**
* This returns the selected template.
*
* @return the currently selected template descriptor
*/
public ITemplateDesc getSelectedTemplate() {
return (ITemplateDesc) ((IStructuredSelection) this.templateViewer.getSelection()).getFirstElement();
}
@Override
public void bind(final ArrayList<Binding> bindList, final EditingDomain editingDomain, final DataBindingContext context, final Implementation impl,
final ImplementationSettings implSettings) {
this.impl = impl;
this.implSettings = implSettings;
this.context = context;
this.domain = editingDomain;
preBind(this.impl, this.implSettings, bindList);
bindList.add(context.bindValue(ViewersObservables.observeSingleSelection(this.generatorViewer),
EMFEditObservables.observeValue(editingDomain, implSettings, CodegenPackage.Literals.IMPLEMENTATION_SETTINGS__GENERATOR_ID),
createGeneratorTargetToModel(), createGeneratorModelToTarget()));
bindList.add(FormEntryBindingFactory.bind(context, this.outputDirEntry, editingDomain, CodegenPackage.Literals.IMPLEMENTATION_SETTINGS__OUTPUT_DIR,
implSettings, null, null));
bindList.add(context.bindValue(ViewersObservables.observeSingleSelection(this.templateViewer),
EMFEditObservables.observeValue(editingDomain, implSettings, CodegenPackage.Literals.IMPLEMENTATION_SETTINGS__TEMPLATE),
createTemplateTargetToModel(), createTemplateModelToTarget()));
this.portMapComposite.setEnabled(this.getEnablePortMap());
this.portMapComposite.getPortMapViewer().setInput(implSettings);
if (this.portMapSelector == null) {
this.portMapSelector = new EMFTableViewerElementSelector(this.portMapComposite.getPortMapViewer());
}
if (!this.implSettings.eAdapters().contains(this.portMapSelector)) {
this.implSettings.eAdapters().add(this.portMapSelector);
}
this.createPropertyBinding();
this.portMapComposite.getAddPropertyButton().setEnabled((getUnmappedRepIds(null).size() > 0) && (this.portMapComposite.isEnabled()));
}
private boolean getEnablePortMap() {
return ((this.getSelectedTemplate() != null) && this.getSelectedTemplate().delegatePortGeneration());
}
/**
* @return
*/
private UpdateValueStrategy createTemplateModelToTarget() {
final EMFUpdateValueStrategy strategy = new EMFUpdateValueStrategy();
strategy.setConverter(new Converter(String.class, ITemplateDesc.class) {
@Override
public Object convert(final Object fromObject) {
if (fromObject == null) {
return null;
}
final String templateId = fromObject.toString();
final ITemplateDesc template = RedhawkCodegenActivator.getCodeGeneratorTemplatesRegistry().findTemplate(templateId);
return template;
}
});
return strategy;
}
/**
* @return
*/
private UpdateValueStrategy createTemplateTargetToModel() {
final EMFEmptyStringToNullUpdateValueStrategy strategy = new EMFEmptyStringToNullUpdateValueStrategy();
strategy.setConverter(new Converter(ITemplateDesc.class, String.class) {
@Override
public Object convert(final Object fromObject) {
if (fromObject == null) {
return null;
}
final ITemplateDesc desc = (ITemplateDesc) fromObject;
return desc.getId();
}
});
return strategy;
}
/**
* @return
*/
private UpdateValueStrategy createGeneratorTargetToModel() {
final EMFEmptyStringToNullUpdateValueStrategy strategy = new EMFEmptyStringToNullUpdateValueStrategy();
strategy.setConverter(new Converter(ICodeGeneratorDescriptor.class, String.class) {
@Override
public Object convert(final Object fromObject) {
if (fromObject == null) {
return null;
}
final ICodeGeneratorDescriptor desc = (ICodeGeneratorDescriptor) fromObject;
return desc.getId();
}
});
return strategy;
}
/**
* @return
*/
private UpdateValueStrategy createGeneratorModelToTarget() {
final EMFUpdateValueStrategy strategy = new EMFUpdateValueStrategy();
strategy.setConverter(new Converter(String.class, ICodeGeneratorDescriptor.class) {
@Override
public Object convert(final Object fromObject) {
if (fromObject == null) {
return null;
}
final String generatorId = fromObject.toString();
final ICodeGeneratorDescriptor generator = RedhawkCodegenActivator.getCodeGeneratorsRegistry().findCodegen(generatorId);
return generator;
}
});
return strategy;
}
/**
* This returns the template specified in the ImplementationSettings or the
* default one that corresponds to the generator passed in
*
* @param generator the generator to get templates for
* @return the specified template or a default one if it's not found
*/
protected ITemplateDesc getTemplateDesc(final ICodeGeneratorDescriptor generator) {
if (generator == null) {
return null;
}
String templateId = this.implSettings.getTemplate();
// If the templateId is null or old style, select the legacy code
// generator template if the implementationSettings has existing props
if (((templateId == null) || ("src/".equals(templateId))) && (this.implSettings.getProperties().size() > 0)) {
templateId = this.implSettings.getGeneratorId();
}
ITemplateDesc template = null;
ITemplateDesc genTemplate = null;
final ITemplateDesc[] temps = RedhawkCodegenActivator.getCodeGeneratorTemplatesRegistry().findTemplatesByCodegen(generator.getId());
for (final ITemplateDesc temp : temps) {
// Keep track of the default generator template
if (temp.getId().equals(generator.getId())) {
genTemplate = temp;
}
if (temp.getId().equals(templateId)) {
template = temp;
break;
}
}
// If we didn't find the template for some reason, return the default
// generator template
return (template != null) ? template : genTemplate; // SUPPRESS CHECKSTYLE AvoidInline
}
/**
* Handle add mapping.
*/
private void handleAddPortMapping() {
final HashSet<String> repIds = getUnmappedRepIds(null);
final GeneratorDialog dialog = new GeneratorDialog(getShell(), "Add Generator Mapping", null, repIds, this.impl.getProgrammingLanguage().getName());
if (dialog.open() == Window.OK) {
final EObject ref = dialog.getValue();
this.domain.getCommandStack().execute(
AddCommand.create(this.domain, this.implSettings, CodegenPackage.Literals.IMPLEMENTATION_SETTINGS__PORT_GENERATORS, ref));
}
}
/**
* Handle edit mapping.
*/
private void handleEditPortMapping() {
final PortRepToGeneratorMap curProp = (PortRepToGeneratorMap) ((IStructuredSelection) this.portMapComposite.getPortMapViewer().getSelection()).getFirstElement();
final HashSet<String> repIds = getUnmappedRepIds(curProp.getRepId());
final GeneratorDialog dialog = new GeneratorDialog(getShell(), "Edit Generator Mapping", curProp, repIds, this.impl.getProgrammingLanguage().getName());
if (dialog.open() == Window.OK) {
final PortRepToGeneratorMap ref = (PortRepToGeneratorMap) dialog.getValue();
this.domain.getCommandStack().execute(
SetCommand.create(this.domain, curProp, CodegenPackage.Literals.PORT_REP_TO_GENERATOR_MAP__GENERATOR, ref.getGenerator()));
}
}
/**
* Handle remove mapping.
*/
private void handleRemovePortMapping() {
final PortRepToGeneratorMap curProp = (PortRepToGeneratorMap) ((IStructuredSelection) this.portMapComposite.getPortMapViewer().getSelection()).getFirstElement();
this.domain.getCommandStack().execute(
RemoveCommand.create(this.domain, this.implSettings, CodegenPackage.Literals.IMPLEMENTATION_SETTINGS__PORT_GENERATORS, curProp));
}
/**
* This returns a set of repIds representing all the ports for the current
* implementation that do not have a generator assigned. If currentRep is
* not null, it adds it to the list.
*
* @param currentRep the repId to add to the set, or null if none should be
* added
* @return set of repIds without generators
*/
private HashSet<String> getUnmappedRepIds(final String currentRep) {
final HashSet<String> repIds = new HashSet<String>();
final Descriptor descriptor = ((SoftPkg) this.impl.eContainer()).getDescriptor();
if ((descriptor != null) && (descriptor.getComponent() != null)) {
final Ports ports = descriptor.getComponent().getComponentFeatures().getPorts();
// Store the current RepIds
for (final Provides p : ports.getProvides()) {
repIds.add(p.getRepID());
}
for (final Uses u : ports.getUses()) {
repIds.add(u.getRepID());
}
// filter out the used ones
for (final PortRepToGeneratorMap r : this.implSettings.getPortGenerators()) {
repIds.remove(r.getRepId());
}
// Add the passed in rep if necessary
if (currentRep != null) {
repIds.add(currentRep);
}
}
return repIds;
}
public void setEditable(final boolean canEdit) {
this.portMapComposite.getAddPropertyButton().setEnabled(canEdit && (getUnmappedRepIds(null).size() > 0) && this.portMapComposite.isEnabled());
this.portMapComposite.setEditable(canEdit);
}
/**
* This is called after a new template is selected that doesn't match the
* previously selected one. When this is called, the stored template has
* been updated.
*
* @param desc the newly selected template descriptor
*/
protected abstract void templateSelected(ITemplateDesc desc);
/**
* Creates the properties entry.
*/
protected abstract void createPropertiesArea();
/**
* This is called when bind is called on the composite, but before any
* bindings are created.
*
* @param impl the current implementation
* @param implSettings the current implementation settings
* @param bindList the binding list used by the bind() method
*/
protected abstract void preBind(final Implementation impl, final ImplementationSettings implSettings, final List<Binding> bindList);
/**
* Creates the property binding.
*/
protected abstract void createPropertyBinding();
}