| /******************************************************************************* |
| * Copyright (c) 2007, 2022 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * EclipseSource Corporation - ongoing enhancements |
| * Hannes Wellmann - Bug 576885 - Unify methods to parse bundle-sets from launch-configs |
| * Hannes Wellmann - Bug 577118 - Handle multiple Plug-in versions in launching facility |
| * Hannes Wellmann - Bug 576886 - Clean up and improve BundleLaunchHelper and extract String literal constants |
| * Hannes Wellmann - Bug 576887 - Handle multiple versions of features and plug-ins for feature-launches |
| * Hannes Wellmann - Bug 576888, Bug 576889 - Consider included child-features and required dependency-features for feature-launches |
| *******************************************************************************/ |
| package org.eclipse.pde.internal.launching.launcher; |
| |
| import static java.util.Collections.emptySet; |
| import static java.util.Comparator.comparing; |
| import static java.util.stream.Collectors.groupingBy; |
| |
| import java.util.*; |
| import java.util.function.*; |
| import java.util.stream.Stream; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.debug.core.ILaunchConfiguration; |
| import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; |
| import org.eclipse.osgi.service.resolver.BundleDescription; |
| import org.eclipse.pde.core.plugin.*; |
| import org.eclipse.pde.internal.build.IPDEBuildConstants; |
| import org.eclipse.pde.internal.core.*; |
| import org.eclipse.pde.internal.core.ifeature.*; |
| import org.eclipse.pde.internal.core.util.VersionUtil; |
| import org.eclipse.pde.internal.launching.IPDEConstants; |
| import org.eclipse.pde.launching.IPDELauncherConstants; |
| import org.osgi.framework.Version; |
| |
| public class BundleLauncherHelper { |
| |
| private BundleLauncherHelper() { // static use only |
| } |
| |
| public static final char VERSION_SEPARATOR = '*'; |
| private static final char START_LEVEL_SEPARATOR = '@'; |
| private static final String AUTO_START_SEPARATOR = ":"; //$NON-NLS-1$ |
| private static final String DEFAULT = "default"; //$NON-NLS-1$ |
| private static final String DEFAULT_START_LEVELS = DEFAULT + AUTO_START_SEPARATOR + DEFAULT; |
| private static final String FEATURE_PLUGIN_RESOLUTION_SEPARATOR = ":"; //$NON-NLS-1$ |
| private static final String FEATURES_ADDITIONAL_PLUGINS_DATA_SEPARATOR = ":"; //$NON-NLS-1$ |
| |
| /** |
| * When creating a mapping of bundles to their start levels, update configurator is set |
| * to auto start at level three. However, if at launch time we are launching with both |
| * simple configurator and update configurator, we change the start level as they |
| * shouldn't be started together. |
| */ |
| public static final String DEFAULT_UPDATE_CONFIGURATOR_START_LEVEL_TEXT = "3"; //$NON-NLS-1$ |
| public static final String DEFAULT_UPDATE_CONFIGURATOR_AUTO_START_TEXT = "true"; //$NON-NLS-1$ |
| public static final String DEFAULT_UPDATE_CONFIGURATOR_START_LEVEL = DEFAULT_UPDATE_CONFIGURATOR_START_LEVEL_TEXT + AUTO_START_SEPARATOR + DEFAULT_UPDATE_CONFIGURATOR_AUTO_START_TEXT; |
| |
| public static Map<IPluginModelBase, String> getWorkspaceBundleMap(ILaunchConfiguration configuration) throws CoreException { |
| return getWorkspaceBundleMap(configuration, new HashMap<>()); |
| } |
| |
| public static Map<IPluginModelBase, String> getMergedBundleMap(ILaunchConfiguration configuration, boolean osgi) throws CoreException { |
| |
| ILaunchConfigurationWorkingCopy wc = getWorkingCopy(configuration); |
| if (!osgi) { |
| |
| migrateLaunchConfiguration(wc); |
| |
| if (wc.getAttribute(IPDELauncherConstants.USE_DEFAULT, true)) { |
| Map<IPluginModelBase, String> map = new LinkedHashMap<>(); |
| IPluginModelBase[] models = PluginRegistry.getActiveModels(); |
| for (IPluginModelBase model : models) { |
| addBundleToMap(map, model, DEFAULT_START_LEVELS); |
| } |
| return map; |
| } |
| |
| } else { |
| migrateOsgiLaunchConfiguration(wc); |
| } |
| |
| if (wc.getAttribute(IPDELauncherConstants.USE_CUSTOM_FEATURES, false)) { |
| return getMergedBundleMapFeatureBased(wc, osgi); |
| } |
| |
| return getAllSelectedPluginBundles(wc); |
| } |
| |
| public static Map<IPluginModelBase, String> getAllSelectedPluginBundles(ILaunchConfiguration config) throws CoreException { |
| Map<String, List<Version>> idVersions = new HashMap<>(); |
| Map<IPluginModelBase, String> map = getWorkspaceBundleMap(config, idVersions); |
| map.putAll(getTargetBundleMap(config, idVersions)); |
| return map; |
| } |
| |
| // --- feature based launches --- |
| |
| private static Map<IPluginModelBase, String> getMergedBundleMapFeatureBased(ILaunchConfiguration configuration, boolean osgi) throws CoreException { |
| |
| String defaultPluginResolution = configuration.getAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_WORKSPACE); |
| |
| Map<IFeature, String> feature2resolution = getSelectedFeatures(configuration); |
| |
| // Get the feature model for each selected feature id and resolve its plugins |
| Set<IPluginModelBase> launchPlugins = new HashSet<>(); |
| |
| feature2resolution.forEach((feature, pluginResolution) -> { |
| if (IPDELauncherConstants.LOCATION_DEFAULT.equalsIgnoreCase(pluginResolution)) { |
| pluginResolution = defaultPluginResolution; |
| } |
| IFeaturePlugin[] featurePlugins = feature.getPlugins(); |
| for (IFeaturePlugin featurePlugin : featurePlugins) { |
| IPluginModelBase plugin = getIncludedPlugin(featurePlugin.getId(), featurePlugin.getVersion(), pluginResolution); |
| if (plugin != null) { |
| launchPlugins.add(plugin); |
| } |
| } |
| IFeatureImport[] featureImports = feature.getImports(); |
| for (IFeatureImport featureImport : featureImports) { |
| if (featureImport.getType() == IFeatureImport.PLUGIN) { |
| IPluginModelBase plugin = getRequiredPlugin(featureImport.getId(), featureImport.getVersion(), featureImport.getMatch(), pluginResolution); |
| if (plugin != null) { |
| launchPlugins.add(plugin); |
| } |
| } |
| } |
| }); |
| |
| Map<IPluginModelBase, AdditionalPluginData> additionalPlugins = getAdditionalPlugins(configuration, true); |
| launchPlugins.addAll(additionalPlugins.keySet()); |
| |
| // Get any plug-ins required by the application/product set on the config |
| if (!osgi) { |
| String[] applicationIds = RequirementHelper.getApplicationRequirements(configuration); |
| for (String applicationId : applicationIds) { |
| IPluginModelBase plugin = getRequiredPlugin(applicationId, null, IMatchRules.NONE, defaultPluginResolution); |
| if (plugin != null) { |
| launchPlugins.add(plugin); |
| } |
| } |
| } |
| // Get all required plugins |
| Set<BundleDescription> additionalBundles = DependencyManager.getDependencies(launchPlugins, false); |
| for (BundleDescription bundle : additionalBundles) { |
| IPluginModelBase plugin = getRequiredPlugin(bundle.getSymbolicName(), bundle.getVersion().toString(), IMatchRules.PERFECT, defaultPluginResolution); |
| launchPlugins.add(Objects.requireNonNull(plugin));// should never be null |
| } |
| |
| // Create the start levels for the selected plugins and add them to the map |
| Map<IPluginModelBase, String> map = new LinkedHashMap<>(); |
| for (IPluginModelBase model : launchPlugins) { |
| AdditionalPluginData additionalPluginData = additionalPlugins.get(model); |
| String startLevels = additionalPluginData != null ? additionalPluginData.startLevels() : DEFAULT_START_LEVELS; |
| addBundleToMap(map, model, startLevels); // might override data of plug-ins included by feature |
| } |
| return map; |
| } |
| |
| private static Map<IFeature, String> getSelectedFeatures(ILaunchConfiguration configuration) throws CoreException { |
| String featureLocation = configuration.getAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE); |
| |
| // Get all available features |
| Map<String, List<List<IFeature>>> featureMaps = getPrioritizedAvailableFeatures(featureLocation); |
| |
| Set<String> selectedFeatures = configuration.getAttribute(IPDELauncherConstants.SELECTED_FEATURES, emptySet()); |
| |
| Map<IFeature, String> feature2pluginResolution = new HashMap<>(); |
| Queue<IFeature> pendingFeatures = new ArrayDeque<>(); |
| for (String currentSelected : selectedFeatures) { |
| String[] attributes = currentSelected.split(FEATURE_PLUGIN_RESOLUTION_SEPARATOR); |
| if (attributes.length > 1) { |
| String id = attributes[0]; |
| String pluginResolution = attributes[1]; |
| IFeature feature = getRequiredFeature(id, null, IMatchRules.GREATER_OR_EQUAL, featureMaps); |
| addFeatureIfAbsent(feature, pluginResolution, feature2pluginResolution, pendingFeatures); // feature should be absent |
| } |
| } |
| |
| while (!pendingFeatures.isEmpty()) { // perform exhaustive breath-first-search for included and required features |
| IFeature feature = pendingFeatures.remove(); |
| String pluginResolution = feature2pluginResolution.get(feature); // inherit resolution from including feature |
| |
| IFeatureChild[] includedFeatures = feature.getIncludedFeatures(); |
| for (IFeatureChild featureChild : includedFeatures) { |
| IFeature child = getIncludedFeature(featureChild.getId(), featureChild.getVersion(), featureMaps); |
| addFeatureIfAbsent(child, pluginResolution, feature2pluginResolution, pendingFeatures); |
| } |
| |
| IFeatureImport[] featureImports = feature.getImports(); |
| for (IFeatureImport featureImport : featureImports) { |
| if (featureImport.getType() == IFeatureImport.FEATURE) { |
| IFeature dependency = getRequiredFeature(featureImport.getId(), featureImport.getVersion(), featureImport.getMatch(), featureMaps); |
| addFeatureIfAbsent(dependency, pluginResolution, feature2pluginResolution, pendingFeatures); |
| } |
| } |
| } |
| return feature2pluginResolution; |
| } |
| |
| private static Map<String, List<List<IFeature>>> getPrioritizedAvailableFeatures(String featureLocation) { |
| FeatureModelManager fmm = PDECore.getDefault().getFeatureModelManager(); |
| List<IFeatureModel[]> featureModelsPerLocation = isWorkspace(featureLocation) // |
| ? List.of(fmm.getWorkspaceModels(), fmm.getExternalModels()) // |
| : Collections.singletonList(fmm.getExternalModels()); |
| |
| Map<String, List<List<IFeature>>> featureMaps = new HashMap<>(); |
| for (IFeatureModel[] featureModels : featureModelsPerLocation) { |
| Map<String, List<IFeature>> id2feature = Arrays.stream(featureModels).map(IFeatureModel::getFeature).collect(groupingBy(IFeature::getId)); |
| id2feature.forEach((id, features) -> featureMaps.computeIfAbsent(id, i -> new ArrayList<>()).add(features)); |
| } |
| return featureMaps; |
| } |
| |
| private static void addFeatureIfAbsent(IFeature feature, String resolution, Map<IFeature, String> featurePluginResolution, Queue<IFeature> pendingFeatures) { |
| if (feature != null && featurePluginResolution.putIfAbsent(feature, resolution) == null) { |
| // Don't add feature more than once to not override the resolution if already present (e.g. a child was specified explicitly) |
| pendingFeatures.add(feature); // ... and to not process it more than once |
| } |
| } |
| |
| private static boolean isWorkspace(String location) { |
| if (IPDELauncherConstants.LOCATION_WORKSPACE.equalsIgnoreCase(location)) { |
| return true; |
| } else if (IPDELauncherConstants.LOCATION_EXTERNAL.equalsIgnoreCase(location)) { |
| return false; |
| } |
| throw new IllegalArgumentException("Unsupported location: " + location); //$NON-NLS-1$ |
| } |
| |
| private static final Comparator<IFeature> NEUTRAL_COMPARATOR = comparing(f -> 0); |
| |
| private static IFeature getIncludedFeature(String id, String version, Map<String, List<List<IFeature>>> prioritizedFeatures) { |
| List<List<IFeature>> features = prioritizedFeatures.get(id); |
| return getIncluded(features, f -> true, IFeature::getVersion, NEUTRAL_COMPARATOR, version); |
| } |
| |
| private static IFeature getRequiredFeature(String id, String version, int versionMatchRule, Map<String, List<List<IFeature>>> prioritizedFeatures) { |
| List<List<IFeature>> features = prioritizedFeatures.get(id); |
| return getRequired(features, f -> true, IFeature::getVersion, NEUTRAL_COMPARATOR, version, versionMatchRule); |
| } |
| |
| private static final Predicate<IPluginModelBase> ENABLED_VALID_PLUGIN_FILTER = p -> p.getBundleDescription() != null && p.isEnabled(); |
| private static final Function<IPluginModelBase, String> GET_PLUGIN_VERSION = m -> m.getPluginBase().getVersion(); |
| private static final Comparator<IPluginModelBase> COMPARE_PLUGIN_RESOLVED = comparing(p -> p.getBundleDescription().isResolved()); |
| |
| private static IPluginModelBase getIncludedPlugin(String id, String version, String pluginLocation) { |
| List<List<IPluginModelBase>> plugins = getPlugins(id, pluginLocation); |
| return getIncluded(plugins, ENABLED_VALID_PLUGIN_FILTER, GET_PLUGIN_VERSION, COMPARE_PLUGIN_RESOLVED, version); |
| } |
| |
| private static IPluginModelBase getRequiredPlugin(String id, String version, int versionMatchRule, String pluginLocation) { |
| List<List<IPluginModelBase>> plugins = getPlugins(id, pluginLocation); |
| return getRequired(plugins, ENABLED_VALID_PLUGIN_FILTER, GET_PLUGIN_VERSION, COMPARE_PLUGIN_RESOLVED, version, versionMatchRule); |
| } |
| |
| private static List<List<IPluginModelBase>> getPlugins(String id, String pluginLocation) { |
| ModelEntry entry = PluginRegistry.findEntry(id); |
| if (entry == null) { |
| return Collections.emptyList(); |
| } |
| List<IPluginModelBase> wsPlugins = List.of(entry.getWorkspaceModels()); // contains no or one element in most cases |
| List<IPluginModelBase> tpPlugins = List.of(entry.getExternalModels()); // contains no or one element in most cases |
| return isWorkspace(pluginLocation) ? List.of(wsPlugins, tpPlugins) : List.of(tpPlugins, wsPlugins); |
| } |
| |
| /** |
| * Selects and returns an {@code included} element for the specified version from the given containers using the following logic: |
| * <p> |
| * <ol> |
| * <li>take first container</li> |
| * <li>if an exactly qualified matching version exists select that</li> |
| * <li>if an unqualified matching version exists select that</li> |
| * <li>if any version exists, select the latest one</li> |
| * <li>if no version was yet selected, go to next container and continue at step 2.</li> |
| * </ol> |
| * </p> |
| * @return the selected included element or null if none was found |
| */ |
| private static <E> E getIncluded(List<List<E>> containers, Predicate<E> filter, Function<E, String> getVersion, Comparator<E> primaryComparator, String version) { |
| if (containers == null || containers.isEmpty()) { |
| return null; |
| } |
| Version includedVersion = Version.parseVersion(version); |
| |
| Comparator<E> compareVersion = primaryComparator.thenComparing(e -> Version.parseVersion(getVersion.apply(e)), Comparator// |
| .<Version, Boolean> comparing(includedVersion::equals) // false < true |
| .thenComparing(v -> VersionUtil.compareMacroMinorMicro(v, includedVersion) == 0) // false < true |
| .thenComparing(Comparator.naturalOrder())); |
| |
| return getMaxElement(containers, filter, compareVersion); |
| } |
| |
| /** |
| * Selects and returns an {@code required} element for the specified version (may be null) and match-rule from the given containers using the following logic: |
| * <p> |
| * <ol> |
| * <li>take first container</li> |
| * <li>filter-out versions that do not obey the match rule with respect to the required version</li> |
| * <li>selected and return latest version available</li> |
| * <li>if no version was yet selected, go to next container and continue at step 2.</li> |
| * </ol> |
| * </p> |
| * @return the selected required element or null if none was found |
| */ |
| private static <E> E getRequired(List<List<E>> containers, Predicate<E> filter, Function<E, String> getVersion, Comparator<E> primaryComparator, String version, int versionMatchRule) { |
| if (containers == null || containers.isEmpty()) { |
| return null; |
| } |
| if (version != null && !version.equals(Version.emptyVersion.toString())) { |
| Predicate<E> matchingVersion = e -> VersionUtil.compare(getVersion.apply(e), version, versionMatchRule); |
| filter = filter.and(matchingVersion); |
| } // if no/empty version is specified take the most recent version from the first/preferred location |
| |
| Comparator<E> compareVersion = primaryComparator.thenComparing(e -> Version.parseVersion(getVersion.apply(e))); |
| |
| return getMaxElement(containers, filter, compareVersion); |
| } |
| |
| private static <E> E getMaxElement(List<List<E>> containers, Predicate<E> filter, Comparator<E> comparator) { |
| for (List<E> container : containers) { |
| Optional<E> selection = container.stream().filter(filter).max(comparator); |
| if (selection.isPresent()) { // take most recent element |
| return selection.get(); |
| } |
| } |
| return null; |
| } |
| |
| // --- plug-in based launches --- |
| |
| private static final BiPredicate<List<Version>, Version> CONTAINS_SAME_VERSION = List::contains; |
| private static final BiPredicate<List<Version>, Version> CONTAINS_SAME_MMM_VERSION = (versions, toAdd) -> versions.stream().anyMatch(v -> VersionUtil.compareMacroMinorMicro(toAdd, v) == 0); |
| |
| private static Map<IPluginModelBase, String> getWorkspaceBundleMap(ILaunchConfiguration configuration, Map<String, List<Version>> idVersions) throws CoreException { |
| Set<String> workspaceBundles = configuration.getAttribute(IPDELauncherConstants.SELECTED_WORKSPACE_BUNDLES, emptySet()); |
| |
| Map<IPluginModelBase, String> map = getBundleMap(workspaceBundles, ModelEntry::getWorkspaceModels, CONTAINS_SAME_VERSION, idVersions); |
| |
| if (configuration.getAttribute(IPDELauncherConstants.AUTOMATIC_ADD, true)) { |
| Set<String> deselectedWorkspaceBundles = configuration.getAttribute(IPDELauncherConstants.DESELECTED_WORKSPACE_BUNDLES, emptySet()); |
| Set<IPluginModelBase> deselectedPlugins = getBundleMap(deselectedWorkspaceBundles, ModelEntry::getWorkspaceModels, null, null).keySet(); |
| IPluginModelBase[] models = PluginRegistry.getWorkspaceModels(); |
| for (IPluginModelBase model : models) { |
| if (model.getPluginBase().getId() != null && !deselectedPlugins.contains(model) && !map.containsKey(model)) { |
| addPlugin(map, model, DEFAULT_START_LEVELS, idVersions, CONTAINS_SAME_VERSION); |
| } |
| } |
| } |
| return map; |
| } |
| |
| private static Map<IPluginModelBase, String> getTargetBundleMap(ILaunchConfiguration configuration, Map<String, List<Version>> idVersions) throws CoreException { |
| Set<String> targetBundles = configuration.getAttribute(IPDELauncherConstants.SELECTED_TARGET_BUNDLES, emptySet()); |
| return getBundleMap(targetBundles, ModelEntry::getExternalModels, CONTAINS_SAME_MMM_VERSION, idVersions); // don't add same major-minor-micro-version more than once |
| } |
| |
| private static Map<IPluginModelBase, String> getBundleMap(Set<String> entries, Function<ModelEntry, IPluginModelBase[]> getModels, BiPredicate<List<Version>, Version> versionFilter, Map<String, List<Version>> idVersions) { |
| Map<IPluginModelBase, String> map = new LinkedHashMap<>(); |
| for (String bundleEntry : entries) { |
| int index = bundleEntry.indexOf(START_LEVEL_SEPARATOR); |
| if (index < 0) { // if no start levels, assume default |
| index = bundleEntry.length(); |
| bundleEntry += START_LEVEL_SEPARATOR + DEFAULT_START_LEVELS; |
| } |
| String idVersion = bundleEntry.substring(0, index); |
| int versionIndex = idVersion.indexOf(VERSION_SEPARATOR); |
| String id = (versionIndex > 0) ? idVersion.substring(0, versionIndex) : idVersion; |
| String version = (versionIndex > 0) ? idVersion.substring(versionIndex + 1) : null; |
| |
| ModelEntry entry = PluginRegistry.findEntry(id); |
| if (entry != null) { |
| IPluginModelBase[] models = getModels.apply(entry); |
| String startData = bundleEntry.substring(index + 1); |
| for (IPluginModelBase model : getSelectedModels(models, version, versionFilter == null)) { |
| addPlugin(map, model, startData, idVersions, versionFilter); |
| } |
| } |
| } |
| return map; |
| } |
| |
| static final Comparator<IPluginModelBase> VERSION = comparing(BundleLauncherHelper::getVersion); |
| |
| private static Iterable<IPluginModelBase> getSelectedModels(IPluginModelBase[] models, String version, boolean greedy) { |
| // match only if... |
| // a) if we have the same version |
| // b) no version (if greedy take latest, else take all) |
| // c) all else fails, if there's just one bundle available, use it |
| Stream<IPluginModelBase> selectedModels = Arrays.stream(models).filter(IPluginModelBase::isEnabled); // workspace models are always enabled, external might be disabled |
| if (version == null) { |
| if (!greedy) { |
| IPluginModelBase latestModel = selectedModels.max(VERSION).orElseThrow(); |
| selectedModels = Stream.of(latestModel); // take only latest |
| } // Otherwise be greedy and take all if versionFilter is null |
| } else { |
| selectedModels = selectedModels.filter(m -> m.getPluginBase().getVersion().equals(version) || models.length == 1); |
| } |
| return selectedModels::iterator; |
| } |
| |
| private static void addPlugin(Map<IPluginModelBase, String> map, IPluginModelBase model, String startData, Map<String, List<Version>> idVersions, BiPredicate<List<Version>, Version> containsVersion) { |
| if (containsVersion == null) { // be greedy and just take all (idVersions is null as well) |
| addBundleToMap(map, model, startData); |
| } else { |
| List<Version> pluginVersions = idVersions.computeIfAbsent(model.getPluginBase().getId(), n -> new ArrayList<>()); |
| Version version = getVersion(model); |
| if (!containsVersion.test(pluginVersions, version)) { // apply version filter |
| pluginVersions.add(version); |
| addBundleToMap(map, model, startData); |
| } |
| } |
| } |
| |
| private static Version getVersion(IPluginModelBase model) { |
| BundleDescription bundleDescription = model.getBundleDescription(); |
| if (bundleDescription == null) { |
| try { |
| return Version.parseVersion(model.getPluginBase().getVersion()); |
| } catch (IllegalArgumentException e) { |
| return Version.emptyVersion; |
| } |
| } |
| return bundleDescription.getVersion(); |
| } |
| |
| /** |
| * Adds the given bundle and start information to the map. This will override anything set |
| * for system bundles, and set their start level to the appropriate level |
| * @param map The map to add the bundles too |
| * @param bundle The bundle to add |
| * @param substring the start information in the form level:autostart |
| */ |
| private static void addBundleToMap(Map<IPluginModelBase, String> map, IPluginModelBase bundle, String startData) { |
| BundleDescription desc = bundle.getBundleDescription(); |
| boolean defaultsl = startData == null || startData.equals(DEFAULT_START_LEVELS); |
| if (desc != null && defaultsl) { |
| String runLevelText = resolveSystemRunLevelText(bundle); |
| String autoText = resolveSystemAutoText(bundle); |
| if (runLevelText != null && autoText != null) { |
| startData = runLevelText + AUTO_START_SEPARATOR + autoText; |
| } |
| } |
| map.put(bundle, startData); |
| } |
| |
| private static final Map<String, String> AUTO_STARTED_BUNDLE_LEVELS = Map.ofEntries( // |
| Map.entry(IPDEBuildConstants.BUNDLE_DS, "1"), //$NON-NLS-1$ |
| Map.entry(IPDEBuildConstants.BUNDLE_SIMPLE_CONFIGURATOR, "1"), //$NON-NLS-1$ |
| Map.entry(IPDEBuildConstants.BUNDLE_EQUINOX_COMMON, "2"), //$NON-NLS-1$ |
| Map.entry(IPDEBuildConstants.BUNDLE_OSGI, "1"), //$NON-NLS-1$ |
| Map.entry(IPDEBuildConstants.BUNDLE_CORE_RUNTIME, DEFAULT), // |
| Map.entry(IPDEBuildConstants.BUNDLE_FELIX_SCR, "1")); //$NON-NLS-1$ |
| |
| public static String resolveSystemRunLevelText(IPluginModelBase model) { |
| BundleDescription description = model.getBundleDescription(); |
| return AUTO_STARTED_BUNDLE_LEVELS.get(description.getSymbolicName()); |
| } |
| |
| public static String resolveSystemAutoText(IPluginModelBase model) { |
| BundleDescription description = model.getBundleDescription(); |
| return AUTO_STARTED_BUNDLE_LEVELS.containsKey(description.getSymbolicName()) ? "true" : null; //$NON-NLS-1$ |
| } |
| |
| public static String formatBundleEntry(IPluginModelBase model, String startLevel, String autoStart) { |
| IPluginBase base = model.getPluginBase(); |
| String id = base.getId(); |
| StringBuilder buffer = new StringBuilder(id); |
| |
| ModelEntry entry = PluginRegistry.findEntry(id); |
| if (entry != null) { |
| boolean isWorkspacePlugin = model.getUnderlyingResource() != null; |
| IPluginModelBase[] entryModels = isWorkspacePlugin ? entry.getWorkspaceModels() : entry.getExternalModels(); |
| if (entryModels.length > 1) { |
| buffer.append(VERSION_SEPARATOR); |
| buffer.append(model.getPluginBase().getVersion()); |
| } |
| } |
| |
| boolean hasStartLevel = startLevel != null && !startLevel.isEmpty(); |
| boolean hasAutoStart = autoStart != null && !autoStart.isEmpty(); |
| |
| if (hasStartLevel || hasAutoStart) { |
| buffer.append(START_LEVEL_SEPARATOR); |
| if (hasStartLevel) { |
| buffer.append(startLevel); |
| } |
| buffer.append(AUTO_START_SEPARATOR); |
| if (hasAutoStart) { |
| buffer.append(autoStart); |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| public static String formatFeatureEntry(String featureId, String pluginResolution) { |
| return featureId + FEATURE_PLUGIN_RESOLUTION_SEPARATOR + pluginResolution; |
| } |
| |
| @SuppressWarnings("deprecation") |
| public static void migrateLaunchConfiguration(ILaunchConfigurationWorkingCopy configuration) throws CoreException { |
| |
| String value = configuration.getAttribute("wsproject", (String) null); //$NON-NLS-1$ |
| if (value != null) { |
| configuration.setAttribute("wsproject", (String) null); //$NON-NLS-1$ |
| if (value.indexOf(';') != -1) { |
| value = value.replace(';', ','); |
| } else if (value.indexOf(':') != -1) { |
| value = value.replace(':', ','); |
| } |
| value = (value.length() == 0 || value.equals(",")) //$NON-NLS-1$ |
| ? null |
| : value.substring(0, value.length() - 1); |
| |
| boolean automatic = configuration.getAttribute(IPDELauncherConstants.AUTOMATIC_ADD, true); |
| String attr = automatic ? IPDELauncherConstants.DESELECTED_WORKSPACE_PLUGINS : IPDELauncherConstants.SELECTED_WORKSPACE_PLUGINS; |
| configuration.setAttribute(attr, value); |
| } |
| |
| String value2 = configuration.getAttribute("extplugins", (String) null); //$NON-NLS-1$ |
| if (value2 != null) { |
| configuration.setAttribute("extplugins", (String) null); //$NON-NLS-1$ |
| if (value2.indexOf(';') != -1) { |
| value2 = value2.replace(';', ','); |
| } else if (value2.indexOf(':') != -1) { |
| value2 = value2.replace(':', ','); |
| } |
| value2 = (value2.length() == 0 || value2.equals(",")) ? null : value2.substring(0, value2.length() - 1); //$NON-NLS-1$ |
| configuration.setAttribute(IPDELauncherConstants.SELECTED_TARGET_PLUGINS, value2); |
| } |
| |
| convertToSet(configuration, IPDELauncherConstants.SELECTED_TARGET_PLUGINS, IPDELauncherConstants.SELECTED_TARGET_BUNDLES); |
| convertToSet(configuration, IPDELauncherConstants.SELECTED_WORKSPACE_PLUGINS, IPDELauncherConstants.SELECTED_WORKSPACE_BUNDLES); |
| convertToSet(configuration, IPDELauncherConstants.DESELECTED_WORKSPACE_PLUGINS, IPDELauncherConstants.DESELECTED_WORKSPACE_BUNDLES); |
| |
| String version = configuration.getAttribute(IPDEConstants.LAUNCHER_PDE_VERSION, (String) null); |
| boolean newApp = TargetPlatformHelper.usesNewApplicationModel(); |
| boolean upgrade = !"3.3".equals(version) && newApp; //$NON-NLS-1$ |
| if (!upgrade) { |
| upgrade = TargetPlatformHelper.getTargetVersion() >= 3.2 && version == null; |
| } |
| if (upgrade) { |
| configuration.setAttribute(IPDEConstants.LAUNCHER_PDE_VERSION, newApp ? "3.3" : "3.2a"); //$NON-NLS-1$ //$NON-NLS-2$ |
| boolean usedefault = configuration.getAttribute(IPDELauncherConstants.USE_DEFAULT, true); |
| boolean automaticAdd = configuration.getAttribute(IPDELauncherConstants.AUTOMATIC_ADD, true); |
| if (!usedefault) { |
| ArrayList<String> list = new ArrayList<>(); |
| if (version == null) { |
| list.add("org.eclipse.core.contenttype"); //$NON-NLS-1$ |
| list.add("org.eclipse.core.jobs"); //$NON-NLS-1$ |
| list.add(IPDEBuildConstants.BUNDLE_EQUINOX_COMMON); |
| list.add("org.eclipse.equinox.preferences"); //$NON-NLS-1$ |
| list.add("org.eclipse.equinox.registry"); //$NON-NLS-1$ |
| } |
| if (!"3.3".equals(version) && newApp) { //$NON-NLS-1$ |
| list.add("org.eclipse.equinox.app"); //$NON-NLS-1$ |
| } |
| Set<String> extensions = new LinkedHashSet<>(configuration.getAttribute(IPDELauncherConstants.SELECTED_WORKSPACE_BUNDLES, emptySet())); |
| Set<String> target = new LinkedHashSet<>(configuration.getAttribute(IPDELauncherConstants.SELECTED_TARGET_BUNDLES, emptySet())); |
| for (String plugin : list) { |
| IPluginModelBase model = PluginRegistry.findModel(plugin); |
| if (model == null) { |
| continue; |
| } |
| if (model.getUnderlyingResource() != null) { |
| if (!automaticAdd) { |
| extensions.add(plugin); |
| } |
| } else { |
| target.add(plugin); |
| } |
| } |
| if (!extensions.isEmpty()) { |
| configuration.setAttribute(IPDELauncherConstants.SELECTED_WORKSPACE_BUNDLES, extensions); |
| } |
| if (!target.isEmpty()) { |
| configuration.setAttribute(IPDELauncherConstants.SELECTED_TARGET_BUNDLES, target); |
| } |
| } |
| } |
| } |
| |
| private static ILaunchConfigurationWorkingCopy getWorkingCopy(ILaunchConfiguration configuration) throws CoreException { |
| if (configuration.isWorkingCopy()) { |
| return (ILaunchConfigurationWorkingCopy) configuration; |
| } |
| return configuration.getWorkingCopy(); |
| } |
| |
| @SuppressWarnings("deprecation") |
| public static void migrateOsgiLaunchConfiguration(ILaunchConfigurationWorkingCopy configuration) throws CoreException { |
| convertToSet(configuration, IPDELauncherConstants.WORKSPACE_BUNDLES, IPDELauncherConstants.SELECTED_WORKSPACE_BUNDLES); |
| convertToSet(configuration, IPDELauncherConstants.TARGET_BUNDLES, IPDELauncherConstants.SELECTED_TARGET_BUNDLES); |
| convertToSet(configuration, IPDELauncherConstants.DESELECTED_WORKSPACE_PLUGINS, IPDELauncherConstants.DESELECTED_WORKSPACE_BUNDLES); |
| } |
| |
| private static void convertToSet(ILaunchConfigurationWorkingCopy wc, String stringAttribute, String listAttribute) throws CoreException { |
| String value = wc.getAttribute(stringAttribute, (String) null); |
| if (value != null) { |
| wc.removeAttribute(stringAttribute); |
| String[] itemArray = value.split(","); //$NON-NLS-1$ |
| Set<String> itemSet = new HashSet<>(Arrays.asList(itemArray)); |
| wc.setAttribute(listAttribute, itemSet); |
| } |
| } |
| |
| /** |
| * Returns a map of IPluginModelBase to their associated String resolution setting. Reads the |
| * additional plug-ins attribute of the given launch config and returns a map of plug-in models |
| * to their resolution. The attribute stores the id, version, enablement and resolution of each plug-in. |
| * The models to be returned are determined by trying to find a model with a matching name, matching version |
| * (or highest) in the resolution location (falling back on other locations if the chosen option is unavailable). |
| * The includeDisabled option allows the returned list to contain only plug-ins that are enabled (checked) in |
| * the config. |
| * |
| * @param config launch config to read attribute from |
| * @param onlyEnabled whether all plug-ins in the attribute should be returned or just the ones marked as enabled/checked |
| * @return map of IPluginModelBase to String resolution setting |
| * @throws CoreException if there is a problem reading the launch config |
| */ |
| public static Map<IPluginModelBase, AdditionalPluginData> getAdditionalPlugins(ILaunchConfiguration config, boolean onlyEnabled) throws CoreException { |
| Map<IPluginModelBase, AdditionalPluginData> resolvedAdditionalPlugins = new HashMap<>(); |
| Set<String> userAddedPlugins = config.getAttribute(IPDELauncherConstants.ADDITIONAL_PLUGINS, emptySet()); |
| String defaultPluginResolution = config.getAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_WORKSPACE); |
| |
| for (String addedPlugin : userAddedPlugins) { |
| String[] pluginData = addedPlugin.split(FEATURES_ADDITIONAL_PLUGINS_DATA_SEPARATOR); |
| boolean checked = Boolean.parseBoolean(pluginData[3]); |
| if (!onlyEnabled || checked) { |
| String id = pluginData[0]; |
| String version = pluginData[1]; |
| String pluginResolution = pluginData[2]; |
| if (IPDELauncherConstants.LOCATION_DEFAULT.equalsIgnoreCase(pluginResolution)) { |
| pluginResolution = defaultPluginResolution; |
| } |
| |
| IPluginModelBase model = getIncludedPlugin(id, version, pluginResolution); |
| if (model != null) { |
| String startLevel = (pluginData.length >= 6) ? pluginData[4] : null; |
| String autoStart = (pluginData.length >= 6) ? pluginData[5] : null; |
| AdditionalPluginData additionalPluginData = new AdditionalPluginData(pluginData[2], checked, startLevel, autoStart); |
| resolvedAdditionalPlugins.put(model, additionalPluginData); |
| } |
| } |
| } |
| return resolvedAdditionalPlugins; |
| } |
| |
| public static String formatAdditionalPluginEntry(IPluginModelBase pluginModel, String pluginResolution, boolean isChecked, String fStartLevel, String fAutoStart) { |
| IPluginBase plugin = pluginModel.getPluginBase(); |
| return String.join(FEATURES_ADDITIONAL_PLUGINS_DATA_SEPARATOR, plugin.getId(), plugin.getVersion(), pluginResolution, String.valueOf(isChecked), fStartLevel, fAutoStart); |
| } |
| |
| public static class AdditionalPluginData { |
| public final String fResolution; |
| public final boolean fEnabled; |
| public final String fStartLevel; |
| public final String fAutoStart; |
| |
| public AdditionalPluginData(String resolution, boolean enabled, String startLevel, String autoStart) { |
| fResolution = resolution; |
| fEnabled = enabled; |
| fStartLevel = (startLevel == null || startLevel.isEmpty()) ? DEFAULT : startLevel; |
| fAutoStart = (autoStart == null || autoStart.isEmpty()) ? DEFAULT : autoStart; |
| } |
| |
| String startLevels() { |
| return fStartLevel + AUTO_START_SEPARATOR + fAutoStart; |
| } |
| } |
| } |