/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation 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: * IBM Corporation - initial API and implementation * Sebastian Davids - bug 50567 Eclipse native environment support on Win98 * Pawel Piech - Bug 82001: When shutting down the IDE, the debugger should first * attempt to disconnect debug targets before terminating them * Alena Laskavaia - Bug 259281 * Marc Khouzam - Bug 313143: Preferred Launch Delegate not recovered from preferences *******************************************************************************/ package org.eclipse.debug.internal.core; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; 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.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IResourceProxy; import org.eclipse.core.resources.IResourceProxyVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationListener; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchDelegate; import org.eclipse.debug.core.ILaunchListener; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.ILaunchMode; import org.eclipse.debug.core.ILaunchesListener; import org.eclipse.debug.core.ILaunchesListener2; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IDisconnect; import org.eclipse.debug.core.model.IPersistableSourceLocator; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector; import org.eclipse.debug.core.sourcelookup.ISourceContainerType; import org.eclipse.debug.core.sourcelookup.ISourcePathComputer; import org.eclipse.debug.internal.core.sourcelookup.SourceContainerType; import org.eclipse.debug.internal.core.sourcelookup.SourcePathComputer; import org.eclipse.osgi.service.environment.Constants; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.ibm.icu.text.MessageFormat; /** * Manages launch configurations, launch configuration types, and registered launches. * * @see ILaunchManager */ public class LaunchManager extends PlatformObject implements ILaunchManager, IResourceChangeListener { /** * Preferred launch delegate preference name. * <p> * Prior to 3.5 this preferred launch delegates for all launch * configuration types were serialized into a single XML string * and stored in this preference. * </p> * <p> * Since 3.5, the preferred launch delegates are stored in a separate * preference for each launch configuration type. The name of this * preference is composed of the prefix, followed by a slash, followed by * the launch configuration type id. The values contain a set of launch * delegates, delimited by a semicolon, and each delegate entry contains * the delegate ID, followed by a comma, followed by comma-delimited * launch modes. * * @since 3.3 */ protected static final String PREF_PREFERRED_DELEGATES = DebugPlugin.getUniqueIdentifier() + ".PREFERRED_DELEGATES"; //$NON-NLS-1$ /** * Constant to define debug.ui for the status codes * * @since 3.2 */ private static final String DEBUG_UI = "org.eclipse.debug.ui"; //$NON-NLS-1$ /** * Listing of unsupported launch configuration names for the Win 32 platform * @since 3.5 */ static final String[] UNSUPPORTED_WIN32_CONFIG_NAMES = new String[] {"aux", "clock$", "com1", "com2", "com3", "com4", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ "com5", "com6", "com7", "com8", "com9", "con", "lpt1", "lpt2", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", "nul", "prn"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ /** * Disallowed characters for launch configuration names * '@' and '&' are disallowed because they corrupt menu items. * * @since 3.5 */ static final char[] DISALLOWED_CONFIG_NAME_CHARS = new char[] { '@', '&','\\', '/', ':', '*', '?', '"', '<', '>', '|', '\0' }; /** * Status code for which a UI prompter is registered. * * @since 3.2 */ protected static final IStatus promptStatus = new Status(IStatus.INFO, DEBUG_UI, 200, IInternalDebugCoreConstants.EMPTY_STRING, null); /** * Step filter manager */ private StepFilterManager fStepFilterManager = null; /** * Notifies a launch config listener in a safe runnable to handle * exceptions. */ class ConfigurationNotifier implements ISafeRunnable { private ILaunchConfigurationListener fListener; private int fType; private ILaunchConfiguration fConfiguration; /** * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) */ @Override public void handleException(Throwable exception) { IStatus status = new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugPlugin.INTERNAL_ERROR, "An exception occurred during launch configuration change notification.", exception); //$NON-NLS-1$ DebugPlugin.log(status); } /** * Notifies the given listener of the add/change/remove * * @param configuration the configuration that has changed * @param update the type of change */ public void notify(ILaunchConfiguration configuration, int update) { fConfiguration = configuration; fType = update; for (ILaunchConfigurationListener iLaunchConfigurationListener : fLaunchConfigurationListeners) { fListener = iLaunchConfigurationListener; SafeRunner.run(this); } fConfiguration = null; fListener = null; } /** * @see org.eclipse.core.runtime.ISafeRunnable#run() */ @Override public void run() throws Exception { switch (fType) { case ADDED: fListener.launchConfigurationAdded(fConfiguration); break; case REMOVED: fListener.launchConfigurationRemoved(fConfiguration); break; case CHANGED: fListener.launchConfigurationChanged(fConfiguration); break; default: break; } } } /** * Notifies a launch listener (multiple launches) in a safe runnable to * handle exceptions. */ class LaunchesNotifier implements ISafeRunnable { private ILaunchesListener fListener; private int fType; private ILaunch[] fNotifierLaunches; private ILaunch[] fRegistered; /** * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) */ @Override public void handleException(Throwable exception) { IStatus status = new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugPlugin.INTERNAL_ERROR, "An exception occurred during launch change notification.", exception); //$NON-NLS-1$ DebugPlugin.log(status); } /** * Notifies the given listener of the adds/changes/removes * * @param launches the launches that changed * @param update the type of change */ public void notify(ILaunch[] launches, int update) { fNotifierLaunches = launches; fType = update; fRegistered = null; for (ILaunchesListener iLaunchesListener : fLaunchesListeners) { fListener = iLaunchesListener; SafeRunner.run(this); } fNotifierLaunches = null; fRegistered = null; fListener = null; } /** * @see org.eclipse.core.runtime.ISafeRunnable#run() */ @Override public void run() throws Exception { switch (fType) { case ADDED: fListener.launchesAdded(fNotifierLaunches); break; case REMOVED: fListener.launchesRemoved(fNotifierLaunches); break; case CHANGED: case TERMINATE: if (fRegistered == null) { List<ILaunch> registered = null; for (int j = 0; j < fNotifierLaunches.length; j++) { if (isRegistered(fNotifierLaunches[j])) { if (registered != null) { registered.add(fNotifierLaunches[j]); } } else { if (registered == null) { registered = new ArrayList<ILaunch>(fNotifierLaunches.length); for (int k = 0; k < j; k++) { registered.add(fNotifierLaunches[k]); } } } } if (registered == null) { fRegistered = fNotifierLaunches; } else { fRegistered = registered.toArray(new ILaunch[registered.size()]); } } if (fRegistered.length > 0) { if (fType == CHANGED) { fListener.launchesChanged(fRegistered); } if (fType == TERMINATE && fListener instanceof ILaunchesListener2) { ((ILaunchesListener2)fListener).launchesTerminated(fRegistered); } } break; default: break; } } } /** * Visitor for handling a resource begin deleted, and the need to check mapped configurations * for auto-deletion * @since 3.4 */ class MappedResourceVisitor implements IResourceDeltaVisitor { /* (non-Javadoc) * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta) */ @Override public boolean visit(IResourceDelta delta) throws CoreException { if (0 != (delta.getFlags() & IResourceDelta.OPEN)) { return false; } if(delta.getKind() == IResourceDelta.REMOVED && delta.getFlags() != IResourceDelta.MOVED_TO) { ArrayList<ILaunchConfiguration> configs = collectAssociatedLaunches(delta.getResource()); for (ILaunchConfiguration config : configs) { try { config.delete(); } catch (CoreException e) { DebugPlugin.log(e.getStatus()); } } return false; } return true; } } /** * Visitor for handling resource deltas. */ class LaunchManagerVisitor implements IResourceDeltaVisitor { /** * @see IResourceDeltaVisitor#visit(IResourceDelta) */ @Override public boolean visit(IResourceDelta delta) { if (delta == null) { return false; } if (0 != (delta.getFlags() & IResourceDelta.OPEN)) { if (delta.getResource() instanceof IProject) { IProject project = (IProject)delta.getResource(); if (project.isOpen()) { LaunchManager.this.projectOpened(project); } else { LaunchManager.this.projectClosed(project); } } return false; } IResource resource = delta.getResource(); if (resource instanceof IFile) { IFile file = (IFile)resource; if (ILaunchConfiguration.LAUNCH_CONFIGURATION_FILE_EXTENSION.equals(file.getFileExtension())) { ILaunchConfiguration handle = new LaunchConfiguration(file); switch (delta.getKind()) { case IResourceDelta.ADDED : LaunchManager.this.launchConfigurationAdded(handle); break; case IResourceDelta.REMOVED : LaunchManager.this.launchConfigurationDeleted(handle); break; case IResourceDelta.CHANGED : LaunchManager.this.launchConfigurationChanged(handle); break; default: break; } } return false; } return true; } } /** * Notifies a launch listener (single launch) in a safe runnable to handle * exceptions. */ class LaunchNotifier implements ISafeRunnable { private ILaunchListener fListener; private int fType; private ILaunch fLaunch; /** * @see org.eclipse.core.runtime.ISafeRunnable#handleException(java.lang.Throwable) */ @Override public void handleException(Throwable exception) { IStatus status = new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugPlugin.INTERNAL_ERROR, "An exception occurred during launch change notification.", exception); //$NON-NLS-1$ DebugPlugin.log(status); } /** * Notifies listeners of the add/change/remove * * @param launch the launch that has changed * @param update the type of change */ public void notify(ILaunch launch, int update) { fLaunch = launch; fType = update; for (ILaunchListener iLaunchListener : fListeners) { fListener = iLaunchListener; SafeRunner.run(this); } fLaunch = null; fListener = null; } /** * @see org.eclipse.core.runtime.ISafeRunnable#run() */ @Override public void run() throws Exception { switch (fType) { case ADDED: fListener.launchAdded(fLaunch); break; case REMOVED: fListener.launchRemoved(fLaunch); break; case CHANGED: if (isRegistered(fLaunch)) { fListener.launchChanged(fLaunch); } break; default: break; } } } /** * Collects files whose extension matches the launch configuration file * extension. */ class ResourceProxyVisitor implements IResourceProxyVisitor { private List<IResource> fList; protected ResourceProxyVisitor(List<IResource> list) { fList= list; } /** * @see org.eclipse.core.resources.IResourceProxyVisitor#visit(org.eclipse.core.resources.IResourceProxy) */ @Override public boolean visit(IResourceProxy proxy) { if (proxy.getType() == IResource.FILE) { if (ILaunchConfiguration.LAUNCH_CONFIGURATION_FILE_EXTENSION.equalsIgnoreCase(proxy.requestFullPath().getFileExtension())) { fList.add(proxy.requestResource()); } return false; } return true; } } /** * Internal class used to hold information about a preferred delegate * * @since 3.3 */ class PreferredDelegate { private ILaunchDelegate fDelegate = null; private String fTypeid = null; private Set<String> fModes = null; public PreferredDelegate(ILaunchDelegate delegate, String typeid, Set<String> modes) { fDelegate = delegate; fTypeid = typeid; fModes = modes; } public String getTypeId() { return fTypeid; } public Set<String> getModes() { return fModes; } public ILaunchDelegate getDelegate() { return fDelegate; } } /** * Types of notifications */ public static final int ADDED = 0; public static final int REMOVED= 1; public static final int CHANGED= 2; public static final int TERMINATE= 3; /** * The collection of native environment variables on the user's system. Cached * after being computed once as the environment cannot change. */ private static HashMap<String, String> fgNativeEnv = null; private static HashMap<String, String> fgNativeEnvCasePreserved = null; /** * Path to the local directory where local launch configurations * are stored with the workspace. */ public static final IPath LOCAL_LAUNCH_CONFIGURATION_CONTAINER_PATH = DebugPlugin.getDefault().getStateLocation().append(".launches"); //$NON-NLS-1$ /** * Returns a Document that can be used to build a DOM tree * @return the Document * @throws ParserConfigurationException if an exception occurs creating the document builder * @since 3.0 */ public static Document getDocument() throws ParserConfigurationException { DocumentBuilderFactory dfactory= DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder= dfactory.newDocumentBuilder(); Document doc= docBuilder.newDocument(); return doc; } /** * Serializes a XML document into a string - encoded in UTF8 format, * with platform line separators. * * @param doc document to serialize * @return the document as a string * @throws TransformerException if an unrecoverable error occurs during the serialization * @throws IOException if the encoding attempted to be used is not supported */ public static String serializeDocument(Document doc) throws TransformerException, IOException { ByteArrayOutputStream s = new ByteArrayOutputStream(); TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$ transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ DOMSource source = new DOMSource(doc); StreamResult outputTarget = new StreamResult(s); transformer.transform(source, outputTarget); return s.toString("UTF8"); //$NON-NLS-1$ } /** * Collection of defined launch configuration type * extensions. */ private List<ILaunchConfigurationType> fLaunchConfigurationTypes = null; /** * Launch configuration cache. Keys are <code>LaunchConfiguration</code>, * values are <code>LaunchConfigurationInfo</code>. */ private Map<ILaunchConfiguration, LaunchConfigurationInfo> fLaunchConfigurations = new HashMap<ILaunchConfiguration, LaunchConfigurationInfo>(10); /** * A cache of launch configuration names currently in the workspace. */ private volatile String[] fSortedConfigNames = null; /** * Collection of all launch configurations in the workspace. * <code>List</code> of <code>ILaunchConfiguration</code>. */ private List<ILaunchConfiguration> fLaunchConfigurationIndex = null; /** * Launch configuration comparator extensions, * keyed by attribute name. */ private Map<String, LaunchConfigurationComparator> fComparators = null; /** * Registered launch modes, or <code>null</code> if not initialized. * Keys are mode identifiers, values are <code>ILaunchMode</code>s. */ private Map<String, ILaunchMode> fLaunchModes = null; /** * A map of LaunchDelegate objects stored by id of delegate, or launch config type */ private HashMap<String, LaunchDelegate> fLaunchDelegates = null; /** * Initial startup cache of preferred delegate so that the debug preferences are only parsed once * * @since 3.3 */ private Set<PreferredDelegate> fPreferredDelegates = null; /** * Collection of launches */ private List<ILaunch> fLaunches = new ArrayList<ILaunch>(10); /** * Set of launches for efficient 'isRegistered()' check TODO remove this - * Launches don't implement hashCode() or equals() - so its no more * efficient than walking the other collection */ private Set<ILaunch> fLaunchSet = new HashSet<ILaunch>(10); /** * Collection of listeners */ private ListenerList<ILaunchListener> fListeners = new ListenerList<>(); /** * Collection of "plural" listeners. * @since 2.1 */ private ListenerList<ILaunchesListener> fLaunchesListeners = new ListenerList<>(); /** * Visitor used to process resource deltas, * to update launch configuration index. */ private LaunchManagerVisitor fgVisitor; /** * Visitor used to process a deleted resource, * to remove mapped launch configurations in the event * auto-removal of launch configurations is enabled * * @since 3.4 */ private MappedResourceVisitor fgMRVisitor; /** * Whether this manager is listening for resource change events */ private boolean fListening = false; /** * Launch configuration listeners */ private ListenerList<ILaunchConfigurationListener> fLaunchConfigurationListeners = new ListenerList<>(); /** * Table of source locator extensions. Keys * are identifiers, and values are associated * configuration elements. */ private Map<String, IConfigurationElement> fSourceLocators = null; /** * The handles of launch configurations being moved, or <code>null</code> */ private ILaunchConfiguration fFrom; private ILaunchConfiguration fTo; /** * Map of source container type extensions. Keys are extension ids * and values are associated configuration elements. */ private Map<String, ISourceContainerType> sourceContainerTypes; /** * Map of source path computer extensions. Keys are extension ids * and values are associated configuration elements. */ private Map<String, ISourcePathComputer> sourcePathComputers; /** * TODO, we can probably remove this too */ private Set<String> fActiveModes; /** * @see ILaunchManager#addLaunch(ILaunch) */ @Override public void addLaunch(ILaunch launch) { if (internalAddLaunch(launch)) { fireUpdate(launch, ADDED); fireUpdate(new ILaunch[] {launch}, ADDED); } } /** * @see ILaunchManager#addLaunchConfigurationListener(ILaunchConfigurationListener) */ @Override public void addLaunchConfigurationListener(ILaunchConfigurationListener listener) { fLaunchConfigurationListeners.add(listener); } /** * @see org.eclipse.debug.core.ILaunchManager#addLaunches(org.eclipse.debug.core.ILaunch[]) */ @Override public void addLaunches(ILaunch[] launches) { List<ILaunch> added = new ArrayList<ILaunch>(launches.length); for (int i = 0; i < launches.length; i++) { if (internalAddLaunch(launches[i])) { added.add(launches[i]); } } if (!added.isEmpty()) { ILaunch[] addedLaunches = added.toArray(new ILaunch[added.size()]); fireUpdate(addedLaunches, ADDED); for (int i = 0; i < addedLaunches.length; i++) { fireUpdate(launches[i], ADDED); } } } /** * @see org.eclipse.debug.core.ILaunchManager#addLaunchListener(org.eclipse.debug.core.ILaunchesListener) */ @Override public void addLaunchListener(ILaunchesListener listener) { fLaunchesListeners.add(listener); } /** * @see ILaunchManager#addLaunchListener(ILaunchListener) */ @Override public void addLaunchListener(ILaunchListener listener) { fListeners.add(listener); } /** * Computes and caches the native system environment variables as a map of * variable names and values (Strings) in the given map. * <p> * Note that WIN32 system environment preserves * the case of variable names but is otherwise case insensitive. * Depending on what you intend to do with the environment, the * lack of normalization may or may not be create problems. This * method preserves mixed-case keys using the variable names * recorded by the OS. * </p> * @param cache the map * @since 3.1 */ private void cacheNativeEnvironment(Map<String, String> cache) { try { String nativeCommand= null; boolean isWin9xME= false; //see bug 50567 String fileName= null; if (Platform.getOS().equals(Constants.OS_WIN32)) { String osName= System.getProperty("os.name"); //$NON-NLS-1$ isWin9xME= osName != null && (osName.startsWith("Windows 9") || osName.startsWith("Windows ME")); //$NON-NLS-1$ //$NON-NLS-2$ if (isWin9xME) { // Win 95, 98, and ME // SET might not return therefore we pipe into a file IPath stateLocation= DebugPlugin.getDefault().getStateLocation(); fileName= stateLocation.toOSString() + File.separator + "env.txt"; //$NON-NLS-1$ nativeCommand= "command.com /C set > " + fileName; //$NON-NLS-1$ } else { // Win NT, 2K, XP nativeCommand= "cmd.exe /C set"; //$NON-NLS-1$ } } else if (!Platform.getOS().equals(Constants.OS_UNKNOWN)){ nativeCommand= "env"; //$NON-NLS-1$ } if (nativeCommand == null) { return; } Process process= Runtime.getRuntime().exec(nativeCommand); if (isWin9xME) { //read piped data on Win 95, 98, and ME Properties p= new Properties(); File file= new File(fileName); try(InputStream stream = new BufferedInputStream(new FileInputStream(file))){ p.load(stream); if (!file.delete()) { file.deleteOnExit(); // if delete() fails try again on VM close } for (Entry<Object, Object> entry : p.entrySet()) { // Win32's environment variables are case insensitive. Put everything // to uppercase so that (for example) the "PATH" variable will match // "pAtH" correctly on Windows. String key = (String) entry.getKey(); //no need to cast value cache.put(key, (String) p.get(key)); } } } else { //read process directly on other platforms //we need to parse out matching '{' and '}' for function declarations in .bash environments // pattern is [func name]=() { and we must find the '}' on its own line with no trailing ';' try (InputStream stream = process.getInputStream(); InputStreamReader isreader = new InputStreamReader(stream); BufferedReader reader = new BufferedReader(isreader)) { String line = reader.readLine(); String key = null; String value = null; String newLine = System.getProperty("line.separator"); //$NON-NLS-1$ while (line != null) { int func = line.indexOf("=()"); //$NON-NLS-1$ if (func > 0) { key = line.substring(0, func); // scan until we find the closing '}' with no // following chars value = line.substring(func + 1); while (line != null && !line.equals("}")) { //$NON-NLS-1$ line = reader.readLine(); if (line != null) { value += newLine + line; } } line = reader.readLine(); } else { int separator = line.indexOf('='); if (separator > 0) { key = line.substring(0, separator); value = line.substring(separator + 1); line = reader.readLine(); if (line != null) { // this line has a '=' read ahead to check // next line for '=', might be broken on // more than one line // also if line starts with non-identifier - // it is remainder of previous variable while (line.indexOf('=') < 0 || (line.length() > 0 && !Character.isJavaIdentifierStart(line.charAt(0)))) { value += newLine + line; line = reader.readLine(); if (line == null) { // if next line read is the end of // the file quit the loop break; } } } } } if (key != null) { cache.put(key, value); key = null; value = null; } else { line = reader.readLine(); } } } } } catch (IOException e) { // Native environment-fetching code failed. // This can easily happen and is not useful to log. } } /** * Clears all launch configurations (if any have been accessed) */ private void clearAllLaunchConfigurations() { if (fLaunchConfigurationTypes != null) { fLaunchConfigurationTypes.clear(); } if (fLaunchConfigurationIndex != null) { fLaunchConfigurationIndex.clear(); } } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchManager#getEncoding(org.eclipse.debug.core.ILaunchConfiguration) */ @Override public String getEncoding(ILaunchConfiguration configuration) throws CoreException { String encoding = configuration.getAttribute(DebugPlugin.ATTR_CONSOLE_ENCODING, (String)null); if(encoding == null) { IResource[] resources = configuration.getMappedResources(); if(resources != null && resources.length > 0) { IResource res = resources[0]; if(res instanceof IFile) { return ((IFile)res).getCharset(); } else if(res instanceof IContainer) { return ((IContainer)res).getDefaultCharset(); } } else { return ResourcesPlugin.getEncoding(); } } return encoding; } /** * The launch config name cache is cleared when a config is added, deleted or changed. */ protected synchronized void clearConfigNameCache() { fSortedConfigNames = null; } /** * Return an instance of DebugException containing the specified message and Throwable. * @param message the message for the new {@link DebugException} * @param throwable the underlying {@link Exception} * @return the new {@link DebugException} */ protected DebugException createDebugException(String message, Throwable throwable) { return new DebugException( new Status( IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, message, throwable ) ); } /** * Return a LaunchConfigurationInfo object initialized from XML contained in * the specified stream. Simply pass out any exceptions encountered so that * caller can deal with them. This is important since caller may need access to the * actual exception. * * @param stream the {@link InputStream} to read from * @return the new {@link LaunchConfigurationInfo} * @throws CoreException if a problem is encountered * @throws ParserConfigurationException if the stream fails to parse * @throws IOException if there is a problem handling the given stream or writing the new info file * @throws SAXException if there is a SAX parse exception */ protected LaunchConfigurationInfo createInfoFromXML(InputStream stream) throws CoreException, ParserConfigurationException, IOException, SAXException { Element root = null; DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); parser.setErrorHandler(new DefaultHandler()); root = parser.parse(new InputSource(stream)).getDocumentElement(); LaunchConfigurationInfo info = new LaunchConfigurationInfo(); info.initializeFromXML(root); return info; } /** * Finds and returns all launch configurations in the given * container (and sub-containers) * * @param container the container to search * @return all launch configurations in the given container */ protected List<ILaunchConfiguration> findLaunchConfigurations(IContainer container) { if (container instanceof IProject && !((IProject)container).isOpen()) { return Collections.EMPTY_LIST; } List<IResource> list = new ArrayList<IResource>(10); ResourceProxyVisitor visitor= new ResourceProxyVisitor(list); try { container.accept(visitor, IResource.NONE); } catch (CoreException ce) { //Closed project...should not be possible with previous check } List<ILaunchConfiguration> configs = new ArrayList<ILaunchConfiguration>(list.size()); for (IResource resource : list) { ILaunchConfiguration config = getLaunchConfiguration((IFile) resource); if(config != null && config.exists()) { configs.add(config); } } return configs; } /** * Searches for the {@link ILaunchConfiguration} with the given name * @param name the name to search for * @return the {@link ILaunchConfiguration} with the given name or <code>null</code> * @since 3.8 */ public ILaunchConfiguration findLaunchConfiguration(String name) { if(name != null) { ILaunchConfiguration[] configs = getLaunchConfigurations(); for (int i = 0; i < configs.length; i++) { if(name.equals(configs[i].getName())) { return configs[i]; } } } return null; } /** * Finds and returns all local launch configurations. * * @return all local launch configurations */ protected List<ILaunchConfiguration> findLocalLaunchConfigurations() { IPath containerPath = LOCAL_LAUNCH_CONFIGURATION_CONTAINER_PATH; final File directory = containerPath.toFile(); if (directory.isDirectory()) { FilenameFilter filter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { return dir.equals(directory) && name.endsWith(ILaunchConfiguration.LAUNCH_CONFIGURATION_FILE_EXTENSION); } }; File[] files = directory.listFiles(filter); if (files.length > 0) { List<ILaunchConfiguration> configs = new ArrayList<ILaunchConfiguration>(10); LaunchConfiguration config = null; for (int i = 0; i < files.length; i++) { config = new LaunchConfiguration(LaunchConfiguration.getSimpleName(files[i].getName()), null); configs.add(config); } return configs; } } return Collections.EMPTY_LIST; } /** * Fires notification to (single) listeners that a launch has been * added/changed/removed. * * @param launch launch that has changed * @param update type of change */ public void fireUpdate(ILaunch launch, int update) { new LaunchNotifier().notify(launch, update); } /** * Fires notification to (plural) listeners that a launch has been * added/changed/removed. * * @param launches launches that have changed * @param update type of change */ public void fireUpdate(ILaunch[] launches, int update) { new LaunchesNotifier().notify(launches, update); } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchManager#generateUniqueLaunchConfigurationNameFrom(java.lang.String) */ @Override public String generateUniqueLaunchConfigurationNameFrom(String baseName) { int index = 1; int length = baseName.length(); int copyIndex = baseName.lastIndexOf(" ("); //$NON-NLS-1$ String base = baseName; if (copyIndex > -1 && length > copyIndex + 2 && baseName.charAt(length - 1) == ')') { String trailer = baseName.substring(copyIndex + 2, length - 1); if (isNumber(trailer)) { try { index = Integer.parseInt(trailer); base = baseName.substring(0, copyIndex); } catch (NumberFormatException nfe) {} } } String newName = base; while (isExistingLaunchConfigurationName(newName)) { newName = MessageFormat.format(DebugCoreMessages.LaunchManager_31, new Object[] { base, Integer.toString(index) }); index++; } return newName; } /** * Return a String that can be used as the name of a launch configuration. The name * is guaranteed to be unique (no existing or temporary launch configurations will have this name). * The name that is returned uses the <code>basename</code> as a starting point. If * there is no existing launch configuration with this name, then <code>basename</code> * is returned. Otherwise, the value returned consists of the specified base plus * some suffix that guarantees uniqueness. Passing <code>null</code> as the set of reserved names will cause this * method to return <code>generateUniqueLaunchConfigurationNameFrom(String baseName)</code>. * * By specifying a set of reserved names, you can further constrain the name that will be generated * by this method. For example you can give a base name of 'test' and a reserved set of [test(1), test(2)], * which will result in a name of 'test(3)' being returned iff a configuration with the name 'test' already exists. * * @return launch configuration name * @param basename the String that the returned name must begin with * @param reservednames a set of strings that is further used to constrain what names can be generated * @since 3.3 */ public String generateUniqueLaunchConfigurationNameFrom(String basename, Set<String> reservednames) { if(reservednames == null) { return generateUniqueLaunchConfigurationNameFrom(basename); } int index = 1; int length= basename.length(); String base = basename; int copyIndex = base.lastIndexOf(" ("); //$NON-NLS-1$ if (copyIndex > -1 && length > copyIndex + 2 && base.charAt(length - 1) == ')') { String trailer = base.substring(copyIndex + 2, length -1); if (isNumber(trailer)) { try { index = Integer.parseInt(trailer); base = base.substring(0, copyIndex); } catch (NumberFormatException nfe) {} } } String newname = base; StringBuffer buffer = null; while (isExistingLaunchConfigurationName(newname) || reservednames.contains(newname)) { buffer = new StringBuffer(base); buffer.append(" ("); //$NON-NLS-1$ buffer.append(String.valueOf(index)); index++; buffer.append(')'); newname = buffer.toString(); } return newname; } /** * Returns a collection of all launch configuration handles in * the workspace. This collection is initialized lazily. * * @return all launch configuration handles */ private synchronized List<ILaunchConfiguration> getAllLaunchConfigurations() { if (fLaunchConfigurationIndex == null) { try { fLaunchConfigurationIndex = new ArrayList<ILaunchConfiguration>(20); List<ILaunchConfiguration> configs = findLocalLaunchConfigurations(); verifyConfigurations(configs, fLaunchConfigurationIndex); configs = findLaunchConfigurations(ResourcesPlugin.getWorkspace().getRoot()); verifyConfigurations(configs, fLaunchConfigurationIndex); } finally { hookResourceChangeListener(); } } return fLaunchConfigurationIndex; } /** * Return a sorted array of the names of all <code>ILaunchConfiguration</code>s in * the workspace. These are cached, and cache is cleared when a new config is added, * deleted or changed. * @return the sorted array of {@link ILaunchConfiguration} names */ protected synchronized String[] getAllSortedConfigNames() { if (fSortedConfigNames == null) { ILaunchConfiguration[] configs = getLaunchConfigurations(); fSortedConfigNames = new String[configs.length]; for (int i = 0; i < configs.length; i++) { fSortedConfigNames[i] = configs[i].getName(); } Arrays.sort(fSortedConfigNames); } return fSortedConfigNames; } /** * Returns the comparator registered for the given attribute, or * <code>null</code> if none. * * @param attributeName attribute for which a comparator is required * @return comparator, or <code>null</code> if none */ protected Comparator<Object> getComparator(String attributeName) { Map<String, LaunchConfigurationComparator> map = getComparators(); return map.get(attributeName); } /** * Returns comparators, loading if required * @return the complete map of {@link ILaunchConfiguration} {@link Comparator}s */ protected Map<String, LaunchConfigurationComparator> getComparators() { initializeComparators(); return fComparators; } /** * Returns the launch configurations specified by the given * XML document. * * @param root XML document * @return list of launch configurations * @throws CoreException if a problem is encountered */ protected List<ILaunchConfiguration> getConfigsFromXML(Element root) throws CoreException { DebugException invalidFormat = new DebugException( new Status( IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.REQUEST_FAILED, DebugCoreMessages.LaunchManager_Invalid_launch_configuration_index__18, null ) ); if (!root.getNodeName().equalsIgnoreCase("launchConfigurations")) { //$NON-NLS-1$ throw invalidFormat; } // read each launch configuration List<ILaunchConfiguration> configs = new ArrayList<ILaunchConfiguration>(4); NodeList list = root.getChildNodes(); int length = list.getLength(); Node node = null; Element entry = null; String memento = null; for (int i = 0; i < length; ++i) { node = list.item(i); short type = node.getNodeType(); if (type == Node.ELEMENT_NODE) { entry = (Element) node; if (!entry.getNodeName().equals("launchConfiguration")) { //$NON-NLS-1$ throw invalidFormat; } memento = entry.getAttribute("memento"); //$NON-NLS-1$ if (memento == null) { throw invalidFormat; } configs.add(getLaunchConfiguration(memento)); } } return configs; } protected ConfigurationNotifier getConfigurationNotifier() { return new ConfigurationNotifier(); } /** * @see ILaunchManager#getDebugTargets() */ @Override public IDebugTarget[] getDebugTargets() { synchronized (fLaunches) { List<IDebugTarget> allTargets = new ArrayList<IDebugTarget>(fLaunches.size()); IDebugTarget[] targets = null; for (ILaunch launch : fLaunches) { targets = launch.getDebugTargets(); for (int i = 0; i < targets.length; i++) { allTargets.add(targets[i]); } } return allTargets.toArray(new IDebugTarget[allTargets.size()]); } } /** * Returns the resource delta visitor for the launch manager. * * @return the resource delta visitor for the launch manager */ private LaunchManagerVisitor getDeltaVisitor() { if (fgVisitor == null) { fgVisitor= new LaunchManagerVisitor(); } return fgVisitor; } /** * Returns the resource delta visitor for auto-removal of mapped launch configurations * @return the resource delta visitor for auto-removal of mapped launch configurations * * @since 3.4 */ private MappedResourceVisitor getMappedResourceVisitor() { if(fgMRVisitor == null) { fgMRVisitor = new MappedResourceVisitor(); } return fgMRVisitor; } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchManager#getEnvironment(org.eclipse.debug.core.ILaunchConfiguration) */ @Override public String[] getEnvironment(ILaunchConfiguration configuration) throws CoreException { Map<String, String> configEnv = configuration.getAttribute(ATTR_ENVIRONMENT_VARIABLES, (Map<String, String>) null); if (configEnv == null) { return null; } Map<String, String> env = new HashMap<String, String>(); // build base environment boolean append = configuration.getAttribute(ATTR_APPEND_ENVIRONMENT_VARIABLES, true); if (append) { env.putAll(getNativeEnvironmentCasePreserved()); } // Add variables from config boolean win32= Platform.getOS().equals(Constants.OS_WIN32); String key = null; String value = null; Object nativeValue = null; String nativeKey = null; for (Entry<String, String> entry : configEnv.entrySet()) { key = entry.getKey(); value = entry.getValue(); // translate any string substitution variables if (value != null) { value = VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(value); } boolean added= false; if (win32) { // First, check if the key is an exact match for an existing key. nativeValue = env.get(key); if (nativeValue != null) { // If an exact match is found, just replace the value env.put(key, value); } else { // Win32 variables are case-insensitive. If an exact match isn't found, iterate to // check for a case-insensitive match. We maintain the key's case (see bug 86725), // but do a case-insensitive comparison (for example, "pAtH" will still override "PATH"). for (Entry<String, String> nativeEntry : env.entrySet()) { nativeKey = (nativeEntry).getKey(); if (nativeKey.equalsIgnoreCase(key)) { nativeEntry.setValue(value); added = true; break; } } } } if (!added) { env.put(key, value); } } List<String> strings = new ArrayList<String>(env.size()); StringBuffer buffer = null; for (Entry<String, String> entry : env.entrySet()) { buffer = new StringBuffer(entry.getKey()); buffer.append('=').append(entry.getValue()); strings.add(buffer.toString()); } return strings.toArray(new String[strings.size()]); } /** * Returns the info object for the specified launch configuration. * If the configuration exists, but is not yet in the cache, * an info object is built and added to the cache. * @param config the {@link ILaunchConfiguration} to get the info object from * @return the {@link LaunchConfigurationInfo} object from the given {@link ILaunchConfiguration} * * @exception CoreException if an exception occurs building * the info object * @exception DebugException if the config does not exist * @since 3.5 */ protected LaunchConfigurationInfo getInfo(LaunchConfiguration config) throws CoreException { LaunchConfigurationInfo info = fLaunchConfigurations.get(config); if (info == null) { IFileStore store = config.getFileStore(); if (config.exists()) { BufferedInputStream stream = null; try { stream = new BufferedInputStream(store.openInputStream(EFS.NONE, null)); info = createInfoFromXML(stream); synchronized (this) { fLaunchConfigurations.put(config, info); } } catch (FileNotFoundException e) { throwException(config, e); } catch (SAXException e) { throwException(config, e); } catch (ParserConfigurationException e) { throwException(config, e); } catch (IOException e) { throwException(config, e); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { throwException(config, e); } } } } else { if (store != null){ throw createDebugException(MessageFormat.format(DebugCoreMessages.LaunchManager_does_not_exist, new Object[] { config.getName(), store.toURI().toString() }), null); } else { throw createDebugException(MessageFormat.format(DebugCoreMessages.LaunchManager_does_not_exist_no_store_found, new Object[] { config.getName() }), null); } } } return info; } /** * @see ILaunchManager#getLaunchConfiguration(IFile) */ @Override public ILaunchConfiguration getLaunchConfiguration(IFile file) { hookResourceChangeListener(); return new LaunchConfiguration(file); } /** * @see ILaunchManager#getLaunchConfiguration(String) */ @Override public ILaunchConfiguration getLaunchConfiguration(String memento) throws CoreException { hookResourceChangeListener(); return new LaunchConfiguration(memento); } /** * @see ILaunchManager#getLaunchConfigurations() */ @Override public synchronized ILaunchConfiguration[] getLaunchConfigurations() { List<ILaunchConfiguration> allConfigs = getAllLaunchConfigurations(); return allConfigs.toArray(new ILaunchConfiguration[allConfigs.size()]); } /** * @see ILaunchManager#getLaunchConfigurations(ILaunchConfigurationType) */ @Override public synchronized ILaunchConfiguration[] getLaunchConfigurations(ILaunchConfigurationType type) throws CoreException { List<ILaunchConfiguration> configs = new ArrayList<ILaunchConfiguration>(); for (ILaunchConfiguration config : getAllLaunchConfigurations()) { if (config.getType().equals(type)) { configs.add(config); } } return configs.toArray(new ILaunchConfiguration[configs.size()]); } /** * Returns all launch configurations that are stored as resources * in the given project. * * @param project a project * @return collection of launch configurations that are stored as resources * in the given project */ protected synchronized List<ILaunchConfiguration> getLaunchConfigurations(IProject project) { List<ILaunchConfiguration> configs = new ArrayList<ILaunchConfiguration>(); for (ILaunchConfiguration config : getAllLaunchConfigurations()) { IFile file = config.getFile(); if (file != null && file.getProject().equals(project)) { configs.add(config); } } return configs; } /** * @see ILaunchManager#getLaunchConfigurationType(String) */ @Override public ILaunchConfigurationType getLaunchConfigurationType(String id) { ILaunchConfigurationType[] types = getLaunchConfigurationTypes(); for(int i = 0; i < types.length; i++) { if (types[i].getIdentifier().equals(id)) { return types[i]; } } return null; } /** * @see ILaunchManager#getLaunchConfigurationTypes() */ @Override public ILaunchConfigurationType[] getLaunchConfigurationTypes() { initializeLaunchConfigurationTypes(); return fLaunchConfigurationTypes.toArray(new ILaunchConfigurationType[fLaunchConfigurationTypes.size()]); } /** * @see ILaunchManager#getLaunches() */ @Override public ILaunch[] getLaunches() { synchronized (fLaunches) { return fLaunches.toArray(new ILaunch[fLaunches.size()]); } } /**) * @see org.eclipse.debug.core.ILaunchManager#getLaunchMode(java.lang.String) */ @Override public ILaunchMode getLaunchMode(String mode) { initializeLaunchModes(); return fLaunchModes.get(mode); } /** * @see org.eclipse.debug.core.ILaunchManager#getLaunchModes() */ @Override public ILaunchMode[] getLaunchModes() { initializeLaunchModes(); Collection<ILaunchMode> collection = fLaunchModes.values(); return collection.toArray(new ILaunchMode[collection.size()]); } /** * Returns all of the launch delegates. The returned listing of delegates cannot be directly used to launch, * instead the method <code>IlaunchDelegate.getDelegate</code> must be used to acquire an executable form of * the delegate, allowing us to maintain lazy loading of the delegates themselves. * @return all of the launch delegates * * @since 3.3 */ public ILaunchDelegate[] getLaunchDelegates() { initializeLaunchDelegates(); Collection<LaunchDelegate> col = fLaunchDelegates.values(); return col.toArray(new ILaunchDelegate[col.size()]); } /** * Returns the listing of launch delegates that apply to the specified * <code>ILaunchConfigurationType</code> id * @param typeid the id of the launch configuration type to get delegates for * @return An array of <code>LaunchDelegate</code>s that apply to the specified launch configuration * type, or an empty array, never <code>null</code> * * @since 3.3 */ public LaunchDelegate[] getLaunchDelegates(String typeid) { initializeLaunchDelegates(); ArrayList<LaunchDelegate> list = new ArrayList<LaunchDelegate>(); for (Entry<String, LaunchDelegate> entry : fLaunchDelegates.entrySet()) { LaunchDelegate ld = entry.getValue(); if (ld.getLaunchConfigurationTypeId().equals(typeid)) { list.add(ld); } } return list.toArray(new LaunchDelegate[list.size()]); } /** * This method returns the <code>ILaunchDelegate</code> instance corresponding to the id * of the launch delegate specified * @param id the id of the <code>ILaunchDelegate</code> to find * @return the <code>ILaunchDelegate</code> or <code>null</code> if not found * * @since 3.3 */ public ILaunchDelegate getLaunchDelegate(String id) { if(id != null) { ILaunchDelegate[] delegates = getLaunchDelegates(); for(int i = 0; i < delegates.length; i++) { if(id.equals(delegates[i].getId())) { return delegates[i]; } } } return null; } /** * Initializes the listing of delegates available to the launching framework * * @since 3.3 */ private synchronized void initializeLaunchDelegates() { if(fLaunchDelegates == null) { fLaunchDelegates = new HashMap<String, LaunchDelegate>(); //get all launch delegate contributions IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.getUniqueIdentifier(), DebugPlugin.EXTENSION_POINT_LAUNCH_DELEGATES); IConfigurationElement[] infos = extensionPoint.getConfigurationElements(); LaunchDelegate delegate = null; for(int i = 0; i < infos.length; i++) { delegate = new LaunchDelegate(infos[i]); fLaunchDelegates.put(delegate.getId(), delegate); } //get all delegates from launch configuration type contributions extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.getUniqueIdentifier(), DebugPlugin.EXTENSION_POINT_LAUNCH_CONFIGURATION_TYPES); infos = extensionPoint.getConfigurationElements(); for(int i = 0; i < infos.length; i++) { //must check to see if delegate is provided in contribution if(infos[i].getAttribute(IConfigurationElementConstants.DELEGATE) != null) { delegate = new LaunchDelegate(infos[i]); fLaunchDelegates.put(delegate.getId(), delegate); } } } } /** * This method is used to initialize a simple listing of all preferred delegates, which is then used by each * <code>ILaunchConfigurationType</code> to find if they have preferred delegates. Once an <code>ILaunchConfigurationType</code> * has used this listing to initialize its preferred delegates it will maintain changes to its preferred delegate, which are * then written back to the preference store only when the launch manager shuts down. * <p> * This cache is not synchronized with the runtime preferred delegates stored in launch configuration types. * </p> * @since 3.3 */ private synchronized void initializePreferredDelegates() { if(fPreferredDelegates == null) { fPreferredDelegates = new HashSet<PreferredDelegate>(); String preferred = Platform.getPreferencesService().getString(DebugPlugin.getUniqueIdentifier(), LaunchManager.PREF_PREFERRED_DELEGATES, IInternalDebugCoreConstants.EMPTY_STRING, null); if(!IInternalDebugCoreConstants.EMPTY_STRING.equals(preferred)) { try { Element root = DebugPlugin.parseDocument(preferred); NodeList nodes = root.getElementsByTagName(IConfigurationElementConstants.DELEGATE); Element element = null; String typeid = null; Set<String> modeset = null; for(int i = 0; i < nodes.getLength(); i++) { element = (Element) nodes.item(i); String delegateid = element.getAttribute(IConfigurationElementConstants.ID); typeid = element.getAttribute(IConfigurationElementConstants.TYPE_ID); String[] modes = element.getAttribute(IConfigurationElementConstants.MODES).split(","); //$NON-NLS-1$ modeset = new HashSet<String>(Arrays.asList(modes)); LaunchDelegate delegate = getLaunchDelegateExtension(typeid, delegateid, modeset); if (delegate != null) { //take type id, modeset, delegate and create entry if(!IInternalDebugCoreConstants.EMPTY_STRING.equals(typeid) & modeset != null) { fPreferredDelegates.add(new PreferredDelegate(delegate, typeid, modeset)); } } } } catch (CoreException e) {DebugPlugin.log(e);} } } } /** * Allows internal access to reset preferred delegates when re-importing * preferences. * * @since 3.6 */ protected void resetPreferredDelegates() { fPreferredDelegates = null; } /** * Allows internal access to a preferred delegate for a given type and mode set * @param typeid the id of the <code>ILaunchConfigurationType</code> to find a delegate for * @param modes the set of modes for the delegate * @return the preferred delegate for the specified type id and mode set, or <code>null</code> if none * * @since 3.3 */ protected ILaunchDelegate getPreferredDelegate(String typeid, Set<String> modes) { // Retrieve preferred delegates using legacy mechanism for backward // compatibility. initializePreferredDelegates(); for (PreferredDelegate pd : fPreferredDelegates) { if(pd.getModes().equals(modes) & pd.getTypeId().equals(typeid)) { return pd.getDelegate(); } } // @since 3.5 // If the legacy mechanism didn't work, try the new preference name for // the given launch type. String preferred = Platform.getPreferencesService().getString(DebugPlugin.getUniqueIdentifier(), "//" + LaunchManager.PREF_PREFERRED_DELEGATES + '/' + typeid, IInternalDebugCoreConstants.EMPTY_STRING, null); //$NON-NLS-1$ if (preferred != null && preferred.length() != 0) { StringTokenizer tokenizer = new StringTokenizer(preferred, ";"); //$NON-NLS-1$ while(tokenizer.hasMoreTokens()) { StringTokenizer tokenizer2 = new StringTokenizer(tokenizer.nextToken(), ","); //$NON-NLS-1$ String delegateId = tokenizer2.nextToken(); HashSet<String> modeset = new HashSet<String>(); while(tokenizer2.hasMoreTokens()) { modeset.add(tokenizer2.nextToken()); } LaunchDelegate delegate = getLaunchDelegateExtension(typeid, delegateId, modeset); if (delegate != null && modeset.equals(modes)) { return delegate; } } } return null; } /** * Returns the launch delegate extension that matches the given type, delegate ID, and * set of modes. * * @param typeId Launch configuration type. * @param id Launch delegate ID. * @param modeset Set of modes that the launch delegate applies to. * @return The launch delegate matching the specified parameters, or * <code>null</code> if not found. * * @since 3.5 */ private LaunchDelegate getLaunchDelegateExtension(String typeId, String id, Set<String> modeset) { LaunchDelegate[] extensions = getLaunchDelegates(typeId); for(int j = 0; j < extensions.length; j++) { if(id.equals(extensions[j].getId())) { List<Set<String>> modesets = extensions[j].getModes(); if(modesets.contains(modeset)) { return extensions[j]; } } } return null; } /** * Returns all launch configurations that are stored locally. * * @return collection of launch configurations stored locally */ protected synchronized List<ILaunchConfiguration> getLocalLaunchConfigurations() { List<ILaunchConfiguration> configs = new ArrayList<ILaunchConfiguration>(); for (ILaunchConfiguration config : getAllLaunchConfigurations()) { if (config.isLocal()) { configs.add(config); } } return configs; } /** * Returns the launch configurations mapping to the specified resource * @param resource the resource to collect mapped launch configurations for * @return a list of launch configurations if found or an empty list, never null * @since 3.2 */ public ILaunchConfiguration[] getMappedConfigurations(IResource resource) { List<ILaunchConfiguration> configurations = new ArrayList<ILaunchConfiguration>(); for (ILaunchConfiguration config : getAllLaunchConfigurations()) { try { IResource[] resources = config.getMappedResources(); if(resources != null) { for(int j = 0; j < resources.length; j++) { if(resources[j].equals(resource)) { configurations.add(config); break; } else if (resource.getType() == IResource.PROJECT && resources[j].getType() == IResource.FILE){ if (resources[j].getProject().equals(resource)) { configurations.add(config); break; } } } } } catch (CoreException ce) { DebugPlugin.log(ce); } } return configurations.toArray(new ILaunchConfiguration[configurations.size()]); } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchManager#getMigrationCandidates() */ @Override public ILaunchConfiguration[] getMigrationCandidates() throws CoreException { List<ILaunchConfiguration> configs = new ArrayList<ILaunchConfiguration>(); for (ILaunchConfiguration config : getAllLaunchConfigurations()) { if (!config.isReadOnly() && config.isMigrationCandidate()) { configs.add(config); } } return configs.toArray(new ILaunchConfiguration[configs.size()]); } /** * @see org.eclipse.debug.core.ILaunchManager#getMovedFrom(org.eclipse.debug.core.ILaunchConfiguration) */ @Override public ILaunchConfiguration getMovedFrom(ILaunchConfiguration addedConfiguration) { if (addedConfiguration.equals(fTo)) { return fFrom; } return null; } /** * @see org.eclipse.debug.core.ILaunchManager#getMovedTo(org.eclipse.debug.core.ILaunchConfiguration) */ @Override public ILaunchConfiguration getMovedTo(ILaunchConfiguration removedConfiguration) { if (removedConfiguration.equals(fFrom)) { return fTo; } return null; } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchManager#getNativeEnvironment() */ @Override public synchronized Map<String, String> getNativeEnvironment() { if (fgNativeEnv == null) { Map<String, String> casePreserved = getNativeEnvironmentCasePreserved(); if (Platform.getOS().equals(Constants.OS_WIN32)) { fgNativeEnv = new HashMap<String, String>(); for (Entry<String, String> entry : casePreserved.entrySet()) { fgNativeEnv.put(entry.getKey().toUpperCase(), entry.getValue()); } } else { fgNativeEnv = new HashMap<String, String>(casePreserved); } } return new HashMap<String, String>(fgNativeEnv); } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchManager#getNativeEnvironmentCasePreserved() */ @Override public synchronized Map<String, String> getNativeEnvironmentCasePreserved() { if (fgNativeEnvCasePreserved == null) { fgNativeEnvCasePreserved = new HashMap<String, String>(); cacheNativeEnvironment(fgNativeEnvCasePreserved); } return new HashMap<String, String>(fgNativeEnvCasePreserved); } /** * @see ILaunchManager#getProcesses() */ @Override public IProcess[] getProcesses() { synchronized (fLaunches) { List<IProcess> allProcesses = new ArrayList<IProcess>(fLaunches.size()); IProcess[] processes = null; for (ILaunch launch : fLaunches) { processes = launch.getProcesses(); for (int i= 0; i < processes.length; i++) { allProcesses.add(processes[i]); } } return allProcesses.toArray(new IProcess[allProcesses.size()]); } } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchManager#getSourceContainerType(java.lang.String) */ @Override public ISourceContainerType getSourceContainerType(String id) { initializeSourceContainerTypes(); return sourceContainerTypes.get(id); } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchManager#getSourceContainerTypes() */ @Override public ISourceContainerType[] getSourceContainerTypes() { initializeSourceContainerTypes(); Collection<ISourceContainerType> containers = sourceContainerTypes.values(); return containers.toArray(new ISourceContainerType[containers.size()]); } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchManager#newSourcePathComputer(org.eclipse.debug.core.ILaunchConfiguration) */ @Override public ISourcePathComputer getSourcePathComputer(ILaunchConfiguration configuration) throws CoreException { String id = null; id = configuration.getAttribute(ISourcePathComputer.ATTR_SOURCE_PATH_COMPUTER_ID, (String)null); if (id == null) { //use default computer for configuration type, if any return configuration.getType().getSourcePathComputer(); } return getSourcePathComputer(id); } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchManager#getSourcePathComputer(java.lang.String) */ @Override public ISourcePathComputer getSourcePathComputer(String id) { initializeSourceContainerTypes(); return sourcePathComputers.get(id); } /** * Starts listening for resource change events */ private synchronized void hookResourceChangeListener() { if (!fListening) { ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_DELETE); fListening = true; } } /** * Load comparator extensions. */ private synchronized void initializeComparators() { if (fComparators == null) { IExtensionPoint extensionPoint= Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.getUniqueIdentifier(), DebugPlugin.EXTENSION_POINT_LAUNCH_CONFIGURATION_COMPARATORS); IConfigurationElement[] infos= extensionPoint.getConfigurationElements(); fComparators = new HashMap<String, LaunchConfigurationComparator>(infos.length); IConfigurationElement configurationElement = null; String attr = null; for (int i= 0; i < infos.length; i++) { configurationElement = infos[i]; attr = configurationElement.getAttribute("attribute"); //$NON-NLS-1$ if (attr != null) { fComparators.put(attr, new LaunchConfigurationComparator(configurationElement)); } else { // invalid status handler IStatus s = new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.INTERNAL_ERROR, MessageFormat.format("Invalid launch configuration comparator extension defined by plug-in {0} - attribute not specified.", new Object[] { configurationElement.getContributor().getName() }), null); //$NON-NLS-1$ DebugPlugin.log(s); } } } } /** * Initializes the listing of <code>LaunchConfigurationType</code>s. */ private synchronized void initializeLaunchConfigurationTypes() { if (fLaunchConfigurationTypes == null) { hookResourceChangeListener(); IExtensionPoint extensionPoint= Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.getUniqueIdentifier(), DebugPlugin.EXTENSION_POINT_LAUNCH_CONFIGURATION_TYPES); IConfigurationElement[] infos = extensionPoint.getConfigurationElements(); fLaunchConfigurationTypes = new ArrayList<ILaunchConfigurationType>(infos.length); for (int i= 0; i < infos.length; i++) { fLaunchConfigurationTypes.add(new LaunchConfigurationType(infos[i])); } } } /** * Load comparator extensions. */ private synchronized void initializeLaunchModes() { if (fLaunchModes == null) { try { IExtensionPoint extensionPoint= Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.getUniqueIdentifier(), DebugPlugin.EXTENSION_POINT_LAUNCH_MODES); IConfigurationElement[] infos= extensionPoint.getConfigurationElements(); fLaunchModes = new HashMap<String, ILaunchMode>(); ILaunchMode mode = null; for (int i= 0; i < infos.length; i++) { mode = new LaunchMode(infos[i]); fLaunchModes.put(mode.getIdentifier(), mode); } } catch (CoreException e) {DebugPlugin.log(e);} } } /** * Initializes source container type and source path computer extensions. */ private synchronized void initializeSourceContainerTypes() { if (sourceContainerTypes == null) { IExtensionPoint extensionPoint= Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.getUniqueIdentifier(), DebugPlugin.EXTENSION_POINT_SOURCE_CONTAINER_TYPES); IConfigurationElement[] extensions = extensionPoint.getConfigurationElements(); sourceContainerTypes = new HashMap<String, ISourceContainerType>(); for (int i = 0; i < extensions.length; i++) { sourceContainerTypes.put( extensions[i].getAttribute(IConfigurationElementConstants.ID), new SourceContainerType(extensions[i])); } extensionPoint= Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.getUniqueIdentifier(), DebugPlugin.EXTENSION_POINT_SOURCE_PATH_COMPUTERS); extensions = extensionPoint.getConfigurationElements(); sourcePathComputers = new HashMap<String, ISourcePathComputer>(); for (int i = 0; i < extensions.length; i++) { sourcePathComputers.put( extensions[i].getAttribute(IConfigurationElementConstants.ID), new SourcePathComputer(extensions[i])); } } } /** * Register source locators. */ private synchronized void initializeSourceLocators() { if (fSourceLocators == null) { IExtensionPoint extensionPoint= Platform.getExtensionRegistry().getExtensionPoint(DebugPlugin.getUniqueIdentifier(), DebugPlugin.EXTENSION_POINT_SOURCE_LOCATORS); IConfigurationElement[] infos= extensionPoint.getConfigurationElements(); fSourceLocators = new HashMap<String, IConfigurationElement>(infos.length); IConfigurationElement configurationElement = null; String id = null; for (int i= 0; i < infos.length; i++) { configurationElement = infos[i]; id = configurationElement.getAttribute(IConfigurationElementConstants.ID); if (id != null) { fSourceLocators.put(id,configurationElement); } else { // invalid status handler IStatus s = new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.INTERNAL_ERROR, MessageFormat.format("Invalid source locator extension defined by plug-in \"{0}\": \"id\" not specified.", new Object[] { configurationElement.getContributor().getName() }), null); //$NON-NLS-1$ DebugPlugin.log(s); } } } } /** * Adds the given launch object to the list of registered launches, * and returns whether the launch was added. * * @param launch launch to register * @return whether the launch was added */ protected boolean internalAddLaunch(ILaunch launch) { // ensure the step filter manager is created on the first launch getStepFilterManager(); synchronized (fLaunches) { if (fLaunches.contains(launch)) { return false; } fLaunches.add(launch); fLaunchSet.add(launch); return true; } } /** * Removes the given launch object from the collection of registered * launches. Returns whether the launch was removed. * * @param launch the launch to remove * @return whether the launch was removed */ protected boolean internalRemoveLaunch(ILaunch launch) { if (launch == null) { return false; } synchronized (fLaunches) { fLaunchSet.remove(launch); return fLaunches.remove(launch); } } /** * @see ILaunchManager#isExistingLaunchConfigurationName(String) */ @Override public boolean isExistingLaunchConfigurationName(String name) { String[] sortedConfigNames = getAllSortedConfigNames(); int index = Arrays.binarySearch(sortedConfigNames, name); if (index < 0) { return false; } return true; } /** * Returns whether the given String is composed solely of digits * @param string the {@link String} to check * @return <code>true</code> if the given {@link String} is a number <code>false</code> otherwise */ private boolean isNumber(String string) { int numChars= string.length(); if (numChars == 0) { return false; } for (int i= 0; i < numChars; i++) { if (!Character.isDigit(string.charAt(i))) { return false; } } return true; } /** * Returns whether the user has selected to delete associated configurations when a * project is deleted. * * @return whether to auto-delete configurations */ private boolean isDeleteConfigurations() { return Platform.getPreferencesService().getBoolean(DebugPlugin.getUniqueIdentifier(), DebugPlugin.PREF_DELETE_CONFIGS_ON_PROJECT_DELETE, true, null); } /* (non-Javadoc) * @see org.eclipse.debug.core.ILaunchManager#isRegistered(org.eclipse.debug.core.ILaunch) */ @Override public boolean isRegistered(ILaunch launch) { synchronized (fLaunches) { return fLaunchSet.contains(launch); } } /** * Returns whether the given launch configuration passes a basic * integrity test by retrieving its type. * * @param config the configuration to verify * @return whether the config meets basic integrity constraints */ protected boolean isValid(ILaunchConfiguration config) { try { config.getType(); } catch (CoreException e) { if (e.getStatus().getCode() != DebugException.MISSING_LAUNCH_CONFIGURATION_TYPE) { // only log warnings due to something other than a missing // launch config type DebugPlugin.log(e); } return false; } return true; } /** * Notifies the launch manager that a launch configuration * has been added. The configuration is added to the index of * configurations by project, and listeners are notified. * * @param config the launch configuration that was added */ protected void launchConfigurationAdded(ILaunchConfiguration config) { if (config.isWorkingCopy()) { return; } if (isValid(config)) { boolean added = false; synchronized (this) { List<ILaunchConfiguration> allConfigs = getAllLaunchConfigurations(); if (!allConfigs.contains(config)) { allConfigs.add(config); added = true; } } if (added) { getConfigurationNotifier().notify(config, ADDED); clearConfigNameCache(); } } else { launchConfigurationDeleted(config); } } /** * Notifies the launch manager that a launch configuration * has been changed. The configuration is removed from the * cache of info objects such that the new attributes will * be updated on the next access. Listeners are notified of * the change. * * @param config the launch configuration that was changed */ protected void launchConfigurationChanged(ILaunchConfiguration config) { synchronized(this) { fLaunchConfigurations.remove(config); } clearConfigNameCache(); if (isValid(config)) { // in case the config has been refreshed and it was removed from the // index due to 'out of synch with local file system' (see bug 36147), // add it back (will only add if required) launchConfigurationAdded(config); getConfigurationNotifier().notify(config, CHANGED); } else { launchConfigurationDeleted(config); } } /** * Notifies the launch manager that a launch configuration * has been deleted. The configuration is removed from the * cache of info and from the index of configurations by * project, and listeners are notified. * * @param config the launch configuration that was deleted */ protected void launchConfigurationDeleted(ILaunchConfiguration config) { boolean removed = false; synchronized (this) { Object key = fLaunchConfigurations.remove(config); removed = key != null; getAllLaunchConfigurations().remove(config); } if (removed) { getConfigurationNotifier().notify(config, REMOVED); clearConfigNameCache(); } } /** * @see ILaunchManager#newSourceLocator(String) */ @Override public IPersistableSourceLocator newSourceLocator(String identifier) throws CoreException { initializeSourceLocators(); IConfigurationElement config = fSourceLocators.get(identifier); if (config == null) { throw new CoreException(new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.INTERNAL_ERROR, MessageFormat.format(DebugCoreMessages.LaunchManager_Source_locator_does_not_exist___0__13, new Object[] { identifier }), null)); } IPersistableSourceLocator sourceLocator = (IPersistableSourceLocator)config.createExecutableExtension("class"); //$NON-NLS-1$ if (sourceLocator instanceof AbstractSourceLookupDirector) { ((AbstractSourceLookupDirector)sourceLocator).setId(identifier); } return sourceLocator; } /** * The specified project has just closed - remove its * launch configurations from the cached index. * * @param project the project that has been closed */ protected void projectClosed(IProject project) { // bug 12134 terminateMappedConfigurations(project); for (ILaunchConfiguration config : getLaunchConfigurations(project)) { launchConfigurationDeleted(config); } } /** * The specified project has just opened - add all launch * configs in the project to the index of all configs. * * @param project the project that has been opened */ protected void projectOpened(IProject project) { for (ILaunchConfiguration config : findLaunchConfigurations(project)) { launchConfigurationAdded(config); } } /** * @see ILaunchManager#removeLaunch(ILaunch) */ @Override public void removeLaunch(final ILaunch launch) { if (internalRemoveLaunch(launch)) { fireUpdate(launch, REMOVED); fireUpdate(new ILaunch[] {launch}, REMOVED); } } /** * @see ILaunchManager#removeLaunchConfigurationListener(ILaunchConfigurationListener) */ @Override public void removeLaunchConfigurationListener(ILaunchConfigurationListener listener) { fLaunchConfigurationListeners.remove(listener); } /** * @see org.eclipse.debug.core.ILaunchManager#removeLaunches(org.eclipse.debug.core.ILaunch[]) */ @Override public void removeLaunches(ILaunch[] launches) { List<ILaunch> removed = new ArrayList<ILaunch>(launches.length); for (int i = 0; i < launches.length; i++) { if (internalRemoveLaunch(launches[i])) { removed.add(launches[i]); } } if (!removed.isEmpty()) { ILaunch[] removedLaunches = removed.toArray(new ILaunch[removed.size()]); fireUpdate(removedLaunches, REMOVED); for (int i = 0; i < removedLaunches.length; i++) { fireUpdate(removedLaunches[i], REMOVED); } } } /** * @see org.eclipse.debug.core.ILaunchManager#removeLaunchListener(org.eclipse.debug.core.ILaunchesListener) */ @Override public void removeLaunchListener(ILaunchesListener listener) { fLaunchesListeners.remove(listener); } /** * @see ILaunchManager#removeLaunchListener(ILaunchListener) */ @Override public void removeLaunchListener(ILaunchListener listener) { fListeners.remove(listener); } /** * Traverses the delta looking for added/removed/changed launch * configuration files. * * @see IResourceChangeListener#resourceChanged(IResourceChangeEvent) */ @Override public void resourceChanged(IResourceChangeEvent event) { IResourceDelta delta = event.getDelta(); if (delta != null) { LaunchManagerVisitor visitor = getDeltaVisitor(); MappedResourceVisitor v = null; if (isDeleteConfigurations()) { v = getMappedResourceVisitor(); } try { delta.accept(visitor); if (v != null) { delta.accept(v); } } catch (CoreException e) { DebugPlugin.log(e.getStatus()); } } } /** * Gets the launch configuration associated with the specified <code>IResource</code>. * This method relies on the resource mapping existing, if no such mapping * exists the launch configuration is ignored. * * @param resource the resource to collect launch configurations for * @return the list of associated launch configurations */ private ArrayList<ILaunchConfiguration> collectAssociatedLaunches(IResource resource) { ArrayList<ILaunchConfiguration> list = new ArrayList<ILaunchConfiguration>(); try { ILaunchConfiguration[] configs = DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurations(); IResource[] resources = null; for(int i = 0; i < configs.length; i++) { if(configs[i].isLocal()) { resources = configs[i].getMappedResources(); if(resources != null) { for(int j = 0; j < resources.length; j++){ if(resource.equals(resources[j]) || resource.getFullPath().isPrefixOf(resources[j].getFullPath())) { list.add(configs[i]); break; } } } } } } catch (CoreException e) { DebugPlugin.log(e); } return list; } /** * Indicates the given launch configuration is being moved from the given * location to the new location. * * @param from the location a launch configuration is being moved from, or * <code>null</code> * @param to the location a launch configuration is being moved to, * or <code>null</code> */ protected void setMovedFromTo(ILaunchConfiguration from, ILaunchConfiguration to) { fFrom = from; fTo = to; } /** * Terminates/Disconnects any active debug targets/processes. * Clears launch configuration types. */ public void shutdown() { fListeners = new ListenerList<>(); fLaunchesListeners = new ListenerList<>(); fLaunchConfigurationListeners = new ListenerList<>(); ILaunch[] launches = getLaunches(); ILaunch launch = null; for (int i= 0; i < launches.length; i++) { launch = launches[i]; if(launch != null) { try { if (launch instanceof IDisconnect) { IDisconnect disconnect = (IDisconnect)launch; if (disconnect.canDisconnect()) { disconnect.disconnect(); } } if (launch.canTerminate()) { launch.terminate(); } } catch (DebugException e) { DebugPlugin.log(e); } } } persistPreferredLaunchDelegates(); clearAllLaunchConfigurations(); fStepFilterManager = null; ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); } /** * Saves the listings of preferred launch delegates from all of the launch configuration types * * @since 3.3 */ public void persistPreferredLaunchDelegates() { ILaunchConfigurationType[] types = getLaunchConfigurationTypes(); for(int i = 0; i < types.length; i++) { persistPreferredLaunchDelegate((LaunchConfigurationType)types[i]); } } /** * Persists the given launch configuration delegate. * @param type Launch configuration type to persist * * @since 3.6 */ public void persistPreferredLaunchDelegate(LaunchConfigurationType type) { String preferenceName = PREF_PREFERRED_DELEGATES + '/' + type.getIdentifier(); Map<Set<String>, ILaunchDelegate> preferred = type.getPreferredDelegates(); if(preferred != null && preferred.size() > 0) { StringBuffer str = new StringBuffer(); for (Entry<Set<String>, ILaunchDelegate> entry : preferred.entrySet()) { Set<String> modes = entry.getKey(); ILaunchDelegate delegate = entry.getValue(); if (delegate != null) { str.append(delegate.getId()); str.append(','); for (String mode : modes) { str.append(mode).append(','); } str.append(';'); } } Preferences.setString(DebugPlugin.getUniqueIdentifier(), preferenceName, str.toString(), null); } else { Preferences.setToDefault(DebugPlugin.getUniqueIdentifier(), preferenceName); } // Reset the legacy preference string. Preferences.setToDefault(DebugPlugin.getUniqueIdentifier(), PREF_PREFERRED_DELEGATES); } /** * finds and terminates any running launch configurations associated with the given resource * @param resource the resource to search for launch configurations and hence launches for * @since 3.2 */ protected void terminateMappedConfigurations(IResource resource) { ILaunch[] launches = getLaunches(); ILaunchConfiguration[] configs = getMappedConfigurations(resource); try { for(int i = 0; i < launches.length; i++) { for(int j = 0; j < configs.length; j++) { if(configs[j].equals(launches[i].getLaunchConfiguration()) & launches[i].canTerminate()) { launches[i].terminate(); } } } } catch(CoreException e) {DebugPlugin.log(e);} } /** * Throws a debug exception with the given throwable that occurred * while processing the given configuration. * @param config the {@link ILaunchConfiguration} causing the exception * @param e the {@link Exception} to throw * @throws DebugException the new {@link DebugException} wrapping the given {@link Exception} and {@link ILaunchConfiguration} * @since 3.5 */ private void throwException(LaunchConfiguration config, Throwable e) throws DebugException { String uri = config.getName(); try { IFileStore store = config.getFileStore(); if (store != null) { uri = store.toString(); } } catch (CoreException ce) { } throw createDebugException(MessageFormat.format(DebugCoreMessages.LaunchManager__0__occurred_while_reading_launch_configuration_file__1___1, new Object[] { e.toString(), uri }), e); } /** * Verify basic integrity of launch configurations in the given list, * adding valid configurations to the collection of all launch configurations. * Exceptions are logged for invalid configurations. * * @param verify the list of configurations to verify * @param valid the list to place valid configurations in */ protected void verifyConfigurations(List<ILaunchConfiguration> verify, List<ILaunchConfiguration> valid) { for (ILaunchConfiguration config : verify) { if (!valid.contains(config) && isValid(config)) { valid.add(config); } } } /** * Returns the name of the given launch mode with accelerators removed, * or <code>null</code> if none. * * @param id launch mode identifier * @return launch mode name with accelerators removed or <code>null</code> */ public String getLaunchModeName(String id) { ILaunchMode launchMode = getLaunchMode(id); if (launchMode != null) { return removeAccelerators(launchMode.getLabel()); } return null; } /** * Returns the label with any accelerators removed. * * @param label label to process * @return label without accelerators */ public static String removeAccelerators(String label) { String title = label; if (title != null) { // strip out any '&' (accelerators) int index = title.indexOf('&'); if (index == 0) { title = title.substring(1); } else if (index > 0) { //DBCS languages use "(&X)" format if (title.charAt(index - 1) == '(' && title.length() >= index + 3 && title.charAt(index + 2) == ')') { String first = title.substring(0, index - 1); String last = title.substring(index + 3); title = first + last; } else if (index < (title.length() - 1)) { String first = title.substring(0, index); String last = title.substring(index + 1); title = first + last; } } } return title; } /** * Returns the singleton step filter manager. * * @return the step filter manager */ public synchronized StepFilterManager getStepFilterManager() { if (fStepFilterManager == null) { fStepFilterManager = new StepFilterManager(); } return fStepFilterManager; } /** * Imports launch configurations represented by the given local files, overwriting * any existing configurations. Sends launch configuration change notification * as required (i.e. added or changed). * <p> * If a file is imported that has the same name as a configuration in the workspace * (i.e. a shared configuration), the shared configuration is deleted (becomes local). * </p> * @param files files to import * @param monitor progress monitor or <code>null</code> * @throws CoreException if an exception occurs while importing configurations * @since 3.4.0 */ public void importConfigurations(File[] files, IProgressMonitor monitor) throws CoreException { Map<String, ILaunchConfiguration> sharedConfigs = new HashMap<String, ILaunchConfiguration>(); for (ILaunchConfiguration config : getAllLaunchConfigurations()) { if (!config.isLocal()) { StringBuffer buf = new StringBuffer(config.getName()); buf.append('.'); buf.append(ILaunchConfiguration.LAUNCH_CONFIGURATION_FILE_EXTENSION); sharedConfigs.put(buf.toString(), config); } } List<Status> stati = null; SubMonitor lmonitor = SubMonitor.convert(monitor, DebugCoreMessages.LaunchManager_29, files.length); for (int i = 0; i < files.length; i++) { if (lmonitor.isCanceled()) { break; } File source = files[i]; lmonitor.subTask(MessageFormat.format(DebugCoreMessages.LaunchManager_28, new Object[] { source.getName() })); IPath location = new Path(LOCAL_LAUNCH_CONFIGURATION_CONTAINER_PATH.toOSString()).append(source.getName()); File target = location.toFile(); IPath locationdir = location.removeLastSegments(1); if(!locationdir.toFile().exists()) { locationdir.toFile().mkdirs(); } boolean added = !target.exists(); try { copyFile(source, target); ILaunchConfiguration configuration = new LaunchConfiguration(LaunchConfiguration.getSimpleName(source.getName()), null); ILaunchConfiguration shared = sharedConfigs.get(target.getName()); if (shared != null) { setMovedFromTo(shared, configuration); shared.delete(); launchConfigurationChanged(configuration); } else if (added) { launchConfigurationAdded(configuration); } else { launchConfigurationChanged(configuration); } } catch (IOException e) { if (stati == null) { stati = new ArrayList<Status>(); } stati.add(new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugPlugin.ERROR, MessageFormat.format(DebugCoreMessages.LaunchManager_27, new Object[] { source.getPath() }), e)); } lmonitor.worked(1); } if (!lmonitor.isCanceled()) { lmonitor.done(); } if (stati != null) { if (stati.size() > 1) { MultiStatus multi = new MultiStatus(DebugPlugin.getUniqueIdentifier(), DebugPlugin.ERROR, DebugCoreMessages.LaunchManager_26, null); for (Status status : stati) { multi.add(status); } throw new CoreException(multi); } else { throw new CoreException(stati.get(0)); } } } /** * Copies a file from one location to another, replacing any existing file. * * @param in the file to copy * @param out the file to be copied out to * @throws IOException if the file read fails * @since 3.4.0 */ private void copyFile(File in, File out) throws IOException { try (FileInputStream fis = new FileInputStream(in); FileOutputStream fos = new FileOutputStream(out)) { byte[] buf = new byte[1024]; int i = 0; while ((i = fis.read(buf)) != -1) { fos.write(buf, 0, i); } } } /** * Returns whether any launch config supports the given mode. * * @param mode launch mode * @return whether any launch config supports the given mode */ public synchronized boolean launchModeAvailable(String mode) { if (fActiveModes == null) { ILaunchConfigurationType[] types = getLaunchConfigurationTypes(); ILaunchMode[] modes = getLaunchModes(); fActiveModes = new HashSet<String>(3); for (int i = 0; i < types.length; i++) { for (int j = 0; j < modes.length; j++) { if (types[i].supportsMode(modes[j].getIdentifier())) { fActiveModes.add(modes[j].getIdentifier()); } } } } return fActiveModes.contains(mode); } /** * @see org.eclipse.debug.core.ILaunchManager#generateLaunchConfigurationName(java.lang.String) */ @Override public String generateLaunchConfigurationName(String namePrefix) { String name = generateUniqueLaunchConfigurationNameFrom(namePrefix); try { isValidLaunchConfigurationName(name); return name; } catch(IllegalArgumentException iae) { //blanket change all reserved names if(Platform.OS_WIN32.equals(Platform.getOS())) { for(int i = 0; i < UNSUPPORTED_WIN32_CONFIG_NAMES.length; i++) { if(UNSUPPORTED_WIN32_CONFIG_NAMES[i].equals(name)) { name = "launch_configuration"; //$NON-NLS-1$ } } } //blanket replace all invalid chars for (int i = 0; i < DISALLOWED_CONFIG_NAME_CHARS.length; i++) { name = name.replace(DISALLOWED_CONFIG_NAME_CHARS[i], '_'); } } //run it through the generator once more in case a replaced name has already been done return generateUniqueLaunchConfigurationNameFrom(name); } /** * @see org.eclipse.debug.core.ILaunchManager#isValidLaunchConfigurationName(java.lang.String) */ @Override public boolean isValidLaunchConfigurationName(String configname) throws IllegalArgumentException { if(Platform.OS_WIN32.equals(Platform.getOS())) { for(int i = 0; i < UNSUPPORTED_WIN32_CONFIG_NAMES.length; i++) { if(configname.equals(UNSUPPORTED_WIN32_CONFIG_NAMES[i])) { throw new IllegalArgumentException(MessageFormat.format(DebugCoreMessages.LaunchManager_invalid_config_name, new Object[] { configname })); } } } for (int i = 0; i < DISALLOWED_CONFIG_NAME_CHARS.length; i++) { if (configname.indexOf(DISALLOWED_CONFIG_NAME_CHARS[i]) > -1) { throw new IllegalArgumentException(MessageFormat.format(DebugCoreMessages.LaunchManager_invalid_config_name_char, new Object[] { String.valueOf(DISALLOWED_CONFIG_NAME_CHARS[i]) })); } } return true; } }