/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.components.xml.merger.gui;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
import de.rcenvironment.components.xml.merger.common.XmlMergerComponentConstants;
import de.rcenvironment.core.component.model.configuration.api.ConfigurationDescription;
import de.rcenvironment.core.component.model.endpoint.api.EndpointDescriptionsManager;
import de.rcenvironment.core.component.workflow.model.api.WorkflowNode;
import de.rcenvironment.core.component.workflow.model.api.WorkflowNodeUtil;
import de.rcenvironment.core.datamodel.api.DataType;
import de.rcenvironment.core.gui.utils.common.components.PropertyTabGuiHelper;
import de.rcenvironment.core.gui.workflow.editor.properties.AbstractWorkflowNodeCommand;
import de.rcenvironment.core.gui.workflow.editor.properties.ValidatingWorkflowNodePropertySection;
import de.rcenvironment.core.gui.workflow.editor.properties.WorkflowNodeCommand;
import de.rcenvironment.core.gui.workflow.executor.properties.AbstractEditScriptRunnable;
import de.rcenvironment.core.gui.workflow.executor.properties.AbstractScriptSection;
import de.rcenvironment.core.utils.common.StringUtils;
/**
* GUI in property tab for tool finding.
*
* @author Markus Kunde
* @author Arne Bachmann
* @author Markus Litz
* @author Miriam Lenk
* @author Jan Flink
* @author Brigitte Boden
* @author Sascha Zur
*/
public class XmlMergerSection extends ValidatingWorkflowNodePropertySection {
private static final int MINIMUM_HEIGHT_OF_FILE_CONTENT_TEXT = 300;
private Button fileChooser;
private Button fileEditor;
private Composite fileGroup;
private Composite contentGroup;
private CLabel fileContentLabel;
private StyledText fileContentText;
private boolean mappingFileAsInput;
private Button mappingFileAsInputButton;
private Button loadMappingFileButton;
private EditScriptRunnable esr;
@Override
protected void createCompositeContent(final Composite parent, final TabbedPropertySheetPage aTabbedPropertySheetPage) {
final TabbedPropertySheetWidgetFactory toolkit = aTabbedPropertySheetPage.getWidgetFactory();
final Composite content = new LayoutComposite(parent);
content.setLayout(new GridLayout(1, true));
final Composite fileChoosingSection = toolkit.createFlatFormComposite(content);
initFileChoosingSection(toolkit, fileChoosingSection);
}
/**
* Initialize file choosing section.
*
* @param toolkit the toolkit to create section content
* @param container parent
*/
private void initFileChoosingSection(final TabbedPropertySheetWidgetFactory toolkit, final Composite container) {
GridData layoutData;
layoutData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING);
container.setLayoutData(layoutData);
container.setLayout(new FillLayout());
final Section section = toolkit.createSection(container, Section.TITLE_BAR | Section.EXPANDED);
section.setText(Messages.fileChoosingSectionName);
final Composite client = toolkit.createComposite(section);
layoutData = new GridData(GridData.FILL_HORIZONTAL);
client.setLayoutData(layoutData);
client.setLayout(new GridLayout(1, false));
Composite radioGroup = toolkit.createComposite(client);
radioGroup.setLayout(new GridLayout(2, false));
mappingFileAsInputButton = new Button(radioGroup, SWT.RADIO);
mappingFileAsInputButton.setText(Messages.mappingFileAsInputButton);
loadMappingFileButton = new Button(radioGroup, SWT.RADIO);
loadMappingFileButton.setText(Messages.mappingFileLoadedButton);
fileGroup = toolkit.createComposite(client);
fileGroup.setLayout(new GridLayout(2, false));
fileGroup.setEnabled(!mappingFileAsInput);
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL_HORIZONTAL;
gridData.grabExcessHorizontalSpace = false;
fileChooser = toolkit.createButton(fileGroup, Messages.fileLinkButtonLabel, SWT.PUSH);
fileEditor = toolkit.createButton(fileGroup, Messages.fileEditorButtonLabel, SWT.PUSH);
layoutData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
contentGroup = toolkit.createComposite(client);
contentGroup.setLayoutData(layoutData);
contentGroup.setLayout(new GridLayout(1, false));
fileContentLabel = toolkit.createCLabel(contentGroup, Messages.actuallyLoadedLabel);
gridData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
gridData.heightHint = MINIMUM_HEIGHT_OF_FILE_CONTENT_TEXT;
fileContentText = new StyledText(contentGroup, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
fileContentText.setEditable(false);
fileContentText.setLayoutData(gridData);
fileContentText.setData(CONTROL_PROPERTY_KEY, XmlMergerComponentConstants.XMLCONTENT_CONFIGNAME);
section.setClient(client);
}
/**
* Open file choosing dialog for Mapping file.
*
*/
private void fileChoosing() {
final IFile file = PropertyTabGuiHelper.selectFileFromProjects(fileGroup.getShell(), Messages.loadTitle, Messages.loadMessage);
if (file != null) {
StringWriter writer = new StringWriter();
try {
IOUtils.copy(file.getContents(), writer);
String theString = writer.toString();
setScriptProperties(node, theString, getMappingType(file.getName()));
} catch (IOException | CoreException e) {
logger.error(StringUtils.format(Messages.logReadFromFileError, Messages.cannotReadContentFromFile, e.getMessage()));
MessageDialog.openError(getComposite().getShell(), Messages.cannotReadContentFromFile,
StringUtils.format(Messages.dialogMessageReadFromFileError, e.getMessage(), Messages.refreshProjectExplorer));
}
refreshSection();
}
}
private String getMappingType(final String fileName) {
if (fileName.endsWith(XmlMergerComponentConstants.XMLFILEEND)) {
return XmlMergerComponentConstants.MAPPINGTYPE_CLASSIC;
} else {
return XmlMergerComponentConstants.MAPPINGTYPE_XSLT;
}
}
@Override
protected Updater createUpdater() {
return new MergerGUIUpdater();
}
@Override
protected Synchronizer createSynchronizer() {
return new MergerGUISynchronizer();
}
/**
* Implementation of {@link DefaultUpdater}.
*
* @author Jan Flink
* @author Sascha Zur
*
*/
private class MergerGUIUpdater extends DefaultUpdater {
@Override
public void updateControl(Control control, String propertyName, String newValue, String oldValue) {
if (!propertyName.equals(XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_CONFIGNAME)) {
super.updateControl(control, propertyName, newValue, oldValue);
}
setXMLFileContent();
}
}
/**
* Implementation of {@link DefaultSynchronizer}.
*
* @author Jan Flink
* @author Sascha Zur
*
*/
private class MergerGUISynchronizer extends DefaultSynchronizer {
@Override
public void handlePropertyChange(String propertyName, String newValue, String oldValue) {
if (!propertyName.equals(XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_CONFIGNAME)) {
super.handlePropertyChange(propertyName, newValue, oldValue);
} else {
getUpdater().updateControl(null, propertyName, newValue, oldValue);
}
}
@Override
protected void handlePropertyChange(Control control, String key, String newValue, String oldValue) {
if (!key.equals(XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_CONFIGNAME)) {
super.handlePropertyChange(control, key, newValue, oldValue);
} else {
getUpdater().updateControl(control, key, newValue, oldValue);
}
}
}
private void setXMLFileContent() {
mappingFileAsInput =
(node.getConfigurationDescription().getConfigurationValue(XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_CONFIGNAME)
.equals(XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_INPUT));
mappingFileAsInputButton.setSelection(mappingFileAsInput);
loadMappingFileButton.setSelection(!mappingFileAsInput);
fileChooser.setEnabled(!mappingFileAsInput);
fileEditor.setEnabled(!mappingFileAsInput && !fileContentText.getText().equals(""));
fileContentLabel.setEnabled(!mappingFileAsInput);
fileContentText.setEnabled(!mappingFileAsInput);
if (mappingFileAsInput) {
fileContentLabel.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_TITLE_INACTIVE_FOREGROUND));
// Setting the focus to the selected radio button. This is necessary when the button has been selected automatically (e.g. after
// a "redo" operation), because otherwise a "save" operation will change the button selection again, causing the bug in
// https://mantis.sc.dlr.de/view.php?id=13578
if (loadMappingFileButton.isFocusControl()) {
mappingFileAsInputButton.setFocus();
}
} else {
fileContentLabel.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_TITLE_FOREGROUND));
if (mappingFileAsInputButton.isFocusControl()) {
loadMappingFileButton.setFocus();
}
}
fileGroup.pack(true);
}
@Override
protected void refreshBeforeValidation() {
updateEditor(node);
setXMLFileContent();
}
@Override
public void setInput(IWorkbenchPart part, ISelection selection) {
super.setInput(part, selection);
setXMLFileContent();
updateEditor(node);
}
@Override
protected Controller createController() {
return new FileController();
}
@Override
public void aboutToBeShown() {
super.aboutToBeShown();
refresh();
}
private void updateEditor(WorkflowNode node) {
if (esr != null && fileContentText != null && esr.getNode().equals(node)) {
esr.update(fileContentText.getText());
}
}
/**
* Custom {@link DefaultController} implementation to handle the activation of the GUI controls.
*
* @author Markus Kunde
*
*/
private final class FileController extends DefaultController {
@Override
protected void widgetSelected(final SelectionEvent event, final Control source) {
if (source == loadMappingFileButton && loadMappingFileButton.getSelection()) {
if (XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_INPUT
.equals(getProperty(XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_CONFIGNAME))) {
execute(new ChangeToLoadedMappingFileCommand(node));
}
} else if (source == mappingFileAsInputButton && mappingFileAsInputButton.getSelection()) {
if (XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_LOADED
.equals(getProperty(XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_CONFIGNAME))) {
execute(new ChangeToMappingFileAsInputCommand(node));
}
} else {
super.widgetSelected(event, source);
if (source == fileChooser) {
fileChoosing();
} else if (source == fileEditor) {
esr = new EditScriptRunnable(node);
esr.run();
}
}
}
}
/**
* Adds the input for the mapping file.
*
*/
private class ChangeToMappingFileAsInputCommand extends AbstractWorkflowNodeCommand {
private String oldXmlContent;
private WorkflowNode node;
ChangeToMappingFileAsInputCommand(WorkflowNode node) {
this.node = node;
}
@Override
protected void execute2() {
ConfigurationDescription configDesc = node.getConfigurationDescription();
configDesc.setConfigurationValue(XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_CONFIGNAME,
XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_INPUT);
oldXmlContent = configDesc.getConfigurationValue((XmlMergerComponentConstants.XMLCONTENT_CONFIGNAME));
configDesc.setConfigurationValue(XmlMergerComponentConstants.XMLCONTENT_CONFIGNAME, "");
addMappingFileInput(node);
}
@Override
protected void undo2() {
ConfigurationDescription configDesc = node.getConfigurationDescription();
configDesc.setConfigurationValue(XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_CONFIGNAME,
XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_LOADED);
configDesc.setConfigurationValue(XmlMergerComponentConstants.XMLCONTENT_CONFIGNAME, oldXmlContent);
removeMappingFileInput(node);
}
}
/**
* Remove the input for the mapping file.
*
*/
private class ChangeToLoadedMappingFileCommand extends AbstractWorkflowNodeCommand {
private WorkflowNode node;
ChangeToLoadedMappingFileCommand(WorkflowNode node) {
this.node = node;
}
@Override
protected void execute2() {
ConfigurationDescription configDesc = node.getConfigurationDescription();
configDesc.setConfigurationValue(XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_CONFIGNAME,
XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_LOADED);
removeMappingFileInput(getWorkflowNode());
}
@Override
protected void undo2() {
ConfigurationDescription configDesc = node.getConfigurationDescription();
configDesc.setConfigurationValue(XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_CONFIGNAME,
XmlMergerComponentConstants.MAPPINGFILE_DEPLOYMENT_INPUT);
addMappingFileInput(getWorkflowNode());
}
}
private void addMappingFileInput(WorkflowNode workflowNode) {
EndpointDescriptionsManager manager = workflowNode.getInputDescriptionsManager();
Map<String, String> metaData = new HashMap<String, String>();
manager.addDynamicEndpointDescription(XmlMergerComponentConstants.INPUT_ID_MAPPING_FILE,
XmlMergerComponentConstants.INPUT_NAME_MAPPING_FILE, DataType.FileReference,
metaData);
}
private void removeMappingFileInput(WorkflowNode workflowNode) {
EndpointDescriptionsManager manager = workflowNode.getInputDescriptionsManager();
manager.removeDynamicEndpointDescription(XmlMergerComponentConstants.INPUT_NAME_MAPPING_FILE);
}
/**
* Implementation of {@link AbstractEditScriptRunnable}.
*
* @author Jan Flink
*/
private class EditScriptRunnable extends AbstractEditScriptRunnable {
private final WorkflowNode node;
EditScriptRunnable(WorkflowNode node) {
this.node = node;
}
public WorkflowNode getNode() {
return node;
}
@Override
protected void setScript(String script) {
setScriptProperties(node, script,
node.getConfigurationDescription().getConfigurationValue(XmlMergerComponentConstants.MAPPINGTYPE_CONFIGNAME));
}
@Override
protected String getScript() {
return node.getConfigurationDescription().getConfigurationValue(XmlMergerComponentConstants.XMLCONTENT_CONFIGNAME);
}
@Override
protected String getScriptName() {
String suffix;
if (node.getConfigurationDescription().getConfigurationValue(XmlMergerComponentConstants.MAPPINGTYPE_CONFIGNAME) != null
&& node.getConfigurationDescription().getConfigurationValue(XmlMergerComponentConstants.MAPPINGTYPE_CONFIGNAME)
.equals(XmlMergerComponentConstants.MAPPINGTYPE_CLASSIC)) {
suffix = ".xml";
} else {
suffix = ".xsl";
}
return "Mapping" + suffix;
}
}
/**
* If a mapping is edited in an editor, the workflow editor must get dirty when the mapping is saved. To do so, a command must be
* executed, but it must contain the correct node.
*
* TODO Let the {@link XmlMergerSection} extend the {@link AbstractScriptSection} to ensure quality and avoid duplicated code.
*
* @param node to execute the save command to.
* @param newValue of the mapping.
* @author Jan Flink
*/
private void setScriptProperties(WorkflowNode node, final String newScriptValue, final String newTypeValue) {
final String oldScriptValue = WorkflowNodeUtil.getConfigurationValue(node, XmlMergerComponentConstants.XMLCONTENT_CONFIGNAME);
final String oldTypeValue = WorkflowNodeUtil.getConfigurationValue(node, XmlMergerComponentConstants.MAPPINGTYPE_CONFIGNAME);
if ((oldScriptValue != null && !oldScriptValue.equals(newScriptValue))
|| (oldScriptValue == null && oldScriptValue != newScriptValue)) {
final WorkflowNodeCommand command =
new SetScriptPropertiesValueCommand(oldScriptValue, newScriptValue, oldTypeValue, newTypeValue);
execute(node, command);
}
}
/**
* Command for setting every property if a file is loaded.
*
* @author Sascha Zur
*/
protected static class SetScriptPropertiesValueCommand extends AbstractWorkflowNodeCommand {
private final String oldScriptValue;
private final String newScriptValue;
private String oldTypeValue;
private String newTypeValue;
public SetScriptPropertiesValueCommand(final String oldScriptValue, final String newScriptValue, final String oldTypeValue,
final String newTypeValue) {
this.oldScriptValue = oldScriptValue;
this.newScriptValue = newScriptValue;
this.oldTypeValue = oldTypeValue;
this.newTypeValue = newTypeValue;
}
@Override
public void execute2() {
ConfigurationDescription configDesc = getProperties().getConfigurationDescription();
configDesc.setConfigurationValue(XmlMergerComponentConstants.XMLCONTENT_CONFIGNAME, newScriptValue);
configDesc.setConfigurationValue(XmlMergerComponentConstants.MAPPINGTYPE_CONFIGNAME, newTypeValue);
}
@Override
public void undo2() {
ConfigurationDescription configDesc = getProperties().getConfigurationDescription();
configDesc.setConfigurationValue(XmlMergerComponentConstants.XMLCONTENT_CONFIGNAME, oldScriptValue);
configDesc.setConfigurationValue(XmlMergerComponentConstants.MAPPINGTYPE_CONFIGNAME, oldTypeValue);
}
}
}