/*=============================================================================#
# Copyright (c) 2015-2016 Stephan Wahlbrink (WalWare.de) 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:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.docmlet.base.ui.processing;
import static de.walware.docmlet.base.ui.processing.DocProcessingConfig.OUT_FILE_EXT_VAR_NAME;
import static de.walware.docmlet.base.ui.processing.DocProcessingConfig.STEP_OUTPUT_FILE_PATH_ATTR_KEY;
import static de.walware.docmlet.base.ui.processing.DocProcessingConfig.STEP_OUTPUT_FORMAT_ATTR_KEY;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.conversion.IConverter;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.variables.IStringVariable;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.databinding.viewers.IViewerObservableValue;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import de.walware.jcommons.collections.ImCollections;
import de.walware.jcommons.collections.ImList;
import de.walware.ecommons.databinding.core.observable.WritableEqualityValue;
import de.walware.ecommons.databinding.core.util.UpdateableErrorValidator;
import de.walware.ecommons.debug.core.variables.ObservableResourcePathVariable;
import de.walware.ecommons.debug.core.variables.ResourceVariableResolver;
import de.walware.ecommons.debug.core.variables.ResourceVariables;
import de.walware.ecommons.ui.util.DialogUtil;
import de.walware.ecommons.ui.util.LayoutUtil;
import de.walware.ecommons.ui.util.UIAccess;
import de.walware.ecommons.ui.workbench.ResourceInputComposite;
import de.walware.ecommons.variables.core.ObservableValueVariable;
import de.walware.ecommons.variables.core.VariableText2;
import de.walware.ecommons.variables.core.VariableUtils;
import de.walware.docmlet.base.internal.ui.processing.Messages;
import de.walware.docmlet.base.ui.processing.DocProcessingConfig.CustomExtFormat;
import de.walware.docmlet.base.ui.processing.DocProcessingConfig.Format;
public abstract class DocProcessingConfigIOStepTab extends DocProcessingConfigStepTab {
private static final String OUTPUT_PATH_DEFAULT_VALUE=
"${" + ResourceVariables.FILE_NAME_BASE_VAR_NAME + "}" + //$NON-NLS-1$ //$NON-NLS-2$
".${" + DocProcessingConfig.OUT_FILE_EXT_VAR_NAME + "}"; //$NON-NLS-1$ //$NON-NLS-2$
private final String outputFormatAttrName;
private final String outputFilePathAttrName;
private final IObservableValue inputFormatValue;
private final IObservableValue outputFormatValue;
private final IObservableValue outputFileExtValue;
private final IObservableValue outputFilePathValue;
private ImList<Format> availableOutputFormats;
private Format defaultOutputFormat;
private final IObservableValue resolvedInputFileValue;
private final IObservableValue resolvedOutputFileExtValue;
private final IObservableValue resolvedOutputFileValue;
private final Matcher validExtMatcher= DocProcessingConfig.VALID_EXT_PATTERN.matcher(""); //$NON-NLS-1$
private Label inputControl;
private ComboViewer outputViewer;
private Text outputExtControl;
private ResourceInputComposite outputPathControl;
private IObservableValue outputPathResourceValue;
public DocProcessingConfigIOStepTab(final DocProcessingConfigMainTab mainTab,
final String stepEnabledAttrName) {
super(mainTab, stepEnabledAttrName);
this.outputFormatAttrName= getAttrQualifier() + '/' + STEP_OUTPUT_FORMAT_ATTR_KEY;
this.outputFilePathAttrName= getAttrQualifier() + '/' + STEP_OUTPUT_FILE_PATH_ATTR_KEY;
final Realm realm= getRealm();
this.inputFormatValue= new WritableValue(realm, null, Format.class);
this.inputFormatValue.addValueChangeListener(this);
this.outputFormatValue= new WritableValue(realm, null, Format.class);
this.outputFormatValue.addValueChangeListener(this);
this.outputFileExtValue= new WritableValue(realm, null, String.class);
this.outputFileExtValue.addValueChangeListener(this);
this.outputFilePathValue= new WritableValue(realm, null, String.class);
this.resolvedInputFileValue= new WritableEqualityValue(realm, null, IFile.class);
this.resolvedInputFileValue.addValueChangeListener(this);
this.resolvedOutputFileExtValue= new WritableEqualityValue(realm, null, String.class);
this.resolvedOutputFileValue= new WritableEqualityValue(realm, null, IFile.class);
}
@Override
protected void initVariables(final Map<String, IStringVariable> variables) {
variables.putAll(getMainTab().getSourceFileVariables());
VariableUtils.add(variables, new ObservableResourcePathVariable( // required for updates
DocProcessingConfig.IN_FILE_PATH_VAR,
this.resolvedInputFileValue ));
VariableUtils.add(variables, INPUT_RESOURCE_VAR_DEFS,
new ResourceVariableResolver(new ResourceVariableResolver.IResolveContext() {
@Override
public IResource getResource() {
return (IResource) DocProcessingConfigIOStepTab.this.resolvedInputFileValue.getValue();
}
}, ResourceVariableResolver.EXISTS_NEVER ));
VariableUtils.add(variables, new ObservableValueVariable(
DocProcessingConfig.OUT_FILE_EXT_VAR,
this.resolvedOutputFileExtValue ));
VariableUtils.add(variables, new ObservableResourcePathVariable(
DocProcessingConfig.OUT_FILE_PATH_VAR,
this.resolvedOutputFileValue ));
}
protected void setInput(final Format format, final IFile file) {
if (format != null && format.equals(this.inputFormatValue.getValue())) {
this.resolvedInputFileValue.setValue(file);
}
else if (file == null || !file.equals(this.resolvedInputFileValue.getValue())) {
this.resolvedInputFileValue.setValue(null);
this.inputFormatValue.setValue(format);
this.resolvedInputFileValue.setValue(file);
}
else {
this.inputFormatValue.setValue(format);
}
}
protected void setAvailableOutputFormats(final List<Format> formats, final String defaultKey) {
if (formats == null) {
throw new NullPointerException("formats"); //$NON-NLS-1$
}
if (defaultKey == null) {
throw new NullPointerException("defaultKey"); //$NON-NLS-1$
}
this.availableOutputFormats= ImCollections.toList(formats);
this.defaultOutputFormat= DocProcessingConfig.getFormat(formats, null, defaultKey);
if (this.defaultOutputFormat == null) {
this.availableOutputFormats= null;
throw new IllegalArgumentException("defaultKey: default format not found"); //$NON-NLS-1$
}
if (this.outputViewer != null) {
this.outputViewer.setInput(formats);
}
}
protected Format getOutputFormat(final String key, final boolean fallbackDefault) {
return DocProcessingConfig.getFormat(this.availableOutputFormats,
(fallbackDefault) ? this.defaultOutputFormat : null,
key );
}
protected void setOutputFormat(final Format format) {
this.outputFormatValue.setValue(format);
}
public Format getInputFormat() {
return (Format) this.inputFormatValue.getValue();
}
public IFile getInputFile() {
return (IFile) this.resolvedInputFileValue.getValue();
}
public IObservableValue getInputFileValue() {
return this.resolvedInputFileValue;
}
public Format getOutputFormat() {
return (Format) this.outputFormatValue.getValue();
}
public IObservableValue getOutputFileExtValue() {
return this.resolvedOutputFileExtValue;
}
public IFile getOutputFile() {
return (IFile) this.resolvedOutputFileValue.getValue();
}
public IObservableValue getOutputFileValue() {
return this.resolvedOutputFileValue;
}
@Override
public String getInfo() {
final StringBuilder sb= getStringBuilder();
final Format inputFormat= getInputFormat();
final Format outputFormat= getOutputFormat();
sb.append((inputFormat != null) ? inputFormat.getInfoLabel() : "?"); //$NON-NLS-1$
sb.append("\u2002\u2192\u2002"); //$NON-NLS-1$
sb.append((outputFormat != null) ? outputFormat.getInfoLabel() : "?"); //$NON-NLS-1$
sb.append('\n');
final DocProcessingOperationSettings operation= getOperation();
sb.append((operation != null) ? operation.getInfo() : " "); //$NON-NLS-1$
return sb.toString();
}
@Override
public void handleValueChange(final ValueChangeEvent event) {
if (event.getObservable() == this.inputFormatValue) {
updateInputText();
resolveInputFile();
}
if (event.getObservable() == this.outputFormatValue) {
final Format format= (Format) event.diff.getNewValue();
this.outputExtControl.setEditable(format instanceof CustomExtFormat);
this.outputFileExtValue.setValue((format != null) ? format.getExt() : ""); //$NON-NLS-1$
resolveOutputFileExt();
}
if (event.getObservable() == this.outputFileExtValue) {
Format format= (Format) this.outputFormatValue.getValue();
if (format instanceof CustomExtFormat) {
final String ext= (String) event.diff.getNewValue();
if (!ext.equals(format.getExt())) {
format= new CustomExtFormat((CustomExtFormat) format, ext);
this.outputFormatValue.setValue(format);
}
}
return;
}
if (event.getObservable() == this.resolvedInputFileValue) {
resolveOutputFileExt();
return;
}
if (event.getObservable() == this.outputPathResourceValue) {
final IResource resource= (IResource) event.diff.getNewValue();
this.resolvedOutputFileValue.setValue((resource instanceof IFile) ? resource : null);
return;
}
super.handleValueChange(event);
}
protected void resolveInputFile() {
IFile file= null;
try {
final Format format= getInputFormat();
if (format != null) {
if (format.matches(DocProcessingConfig.SOURCE_FORMAT_KEY)) {
file= getMainTab().getSourceFile();
}
else {
file= getInputFile(format);
}
}
}
catch (final Exception e) {}
this.resolvedInputFileValue.setValue(file);
}
protected IFile getInputFile(final Format format) throws CoreException {
return null;
}
protected void resolveOutputFileExt() {
String ext= null;
try {
final Format format= getOutputFormat();
if (format != null) {
final IFile inputFile= getInputFile();
ext= format.getExt(getValidExt((inputFile != null) ? inputFile.getName() : null));
}
if (ext != null && !this.validExtMatcher.reset(ext).matches()) {
ext= null;
}
}
catch (final Exception e) {}
this.resolvedOutputFileExtValue.setValue(ext);
}
private String getValidExt(final String name) {
if (name != null) {
final int idx= name.lastIndexOf('.');
if (idx >= 0) {
final String ext= name.substring(idx + 1);
if (this.validExtMatcher.reset(ext).matches()) {
return ext;
}
}
}
return null;
}
@Override
protected void addControls(final Composite parent) {
{ final Composite group= createIOGroup(parent);
group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
}
{ final Composite group= createOperationGroup(parent);
group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
}
}
protected Composite createIOGroup(final Composite parent) {
final Group group= new Group(parent, SWT.NONE);
group.setLayout(LayoutUtil.createGroupGrid(4));
group.setText("IO"); //$NON-NLS-1$
{ final Label label= new Label(group, SWT.NONE);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
label.setText(Messages.StepTab_In_label);
}
{ final Label label= new Label(group, SWT.NONE);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
this.inputControl= label;
updateInputText();
}
{ final Label label= new Label(group, SWT.NONE);
label.setText(Messages.StepTab_Out_label);
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
}
{ { final Label label= new Label(group, SWT.NONE);
label.setText(Messages.StepTab_Out_Format_label + ':');
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
}
final Composite composite= new Composite(group, SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
composite.setLayout(LayoutUtil.createCompositeGrid(3));
{ final ComboViewer viewer= new ComboViewer(composite, SWT.DROP_DOWN | SWT.READ_ONLY | SWT.BORDER);
final Combo combo= viewer.getCombo();
final LabelProvider labelProvider= new LabelProvider();
viewer.setContentProvider(new ArrayContentProvider());
viewer.setLabelProvider(labelProvider);
viewer.setInput(this.availableOutputFormats);
final GridData gd= new GridData(SWT.FILL, SWT.CENTER, false, false);
gd.widthHint= LayoutUtil.hintWidth(combo, this.availableOutputFormats, labelProvider);
combo.setLayoutData(gd);
this.outputViewer= viewer;
}
{ final Label label= new Label(composite, SWT.NONE);
label.setText(Messages.StepTab_Out_FileExt_label + " (" + OUT_FILE_EXT_VAR_NAME + "):"); //$NON-NLS-1$ //$NON-NLS-2$
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
}
{ final Text text= new Text(composite, SWT.BORDER);
text.setEditable(false);
final GridData gd= new GridData(SWT.LEFT, SWT.CENTER, true, false);
gd.widthHint= LayoutUtil.hintWidth(text, 25);
text.setLayoutData(gd);
this.outputExtControl= text;
}
}
LayoutUtil.addGDDummy(group); // To
{ final Label label= new Label(group, SWT.NONE);
label.setText(Messages.StepTab_Out_FilePath_label + ':');
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
}
{ final ResourceInputComposite input= new ResourceInputComposite(group,
ResourceInputComposite.STYLE_TEXT,
ResourceInputComposite.MODE_FILE | ResourceInputComposite.MODE_WS_ONLY,
Messages.StepTab_Out_FilePath_label );
input.setShowInsertVariable(true, DialogUtil.DEFAULT_INTERACTIVE_RESOURCE_FILTERS, null);
input.getValidator().setOnExisting(IStatus.OK);
input.getValidator().setOnLateResolve(IStatus.OK);
input.getValidator().setRelative(getMainTab().getWorkingDirectoryPathVariable(), -1);
input.getValidator().setIgnoreRelative(true);
final Map<String, IStringVariable> variables= new HashMap<>();
variables.putAll(getStepVariables());
variables.remove(DocProcessingConfig.OUT_FILE_PATH_VAR_NAME);
input.getValidator().setVariableResolver(new VariableText2(variables));
input.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
this.outputPathControl= input;
this.outputPathResourceValue= input.getValidator().getWorkspaceResourceObservable();
this.outputPathResourceValue.addValueChangeListener(this);
}
{ final Label label= new Label(group, SWT.NONE);
label.setText("(relative to working directory)");
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
}
return group;
}
protected void updateInputText() {
final Format inputFormat= getInputFormat();
if (!UIAccess.isOkToUse(this.inputControl) || inputFormat == null) {
return;
}
this.inputControl.setText(inputFormat.getInfoLabel());
}
@Override
protected void addBindings(final DataBindingContext dbc) {
super.addBindings(dbc);
final IViewerObservableValue outputFormatObservable= ViewersObservables.observeSingleSelection(this.outputViewer);
dbc.bindValue(outputFormatObservable, this.outputFormatValue,
null,
new UpdateValueStrategy().setConverter(new IConverter() {
@Override
public Object getToType() {
return Format.class;
}
@Override
public Object getFromType() {
return Format.class;
}
@Override
public Object convert(final Object fromObject) {
if (fromObject != null) {
return DocProcessingConfig.getFormat(
DocProcessingConfigIOStepTab.this.availableOutputFormats,
((Format) fromObject).getKey() );
}
return null;
}
}));
dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(this.outputExtControl),
this.outputFileExtValue,
new UpdateValueStrategy(), //?
null );
dbc.bindValue(this.outputPathControl.getObservable(),
this.outputFilePathValue,
new UpdateValueStrategy().setAfterGetValidator(
new UpdateableErrorValidator(this.outputPathControl.getValidator()) ),
null );
}
@Override
public void setDefaults(final ILaunchConfigurationWorkingCopy configuration) {
super.setDefaults(configuration);
configuration.setAttribute(this.outputFormatAttrName, this.defaultOutputFormat.getKey());
configuration.setAttribute(this.outputFilePathAttrName, OUTPUT_PATH_DEFAULT_VALUE);
}
@Override
protected void doInitialize(final ILaunchConfiguration configuration) {
super.doInitialize(configuration);
{ String key= this.defaultOutputFormat.getKey();
try {
key= configuration.getAttribute(this.outputFormatAttrName, key);
}
catch (final CoreException e) {
logReadingError(e);
}
final Format format= getOutputFormat(key, true);
this.outputFormatValue.setValue(format);
}
{ String path= OUTPUT_PATH_DEFAULT_VALUE;
try {
path= configuration.getAttribute(this.outputFilePathAttrName, path);
}
catch (final CoreException e) {
logReadingError(e);
}
this.outputFilePathValue.setValue(path);
}
}
@Override
protected void doSave(final ILaunchConfigurationWorkingCopy configuration) {
super.doSave(configuration);
{ final Format format= getOutputFormat();
configuration.setAttribute(this.outputFormatAttrName, format.getKey());
}
{ final String path= (String) this.outputFilePathValue.getValue();
configuration.setAttribute(this.outputFilePathAttrName, path);
}
}
}