cdt/cdt_5_0_x/org.eclipse.cdt.debug.core/src/org/eclipse/cdt/debug/core/executables/ExecutablesManager.java
changeset 14 c50c3d06898c
parent 0 0e6d23e2b466
child 15 3ac8c55882b5
equal deleted inserted replaced
8:2cfb52d98e82 14:c50c3d06898c
     9  * Nokia - Initial API and implementation
     9  * Nokia - Initial API and implementation
    10  *******************************************************************************/
    10  *******************************************************************************/
    11 
    11 
    12 package org.eclipse.cdt.debug.core.executables;
    12 package org.eclipse.cdt.debug.core.executables;
    13 
    13 
       
    14 import java.text.DateFormat;
    14 import java.util.ArrayList;
    15 import java.util.ArrayList;
    15 import java.util.Arrays;
       
    16 import java.util.Collection;
    16 import java.util.Collection;
    17 import java.util.Collections;
    17 import java.util.Collections;
    18 import java.util.Comparator;
    18 import java.util.Comparator;
       
    19 import java.util.Date;
    19 import java.util.HashMap;
    20 import java.util.HashMap;
    20 import java.util.List;
    21 import java.util.List;
    21 
    22 import java.util.Map;
       
    23 
       
    24 import org.eclipse.cdt.core.model.CoreModel;
       
    25 import org.eclipse.cdt.core.settings.model.CProjectDescriptionEvent;
       
    26 import org.eclipse.cdt.core.settings.model.ICProjectDescription;
       
    27 import org.eclipse.cdt.core.settings.model.ICProjectDescriptionListener;
    22 import org.eclipse.cdt.debug.core.CDebugCorePlugin;
    28 import org.eclipse.cdt.debug.core.CDebugCorePlugin;
       
    29 import org.eclipse.core.resources.IProject;
       
    30 import org.eclipse.core.resources.IProjectDescription;
       
    31 import org.eclipse.core.resources.IResource;
       
    32 import org.eclipse.core.resources.IResourceChangeEvent;
       
    33 import org.eclipse.core.resources.IResourceChangeListener;
       
    34 import org.eclipse.core.resources.IResourceDelta;
       
    35 import org.eclipse.core.resources.IResourceDeltaVisitor;
       
    36 import org.eclipse.core.resources.ResourcesPlugin;
       
    37 import org.eclipse.core.runtime.CoreException;
       
    38 import org.eclipse.core.runtime.IConfigurationElement;
       
    39 import org.eclipse.core.runtime.IExtension;
       
    40 import org.eclipse.core.runtime.IExtensionPoint;
       
    41 import org.eclipse.core.runtime.IExtensionRegistry;
    23 import org.eclipse.core.runtime.IPath;
    42 import org.eclipse.core.runtime.IPath;
    24 import org.eclipse.core.runtime.IProgressMonitor;
    43 import org.eclipse.core.runtime.IProgressMonitor;
    25 import org.eclipse.core.runtime.IStatus;
    44 import org.eclipse.core.runtime.IStatus;
    26 import org.eclipse.core.runtime.MultiStatus;
    45 import org.eclipse.core.runtime.MultiStatus;
       
    46 import org.eclipse.core.runtime.Platform;
    27 import org.eclipse.core.runtime.PlatformObject;
    47 import org.eclipse.core.runtime.PlatformObject;
    28 import org.eclipse.core.runtime.Status;
    48 import org.eclipse.core.runtime.Status;
       
    49 import org.eclipse.core.runtime.SubMonitor;
    29 import org.eclipse.core.runtime.SubProgressMonitor;
    50 import org.eclipse.core.runtime.SubProgressMonitor;
    30 import org.eclipse.core.runtime.jobs.Job;
    51 import org.eclipse.core.runtime.jobs.Job;
    31 import org.eclipse.debug.core.DebugPlugin;
    52 import org.eclipse.osgi.service.debug.DebugOptions;
       
    53 import org.osgi.framework.BundleContext;
       
    54 import org.osgi.framework.ServiceReference;
    32 
    55 
    33 /**
    56 /**
    34  * The Executables Manager maintains a collection of executables built by all of
    57  * The Executables Manager maintains a collection of executables built by all of
    35  * the projects in the workspace. Executables are contributed by instances of
    58  * the projects in the workspace. Executables are contributed by instances of
    36  * IExecutablesProvider.
    59  * IExecutablesProvider.
    37  * 
    60  * 
    38  * @author Ken Ryall
    61  * @author Ken Ryall
    39  * 
    62  * 
    40  */
    63  */
    41 public class ExecutablesManager extends PlatformObject {
    64 public class ExecutablesManager extends PlatformObject implements IResourceChangeListener, ICProjectDescriptionListener {
    42 
    65 
    43 	private HashMap<String, Executable> executables = new HashMap<String, Executable>();
    66 	private static final String EXECUTABLES_MANAGER_DEBUG_TRACING = CDebugCorePlugin.PLUGIN_ID + "EXECUTABLES_MANAGER_DEBUG_TRACING"; //$NON-NLS-1$
       
    67 	
       
    68 	private Map<IProject, IProjectExecutablesProvider> executablesProviderMap = new HashMap<IProject, IProjectExecutablesProvider>();
       
    69 	private Map<IProject, List<Executable>> executablesMap = new HashMap<IProject, List<Executable>>();
    44 	private List<IExecutablesChangeListener> changeListeners = Collections.synchronizedList(new ArrayList<IExecutablesChangeListener>());
    70 	private List<IExecutablesChangeListener> changeListeners = Collections.synchronizedList(new ArrayList<IExecutablesChangeListener>());
    45 	private List<ISourceFileRemapping> sourceFileRemappings = Collections.synchronizedList(new ArrayList<ISourceFileRemapping>());
    71 	private List<IProjectExecutablesProvider> executableProviders;
    46 	private List<IExecutableProvider> executableProviders = Collections.synchronizedList(new ArrayList<IExecutableProvider>());
    72 	private List<ISourceFilesProvider> sourceFileProviders;
    47 	private List<ISourceFilesProvider> sourceFileProviders = Collections.synchronizedList(new ArrayList<ISourceFilesProvider>());
    73 	private List<ISourceFileRemapping> sourceFileRemappings;
    48 	private List<IExecutableImporter> executableImporters = Collections.synchronizedList(new ArrayList<IExecutableImporter>());
    74 	private List<IExecutableImporter> executableImporters;
    49 	private boolean refreshNeeded = true;
    75 	
    50 	private boolean tempDisableRefresh = false;
    76 	private boolean DEBUG;
    51 	
    77 	
    52 	private Job refreshJob = new Job("Get Executables") {
    78 	private Job refreshJob = new Job("Get Executables") {
    53 
    79 
    54 		@Override
    80 		@Override
    55 		public IStatus run(IProgressMonitor monitor) {
    81 		public IStatus run(IProgressMonitor monitor) {
    56 			refreshExecutables(monitor);
    82 				
       
    83 			trace("Get Executables job started at " + getStringFromTimestamp(System.currentTimeMillis()));
       
    84 			
       
    85 			List<IProject> projects = getProjectsToCheck();
       
    86 
       
    87 			SubMonitor subMonitor = SubMonitor.convert(monitor, projects.size());
       
    88 
       
    89 			for (IProject project : projects) {
       
    90 				if (subMonitor.isCanceled()) {
       
    91 					trace("Get Executables job cancelled at " + getStringFromTimestamp(System.currentTimeMillis()));
       
    92 					return Status.CANCEL_STATUS;
       
    93 				}
       
    94 				
       
    95 				subMonitor.subTask("Checking project: " + project.getName());
       
    96 
       
    97 				// get the executables provider for this project
       
    98 				IProjectExecutablesProvider provider = getExecutablesProviderForProject(project);
       
    99 				if (provider != null) {
       
   100 					trace("Getting executables for project: " + project.getName() + " using " + provider.toString());
       
   101 
       
   102 					// store the list of executables for this project
       
   103 					synchronized (executablesMap) {
       
   104 						executablesMap.put(project, provider.getExecutables(project, subMonitor.newChild(1, SubMonitor.SUPPRESS_NONE)));
       
   105 					}
       
   106 				}
       
   107 			}
       
   108 			
       
   109 			// notify the listeners
       
   110 			synchronized (changeListeners) {
       
   111 				for (IExecutablesChangeListener listener : changeListeners) {
       
   112 					listener.executablesListChanged();
       
   113 				}
       
   114 			}
       
   115 
       
   116 			trace("Get Executables job finished at " + getStringFromTimestamp(System.currentTimeMillis()));
       
   117 
    57 			return Status.OK_STATUS;
   118 			return Status.OK_STATUS;
    58 		}
   119 		}
    59 	};
   120 	};
    60 
   121 
    61 	private static ExecutablesManager executablesManager = null;
   122 	private static ExecutablesManager executablesManager = null;
    62 
   123 
       
   124 	/**
       
   125 	 * Get the executables manager instance
       
   126 	 * @return the executables manager
       
   127 	 */
    63 	public static ExecutablesManager getExecutablesManager() {
   128 	public static ExecutablesManager getExecutablesManager() {
    64 		if (executablesManager == null)
   129 		if (executablesManager == null)
    65 			executablesManager = new ExecutablesManager();
   130 			executablesManager = new ExecutablesManager();
    66 		return executablesManager;
   131 		return executablesManager;
    67 	}
   132 	}
    68 
   133 
    69 	public ExecutablesManager() {
   134 	public ExecutablesManager() {
    70 		addSourceFileRemapping(new StandardSourceFileRemapping());
   135 		
    71 		addExecutableImporter(new StandardExecutableImporter());
   136 		// check if debugging is enabled
    72 		addExecutablesProvider(new StandardExecutableProvider());
   137 		BundleContext context = CDebugCorePlugin.getDefault().getBundle().getBundleContext();
    73 		addSourceFilesProvider(new StandardSourceFilesProvider());
   138 		if (context != null) {
    74 	}
   139 			ServiceReference reference = CDebugCorePlugin.getDefault().getBundle().getBundleContext().getServiceReference(DebugOptions.class.getName());
    75 
   140 			if (reference != null) {
       
   141 				DebugOptions service = (DebugOptions) context.getService(reference);
       
   142 				if (service != null) {
       
   143 					try {
       
   144 						DEBUG = service.getBooleanOption(EXECUTABLES_MANAGER_DEBUG_TRACING, false);
       
   145 					} finally {
       
   146 						// we have what we want - release the service
       
   147 						context.ungetService(reference);
       
   148 					}
       
   149 				}
       
   150 			}
       
   151 		}
       
   152 
       
   153 		refreshJob.setPriority(Job.SHORT);
       
   154 		
       
   155 		// load the extension points
       
   156 		loadExecutableProviderExtensions();
       
   157 		loadSoureFileProviderExtensions();
       
   158 		loadSoureRemappingExtensions();
       
   159 		loadExecutableImporterExtensions();
       
   160 		
       
   161 		// add the standard providers
       
   162 		executableProviders.add(0, new StandardExecutableProvider());
       
   163 		sourceFileProviders.add(0, new StandardSourceFilesProvider());
       
   164 		sourceFileRemappings.add(0, new StandardSourceFileRemapping());
       
   165 		executableImporters.add(0, new StandardExecutableImporter());
       
   166 		
       
   167 		// listen for events we're interested in
       
   168 		ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.POST_BUILD);
       
   169 		CoreModel.getDefault().getProjectDescriptionManager().addCProjectDescriptionListener(this,
       
   170 				CProjectDescriptionEvent.DATA_APPLIED);
       
   171 		
       
   172 		// schedule a refresh so we get up to date
       
   173 		scheduleRefresh();
       
   174 	}
       
   175 
       
   176 	/**
       
   177 	 * Adds an executable listener
       
   178 	 * @param listener the listener to add
       
   179 	 */
    76 	public void addExecutablesChangeListener(IExecutablesChangeListener listener) {
   180 	public void addExecutablesChangeListener(IExecutablesChangeListener listener) {
    77 		changeListeners.add(listener);
   181 		changeListeners.add(listener);
    78 	}
   182 	}
    79 
   183 
       
   184 	/**
       
   185 	 * Removes an executable listener
       
   186 	 * @param listener the listener to remove
       
   187 	 */
    80 	public void removeExecutablesChangeListener(IExecutablesChangeListener listener) {
   188 	public void removeExecutablesChangeListener(IExecutablesChangeListener listener) {
    81 		changeListeners.remove(listener);
   189 		changeListeners.remove(listener);
    82 	}
   190 	}
    83 
   191 
    84 	public void addSourceFileRemapping(ISourceFileRemapping remapping) {
   192 	/**
    85 		sourceFileRemappings.add(remapping);
   193 	 * Gets the list of executables in the workspace.
    86 	}
   194 	 * @return the list of executables which may be empty
    87 
   195 	 */
    88 	public void removeSourceFileRemapping(ISourceFileRemapping remapping) {
   196 	public Collection<Executable> getExecutables() {
    89 		sourceFileRemappings.remove(remapping);
   197 		
    90 	}
   198 		trace("getExecutables called at " + getStringFromTimestamp(System.currentTimeMillis()));
    91 
   199 
    92 	public void addExecutableImporter(IExecutableImporter importer) {
   200 		List<Executable> executables = new ArrayList<Executable>();
    93 		executableImporters.add(importer);
   201 
    94 	}
   202 		synchronized (executablesMap) {
    95 
   203 			for (List<Executable> exes : executablesMap.values()) {
    96 	public void removeExecutableImporter(IExecutableImporter importer) {
   204 				for (Executable exe : exes) {
    97 		executableImporters.remove(importer);
   205 					if (!executables.contains(exe)) {
    98 	}
   206 						executables.add(exe);
    99 
   207 					}
   100 	public void addExecutablesProvider(IExecutableProvider provider) {
   208 				}
   101 		executableProviders.add(provider);
   209 			}
   102 	}
   210 		}
   103 
   211 
   104 	public void addSourceFilesProvider(ISourceFilesProvider provider) {
   212 		trace("getExecutables returned at " + getStringFromTimestamp(System.currentTimeMillis()));
   105 		sourceFileProviders.add(provider);
   213 
   106 	}
   214 		return executables;
   107 
   215 	}
   108 	public void removeSourceFilesProvider(ISourceFilesProvider provider) {
   216 
   109 		sourceFileProviders.remove(provider);
   217 	/**
   110 	}
   218 	 * Gets the collection of executables for the given project
   111 
   219 	 * @param project the project
   112 	public void removeExecutablesProvider(IExecutableProvider provider) {
   220 	 * @return collection of executables which may be empty
   113 		executableProviders.remove(provider);
   221 	 */
   114 	}
   222 	public Collection<Executable> getExecutablesForProject(IProject project) {
   115 
   223 		List<Executable> executables = new ArrayList<Executable>();
   116 	public IStatus refreshExecutables(IProgressMonitor monitor) {
   224 
   117 		if (tempDisableRefresh) {
   225 		synchronized (executablesMap) {
   118 			return Status.OK_STATUS;
   226 			List<Executable> exes = executablesMap.get(project);
   119 		}
   227 			if (exes != null) {
   120 
   228 				for (Executable exe : exes) {
   121 		
   229 					if (!executables.contains(exe)) {
   122 		synchronized (executables) {
   230 						executables.add(exe);
   123 			HashMap<String, Executable> oldList = new HashMap<String, Executable>(executables);
   231 					}
   124 			executables.clear();
   232 				}
   125 
   233 			}
   126 			IExecutableProvider[] exeProviders = getExecutableProviders();
   234 		}
   127 
   235 
   128 			Arrays.sort(exeProviders, new Comparator<IExecutableProvider>() {
   236 		return executables;
   129 
   237 	}
   130 				public int compare(IExecutableProvider arg0, IExecutableProvider arg1) {
   238 
   131 					int p0 = arg0.getPriority();
   239 	/**
   132 					int p1 = arg1.getPriority();
   240 	 * Attempt to remap the path to the given source file in the given executable using
   133 					if (p0 > p1)
   241 	 * source file mapping extensions
   134 						return 1;
   242 	 * @param executable the executable
   135 					if (p0 < p1)
   243 	 * @param filePath the absolute path to the source file
   136 						return -1;
   244 	 * @return the new path to the source file, which was remapped if possible
   137 					return 0;
   245 	 */
   138 				}});
       
   139 
       
   140 			refreshNeeded = false;
       
   141 			monitor.beginTask("Refresh Executables", exeProviders.length);
       
   142 			for (IExecutableProvider provider : exeProviders) {
       
   143 				Executable[] exes = provider.getExecutables(new SubProgressMonitor(monitor, 1));
       
   144 				for (Executable executable : exes) {
       
   145 					executables.put(executable.getPath().toOSString(), executable);
       
   146 				}
       
   147 			}
       
   148 			monitor.done();
       
   149 
       
   150 			synchronized (changeListeners) {
       
   151 				Collection<Executable> newExes = executables.values();
       
   152 				Executable[] exeArray = newExes.toArray(new Executable[newExes.size()]);
       
   153 				Collection<Executable> oldExes = oldList.values();
       
   154 				Executable[] oldArray = oldExes.toArray(new Executable[oldExes.size()]);				
       
   155 				for (IExecutablesChangeListener listener : changeListeners) {
       
   156 					listener.executablesChanged(new ExecutablesChangeEvent(oldArray, exeArray));
       
   157 				}
       
   158 			}
       
   159 		}
       
   160 
       
   161 		return monitor.isCanceled() ? Status.CANCEL_STATUS : Status.OK_STATUS;
       
   162 	}
       
   163 
       
   164 	public Executable[] getExecutables() {
       
   165 		if (refreshNeeded) {
       
   166 			try {
       
   167 				refreshJob.schedule();
       
   168 				refreshJob.join();
       
   169 			} catch (InterruptedException e) {
       
   170 				DebugPlugin.log( e );
       
   171 			}
       
   172 		}
       
   173 		
       
   174 		synchronized (executables)
       
   175 		{
       
   176 			Collection<Executable> exes = executables.values();
       
   177 			return exes.toArray(new Executable[exes.size()]);
       
   178 		}
       
   179 	}
       
   180 
       
   181 	public String remapSourceFile(Executable executable, String filePath) {
   246 	public String remapSourceFile(Executable executable, String filePath) {
   182 		synchronized (sourceFileRemappings) {
   247 		synchronized (sourceFileRemappings) {
   183 			for (ISourceFileRemapping remapping : sourceFileRemappings) {
   248 			for (ISourceFileRemapping remapping : sourceFileRemappings) {
   184 				String remappedPath = remapping.remapSourceFile(executable.getPath(), filePath);
   249 				String remappedPath = remapping.remapSourceFile(executable.getPath(), filePath);
   185 				if (!remappedPath.equals(filePath))
   250 				if (!remappedPath.equals(filePath))
   187 			}
   252 			}
   188 		}
   253 		}
   189 		return filePath;
   254 		return filePath;
   190 	}
   255 	}
   191 
   256 
       
   257 	/**
       
   258 	 * Import the given executables into the manager
       
   259 	 * @param fileNames the absolute paths of the executables to import
       
   260 	 * @param monitor progress monitor
       
   261 	 */
   192 	public void importExecutables(final String[] fileNames, IProgressMonitor monitor) {
   262 	public void importExecutables(final String[] fileNames, IProgressMonitor monitor) {
   193 		try {
   263 
   194 			
   264 		monitor.beginTask("Import Executables", executableImporters.size());
   195 			tempDisableRefresh = true;
   265 		synchronized (executableImporters) {
   196 			monitor.beginTask("Import Executables", executableImporters.size());
   266 			Collections.sort(executableImporters, new Comparator<IExecutableImporter>() {
   197 			synchronized (executableImporters) {
   267 
   198 				Collections.sort(executableImporters, new Comparator<IExecutableImporter>() {
   268 				public int compare(IExecutableImporter arg0, IExecutableImporter arg1) {
   199 
   269 					int p0 = arg0.getPriority(fileNames);
   200 					public int compare(IExecutableImporter arg0, IExecutableImporter arg1) {
   270 					int p1 = arg1.getPriority(fileNames);
   201 						int p0 = arg0.getPriority(fileNames);
   271 					if (p0 < p1)
   202 						int p1 = arg1.getPriority(fileNames);
   272 						return 1;
   203 						if (p0 < p1)
   273 					if (p0 > p1)
   204 							return 1;
   274 						return -1;
   205 						if (p0 > p1)
   275 					return 0;
   206 							return -1;
   276 				}});
   207 						return 0;
   277 
   208 					}});
   278 			for (IExecutableImporter importer : executableImporters) {
   209 
   279 				boolean handled = importer.importExecutables(fileNames, new SubProgressMonitor(monitor, 1));
   210 				for (IExecutableImporter importer : executableImporters) {
   280 				if (handled || monitor.isCanceled()) {
   211 					boolean handled = importer.importExecutables(fileNames, new SubProgressMonitor(monitor, 1));
   281 					break;
   212 					if (handled || monitor.isCanceled()) {
   282 				}
   213 						break;
   283 			}
   214 					}
   284 		}
   215 				}
   285 		
   216 			}
   286 		scheduleRefresh();
   217 
   287 	}
   218 		} finally {
   288 
   219 			tempDisableRefresh = false;
   289 	/**
   220 		}
   290 	 * Determines if the given executable is currently known by the manager
   221 		
   291 	 * @param exePath the absolute path to the executable
   222 		refreshExecutables(monitor);
   292 	 * @return true if the manager knows about it, false otherwise
   223 		monitor.done();
   293 	 */
   224 	}
       
   225 
       
   226 	public ISourceFileRemapping[] getSourceFileRemappings() {
       
   227 		return sourceFileRemappings.toArray(new ISourceFileRemapping[sourceFileRemappings.size()]);
       
   228 	}
       
   229 
       
   230 	public IExecutableProvider[] getExecutableProviders() {
       
   231 		return executableProviders.toArray(new IExecutableProvider[executableProviders.size()]);
       
   232 	}
       
   233 
       
   234 	public ISourceFilesProvider[] getSourceFileProviders() {
       
   235 		return sourceFileProviders.toArray(new ISourceFilesProvider[sourceFileProviders.size()]);
       
   236 	}
       
   237 
       
   238 	public IExecutableImporter[] getExecutableImporters() {
       
   239 		return executableImporters.toArray(new IExecutableImporter[executableImporters.size()]);
       
   240 	}
       
   241 
       
   242 	public void scheduleRefresh(IExecutableProvider provider, long delay) {
       
   243 		refreshNeeded = true;
       
   244 		refreshJob.schedule(delay);
       
   245 	}
       
   246 
       
   247 	public boolean refreshNeeded() {
       
   248 		return refreshNeeded;
       
   249 	}
       
   250 	
       
   251 	public boolean executableExists(IPath exePath) {
   294 	public boolean executableExists(IPath exePath) {
   252 		synchronized (executables) {
   295 		synchronized (executablesMap) {
   253 			return executables.containsKey(exePath.toOSString());			
   296 			for (List<Executable> exes : executablesMap.values()) {
   254 		}
   297 				for (Executable exe : exes) {
   255 	}
   298 					if (exe.getPath().equals(exePath)) {
   256 
   299 						return true;
   257 	public String[] getSourceFiles(final Executable executable,
   300 					}
   258 			IProgressMonitor monitor) {
   301 				}
       
   302 			}
       
   303 		}
       
   304 		
       
   305 		return false;
       
   306 	}
       
   307 
       
   308 	/**
       
   309 	 * Get the list of source files for the given executable
       
   310 	 * @param executable the executable
       
   311 	 * @param monitor progress monitor
       
   312 	 * @return an array of source files which may be empty
       
   313 	 */
       
   314 	public String[] getSourceFiles(final Executable executable, IProgressMonitor monitor) {
   259 		String[] result = new String[0];
   315 		String[] result = new String[0];
       
   316 
       
   317 		trace("getSourceFiles called at " + getStringFromTimestamp(System.currentTimeMillis()) + " for " + executable.getPath().toOSString());
       
   318 
   260 		synchronized (sourceFileProviders) {
   319 		synchronized (sourceFileProviders) {
   261 			Collections.sort(sourceFileProviders, new Comparator<ISourceFilesProvider>() {
   320 			Collections.sort(sourceFileProviders, new Comparator<ISourceFilesProvider>() {
   262 
   321 
   263 				public int compare(ISourceFilesProvider arg0, ISourceFilesProvider arg1) {
   322 				public int compare(ISourceFilesProvider arg0, ISourceFilesProvider arg1) {
   264 					int p0 = arg0.getPriority(executable);
   323 					int p0 = arg0.getPriority(executable);
   271 				}});
   330 				}});
   272 			
   331 			
   273 			monitor.beginTask("Finding source files in " + executable.getName(), sourceFileProviders.size());
   332 			monitor.beginTask("Finding source files in " + executable.getName(), sourceFileProviders.size());
   274 			for (ISourceFilesProvider provider : sourceFileProviders) {
   333 			for (ISourceFilesProvider provider : sourceFileProviders) {
   275 				String[] sourceFiles = provider.getSourceFiles(executable, new SubProgressMonitor(monitor, 1));
   334 				String[] sourceFiles = provider.getSourceFiles(executable, new SubProgressMonitor(monitor, 1));
   276 				if (sourceFiles.length > 0)
   335 				if (sourceFiles.length > 0) {
   277 				{
       
   278 					result = sourceFiles;
   336 					result = sourceFiles;
       
   337 
       
   338 					trace("getSourceFiles got " + sourceFiles.length + " files from " + provider.toString());
       
   339 
   279 					break;
   340 					break;
   280 				}
   341 				}
   281 			}
   342 			}
   282 			monitor.done();
   343 			monitor.done();
   283 		}
   344 		}
       
   345 
       
   346 		trace("getSourceFiles returned at " + getStringFromTimestamp(System.currentTimeMillis()));
       
   347 
   284 		return result;
   348 		return result;
   285 	}
   349 	}
   286 
   350 
       
   351 	/**
       
   352 	 * Removes the given executables
       
   353 	 * @param executables the array of executables to be removed
       
   354 	 * @param monitor progress monitor
       
   355 	 * @return IStatus of the operation
       
   356 	 */
   287 	public IStatus removeExecutables(Executable[] executables, IProgressMonitor monitor) {
   357 	public IStatus removeExecutables(Executable[] executables, IProgressMonitor monitor) {
   288 		IExecutableProvider[] exeProviders = getExecutableProviders();
   358 		MultiStatus status = new MultiStatus(CDebugCorePlugin.PLUGIN_ID, IStatus.WARNING, "Couldn't remove all of the selected executables", null);
   289 
   359 		
   290 		IStatus result = Status.OK_STATUS;
   360 		monitor.beginTask("Remove Executables", executables.length);
   291 		
       
   292 		Arrays.sort(exeProviders, new Comparator<IExecutableProvider>() {
       
   293 
       
   294 			public int compare(IExecutableProvider arg0, IExecutableProvider arg1) {
       
   295 				int p0 = arg0.getPriority();
       
   296 				int p1 = arg1.getPriority();
       
   297 				if (p0 > p1)
       
   298 					return 1;
       
   299 				if (p0 < p1)
       
   300 					return -1;
       
   301 				return 0;
       
   302 			}
       
   303 		});
       
   304 
       
   305 		MultiStatus combinedStatus = new MultiStatus(CDebugCorePlugin.PLUGIN_ID, IStatus.WARNING, "Couldn't remove all of the selected executables", null);
       
   306 		refreshNeeded = false;
       
   307 		monitor.beginTask("Remove Executables", exeProviders.length);
       
   308 		for (Executable executable : executables) {
   361 		for (Executable executable : executables) {
   309 			boolean handled = false;
   362 			
   310 			IStatus rmvStatus = Status.OK_STATUS;;
   363 			IProjectExecutablesProvider provider = getExecutablesProviderForProject(executable.getProject());
   311 			for (IExecutableProvider provider : exeProviders) {
   364 			if (provider != null) {
   312 				if (!handled)
   365 				IStatus result = provider.removeExecutable(executable, new SubProgressMonitor(monitor, 1));
   313 				{
   366 				if (result.isOK()) {
   314 					rmvStatus = provider.removeExecutable(executable, new SubProgressMonitor(monitor, 1));
   367 					// remove the exe from the list
   315 					handled = rmvStatus.getSeverity() == IStatus.OK;
   368 					List<Executable> exes = executablesMap.get(executable.getProject());
   316 				}				
   369 					if (exes != null) {
   317 			}
   370 						exes.remove(executable);
   318 			if (!handled)
   371 					}
   319 			{
   372 				} else {
   320 				combinedStatus.add(rmvStatus);
   373 					status.add(result);
   321 				result = combinedStatus;
   374 				}
   322 			}
   375 			}
   323 		}
   376 		}
   324 		monitor.done();
   377 
   325 		
   378 		// notify listeners that the list has changed.  only do this if at least one delete succeeded.
   326 		return result;
   379 		if (status.getChildren().length != executables.length) {
   327 	}
   380 			synchronized (changeListeners) {
   328 
   381 				for (IExecutablesChangeListener listener : changeListeners) {
   329 	public void setRefreshNeeded(boolean refresh) {
   382 					listener.executablesListChanged();
   330 		refreshNeeded = true;
   383 				}
   331 	}
   384 			}
   332 
   385 		}
       
   386 		
       
   387 		return status;
       
   388 	}
       
   389 
       
   390 	/**
       
   391 	 * Refresh the list of executables for the given projects
       
   392 	 * @param projects the list of projects, or null.  if null or the list
       
   393 	 * is empty, all projects will be refreshed.
       
   394 	 */
       
   395 	public void refresh(List<IProject> projects) {
       
   396 		if (projects == null || projects.size() == 0) {
       
   397 			// clear the entire cache
       
   398 			executablesMap.clear();
       
   399 		} else {
       
   400 			for (IProject project : projects) {
       
   401 				executablesMap.remove(project);
       
   402 			}
       
   403 		}
       
   404 		
       
   405 		scheduleRefresh();
       
   406 	}
       
   407 
       
   408 	public void resourceChanged(IResourceChangeEvent event) {
       
   409 
       
   410 		synchronized (executablesMap) {
       
   411 			// project needs to be refreshed after a build/clean as the binary may
       
   412 			// be added/removed/renamed etc.
       
   413 			if (event.getType() == IResourceChangeEvent.POST_BUILD) {
       
   414 				Object obj = event.getSource();
       
   415 				if (obj != null && obj instanceof IProject) {
       
   416 					if (executablesMap.containsKey(obj)) {
       
   417 						List<Executable> executables = executablesMap.remove(obj);
       
   418 
       
   419 						trace("Scheduling refresh because project " + ((IProject)obj).getName() + " built or cleaned");
       
   420 						
       
   421 						scheduleRefresh();
       
   422 
       
   423 						// notify the listeners that these executables have possibly changed
       
   424 						if (executables != null && executables.size() > 0) {
       
   425 							synchronized (changeListeners) {
       
   426 								for (IExecutablesChangeListener listener : changeListeners) {
       
   427 									listener.executablesChanged(executables);
       
   428 								}
       
   429 							}
       
   430 						}
       
   431 					}
       
   432 				}
       
   433 				return;
       
   434 			}
       
   435 			
       
   436 			// refresh when projects are opened or closed. note that deleted
       
   437 			// projects are handled later in this method. new projects are handled
       
   438 			// in handleEvent.  resource changed events always start at the workspace
       
   439 			// root, so projects are the next level down
       
   440 			boolean refreshNeeded = false;
       
   441 			IResourceDelta[] projects = event.getDelta().getAffectedChildren();
       
   442 			for (IResourceDelta projectDelta : projects) {
       
   443 				if ((projectDelta.getFlags() & IResourceDelta.OPEN) != 0) {
       
   444 					if (projectDelta.getKind() == IResourceDelta.CHANGED) {
       
   445 						// project was opened or closed
       
   446 						if (executablesMap.containsKey(projectDelta.getResource())) {
       
   447 							executablesMap.remove(projectDelta.getResource());
       
   448 						}
       
   449 						refreshNeeded = true;
       
   450 					}
       
   451 				}
       
   452 			}
       
   453 			
       
   454 			if (refreshNeeded) {
       
   455 				trace("Scheduling refresh because project(s) opened or closed");
       
   456 
       
   457 				scheduleRefresh();
       
   458 				return;
       
   459 			}
       
   460 
       
   461 			try {
       
   462 				event.getDelta().accept(new IResourceDeltaVisitor() {
       
   463 
       
   464 					public boolean visit(IResourceDelta delta) throws CoreException {
       
   465 						if (delta.getKind() == IResourceDelta.ADDED || delta.getKind() == IResourceDelta.REMOVED) {
       
   466 							IResource deltaResource = delta.getResource();
       
   467 							if (deltaResource != null) {
       
   468 								boolean refresh = false;
       
   469 								if (delta.getKind() == IResourceDelta.REMOVED && deltaResource instanceof IProject) {
       
   470 									// project deleted
       
   471 									if (executablesMap.containsKey(deltaResource)) {
       
   472 										executablesMap.remove(deltaResource);
       
   473 										refresh = true;
       
   474 
       
   475 										trace("Scheduling refresh because project " + deltaResource.getName() + " deleted");
       
   476 									}
       
   477 								} else {
       
   478 									// see if a binary has been added/removed
       
   479 									IPath resourcePath = deltaResource.getLocation();
       
   480 									if (resourcePath != null && Executable.isExecutableFile(resourcePath)) {
       
   481 										if (executablesMap.containsKey(deltaResource.getProject())) {
       
   482 											executablesMap.remove(deltaResource.getProject());
       
   483 											refresh = true;
       
   484 
       
   485 											trace("Scheduling refresh because a binary was added/removed");
       
   486 										}
       
   487 									}
       
   488 								}
       
   489 
       
   490 								if (refresh) {
       
   491 									scheduleRefresh();
       
   492 									return false;
       
   493 								}
       
   494 							}
       
   495 						}
       
   496 						return true;
       
   497 					}
       
   498 				});
       
   499 			} catch (CoreException e) {
       
   500 			}
       
   501 		}
       
   502 	}
       
   503 
       
   504 	public void handleEvent(CProjectDescriptionEvent event) {
       
   505 		// this handles the cases where the active build configuration changes,
       
   506 		// and when new projects are created or loaded at startup.
       
   507 		boolean refresh = false;
       
   508 
       
   509 		int eventType = event.getEventType();
       
   510 
       
   511 		if (eventType == CProjectDescriptionEvent.DATA_APPLIED) {
       
   512 			
       
   513 			synchronized (executablesMap) {
       
   514 				// see if the active build config has changed
       
   515 				ICProjectDescription newDesc = event.getNewCProjectDescription();
       
   516 				ICProjectDescription oldDesc = event.getOldCProjectDescription();
       
   517 				if (oldDesc != null && newDesc != null) {
       
   518 					String newConfigName = newDesc.getActiveConfiguration().getName();
       
   519 					String oldConfigName = oldDesc.getActiveConfiguration().getName();
       
   520 					if (!newConfigName.equals(oldConfigName)) {
       
   521 						if (executablesMap.containsKey(newDesc.getProject())) {
       
   522 							executablesMap.remove(newDesc.getProject());
       
   523 							refresh = true;
       
   524 
       
   525 							trace("Scheduling refresh because active build configuration changed");
       
   526 						}
       
   527 					}
       
   528 				} else if (newDesc != null && oldDesc == null) {
       
   529 					// project just created
       
   530 					refresh = true;
       
   531 
       
   532 					trace("Scheduling refresh because project " + newDesc.getProject().getName() + " created");
       
   533 				}
       
   534 			}
       
   535 		}
       
   536 
       
   537 		if (refresh) {
       
   538 			scheduleRefresh();
       
   539 		}
       
   540 	}
       
   541 
       
   542 	private List<IProject> getProjectsToCheck() {
       
   543 
       
   544 		List<IProject> projects = new ArrayList<IProject>();
       
   545 		
       
   546 		synchronized (executablesMap) {
       
   547 			// look for any CDT projects not in our cache
       
   548 			for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
       
   549 				if (!executablesMap.containsKey(project)) {
       
   550 					if (CoreModel.hasCNature(project)) {
       
   551 						projects.add(project);
       
   552 					}
       
   553 				}
       
   554 			}
       
   555 		}
       
   556 
       
   557 		return projects;
       
   558 	}
       
   559 
       
   560 	private void scheduleRefresh() {
       
   561 		trace("scheduleRefresh called at " + getStringFromTimestamp(System.currentTimeMillis()));
       
   562 
       
   563 		refreshJob.cancel();
       
   564 		refreshJob.schedule();
       
   565 	}
       
   566 
       
   567 	private IProjectExecutablesProvider getExecutablesProviderForProject(IProject project) {
       
   568 		IProjectExecutablesProvider provider = executablesProviderMap.get(project);
       
   569 		if (provider == null) {
       
   570 			// not cached yet.  get the list of project natures from the providers and
       
   571 			// pick the one with the closest match
       
   572 			try {
       
   573 				IProjectDescription description = project.getDescription();
       
   574 				int mostNaturesMatched = 0;
       
   575 				for (IProjectExecutablesProvider exeProvider : executableProviders) {
       
   576 					List<String> natures = exeProvider.getProjectNatures();
       
   577 
       
   578 					int naturesMatched = 0;
       
   579 					for (String nature : description.getNatureIds()) {
       
   580 						if (natures.contains(nature)) {
       
   581 							naturesMatched++;
       
   582 						}
       
   583 					}
       
   584 					
       
   585 					if (naturesMatched > mostNaturesMatched) {
       
   586 						provider = exeProvider;
       
   587 						mostNaturesMatched = naturesMatched;
       
   588 					}
       
   589 				}
       
   590 
       
   591 				// cache it
       
   592 				executablesProviderMap.put(project, provider);
       
   593 
       
   594 			} catch (CoreException e) {
       
   595 				e.printStackTrace();
       
   596 			}
       
   597 		}
       
   598 		
       
   599 		return provider;
       
   600 	}
       
   601 
       
   602 	private void loadExecutableProviderExtensions() {
       
   603 		executableProviders = Collections.synchronizedList(new ArrayList<IProjectExecutablesProvider>());
       
   604 
       
   605 		IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
       
   606 		IExtensionPoint extensionPoint = extensionRegistry.getExtensionPoint(CDebugCorePlugin.PLUGIN_ID + ".ExecutablesProvider"); //$NON-NLS-1$
       
   607 		IExtension[] extensions = extensionPoint.getExtensions();
       
   608 		
       
   609 		for (int i = 0; i < extensions.length; i++) {
       
   610 			IExtension extension = extensions[i];
       
   611 			IConfigurationElement[] elements = extension.getConfigurationElements();
       
   612 			IConfigurationElement element = elements[0];
       
   613 			
       
   614 			boolean failed = false;
       
   615 			try {
       
   616 				Object extObject = element.createExecutableExtension("class"); //$NON-NLS-1$
       
   617 				if (extObject instanceof IProjectExecutablesProvider) {
       
   618 					executableProviders.add((IProjectExecutablesProvider)extObject);
       
   619 				} else {
       
   620 					failed = true;
       
   621 				}
       
   622 			} 
       
   623 			catch (CoreException e) {
       
   624 				failed = true;
       
   625 			}
       
   626 			
       
   627 			if (failed) {
       
   628 				CDebugCorePlugin.log("Unable to load ExecutablesProvider extension from " + extension.getContributor().getName());
       
   629 			}
       
   630 		}
       
   631 	}
       
   632 	
       
   633 	private void loadSoureFileProviderExtensions() {
       
   634 		sourceFileProviders = Collections.synchronizedList(new ArrayList<ISourceFilesProvider>());
       
   635 
       
   636 		IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
       
   637 		IExtensionPoint extensionPoint = extensionRegistry.getExtensionPoint(CDebugCorePlugin.PLUGIN_ID + ".SourceFilesProvider"); //$NON-NLS-1$
       
   638 		IExtension[] extensions = extensionPoint.getExtensions();
       
   639 		
       
   640 		for (int i = 0; i < extensions.length; i++) {
       
   641 			IExtension extension = extensions[i];
       
   642 			IConfigurationElement[] elements = extension.getConfigurationElements();
       
   643 			IConfigurationElement element = elements[0];
       
   644 			
       
   645 			boolean failed = false;
       
   646 			try {
       
   647 				Object extObject = element.createExecutableExtension("class"); //$NON-NLS-1$
       
   648 				if (extObject instanceof ISourceFilesProvider) {
       
   649 					sourceFileProviders.add((ISourceFilesProvider)extObject);
       
   650 				} else {
       
   651 					failed = true;
       
   652 				}
       
   653 			} 
       
   654 			catch (CoreException e) {
       
   655 				failed = true;
       
   656 			}
       
   657 			
       
   658 			if (failed) {
       
   659 				CDebugCorePlugin.log("Unable to load SourceFilesProvider extension from " + extension.getContributor().getName());
       
   660 			}
       
   661 		}
       
   662 	}
       
   663 
       
   664 	private void loadSoureRemappingExtensions() {
       
   665 		sourceFileRemappings = Collections.synchronizedList(new ArrayList<ISourceFileRemapping>());
       
   666 
       
   667 		IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
       
   668 		IExtensionPoint extensionPoint = extensionRegistry.getExtensionPoint(CDebugCorePlugin.PLUGIN_ID + ".SourceRemappingProvider"); //$NON-NLS-1$
       
   669 		IExtension[] extensions = extensionPoint.getExtensions();
       
   670 		
       
   671 		for (int i = 0; i < extensions.length; i++) {
       
   672 			IExtension extension = extensions[i];
       
   673 			IConfigurationElement[] elements = extension.getConfigurationElements();
       
   674 			IConfigurationElement element = elements[0];
       
   675 			
       
   676 			boolean failed = false;
       
   677 			try {
       
   678 				Object extObject = element.createExecutableExtension("class"); //$NON-NLS-1$
       
   679 				if (extObject instanceof ISourceFileRemapping) {
       
   680 					sourceFileRemappings.add((ISourceFileRemapping)extObject);
       
   681 				} else {
       
   682 					failed = true;
       
   683 				}
       
   684 			} 
       
   685 			catch (CoreException e) {
       
   686 				failed = true;
       
   687 			}
       
   688 			
       
   689 			if (failed) {
       
   690 				CDebugCorePlugin.log("Unable to load SourceRemappingProvider extension from " + extension.getContributor().getName());
       
   691 			}
       
   692 		}
       
   693 	}
       
   694 
       
   695 	private void loadExecutableImporterExtensions() {
       
   696 		executableImporters = Collections.synchronizedList(new ArrayList<IExecutableImporter>());
       
   697 
       
   698 		IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
       
   699 		IExtensionPoint extensionPoint = extensionRegistry.getExtensionPoint(CDebugCorePlugin.PLUGIN_ID + ".ExecutablesImporter"); //$NON-NLS-1$
       
   700 		IExtension[] extensions = extensionPoint.getExtensions();
       
   701 		
       
   702 		for (int i = 0; i < extensions.length; i++) {
       
   703 			IExtension extension = extensions[i];
       
   704 			IConfigurationElement[] elements = extension.getConfigurationElements();
       
   705 			IConfigurationElement element = elements[0];
       
   706 			
       
   707 			boolean failed = false;
       
   708 			try {
       
   709 				Object extObject = element.createExecutableExtension("class"); //$NON-NLS-1$
       
   710 				if (extObject instanceof IExecutableImporter) {
       
   711 					executableImporters.add((IExecutableImporter)extObject);
       
   712 				} else {
       
   713 					failed = true;
       
   714 				}
       
   715 			} 
       
   716 			catch (CoreException e) {
       
   717 				failed = true;
       
   718 			}
       
   719 			
       
   720 			if (failed) {
       
   721 				CDebugCorePlugin.log("Unable to load ExecutablesImporter extension from " + extension.getContributor().getName());
       
   722 			}
       
   723 		}
       
   724 	}
       
   725 
       
   726 	private void trace(String msg) {
       
   727 		if (DEBUG) {
       
   728 			// TODO use Logger?
       
   729 			System.out.println(msg);
       
   730 		}
       
   731 	}
       
   732 	
       
   733 	private String getStringFromTimestamp(long timestamp) {
       
   734 		return DateFormat.getTimeInstance(DateFormat.MEDIUM).format(new Date(timestamp));
       
   735 	}
   333 }
   736 }