Copyright (c) 2000, 2018 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation Axel Richard (Obeo) - Bug 41353 - Launch configurations prototypes
/******************************************************************************* * Copyright (c) 2000, 2018 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Axel Richard (Obeo) - Bug 41353 - Launch configurations prototypes *******************************************************************************/
package org.eclipse.debug.internal.core; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import com.ibm.icu.text.MessageFormat;
A working copy launch configuration
/** * A working copy launch configuration */
public class LaunchConfigurationWorkingCopy extends LaunchConfiguration implements ILaunchConfigurationWorkingCopy {
Handle of original launch configuration this working copy is based on
/** * Handle of original launch configuration this * working copy is based on */
private LaunchConfiguration fOriginal;
Handle to a parent working copy
Since:3.3
/** * Handle to a parent working copy * @since 3.3 */
private LaunchConfigurationWorkingCopy fParent = null;
Working copy of attributes.
/** * Working copy of attributes. */
private LaunchConfigurationInfo fInfo;
Whether this working copy has been modified since it was created
/** * Whether this working copy has been modified since * it was created */
private boolean fDirty;
Indicates whether this working copy has been explicitly renamed.
/** * Indicates whether this working copy has been explicitly renamed. */
private boolean fRenamed;
Suppress change notification until created
/** * Suppress change notification until created */
private boolean fSuppressChange ;
Constructs a working copy of the specified launch configuration.
Params:
  • original – launch configuration to make a working copy of
Throws:
  • CoreException – if unable to initialize this working copy's attributes based on the original configuration
/** * Constructs a working copy of the specified launch * configuration. * * @param original launch configuration to make * a working copy of * @exception CoreException if unable to initialize this * working copy's attributes based on the original configuration */
protected LaunchConfigurationWorkingCopy(LaunchConfiguration original) throws CoreException { super(original.getName(), original.getContainer(), original.isPrototype()); copyFrom(original); setOriginal(original); fSuppressChange = false; } @Override protected void initialize() { fDirty = false; fRenamed = false; fSuppressChange = true; super.initialize(); }
Constructs a working copy of the specified launch configuration as its parent.
Params:
  • parent – launch configuration to make a working copy of
Throws:
  • CoreException – if unable to initialize this working copy's attributes based on the original configuration
/** * Constructs a working copy of the specified launch configuration as its parent. * * @param parent launch configuration to make * a working copy of * @exception CoreException if unable to initialize this * working copy's attributes based on the original configuration */
protected LaunchConfigurationWorkingCopy(LaunchConfigurationWorkingCopy parent) throws CoreException { super(parent.getName(), parent.getContainer(), parent.isPrototype()); copyFrom(parent); setOriginal((LaunchConfiguration) parent.getOriginal()); fParent = parent; fSuppressChange = false; }
Constructs a copy of the specified launch configuration, with the given (new) name.
Params:
  • original – launch configuration to make a working copy of
  • name – the new name for the copy of the launch configuration
Throws:
  • CoreException – if unable to initialize this working copy's attributes based on the original configuration
/** * Constructs a copy of the specified launch * configuration, with the given (new) name. * * @param original launch configuration to make * a working copy of * @param name the new name for the copy of the launch * configuration * @exception CoreException if unable to initialize this * working copy's attributes based on the original configuration */
protected LaunchConfigurationWorkingCopy(LaunchConfiguration original, String name) throws CoreException { super(name, original.getContainer(), original.isPrototype()); copyFrom(original); fSuppressChange = false; }
Constructs a new working copy to be created in the specified location.
Params:
  • container – the container that the configuration will be created in or null if to be local
  • name – the name of the new launch configuration
  • type – the type of this working copy
/** * Constructs a new working copy to be created in the specified * location. * * @param container the container that the configuration will be created in * or <code>null</code> if to be local * @param name the name of the new launch configuration * @param type the type of this working copy */
protected LaunchConfigurationWorkingCopy(IContainer container, String name, ILaunchConfigurationType type) { this(container, name, type, false); }
Constructs a new working copy to be created in the specified location.
Params:
  • container – the container that the configuration will be created in or null if to be local
  • name – the name of the new launch configuration
  • type – the type of this working copy
  • prototype – if this copy is a prototype or not
Since:3.12
/** * Constructs a new working copy to be created in the specified * location. * * @param container the container that the configuration will be created in * or <code>null</code> if to be local * @param name the name of the new launch configuration * @param type the type of this working copy * @param prototype if this copy is a prototype or not * * @since 3.12 */
protected LaunchConfigurationWorkingCopy(IContainer container, String name, ILaunchConfigurationType type, boolean prototype) { super(name, container, prototype); setInfo(new LaunchConfigurationInfo()); getInfo().setType(type); getInfo().setIsPrototype(prototype); fSuppressChange = false; } @Override public boolean isDirty() { return fDirty; } @Override public synchronized ILaunchConfiguration doSave() throws CoreException { return doSave(new NullProgressMonitor()); }
Saves with progress.
Params:
Throws:
Returns:the saved ILaunchConfiguration
Since:3.3
/** * Saves with progress. * * @param monitor the {@link IProgressMonitor} * @return the saved <code>ILaunchConfiguration</code> * @throws CoreException if a problem is encountered * * @since 3.3 */
public synchronized ILaunchConfiguration doSave(IProgressMonitor monitor) throws CoreException { SubMonitor lmonitor = SubMonitor.convert(monitor, 1); try { if (getParent() != null) { // save to parent working copy LaunchConfigurationWorkingCopy wc = (LaunchConfigurationWorkingCopy) getParent(); if(isMoved()) { wc.rename(getName()); wc.setContainer(getContainer()); } wc.setAttributes(getInfo().getAttributes()); updateMonitor(lmonitor, 1); return wc; } else { boolean useRunnable= true; if (isLocal()) { if (isMoved()) { // If this config was moved from a shared location, saving // it will delete the original from the workspace. Use runnable. useRunnable= !isNew() && !getOriginal().isLocal(); } else { useRunnable= false; } } if (useRunnable) { IWorkspaceRunnable wr = this::doSave0; ResourcesPlugin.getWorkspace().run(wr, null, 0, lmonitor.newChild(1)); } else { //file is persisted in the metadata not the workspace doSave0(lmonitor.newChild(1)); } getLaunchManager().setMovedFromTo(null, null); } } finally { if(lmonitor != null) { lmonitor.done(); } } return new LaunchConfiguration(getName(), getContainer(), isPrototype()); }
Performs the actual saving of the launch configuration.
Params:
Throws:
/** * Performs the actual saving of the launch configuration. * @param monitor the {@link IProgressMonitor} * @throws CoreException if a problem is encountered */
private void doSave0(IProgressMonitor monitor) throws CoreException { SubMonitor lmonitor = SubMonitor.convert(monitor, MessageFormat.format(DebugCoreMessages.LaunchConfigurationWorkingCopy_0, new Object[] { getName() }), 2); try { // set up from/to information if this is a move boolean moved = (!isNew() && isMoved()); if (moved) { ILaunchConfiguration to = new LaunchConfiguration(getName(), getContainer(), isPrototype()); ILaunchConfiguration from = getOriginal(); getLaunchManager().setMovedFromTo(from, to); } ILaunchConfiguration orig = getOriginal(); updateMonitor(lmonitor, 1); writeNewFile(lmonitor.newChild(1)); // delete the old file if this is not a new configuration // or the file was renamed/moved if (moved) { orig.delete(); } fDirty = false; } finally { if(lmonitor != null) { lmonitor.done(); } } }
Writes the new configuration information to a file.
Params:
Throws:
/** * Writes the new configuration information to a file. * @param monitor the {@link IProgressMonitor} * * @exception CoreException if writing the file fails */
protected void writeNewFile(IProgressMonitor monitor) throws CoreException { String xml = null; try { xml = getInfo().getAsXML(); } catch (Exception e) { throw new DebugException( new Status( IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, MessageFormat.format(DebugCoreMessages.LaunchConfigurationWorkingCopy__0__occurred_generating_launch_configuration_XML__1, new Object[] { e.toString() }), null ) ); } SubMonitor lmonitor = SubMonitor.convert(monitor, IInternalDebugCoreConstants.EMPTY_STRING, 5); try { boolean added = false; if (isLocal()) { // use java.io to update configuration file try { lmonitor.subTask(DebugCoreMessages.LaunchConfigurationWorkingCopy_1); IFileStore file = getFileStore(); if (file == null) { throw new DebugException( new Status( IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, DebugCoreMessages.LaunchConfigurationWorkingCopy_4, null ) ); } IFileStore dir = file.getParent(); dir.mkdir(EFS.SHALLOW, null); if (!file.fetchInfo().exists()) { added = true; updateMonitor(lmonitor, 1); } try (BufferedOutputStream stream = new BufferedOutputStream(file.openOutputStream(EFS.NONE, null))) { stream.write(xml.getBytes(StandardCharsets.UTF_8)); } //notify file saved updateMonitor(lmonitor, 1); } catch (IOException ie) { lmonitor.done(); throw new DebugException( new Status( IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, MessageFormat.format(DebugCoreMessages.LaunchConfigurationWorkingCopy__0__occurred_generating_launch_configuration_XML__1, new Object[] { ie.toString() }), null ) ); } } else { // use resource API to update configuration file IFile file = getFile(); if (file == null) { lmonitor.done(); throw new DebugException( new Status( IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, DebugCoreMessages.LaunchConfigurationWorkingCopy_5, null )); } IContainer dir = file.getParent(); if (!dir.exists()) { lmonitor.done(); throw new DebugException( new Status( IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, DebugCoreMessages.LaunchConfigurationWorkingCopy_Specified_container_for_launch_configuration_does_not_exist_2, null ) ); } ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8)); SubMonitor smonitor = null; if (!file.exists()) { added = true; //create file input stream: work one unit in a sub monitor smonitor = lmonitor.newChild(1); smonitor.setTaskName(MessageFormat.format(DebugCoreMessages.LaunchConfigurationWorkingCopy_2, new Object[] { getName() })); file.create(stream, false, smonitor); } else { // validate edit if (file.isReadOnly()) { IStatus status = ResourcesPlugin.getWorkspace().validateEdit(new IFile[] {file}, null); if (!status.isOK()) { lmonitor.done(); throw new CoreException(status); } } //set the contents of the file: work 1 unit in a sub monitor smonitor = lmonitor.newChild(1); smonitor.setTaskName(MessageFormat.format(DebugCoreMessages.LaunchConfigurationWorkingCopy_3, new Object[] { getName() })); file.setContents(stream, true, false, smonitor); } } // notify of add/change for both local and shared configurations - see bug 288368 if (added) { getLaunchManager().launchConfigurationAdded(new LaunchConfiguration(getName(), getContainer(), isPrototype())); } else { getLaunchManager().launchConfigurationChanged(new LaunchConfiguration(getName(), getContainer(), isPrototype())); } } finally { if(lmonitor != null) { lmonitor.done(); } } }
Updates the given monitor with the given tick count and polls for cancellation. If the monitor is cancelled an OperationCanceledException is thrown
Params:
  • monitor – the IProgressMonitor
  • ticks – the amount of work to advance the monitor
Throws:
/** * Updates the given monitor with the given tick count and polls for cancellation. If the monitor * is cancelled an {@link OperationCanceledException} is thrown * @param monitor the {@link IProgressMonitor} * @param ticks the amount of work to advance the monitor * @throws OperationCanceledException if the user cancels the operation */
private void updateMonitor(IProgressMonitor monitor, int ticks) throws OperationCanceledException { if(monitor != null) { monitor.worked(ticks); if (monitor.isCanceled()) { throw new OperationCanceledException(); } } } @Override public void setAttribute(String attributeName, int value) { getInfo().setAttribute(attributeName, Integer.valueOf(value)); setDirty(); } @Override public void setAttribute(String attributeName, String value) { getInfo().setAttribute(attributeName, value); setDirty(); } @Override public void setAttribute(String attributeName, boolean value) { getInfo().setAttribute(attributeName, Boolean.valueOf(value)); setDirty(); } @Override public void setAttribute(String attributeName, List<String> value) { getInfo().setAttribute(attributeName, value); setDirty(); } @Override public void setAttribute(String attributeName, Map<String, String> value) { getInfo().setAttribute(attributeName, value); setDirty(); } @Override public void setAttribute(String attributeName, Set<String> value) { getInfo().setAttribute(attributeName, value); setDirty(); } @Override public void setAttribute(String attributeName, Object value) { getInfo().setAttribute(attributeName, value); setDirty(); } @Override public ILaunchConfiguration getOriginal() { ILaunchConfiguration config = fOriginal; ILaunchConfigurationWorkingCopy parent = fParent; while(parent != null) { config = parent.getOriginal(); parent = parent.getParent(); } return config; } @Override public ILaunchConfigurationWorkingCopy getParent() { return fParent; }
Sets the launch configuration this working copy is based on. Initializes the attributes of this working copy to the current values of the given configuration.
Params:
  • original – the launch configuration this working copy is based on.
Throws:
  • CoreException – if unable to initialize this working copy based on the original's current attribute set
/** * Sets the launch configuration this working copy * is based on. Initializes the attributes of this * working copy to the current values of the given * configuration. * * @param original the launch configuration this working * copy is based on. * @exception CoreException if unable to initialize this * working copy based on the original's current attribute * set */
private void copyFrom(LaunchConfiguration original) throws CoreException { LaunchConfigurationInfo info = original.getInfo(); setInfo(info.getCopy()); fDirty = false; }
Sets the launch configuration this working copy is based on.
Params:
  • original – the launch configuration this working copy is based on.
/** * Sets the launch configuration this working copy * is based on. * * @param original the launch configuration this working * copy is based on. */
private void setOriginal(LaunchConfiguration original) { fOriginal = original; }
Sets the working copy info object for this working copy.
Params:
  • info – a copy of attributes from this working copy's original launch configuration
/** * Sets the working copy info object for this working copy. * * @param info a copy of attributes from this working copy's * original launch configuration */
protected void setInfo(LaunchConfigurationInfo info) { fInfo = info; } @Override public boolean isWorkingCopy() { return true; }
A working copy keeps a local info object that is not cached with the launch manager.
See Also:
  • getInfo.getInfo()
/** * A working copy keeps a local info object that is not * cached with the launch manager. * * @see LaunchConfiguration#getInfo() */
@Override protected LaunchConfigurationInfo getInfo() { return fInfo; }
Sets this working copy's state to dirty. Notifies listeners that this working copy has changed.
/** * Sets this working copy's state to dirty. * Notifies listeners that this working copy has * changed. */
private void setDirty() { fDirty = true; if (!suppressChangeNotification()) { getLaunchManager().getConfigurationNotifier().notify(this, LaunchManager.CHANGED); } } @Override public void setModes(Set<String> modes) { getInfo().setAttribute(ATTR_LAUNCH_MODES, (modes.size() > 0 ? modes : null)); setDirty(); }
See Also:
  • addModes.addModes(Set)
/** * @see org.eclipse.debug.core.ILaunchConfigurationWorkingCopy#addModes(java.util.Set) */
@Override public void addModes(Set<String> modes) { try { Set<String> opts = getModes(); if(opts.addAll(modes)) { getInfo().setAttribute(ATTR_LAUNCH_MODES, opts); setDirty(); } } catch (CoreException e) { DebugPlugin.log(e); } }
See Also:
  • removeModes.removeModes(Set)
/** * @see org.eclipse.debug.core.ILaunchConfigurationWorkingCopy#removeModes(java.util.Set) */
@Override public void removeModes(Set<String> options) { try { Set<String> opts = getModes(); if(opts.removeAll(options)) { getInfo().setAttribute(ATTR_LAUNCH_MODES, (opts.size() < 1 ? null : opts)); setDirty(); } } catch (CoreException e) { DebugPlugin.log(e); } }
See Also:
  • rename.rename(String)
/** * @see ILaunchConfigurationWorkingCopy#rename(String) */
@Override public void rename(String name) { if (!getName().equals(name)) { setName(name); fRenamed = isNew() || !(getOriginal().getName().equals(name)); } }
Sets the new name for this configuration.
Params:
  • name – the new name for this configuration
/** * Sets the new name for this configuration. * * @param name the new name for this configuration */
@Override protected void setName(String name) { super.setName(name); setDirty(); }
Returns whether this working copy is new, or is a working copy of another launch configuration.
Returns:whether this working copy is new, or is a working copy of another launch configuration
/** * Returns whether this working copy is new, or is a * working copy of another launch configuration. * * @return whether this working copy is new, or is a * working copy of another launch configuration */
protected boolean isNew() { return getOriginal() == null; }
Returns whether this working copy is new or if its location has changed from that of its original.
Returns:whether this working copy is new or if its location has changed from that of its original
/** * Returns whether this working copy is new or if its * location has changed from that of its original. * * @return whether this working copy is new or if its * location has changed from that of its original */
protected boolean isMoved() { if (isNew() || fRenamed) { return true; } IContainer newContainer = getContainer(); IContainer originalContainer = ((LaunchConfiguration)getOriginal()).getContainer(); if (newContainer == originalContainer) { return false; } if (newContainer == null) { return !originalContainer.equals(newContainer); } return !newContainer.equals(originalContainer); }
A working copy cannot generate a memento.
See Also:
  • getMemento.getMemento()
/** * A working copy cannot generate a memento. * * @see ILaunchConfiguration#getMemento() */
@Override public String getMemento() { return null; }
Returns whether change notification should be suppressed
Returns:if changes notification should be suppressed
/** * Returns whether change notification should be * suppressed * @return if changes notification should be suppressed */
protected boolean suppressChangeNotification() { return fSuppressChange; } @Override public void setContainer(IContainer container) { if (equalOrNull(getContainer(), container)) { return; } super.setContainer(container); setDirty(); } @Override public void setAttributes(Map<String, ? extends Object> attributes) { getInfo().setAttributes(attributes); setDirty(); } @Override public void setMappedResources(IResource[] resources) { ArrayList<String> paths = null; ArrayList<String> types = null; if(resources != null && resources.length > 0) { paths = new ArrayList<>(resources.length); types = new ArrayList<>(resources.length); for (IResource resource : resources) { if(resource != null) { paths.add(resource.getFullPath().toPortableString()); types.add(Integer.valueOf(resource.getType()).toString()); } } } setAttribute(LaunchConfiguration.ATTR_MAPPED_RESOURCE_PATHS, paths); setAttribute(LaunchConfiguration.ATTR_MAPPED_RESOURCE_TYPES, types); } @Override public void setPreferredLaunchDelegate(Set<String> modes, String delegateId) { if(modes != null) { try { Map<String, String> delegates = getAttribute(LaunchConfiguration.ATTR_PREFERRED_LAUNCHERS, (Map<String, String>) null); //copy map to avoid pointer issues Map<String, String> map = new HashMap<>(); if (delegates != null) { map.putAll(delegates); } if (delegateId == null) { map.remove(modes.toString()); } else { map.put(modes.toString(), delegateId); } setAttribute(LaunchConfiguration.ATTR_PREFERRED_LAUNCHERS, map); } catch (CoreException ce) {DebugPlugin.log(ce);} } } @Override public ILaunchConfigurationWorkingCopy getWorkingCopy() throws CoreException { return new LaunchConfigurationWorkingCopy(this); } @Override public Object removeAttribute(String attributeName) { return getInfo().removeAttribute(attributeName); } @Override public void copyAttributes(ILaunchConfiguration prototype) throws CoreException { Map<String, Object> map = prototype.getAttributes(); LaunchConfigurationInfo info = getInfo(); info.setPrototype(prototype); Set<String> prototypeVisibleAttributes = prototype.getPrototypeVisibleAttributes(); if (prototypeVisibleAttributes != null) { prototypeVisibleAttributes.forEach(key -> { Object value = map.get(key); if (value != null) { info.setAttribute(key, value); } }); } } @Override public void setPrototype(ILaunchConfiguration prototype, boolean copy) throws CoreException { if (prototype != null && !prototype.isPrototype()) { throw new CoreException(new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugCoreMessages.LaunchConfigurationWorkingCopy_6)); } if (prototype != null && prototype.isWorkingCopy()) { throw new CoreException(new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugCoreMessages.LaunchConfigurationWorkingCopy_7)); } if (prototype == null) { getInfo().setPrototype(null); removeAttribute(ATTR_PROTOTYPE); } else { if (isPrototype()) { throw new CoreException(new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugCoreMessages.LaunchConfigurationWorkingCopy_8)); } getInfo().setPrototype(prototype); if (copy) { copyAttributes(prototype); } setAttribute(ATTR_PROTOTYPE, prototype.getMemento()); setAttribute(IS_PROTOTYPE, false); } } @Override public ILaunchConfiguration doSave(int flag) throws CoreException { Collection<ILaunchConfiguration> children = null; if (UPDATE_PROTOTYPE_CHILDREN == flag) { if (!isNew() && isMoved() && getParent() == null) { children = getOriginal().getPrototypeChildren(); } } ILaunchConfiguration saved = doSave(); if (children != null) { for (ILaunchConfiguration child : children) { ILaunchConfigurationWorkingCopy wc = child.getWorkingCopy(); wc.setPrototype(saved, false); wc.doSave(); } } return saved; } @Override public void setPrototypeAttributeVisibility(String attribute, boolean visible) throws CoreException { super.setPrototypeAttributeVisibility(attribute, visible); setDirty(); } }