/*=============================================================================# # Copyright (c) 2008-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.DocProcessingUI.PREVIEW_OUTPUT_STEP; import static de.walware.docmlet.base.ui.processing.DocProcessingUI.PRODUCE_OUTPUT_STEP; import static de.walware.docmlet.base.ui.processing.DocProcessingUI.WEAVE_STEP; import java.lang.reflect.InvocationTargetException; import java.util.Comparator; import java.util.IdentityHashMap; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationListener; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.viewers.DecorationOverlayIcon; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.statushandlers.StatusManager; import de.walware.jcommons.collections.CopyOnWriteIdentityListSet; import de.walware.jcommons.collections.IdentityCollection; import de.walware.jcommons.collections.IdentitySet; import de.walware.jcommons.collections.ImCollections; import de.walware.jcommons.collections.ImList; import de.walware.ecommons.ICommonStatusConstants; import de.walware.ecommons.IDisposable; import de.walware.ecommons.debug.core.util.OverlayLaunchConfiguration; import de.walware.ecommons.debug.ui.config.LaunchConfigUtils; import de.walware.ecommons.ui.SharedUIResources; import de.walware.ecommons.ui.util.DialogUtil; import de.walware.ecommons.ui.util.MessageUtil; import de.walware.ecommons.ui.util.UIAccess; import de.walware.docmlet.base.internal.ui.DocBaseUIPlugin; import de.walware.docmlet.base.internal.ui.processing.DocProcessingRegistry; import de.walware.docmlet.base.internal.ui.processing.Messages; import de.walware.docmlet.base.ui.DocBaseUI; import de.walware.docmlet.base.ui.DocBaseUIResources; /** * Manages configuration of a document processing type. */ public class DocProcessingManager extends DocProcessingRegistry.TypeElement implements ILaunchConfigurationListener, IDisposable { private static final Comparator<ILaunchConfiguration> CONFIG_COMPARATOR= new LaunchConfigUtils.LaunchConfigurationComparator(); private static final String ACTIVE_CONFIG_KEY= "activeConfig"; //$NON-NLS-1$ protected static final byte WEAVE_BIT= 0b0_00000010; protected static final byte PRODUCE_OUTPUT_BIT= 0b0_00001000; protected static final byte OPEN_OUTPUT_BIT= 0b0_00100000; public static interface IProcessingListener { public void activeConfigChanged(ILaunchConfiguration config); public void availableConfigChanged(ImList<ILaunchConfiguration> configs); } private String contentTypeId; private final CopyOnWriteIdentityListSet<IProcessingListener> listenerSet= new CopyOnWriteIdentityListSet<>(); private ILaunchConfigurationType configType; private ImList<ILaunchConfiguration> currentConfigs; private ILaunchConfiguration activeConfig; private String configImageKey; private String activeConfigImageKey; public DocProcessingManager() { } @Override protected void init(final String contentTypeId, final String configTypeId) { this.contentTypeId= contentTypeId; final ILaunchManager launchManager= DebugPlugin.getDefault().getLaunchManager(); this.configType= launchManager.getLaunchConfigurationType(configTypeId); if (this.configType == null) { throw new IllegalArgumentException("configTypeId= " + configTypeId); //$NON-NLS-1$ } launchManager.addLaunchConfigurationListener(this); final IDialogSettings settings= getDialogSettings(); final String s= settings.get(ACTIVE_CONFIG_KEY); if (s != null && !s.isEmpty()) { for (final ILaunchConfiguration config : getAvailableConfigs()) { if (s.equals(config.getName())) { setActiveConfig(config); break; } } } initImages(); } private void initImages() { this.configImageKey= this.configType.getIdentifier(); this.activeConfigImageKey= this.configImageKey + "_ActiveConfig"; //$NON-NLS-1$ final ImageRegistry imageRegistry= DocBaseUIPlugin.getInstance().getImageRegistry(); final Image image= DebugUITools.getImage(this.configType.getIdentifier()); if (imageRegistry.getDescriptor(this.configImageKey) == null) { imageRegistry.put(this.configImageKey, image); } if (imageRegistry.getDescriptor(this.activeConfigImageKey) == null) { final ImageDescriptor activeConfigImageDescriptor= new DecorationOverlayIcon(image, new ImageDescriptor[] { null, null, null, SharedUIResources.getImages().getDescriptor(SharedUIResources.OVR_DEFAULT_MARKER_IMAGE_ID), null }, new Point(image.getBounds().width, image.getBounds().height) ); imageRegistry.put(this.activeConfigImageKey, activeConfigImageDescriptor); } } public String getContentTypeId() { return this.contentTypeId; } protected IDialogSettings getDialogSettings() { return DialogUtil.getDialogSettings(DocBaseUIPlugin.getInstance(), this.contentTypeId); } @Override public void dispose() { final ILaunchManager launchManager= DebugPlugin.getDefault().getLaunchManager(); if (launchManager != null) { launchManager.removeLaunchConfigurationListener(this); } final IDialogSettings settings= getDialogSettings(); settings.put(ACTIVE_CONFIG_KEY, (this.activeConfig != null) ? this.activeConfig.getName() : null); } protected byte getBits(final IdentityCollection<String> flags) { byte bits; if (flags.contains(DocProcessingUI.PROCESSING_STEPS_FLAG)) { bits= WEAVE_BIT | PRODUCE_OUTPUT_BIT; } else { bits= 0; if (flags.contains(WEAVE_STEP)) { bits|= WEAVE_BIT; } if (flags.contains(PRODUCE_OUTPUT_STEP)) { bits|= PRODUCE_OUTPUT_BIT; } if (flags.contains(PREVIEW_OUTPUT_STEP)) { bits|= OPEN_OUTPUT_BIT; } } return bits; } @Override public void launchConfigurationAdded(final ILaunchConfiguration configuration) { try { if (configuration.getType() == this.configType) { if (DebugPlugin.getDefault().getLaunchManager().getMovedFrom(configuration) == this.activeConfig) { update(true, true, configuration); } else { update(true, false, null); } } } catch (final CoreException e) { } } @Override public void launchConfigurationChanged(final ILaunchConfiguration configuration) { try { if (configuration.getType() == this.configType && !configuration.isWorkingCopy()) { if (DebugPlugin.getDefault().getLaunchManager().getMovedFrom(configuration) == this.activeConfig) { update(true, true, configuration); } else { update(true, false, null); } } } catch (final CoreException e) { } } @Override public void launchConfigurationRemoved(final ILaunchConfiguration configuration) { try { // no possible to test for type (exception) if (configuration == this.activeConfig) { update(true, true, null); } else { final ImList<ILaunchConfiguration> configs= this.currentConfigs; if (configs != null) { for (final ILaunchConfiguration config : configs) { if (config == configuration) { update(true, false, null); break; } } } } } catch (final CoreException e) { } } private synchronized void update(final boolean updateList, boolean updateActive, final ILaunchConfiguration newActive) throws CoreException { if (updateActive && this.activeConfig == newActive) { updateActive= false; } final ImList<IProcessingListener> listeners= this.listenerSet.toList(); if (updateActive) { this.activeConfig= newActive; } if (updateList) { if (!listeners.isEmpty()) { final ImList<ILaunchConfiguration> configs= updateAvailableConfigs(); for (final IProcessingListener listener : listeners) { try { listener.availableConfigChanged(configs); } catch (final Exception e) { DocBaseUIPlugin.log(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID, 0, "An error occurred while notifiying a listener.", e )); } } } else { this.currentConfigs= null; } } if (updateActive) { for (final IProcessingListener listener : listeners) { try { listener.activeConfigChanged(newActive); } catch (final Exception e) { DocBaseUIPlugin.log(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID, 0, "An error occurred while notifiying a listener.", e )); } } } } private ImList<ILaunchConfiguration> updateAvailableConfigs() throws CoreException { return this.currentConfigs= ImCollections.newList( DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurations(this.configType), CONFIG_COMPARATOR ); } public void addProcessingListener(final IProcessingListener listener) { this.listenerSet.add(listener); } public void removeProcessingListener(final IProcessingListener listener) { this.listenerSet.remove(listener); } public ILaunchConfigurationType getConfigurationType() { return this.configType; } public ImList<ILaunchConfiguration> getAvailableConfigs() { ImList<ILaunchConfiguration> configs= this.currentConfigs; if (configs == null) { try { configs= updateAvailableConfigs(); } catch (final CoreException e) { StatusManager.getManager().handle(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID, -1, "Loading available configurations failed.", e)); //$NON-NLS-1$ configs= ImCollections.emptyList(); } } return configs; } public void setActiveConfig(final ILaunchConfiguration configuration) { try { update(false, true, configuration); } catch (final CoreException e) { StatusManager.getManager().handle(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID, -1, "Setting configuration as default failed.", e)); //$NON-NLS-1$ } } public ILaunchConfiguration getActiveConfig() { return this.activeConfig; } public void openConfigurationDialog(final Shell shell, IStructuredSelection selection) { if (selection == null || selection.isEmpty()) { selection= new StructuredSelection(this.configType); } DebugUITools.openLaunchConfigurationDialogOnGroup(shell, selection, "org.eclipse.ui.externaltools.launchGroup"); //$NON-NLS-1$ } public void launch(final ILaunchConfiguration configuration, final IFile target, final IdentitySet<String> flags) { final String label= getLabel(configuration, flags, true); UIAccess.getDisplay().syncExec(new Runnable() { @Override public void run() { final IWorkbenchPage page= UIAccess.getActiveWorkbenchPage(false); page.activate(page.getActiveEditor()); } }); final IRunnableWithProgress runnable= new IRunnableWithProgress() { @Override public void run(final IProgressMonitor monitor) throws InvocationTargetException { monitor.beginTask(label, 1); try { final ILaunchConfiguration config= new OverlayLaunchConfiguration(configuration, createRunAttributes(target, flags) ); final String mode= ILaunchManager.RUN_MODE; final byte bits= getBits(flags); if (bits == 0 || (bits & WEAVE_BIT) != 0) { config.launch(mode, new SubProgressMonitor(monitor, 1), true, false); } else { config.launch(mode, new SubProgressMonitor(monitor, 1), false, false); } } catch (final CoreException e) { throw new InvocationTargetException(e); } } }; try { PlatformUI.getWorkbench().getProgressService().busyCursorWhile(runnable); } catch (final InvocationTargetException e) { StatusManager.getManager().handle(new Status(IStatus.ERROR, DocBaseUI.PLUGIN_ID, ICommonStatusConstants.LAUNCHING, Messages.Processing_Launch_error_message + label, e.getTargetException()), StatusManager.LOG | StatusManager.SHOW); } catch (final InterruptedException e) { } } protected Map<String, Object> createRunAttributes(final IFile target, final IdentitySet<String> flags) { final Map<String, Object> map= new IdentityHashMap<>(4); map.put(DocProcessingUI.CONTENT_TYPE_ID_ATTR_NAME, getContentTypeId()); if (flags != null) { map.put(DocProcessingUI.RUN_STEPS_ATTR_NAME, flags); } if (target != null) { map.put(DocProcessingUI.TARGET_PATH_ATTR_NAME, target.getFullPath().toPortableString()); } return map; } public ImageDescriptor getImageDescriptor(final ILaunchConfiguration configuration) { return DocBaseUIPlugin.getInstance().getImageRegistry().getDescriptor( (this.activeConfig == configuration) ? this.activeConfigImageKey : this.configImageKey ); } public Image getImage(final ILaunchConfiguration configuration) { return DocBaseUIPlugin.getInstance().getImageRegistry().get( (this.activeConfig == configuration) ? this.activeConfigImageKey : this.configImageKey ); } public String getLabel(final ILaunchConfiguration configuration, final IdentityCollection<String> flags, final boolean noMnemonics) { String label= getActionLabel(flags); if (configuration != null) { label += " '" + configuration.getName() + "'"; //$NON-NLS-1$ //$NON-NLS-2$ } return (noMnemonics) ? MessageUtil.removeMnemonics(label) : label; } public Image getActionImage(final IdentityCollection<String> flags) { return getActionImage(getBits(flags)); } protected Image getActionImage(final byte bits) { switch (bits) { case 0: case WEAVE_BIT | PRODUCE_OUTPUT_BIT | OPEN_OUTPUT_BIT: return DocBaseUIResources.INSTANCE.getImage(DocBaseUIResources.TOOL_PROCESSANDPREVIEW_IMAGE_ID); case WEAVE_BIT | PRODUCE_OUTPUT_BIT: return DocBaseUIResources.INSTANCE.getImage(DocBaseUIResources.TOOL_PROCESS_IMAGE_ID); case PRODUCE_OUTPUT_BIT: return DocBaseUIResources.INSTANCE.getImage(DocBaseUIResources.TOOL_PREVIEW_IMAGE_ID); default: return null; } } public String getActionLabel(final IdentityCollection<String> flags) { return getActionLabel(getBits(flags)); } protected String getActionLabel(final byte bits) { switch (bits) { case 0: case WEAVE_BIT | PRODUCE_OUTPUT_BIT | OPEN_OUTPUT_BIT: return Messages.ProcessingAction_ProcessAndPreview_label; case WEAVE_BIT | PRODUCE_OUTPUT_BIT: return Messages.ProcessingAction_ProcessDoc_label; case WEAVE_BIT: return Messages.ProcessingAction_Weave_label; case PRODUCE_OUTPUT_BIT: return Messages.ProcessingAction_ProduceOutput_label; case OPEN_OUTPUT_BIT: return Messages.ProcessingAction_PreviewOutput_label; default: throw new IllegalArgumentException(); } } }