/*=============================================================================#
# 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.WD_LOC_VAR_NAME;
import static de.walware.docmlet.base.ui.processing.DocProcessingConfig.WD_PATH_VAR_NAME;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.variables.IDynamicVariable;
import org.eclipse.core.variables.IStringVariable;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.IWorkbenchPage;
import de.walware.jcommons.collections.ImCollections;
import de.walware.jcommons.collections.ImList;
import de.walware.ecommons.debug.core.variables.ResourceVariableResolver;
import de.walware.ecommons.debug.core.variables.ResourceVariables;
import de.walware.ecommons.io.FileValidator;
import de.walware.ecommons.ui.util.UIAccess;
import de.walware.ecommons.ui.workbench.ResourceVariableUtil;
import de.walware.ecommons.variables.core.DynamicVariable;
import de.walware.ecommons.variables.core.StaticVariable;
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.DocBaseUI;
import de.walware.docmlet.base.ui.processing.DocProcessingConfig.Format;
import de.walware.docmlet.base.ui.processing.operations.CheckOutputOperation;
import de.walware.docmlet.base.ui.processing.operations.OpenUsingEclipseOperation;
public abstract class DocProcessingToolConfig {
public static class StepConfig {
public static final byte RUN_NO= 0;
public static final byte RUN_DEFAULT= 1;
public static final byte RUN_EXPLICITE= 2;
private final DocProcessingToolConfig config;
private final String id;
private final String label;
private byte run;
private boolean isEnabled;
private IFile inputFile;
private ResourceVariableUtil inputFileUtil;
private IFile outputFile;
private DocProcessingOperation operation;
private List<DocProcessingOperation> preOperations;
private List<DocProcessingOperation> postOperations;
private VariableText2 variableText;
public StepConfig(final DocProcessingToolConfig config, final String id, final String label) {
this.config= config;
this.id= id;
this.label= label;
this.run= RUN_NO;
}
public final DocProcessingToolConfig getToolConfig() {
return this.config;
}
public final String getId() {
return this.id;
}
public String getLabel() {
return this.label;
}
public void initRun(final byte run, final ILaunchConfiguration configuration)
throws CoreException {
this.isEnabled= configuration.getAttribute(
getId() + '/' + DocProcessingConfig.STEP_ENABLED_ATTR_KEY, true);
if (run == StepConfig.RUN_DEFAULT) {
this.run= (this.isEnabled) ? StepConfig.RUN_DEFAULT : StepConfig.RUN_NO;
}
else {
this.run= run;
}
}
public final byte getRun() {
return this.run;
}
public final boolean isRun() {
return (this.run != 0);
}
public boolean isEnabled() {
return this.isEnabled;
}
protected boolean resolveOutputFile() {
return true;
}
public VariableText2 getVariableResolver() {
if (this.variableText == null) {
final Map<String, IStringVariable> variables= new HashMap<>();
variables.putAll(this.config.getVariables());
this.variableText= new VariableText2(variables);
}
return this.variableText;
}
public void initIOFiles(final IFile inputFile, final ILaunchConfiguration configuration,
final SubMonitor m) throws CoreException {
m.setWorkRemaining(1 + 3);
if (inputFile == null) {
throw new NullPointerException("inputFile"); //$NON-NLS-1$
}
setInputFile(inputFile);
if (!resolveOutputFile()) {
return;
}
m.worked(1);
if (m.isCanceled()) {
throw new CoreException(Status.CANCEL_STATUS);
}
final String formatAttrName= getId() + '/' + DocProcessingConfig.STEP_OUTPUT_FORMAT_ATTR_KEY;
final String fileAttrName= getId() + '/' + DocProcessingConfig.STEP_OUTPUT_FILE_PATH_ATTR_KEY;
final String formatKey= configuration.getAttribute(formatAttrName, (String) null);
if (formatKey == null) {
createMissingConfigAttr(formatAttrName);
}
final String filePath= configuration.getAttribute(fileAttrName, (String) null);
if (filePath == null) {
createMissingConfigAttr(fileAttrName);
}
m.worked(1);
if (m.isCanceled()) {
throw new CoreException(Status.CANCEL_STATUS);
}
String outputExt;
try {
outputExt= getToolConfig().getOutputExt(this, formatKey, m.newChild(1));
if (outputExt == null) {
throw new NullPointerException("outputExt"); //$NON-NLS-1$
}
final Map<String, IStringVariable> variables= getVariableResolver().getExtraVariables();
VariableUtils.add(variables, new StaticVariable(
DocProcessingConfig.OUT_FILE_EXT_VAR,
outputExt ));
m.worked(0);
if (m.isCanceled()) {
throw new CoreException(Status.CANCEL_STATUS);
}
final FileValidator validator= new FileValidator(false);
validator.setResourceLabel("output file");
validator.setRequireWorkspace(true, true);
validator.setOnDirectory(IStatus.ERROR);
validator.setRelative(getToolConfig().getVariables().get(WD_PATH_VAR_NAME), -1);
validator.setVariableResolver(getVariableResolver());
validator.setExplicit(filePath);
if (validator.getStatus().getSeverity() == IStatus.ERROR) {
throw createValidationFailed(validator);
}
setOutputFile((IFile) validator.getWorkspaceResource());
}
catch (final Exception e) {
throw new CoreException(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID,
NLS.bind("Failed to initialize IO configuration for {0}.",
getLabel() ),
e ));
}
}
protected void setInputFile(final IFile file) {
if (file == null) {
throw new NullPointerException("file"); //$NON-NLS-1$
}
this.inputFile= file;
this.inputFileUtil= new ResourceVariableUtil(
getToolConfig().getSourceFileVariableUtil(),
file );
{ final Map<String, IStringVariable> variables= getVariableResolver().getExtraVariables();
VariableUtils.add(variables, new StaticVariable(
DocProcessingConfig.IN_FILE_PATH_VAR,
file.getFullPath().toString() ));
VariableUtils.add(variables,
ResourceVariables.getSingleResourceVariables(),
new ResourceVariableResolver(this.inputFileUtil, ResourceVariableResolver.EXISTS_NEVER) );
}
}
protected void setOutputFile(final IFile file) {
if (file == null) {
throw new NullPointerException("file"); //$NON-NLS-1$
}
this.outputFile= file;
final Map<String, IStringVariable> variables= getVariableResolver().getExtraVariables();
VariableUtils.add(variables, new StaticVariable(
DocProcessingConfig.OUT_FILE_PATH_VAR,
file.getFullPath().toString() ));
}
public IFile getInputFile() {
return this.inputFile;
}
public ResourceVariableUtil getInputFileUtil() {
return this.inputFileUtil;
}
public IFile getOutputFile() {
return this.outputFile;
}
public void initOperation(final ILaunchConfiguration configuration,
final SubMonitor m) throws CoreException {
m.setWorkRemaining(2 + 2);
final String idAttrName= getId() + '/' + DocProcessingConfig.STEP_OPERATION_ID_ATTR_KEY;
final String settingsAttrName= getId() + '/' + DocProcessingConfig.STEP_OPERATION_SETTINGS_ATTR_KEY;
final String id= configuration.getAttribute(idAttrName, (String) null);
if (id == null) {
throw createMissingConfigAttr(idAttrName);
}
if (id.isEmpty()) {
return;
}
try {
this.operation= getToolConfig().createStepOperation(id);
if (this.operation == null) {
throw new UnsupportedOperationException("operationId= " + id); //$NON-NLS-1$
}
m.worked(2);
final Map<String, String> settings= configuration.getAttribute(settingsAttrName,
Collections.<String, String>emptyMap() );
this.operation.init(this, settings, m.newChild(2));
}
catch (final Exception e) {
throw new CoreException(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID,
NLS.bind(Messages.ProcessingConfig_InitOperation_error_Failed_message,
(this.operation != null) ? this.operation.getLabel() : "?",
getLabel() ),
e ));
}
}
public DocProcessingOperation getOperation() {
return this.operation;
}
public void initPre(final ILaunchConfiguration configuration,
final SubMonitor m) throws CoreException {
}
protected void addPre(final DocProcessingOperation operation) {
if (this.preOperations == null) {
this.preOperations= new ArrayList<>(4);
}
this.preOperations.add(operation);
}
public List<DocProcessingOperation> getPre() {
return this.preOperations;
}
public void initPost(final ILaunchConfiguration configuration,
final SubMonitor m) throws CoreException {
}
protected void addPost(final DocProcessingOperation operation) {
if (this.postOperations == null) {
this.postOperations= new ArrayList<>(4);
}
this.postOperations.add(operation);
}
public List<DocProcessingOperation> getPost() {
return this.postOperations;
}
protected boolean isOptionEnabled(final String key) {
switch (key) {
case "always": //$NON-NLS-1$
return isRun();
case "step_only": //$NON-NLS-1$
return (getRun() == RUN_EXPLICITE);
default:
return false;
}
}
@Override
public String toString() {
final StringBuilder sb= new StringBuilder("StepConfig"); //$NON-NLS-1$
sb.append(" {").append("id= ").append(getId()).append("}"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return sb.toString();
}
}
public static class ProcessingStepConfig extends StepConfig {
public ProcessingStepConfig(final DocProcessingToolConfig config, final String id,
final String label) {
super(config, id, label);
}
@Override
public void initPost(final ILaunchConfiguration configuration,
final SubMonitor m) throws CoreException {
if (isOptionEnabled(configuration.getAttribute(
getId() + '/' + DocProcessingConfig.STEP_POST_OPEN_OUTPUT_ENABLED_ATTR_KEY, "always" ))) { //$NON-NLS-1$
final CheckOutputOperation operation= new CheckOutputOperation();
operation.init(this, Collections.EMPTY_MAP, m);
addPost(operation);
}
m.setWorkRemaining(1);
if (isOptionEnabled(configuration.getAttribute(
getId() + '/' + DocProcessingConfig.STEP_POST_OPEN_OUTPUT_ENABLED_ATTR_KEY, "disabled" ))) { //$NON-NLS-1$
final OpenUsingEclipseOperation operation= new OpenUsingEclipseOperation(
getOutputFile() );
operation.init(this, Collections.EMPTY_MAP, m);
operation.setFailSeverity((getRun() == StepConfig.RUN_EXPLICITE) ?
IStatus.ERROR : IStatus.WARNING );
addPost(operation);
}
}
}
public static class PreviewStepConfig extends StepConfig {
public PreviewStepConfig(final DocProcessingToolConfig config) {
super(config, DocProcessingConfig.BASE_PREVIEW_ATTR_QUALIFIER, Messages.Preview_label);
}
@Override
protected boolean resolveOutputFile() {
return false;
}
}
protected static CoreException createMissingConfigAttr(final String attrName) {
return new CoreException(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID,
NLS.bind("Invalid configuration: configuration attribute ''{0}'' is missing.", attrName) ));
}
protected static CoreException createValidationFailed(final FileValidator validator) {
final IStatus status= validator.getStatus();
return new CoreException(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID,
status.getMessage() ));
}
private ImList<StepConfig> steps;
private Map<String, IStringVariable> globalVariables;
private IFile sourceFile;
private ResourceVariableUtil sourceFileUtil;
private IContainer workingDirectory;
public DocProcessingToolConfig() {
}
protected void setSteps(final StepConfig... steps) {
this.steps= ImCollections.newList(steps);
}
public ImList<StepConfig> getSteps() {
return this.steps;
}
public StepConfig getStep(final String stepId) {
for (final StepConfig aStep : this.steps) {
if (aStep.getId() == stepId) {
return aStep;
}
}
return null;
}
public Map<String, IStringVariable> getVariables() {
if (this.globalVariables == null) {
this.globalVariables= new HashMap<>();
}
return this.globalVariables;
}
public void initSourceFile(final ILaunchConfiguration configuration,
final SubMonitor m) throws CoreException {
final FileValidator validator= new FileValidator(true);
validator.setResourceLabel("source document");
validator.setRequireWorkspace(true, true);
validator.setOnDirectory(IStatus.ERROR);
final String path= configuration.getAttribute(DocProcessingUI.TARGET_PATH_ATTR_NAME,
(String) null );
if (path != null) {
validator.setExplicit(path);
}
else {
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
final ResourceVariableUtil util= new ResourceVariableUtil();
util.getResource();
DocProcessingToolConfig.this.sourceFileUtil= util;
}
});
if (this.sourceFileUtil.getResource() == null) {
throw new CoreException(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID,
"No resource for 'source document' selected in the active Workbench window." ));
}
validator.setExplicit(this.sourceFileUtil.getResource());
}
if (validator.getStatus().getSeverity() == IStatus.ERROR) {
throw createValidationFailed(validator);
}
setSourceFile((IFile) validator.getWorkspaceResource());
}
protected void setSourceFile(final IFile file) {
this.sourceFile= file;
if (this.sourceFileUtil == null) {
UIAccess.getDisplay().syncExec(new Runnable() {
@Override
public void run() {
final ResourceVariableUtil util= new ResourceVariableUtil(file);
DocProcessingToolConfig.this.sourceFileUtil= util;
}
});
}
{ final Map<String, IStringVariable> variables= getVariables();
VariableUtils.add(variables,
ResourceVariables.getSingleResourceVariables(),
new ResourceVariableResolver(this.sourceFileUtil) );
VariableUtils.add(variables, new StaticVariable(
DocProcessingConfig.SOURCE_FILE_PATH_VAR,
file.getFullPath().toString() ));
}
}
public IWorkbenchPage getWorkbenchPage() {
return this.sourceFileUtil.getWorkbenchPage();
}
public IFile getSourceFile() {
return this.sourceFile;
}
public ResourceVariableUtil getSourceFileVariableUtil() {
return this.sourceFileUtil;
}
public void initWorkingDirectory(final ILaunchConfiguration configuration,
final SubMonitor m) throws CoreException {
final String wdAttrName= DocProcessingConfig.WORKING_DIRECTORY_ATTR_NAME;
final FileValidator validator= new FileValidator(true);
validator.setResourceLabel("working directory");
validator.setRequireWorkspace(true, true);
validator.setOnFile(IStatus.ERROR);
validator.setVariableResolver(new VariableText2(getVariables()));
final String path= configuration.getAttribute(wdAttrName, (String) null);
if (path != null && !path.isEmpty()) {
validator.setExplicit(path);
}
else {
throw createMissingConfigAttr(wdAttrName);
}
if (validator.getStatus().getSeverity() == IStatus.ERROR) {
throw createValidationFailed(validator);
}
setWorkingDirectory((IContainer) validator.getWorkspaceResource());
}
protected void setWorkingDirectory(final IContainer directory) {
this.workingDirectory= directory;
final ResourceVariableResolver resolver= new ResourceVariableResolver() {
@Override
public String resolveValue(final IDynamicVariable variable, final String argument)
throws CoreException {
switch (variable.getName()) {
case WD_LOC_VAR_NAME:
return toLocValue(variable, getWorkingDirectory());
case WD_PATH_VAR_NAME:
return toPathValue(variable, getWorkingDirectory());
default:
throw new UnsupportedOperationException(variable.getName());
}
}
};
final Map<String, IStringVariable> variables= getVariables();
VariableUtils.add(variables, new DynamicVariable.ResolverVariable(
WD_LOC_VAR_NAME, null, false, resolver ));
VariableUtils.add(variables, new DynamicVariable.ResolverVariable(
WD_PATH_VAR_NAME, null, false, resolver ));
}
public IContainer getWorkingDirectory() {
return this.workingDirectory;
}
protected String getOutputExt(final StepConfig stepConfig, final String formatKey,
final SubMonitor subMonitor) throws CoreException {
if (formatKey.startsWith(Format.EXT_TYPE + ":")) {
return formatKey.substring((Format.EXT_TYPE + ":").length());
}
throw new UnsupportedOperationException("formatKey= " + formatKey); //$NON-NLS-1$
}
protected String resolveExt(final StepConfig stepConfig, final Format format) {
return format.getExt(stepConfig.getInputFile().getFileExtension());
}
protected abstract DocProcessingOperation createStepOperation(String id);
}