| /*=============================================================================# |
| # Copyright (c) 2015, 2019 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.docmlet.base.ui.processing; |
| |
| import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert; |
| import static org.eclipse.statet.jcommons.lang.ObjectUtils.nullable; |
| |
| import static org.eclipse.statet.docmlet.base.ui.processing.DocProcessingConfig.WD_LOC_VAR_NAME; |
| import static org.eclipse.statet.docmlet.base.ui.processing.DocProcessingConfig.WD_PATH_VAR_NAME; |
| import static org.eclipse.statet.docmlet.base.ui.processing.DocProcessingOperation.NO_SETTINGS; |
| |
| import java.util.ArrayList; |
| 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.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| 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 org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.collections.ImList; |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| |
| import org.eclipse.statet.ecommons.io.FileValidator; |
| import org.eclipse.statet.ecommons.resources.core.variables.ResourceVariableResolver; |
| import org.eclipse.statet.ecommons.resources.core.variables.ResourceVariables; |
| import org.eclipse.statet.ecommons.ui.util.UIAccess; |
| import org.eclipse.statet.ecommons.ui.workbench.workspace.ResourceVariableUtil; |
| import org.eclipse.statet.ecommons.variables.core.DynamicVariable; |
| import org.eclipse.statet.ecommons.variables.core.StaticVariable; |
| import org.eclipse.statet.ecommons.variables.core.VariableText2; |
| import org.eclipse.statet.ecommons.variables.core.VariableUtils; |
| |
| import org.eclipse.statet.docmlet.base.ui.DocmlBaseUI; |
| import org.eclipse.statet.docmlet.base.ui.processing.DocProcessingConfig.Format; |
| import org.eclipse.statet.docmlet.base.ui.processing.operations.CheckOutputOperation; |
| import org.eclipse.statet.docmlet.base.ui.processing.operations.OpenUsingEclipseOperation; |
| import org.eclipse.statet.internal.docmlet.base.ui.processing.Messages; |
| |
| |
| @NonNullByDefault |
| 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 @Nullable DocProcessingOperation operation; |
| |
| private final List<DocProcessingOperation> preOperations= new ArrayList<>(); |
| private final List<DocProcessingOperation> postOperations= new ArrayList<>(); |
| |
| private @Nullable 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() { |
| VariableText2 variableText= this.variableText; |
| if (variableText == null) { |
| final Map<String, IStringVariable> variables= new HashMap<>(); |
| variables.putAll(this.config.getVariables()); |
| variableText= new VariableText2(variables); |
| this.variableText= variableText; |
| } |
| |
| return 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) { |
| throw createMissingConfigAttr(formatAttrName); |
| } |
| final String filePath= configuration.getAttribute(fileAttrName, (String) null); |
| if (filePath == null) { |
| throw 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)); |
| |
| 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(nonNullAssert((IFile)validator.getWorkspaceResource())); |
| } |
| catch (final Exception e) { |
| throw new CoreException(new Status(IStatus.ERROR, DocmlBaseUI.BUNDLE_ID, |
| NLS.bind("Failed to initialize IO configuration for {0}.", |
| getLabel() ), |
| e )); |
| } |
| } |
| |
| protected void setInputFile(final IFile file) { |
| this.inputFile= nonNullAssert(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) { |
| this.outputFile= nonNullAssert(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; |
| } |
| DocProcessingOperation operation= null; |
| try { |
| operation= getToolConfig().createStepOperation(id); |
| if (operation == null) { |
| throw new UnsupportedOperationException("operationId= " + id); //$NON-NLS-1$ |
| } |
| m.worked(2); |
| |
| final Map<String, String> settings= nonNullAssert(configuration.getAttribute( |
| settingsAttrName, NO_SETTINGS )); |
| operation.init(this, settings, m.newChild(2)); |
| this.operation= operation; |
| } |
| catch (final Exception e) { |
| throw new CoreException(new Status(IStatus.ERROR, DocmlBaseUI.BUNDLE_ID, |
| NLS.bind(Messages.ProcessingConfig_InitOperation_error_Failed_message, |
| (operation != null) ? operation.getLabel() : "?", |
| getLabel() ), |
| e )); |
| } |
| } |
| |
| public @Nullable DocProcessingOperation getOperation() { |
| return this.operation; |
| } |
| |
| |
| public void initPre(final ILaunchConfiguration configuration, |
| final SubMonitor m) throws CoreException { |
| } |
| |
| protected void addPre(final DocProcessingOperation operation) { |
| 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) { |
| this.postOperations.add(operation); |
| } |
| |
| public List<DocProcessingOperation> getPost() { |
| return this.postOperations; |
| } |
| |
| protected boolean isOptionEnabled(final @Nullable String key) { |
| if (key == null) { |
| return false; |
| } |
| 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 { |
| m.setWorkRemaining(2); |
| |
| 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, NO_SETTINGS, |
| m.newChild(1, SubMonitor.SUPPRESS_BEGINTASK | SubMonitor.SUPPRESS_SETTASKNAME) ); |
| 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, NO_SETTINGS, |
| m.newChild(1, SubMonitor.SUPPRESS_BEGINTASK | SubMonitor.SUPPRESS_SETTASKNAME) ); |
| operation.setFailSeverity((getRun() == StepConfig.RUN_EXPLICITE) ? |
| IStatus.ERROR : IStatus.WARNING ); |
| addPost(operation); |
| } |
| |
| m.done(); |
| } |
| |
| } |
| |
| 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, DocmlBaseUI.BUNDLE_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, DocmlBaseUI.BUNDLE_ID, |
| status.getMessage() )); |
| } |
| |
| |
| private ImList<StepConfig> steps; |
| |
| private @Nullable 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 @Nullable StepConfig getStep(final String stepId) { |
| for (final StepConfig aStep : this.steps) { |
| if (aStep.getId() == stepId) { |
| return aStep; |
| } |
| } |
| return null; |
| } |
| |
| public Map<String, IStringVariable> getVariables() { |
| Map<String, IStringVariable> globalVariables= this.globalVariables; |
| if (globalVariables == null) { |
| globalVariables= new HashMap<>(); |
| this.globalVariables= globalVariables; |
| } |
| return 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); |
| |
| IPath explicitePath= null; |
| { final String path= configuration.getAttribute(DocProcessingUI.SOURCE_PATH_ATTR_NAME, |
| (String)null ); |
| if (path != null) { |
| explicitePath= Path.fromPortableString(path); |
| } |
| } |
| |
| if (explicitePath != null) { |
| validator.setExplicit(explicitePath); |
| } |
| else { |
| UIAccess.getDisplay().syncExec(() -> { |
| final ResourceVariableUtil util= new ResourceVariableUtil(); |
| util.getResource(); |
| DocProcessingToolConfig.this.sourceFileUtil= util; |
| }); |
| if (this.sourceFileUtil.getResource() == null) { |
| throw new CoreException(new Status(IStatus.ERROR, DocmlBaseUI.BUNDLE_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(nonNullAssert((IFile)validator.getWorkspaceResource())); |
| } |
| |
| protected void setSourceFile(final IFile file) { |
| this.sourceFile= file; |
| |
| ResourceVariableUtil sourceFileUtil= nullable(this.sourceFileUtil); |
| if (sourceFileUtil == null) { |
| sourceFileUtil= UIAccess.syncExecGet(() -> new ResourceVariableUtil(file)); |
| this.sourceFileUtil= nonNullAssert(sourceFileUtil); |
| } |
| |
| { 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(nonNullAssert( |
| (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 @Nullable 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 m) throws CoreException { |
| if (formatKey.startsWith(Format.EXT_TYPE + ":")) { |
| return formatKey.substring((Format.EXT_TYPE + ":").length()); |
| } |
| throw new UnsupportedOperationException("formatKey= " + formatKey); //$NON-NLS-1$ |
| } |
| |
| protected @Nullable String resolveExt(final StepConfig stepConfig, final Format format) { |
| return format.getExt(stepConfig.getInputFile().getFileExtension()); |
| } |
| |
| protected abstract @Nullable DocProcessingOperation createStepOperation(String id); |
| |
| } |