| /******************************************************************************* |
| * Copyright (c) 2000, 2006 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 |
| *******************************************************************************/ |
| package org.eclipse.ui.externaltools.internal.model; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.core.resources.ICommand; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| 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.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.debug.core.DebugPlugin; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.ILaunchConfigurationType; |
| import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; |
| import org.eclipse.debug.core.ILaunchManager; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.externaltools.internal.registry.ExternalToolMigration; |
| |
| /** |
| * Utility methods for working with external tool project builders. |
| */ |
| public class BuilderUtils { |
| |
| public static final String LAUNCH_CONFIG_HANDLE = "LaunchConfigHandle"; //$NON-NLS-1$ |
| |
| /** |
| * Constant used to find a builder using the 3.0-interim format |
| */ |
| public static final String BUILDER_FOLDER_NAME= ".externalToolBuilders"; //$NON-NLS-1$ |
| /** |
| * Constant used to represent the current project in the 3.0-final format. |
| */ |
| public static final String PROJECT_TAG= "<project>"; //$NON-NLS-1$ |
| |
| public static final String VERSION_1_0= "1.0"; //$NON-NLS-1$ |
| public static final String VERSION_2_1= "2.1"; //$NON-NLS-1$ |
| // The format shipped up to and including Eclipse 3.0 RC1 |
| public static final String VERSION_3_0_interim= "3.0.interim"; //$NON-NLS-1$ |
| // The format shipped in Eclipse 3.0 final |
| public static final String VERSION_3_0_final= "3.0"; //$NON-NLS-1$ |
| |
| // Extension point constants. |
| private static final String TAG_CONFIGURATION_MAP= "configurationMap"; //$NON-NLS-1$ |
| private static final String TAG_SOURCE_TYPE= "sourceType"; //$NON-NLS-1$ |
| private static final String TAG_BUILDER_TYPE= "builderType"; //$NON-NLS-1$ |
| |
| private static final String BUILD_TYPE_SEPARATOR = ","; //$NON-NLS-1$ |
| private static final int[] DEFAULT_BUILD_TYPES= new int[] { |
| IncrementalProjectBuilder.INCREMENTAL_BUILD, |
| IncrementalProjectBuilder.FULL_BUILD}; |
| |
| /** |
| * Returns a launch configuration from the given ICommand arguments. If the |
| * given arguments are from an old-style external tool, an unsaved working |
| * copy will be created from the arguments and returned. |
| * |
| * @param commandArgs the builder ICommand arguments |
| * @return a launch configuration, a launch configuration working copy, or |
| * <code>null</code> if not possible. |
| */ |
| public static ILaunchConfiguration configFromBuildCommandArgs(IProject project, Map commandArgs, String[] version) { |
| String configHandle = (String) commandArgs.get(LAUNCH_CONFIG_HANDLE); |
| if (configHandle == null) { |
| // Probably an old-style (Eclipse 1.0 or 2.0) external tool. Try to migrate. |
| version[0]= VERSION_1_0; |
| return ExternalToolMigration.configFromArgumentMap(commandArgs); |
| } |
| ILaunchManager manager= DebugPlugin.getDefault().getLaunchManager(); |
| ILaunchConfiguration configuration= null; |
| if (configHandle.startsWith(PROJECT_TAG)) { |
| version[0]= VERSION_3_0_final; |
| IPath path= new Path(configHandle); |
| IFile file= project.getFile(path.removeFirstSegments(1)); |
| if (file.exists()) { |
| configuration= manager.getLaunchConfiguration(file); |
| } |
| } else { |
| // Try treating the handle as a file name. |
| // This is the format used in 3.0 RC1. |
| IPath path= new Path(BUILDER_FOLDER_NAME).append(configHandle); |
| IFile file= project.getFile(path); |
| if (file.exists()) { |
| version[0]= VERSION_3_0_interim; |
| configuration= manager.getLaunchConfiguration(file); |
| } else { |
| try { |
| // Treat the configHandle as a memento. This is the format |
| // used in Eclipse 2.1. |
| configuration = manager.getLaunchConfiguration(configHandle); |
| } catch (CoreException e) { |
| } |
| if (configuration != null) { |
| version[0]= VERSION_2_1; |
| } |
| } |
| } |
| return configuration; |
| } |
| |
| /** |
| * Returns an <code>ICommand</code> from the given launch configuration. |
| * |
| * @param project the project the ICommand is relevant to |
| * @param config the launch configuration to create the command from |
| * @return the new command. <code>null</code> can be returned if problems occur during |
| * the translation. |
| */ |
| public static ICommand commandFromLaunchConfig(IProject project, ILaunchConfiguration config) { |
| ICommand newCommand = null; |
| try { |
| newCommand = project.getDescription().newCommand(); |
| newCommand = toBuildCommand(project, config, newCommand); |
| configureTriggers(config, newCommand); |
| } catch (CoreException exception) { |
| Shell shell= ExternalToolsPlugin.getActiveWorkbenchShell(); |
| if (shell != null) { |
| MessageDialog.openError(shell, ExternalToolsModelMessages.BuilderUtils_5, ExternalToolsModelMessages.BuilderUtils_6); |
| } |
| return null; |
| } |
| return newCommand; |
| } |
| |
| public static void configureTriggers(ILaunchConfiguration config, ICommand newCommand) throws CoreException { |
| newCommand.setBuilding(IncrementalProjectBuilder.FULL_BUILD, false); |
| newCommand.setBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD, false); |
| newCommand.setBuilding(IncrementalProjectBuilder.AUTO_BUILD, false); |
| newCommand.setBuilding(IncrementalProjectBuilder.CLEAN_BUILD, false); |
| String buildKinds= config.getAttribute(IExternalToolConstants.ATTR_RUN_BUILD_KINDS, (String)null); |
| int[] triggers= BuilderUtils.buildTypesToArray(buildKinds); |
| for (int i = 0; i < triggers.length; i++) { |
| switch (triggers[i]) { |
| case IncrementalProjectBuilder.FULL_BUILD: |
| newCommand.setBuilding(IncrementalProjectBuilder.FULL_BUILD, true); |
| break; |
| case IncrementalProjectBuilder.INCREMENTAL_BUILD: |
| newCommand.setBuilding(IncrementalProjectBuilder.INCREMENTAL_BUILD, true); |
| break; |
| case IncrementalProjectBuilder.AUTO_BUILD: |
| newCommand.setBuilding(IncrementalProjectBuilder.AUTO_BUILD, true); |
| break; |
| case IncrementalProjectBuilder.CLEAN_BUILD: |
| newCommand.setBuilding(IncrementalProjectBuilder.CLEAN_BUILD, true); |
| break; |
| } |
| } |
| if (!config.getAttribute(IExternalToolConstants.ATTR_TRIGGERS_CONFIGURED, false)) { |
| ILaunchConfigurationWorkingCopy copy= config.getWorkingCopy(); |
| copy.setAttribute(IExternalToolConstants.ATTR_TRIGGERS_CONFIGURED, true); |
| copy.doSave(); |
| } |
| } |
| |
| /** |
| * Returns whether the given configuration is an "unmigrated" builder. |
| * Unmigrated builders are external tools that are stored in an old format |
| * but have not been migrated by the user. Old format builders are always |
| * translated into launch config working copies in memory, but they're not |
| * considered "migrated" until the config has been saved and the project spec |
| * updated. |
| * @param config the config to examine |
| * @return whether the given config represents an unmigrated builder |
| */ |
| public static boolean isUnmigratedConfig(ILaunchConfiguration config) { |
| return config.isWorkingCopy() && ((ILaunchConfigurationWorkingCopy) config).getOriginal() == null; |
| } |
| |
| /** |
| * Converts the given config to a build command which is stored in the |
| * given command. |
| * |
| * @return the configured build command |
| */ |
| public static ICommand toBuildCommand(IProject project, ILaunchConfiguration config, ICommand command) throws CoreException { |
| Map args= null; |
| if (isUnmigratedConfig(config)) { |
| // This config represents an old external tool builder that hasn't |
| // been edited. Try to find the old ICommand and reuse the arguments. |
| // The goal here is to not change the storage format of old, unedited builders. |
| ICommand[] commands= project.getDescription().getBuildSpec(); |
| for (int i = 0; i < commands.length; i++) { |
| ICommand projectCommand = commands[i]; |
| String name= ExternalToolMigration.getNameFromCommandArgs(projectCommand.getArguments()); |
| if (name != null && name.equals(config.getName())) { |
| args= projectCommand.getArguments(); |
| break; |
| } |
| } |
| } else { |
| if (config instanceof ILaunchConfigurationWorkingCopy) { |
| ILaunchConfigurationWorkingCopy workingCopy= (ILaunchConfigurationWorkingCopy) config; |
| if (workingCopy.getOriginal() != null) { |
| config= workingCopy.getOriginal(); |
| } |
| } |
| args= new HashMap(); |
| // Launch configuration builders are stored with a project-relative path |
| StringBuffer buffer= new StringBuffer(PROJECT_TAG); |
| // Append the project-relative path (workspace path minus first segment) |
| buffer.append('/').append(config.getFile().getFullPath().removeFirstSegments(1)); |
| args.put(LAUNCH_CONFIG_HANDLE, buffer.toString()); |
| } |
| command.setBuilderName(ExternalToolBuilder.ID); |
| command.setArguments(args); |
| return command; |
| } |
| |
| /** |
| * Returns the type of launch configuration that should be created when |
| * duplicating the given configuration as a project builder. Queries to see |
| * if an extension has been specified to explicitly declare the mapping. |
| */ |
| public static ILaunchConfigurationType getConfigurationDuplicationType(ILaunchConfiguration config) throws CoreException { |
| IExtensionPoint ep= Platform.getExtensionRegistry().getExtensionPoint(IExternalToolConstants.PLUGIN_ID, IExternalToolConstants.EXTENSION_POINT_CONFIGURATION_DUPLICATION_MAPS); |
| IConfigurationElement[] elements = ep.getConfigurationElements(); |
| String sourceType= config.getType().getIdentifier(); |
| String builderType= null; |
| for (int i= 0; i < elements.length; i++) { |
| IConfigurationElement element= elements[i]; |
| if (element.getName().equals(TAG_CONFIGURATION_MAP) && sourceType.equals(element.getAttribute(TAG_SOURCE_TYPE))) { |
| builderType= element.getAttribute(TAG_BUILDER_TYPE); |
| break; |
| } |
| } |
| if (builderType != null) { |
| ILaunchConfigurationType type= DebugPlugin.getDefault().getLaunchManager().getLaunchConfigurationType(builderType); |
| if (type != null) { |
| return type; |
| } |
| } |
| return config.getType(); |
| } |
| |
| /** |
| * Returns the folder where project builders should be stored or |
| * <code>null</code> if the folder could not be created |
| */ |
| public static IFolder getBuilderFolder(IProject project, boolean create) { |
| IFolder folder = project.getFolder(BUILDER_FOLDER_NAME); |
| if (!folder.exists() && create) { |
| try { |
| folder.create(true, true, new NullProgressMonitor()); |
| } catch (CoreException e) { |
| return null; |
| } |
| } |
| return folder; |
| } |
| |
| /** |
| * Returns a duplicate of the given configuration. The new configuration |
| * will be of the same type as the given configuration or of the duplication |
| * type registered for the given configuration via the extension point |
| * IExternalToolConstants.EXTENSION_POINT_CONFIGURATION_DUPLICATION_MAPS. |
| */ |
| public static ILaunchConfiguration duplicateConfiguration(IProject project, ILaunchConfiguration config) throws CoreException { |
| Map attributes= config.getAttributes(); |
| String newName= new StringBuffer(config.getName()).append(ExternalToolsModelMessages.BuilderUtils_7).toString(); |
| newName= DebugPlugin.getDefault().getLaunchManager().generateUniqueLaunchConfigurationNameFrom(newName); |
| ILaunchConfigurationType newType= getConfigurationDuplicationType(config); |
| ILaunchConfigurationWorkingCopy newWorkingCopy= newType.newInstance(getBuilderFolder(project, true), newName); |
| newWorkingCopy.setAttributes(attributes); |
| return newWorkingCopy.doSave(); |
| } |
| |
| /** |
| * Migrates the launch configuration working copy, which is based on an old- |
| * style external tool builder, to a new, saved launch configuration. The |
| * returned launch configuration will contain the same attributes as the |
| * given working copy with the exception of the configuration name, which |
| * may be changed during the migration. The name of the configuration will |
| * only be changed if the current name is not a valid name for a saved |
| * config. |
| * |
| * @param workingCopy the launch configuration containing attributes from an |
| * old-style project builder. |
| * @return ILaunchConfiguration a new, saved launch configuration whose |
| * attributes match those of the given working copy as well as possible |
| * @throws CoreException if an exception occurs while attempting to save the |
| * new launch configuration |
| */ |
| public static ILaunchConfiguration migrateBuilderConfiguration(IProject project, ILaunchConfigurationWorkingCopy workingCopy) throws CoreException { |
| workingCopy.setContainer(getBuilderFolder(project, true)); |
| // Before saving, make sure the name is valid |
| String name= workingCopy.getName(); |
| name= name.replace('/', '.'); |
| if (name.charAt(0) == ('.')) { |
| name = name.substring(1); |
| } |
| IStatus status = ResourcesPlugin.getWorkspace().validateName(name, IResource.FILE); |
| if (!status.isOK()) { |
| name = "ExternalTool"; //$NON-NLS-1$ |
| } |
| name = DebugPlugin.getDefault().getLaunchManager().generateUniqueLaunchConfigurationNameFrom(name); |
| workingCopy.rename(name); |
| return workingCopy.doSave(); |
| } |
| |
| /** |
| * Converts the build types string into an array of |
| * build kinds. |
| * |
| * @param buildTypes the string of built types to convert |
| * @return the array of build kinds. |
| */ |
| public static int[] buildTypesToArray(String buildTypes) { |
| if (buildTypes == null || buildTypes.length() == 0) { |
| return DEFAULT_BUILD_TYPES; |
| } |
| |
| int count = 0; |
| boolean incremental = false; |
| boolean full = false; |
| boolean auto = false; |
| boolean clean= false; |
| |
| StringTokenizer tokenizer = new StringTokenizer(buildTypes, BUILD_TYPE_SEPARATOR); |
| while (tokenizer.hasMoreTokens()) { |
| String token = tokenizer.nextToken(); |
| if (IExternalToolConstants.BUILD_TYPE_INCREMENTAL.equals(token)) { |
| if (!incremental) { |
| incremental = true; |
| count++; |
| } |
| } else if (IExternalToolConstants.BUILD_TYPE_FULL.equals(token)) { |
| if (!full) { |
| full = true; |
| count++; |
| } |
| } else if (IExternalToolConstants.BUILD_TYPE_AUTO.equals(token)) { |
| if (!auto) { |
| auto = true; |
| count++; |
| } |
| } else if (IExternalToolConstants.BUILD_TYPE_CLEAN.equals(token)) { |
| if (!clean) { |
| clean = true; |
| count++; |
| } |
| } |
| } |
| |
| int[] results = new int[count]; |
| count = 0; |
| if (incremental) { |
| results[count] = IncrementalProjectBuilder.INCREMENTAL_BUILD; |
| count++; |
| } |
| if (full) { |
| results[count] = IncrementalProjectBuilder.FULL_BUILD; |
| count++; |
| } |
| if (auto) { |
| results[count] = IncrementalProjectBuilder.AUTO_BUILD; |
| count++; |
| } |
| if (clean) { |
| results[count] = IncrementalProjectBuilder.CLEAN_BUILD; |
| count++; |
| } |
| |
| return results; |
| } |
| } |