diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDELabelProvider.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDELabelProvider.java index 4f29c9b8ed..9180bb8c61 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDELabelProvider.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDELabelProvider.java @@ -22,6 +22,7 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; +import org.eclipse.jdt.launching.environments.IExecutionEnvironment; import org.eclipse.jdt.ui.ISharedImages; import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jface.resource.ImageDescriptor; @@ -81,7 +82,6 @@ import org.eclipse.pde.internal.core.isite.ISiteFeature; import org.eclipse.pde.internal.core.plugin.ImportObject; import org.eclipse.pde.internal.core.schema.SchemaRegistry; -import org.eclipse.pde.internal.core.text.bundle.ExecutionEnvironment; import org.eclipse.pde.internal.core.text.bundle.ExportPackageObject; import org.eclipse.pde.internal.core.text.bundle.ImportPackageObject; import org.eclipse.pde.internal.core.text.bundle.PackageObject; @@ -171,8 +171,8 @@ public String getText(Object obj) { if (obj instanceof PackageObject) { return getObjectText((PackageObject) obj); } - if (obj instanceof ExecutionEnvironment) { - return getObjectText((ExecutionEnvironment) obj); + if (obj instanceof IExecutionEnvironment ee) { + return preventNull(ee.getId()); } if (obj instanceof Locale) { return getObjectText((Locale) obj); @@ -183,10 +183,6 @@ public String getText(Object obj) { return super.getText(obj); } - private String getObjectText(ExecutionEnvironment environment) { - return preventNull(environment.getName()); - } - public String getObjectText(IPluginBase pluginBase) { String name = isFullNameModeEnabled() ? pluginBase.getTranslatedName() : pluginBase.getId(); name = preventNull(name); @@ -529,8 +525,8 @@ public Image getImage(Object obj) { if (obj instanceof PackageObject) { return getObjectImage((PackageObject) obj); } - if (obj instanceof ExecutionEnvironment) { - return getObjectImage((ExecutionEnvironment) obj); + if (obj instanceof IExecutionEnvironment) { + return get(PDEPluginImages.DESC_JAVA_LIB_OBJ); } if (obj instanceof ResolverError) { return getObjectImage((ResolverError) obj); @@ -554,10 +550,6 @@ private Image getObjectImage(ResolverError obj) { }; } - private Image getObjectImage(ExecutionEnvironment environment) { - return get(PDEPluginImages.DESC_JAVA_LIB_OBJ); - } - private Image getObjectImage(IPlugin plugin) { return getObjectImage(plugin, false, false); } diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/ModelDataTransfer.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/ModelDataTransfer.java index 1292265364..faf8af2868 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/ModelDataTransfer.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/ModelDataTransfer.java @@ -18,7 +18,10 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.Serializable; +import org.eclipse.jdt.launching.environments.IExecutionEnvironment; +import org.eclipse.pde.internal.core.util.VMUtil; import org.eclipse.swt.dnd.ByteArrayTransfer; import org.eclipse.swt.dnd.TransferData; @@ -50,12 +53,12 @@ public ModelDataTransfer() { @Override protected int[] getTypeIds() { - return new int[] {TYPEID}; + return new int[] { TYPEID }; } @Override protected String[] getTypeNames() { - return new String[] {TYPE_NAME}; + return new String[] { TYPE_NAME }; } @Override @@ -72,6 +75,9 @@ protected void javaToNative(Object data, TransferData transferData) { //write each object for (Object object : objects) { + if (object instanceof IExecutionEnvironment ee) { + object = new EESerializationSurogate(ee.getId()); + } objectOut.writeObject(object); } @@ -87,17 +93,28 @@ protected void javaToNative(Object data, TransferData transferData) { @Override protected Object nativeToJava(TransferData transferData) { byte[] bytes = (byte[]) super.nativeToJava(transferData); - if (bytes == null) + if (bytes == null) { return null; + } try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes))) { int count = in.readInt(); Object[] objects = new Object[count]; for (int i = 0; i < count; i++) { - objects[i] = in.readObject(); + Object object = in.readObject(); + if (object instanceof EESerializationSurogate ee) { + object = VMUtil.getExecutionEnvironment(ee.eeId()); + } + objects[i] = object; } return objects; } catch (ClassNotFoundException | IOException e) { return null; } } + + private record EESerializationSurogate(String eeId) implements Serializable { + // JDT doesn't want to make IExecutionEnvironment serializable, so we + // need special handling in PDE: + // https://github.com/eclipse-jdt/eclipse.jdt.debug/pull/456 + } } diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/BundleSourcePage.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/BundleSourcePage.java index da55a087a2..59fd110bff 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/BundleSourcePage.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/BundleSourcePage.java @@ -17,8 +17,11 @@ package org.eclipse.pde.internal.ui.editor.plugin; import java.util.Map; +import java.util.stream.Stream; import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.launching.JavaRuntime; +import org.eclipse.jdt.launching.environments.IExecutionEnvironment; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.ITextSelection; @@ -33,6 +36,7 @@ import org.eclipse.pde.core.plugin.IPluginLibrary; import org.eclipse.pde.core.plugin.IPluginModelBase; import org.eclipse.pde.internal.core.ibundle.IBundleModel; +import org.eclipse.pde.internal.core.ibundle.IBundlePluginModel; import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase; import org.eclipse.pde.internal.core.ibundle.IManifestHeader; import org.eclipse.pde.internal.core.plugin.ImportObject; @@ -74,6 +78,7 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.osgi.framework.Constants; +import org.osgi.framework.namespace.ExecutionEnvironmentNamespace; public class BundleSourcePage extends KeyValueSourcePage { @@ -108,7 +113,12 @@ public Object[] getChildren(Object parent) { } else if (parent instanceof ExportPackageHeader exportHeader) { return exportHeader.getPackages(); } else if (parent instanceof RequiredExecutionEnvironmentHeader breeHeader) { - return breeHeader.getElements(); + return toEEArray(breeHeader.getEnvironments().stream()); + } else if (parent instanceof IManifestHeader header + && Constants.REQUIRE_CAPABILITY.equals(header.getName())) { + Stream.Builder eeRequirements = Stream.builder(); + ExecutionEnvironmentSection.addRequiredEEs(header, eeRequirements); + return toEEArray(eeRequirements.build()); } else if (parent instanceof RequireBundleHeader requireBundleHeader) { return requireBundleHeader.getRequiredBundles(); } else if (parent instanceof BundleClasspathHeader) { @@ -117,6 +127,10 @@ public Object[] getChildren(Object parent) { return new Object[0]; } + private Object[] toEEArray(Stream stream) { + return stream.map(JavaRuntime.getExecutionEnvironmentsManager()::getEnvironment).toArray(); + } + @Override public boolean hasChildren(Object parent) { return getChildren(parent).length > 0; @@ -158,8 +172,8 @@ private class BundleLabelProvider extends LabelProvider { public String getText(Object obj) { if (obj instanceof PackageObject packageObject) { return packageObject.getName(); - } else if (obj instanceof ExecutionEnvironment ee) { - return ee.getName(); + } else if (obj instanceof IExecutionEnvironment ee) { + return ee.getId(); } else if (obj instanceof RequireBundleObject requireBundle) { return getTextRequireBundle(requireBundle); } else if (obj instanceof ManifestHeader header) { @@ -205,7 +219,7 @@ public Image getImage(Object obj) { PDELabelProvider labelProvider = PDEPlugin.getDefault().getLabelProvider(); if (obj instanceof PackageObject) { return labelProvider.get(PDEPluginImages.DESC_PACKAGE_OBJ); - } else if (obj instanceof ExecutionEnvironment) { + } else if (obj instanceof IExecutionEnvironment) { return labelProvider.get(PDEPluginImages.DESC_JAVA_LIB_OBJ); } else if (obj instanceof RequireBundleObject requireBundle) { int flags = SharedLabelProvider.F_EXTERNAL; @@ -405,7 +419,7 @@ private String getHeaderName(PDEManifestElement element) { return Constants.EXPORT_PACKAGE; } else if (element instanceof ImportPackageObject) { return Constants.IMPORT_PACKAGE; - } else if (element instanceof ExecutionEnvironment) { + } else if (element instanceof ExecutionEnvironment || element instanceof IExecutionEnvironment) { return Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT; } else if (element instanceof RequireBundleObject) { return Constants.REQUIRE_BUNDLE; @@ -445,14 +459,60 @@ public IDocumentRange findRange() { if (library.getPluginModel() instanceof IBundlePluginModelBase pluginModel) { return getSpecificRange(pluginModel.getBundleModel(), Constants.BUNDLE_CLASSPATH, library.getName()); } - } else if (selection instanceof ExecutionEnvironment ee) { - return getSpecificRange(ee.getModel(), Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, ee.getName()); + } else if (selection instanceof IExecutionEnvironment ee) { + if (getPluginModel() instanceof IBundlePluginModel bundlePlugin) { + return getEEsSpecificRange(ee, bundlePlugin.getBundleModel()); + } } else if (selection instanceof RequireBundleObject requiredBundle) { return getSpecificRange(requiredBundle.getModel(), Constants.REQUIRE_BUNDLE, requiredBundle.getId()); } return null; } + private IDocumentRange getEEsSpecificRange(IExecutionEnvironment ee, IBundleModel bundleModel) { + IManifestHeader breeHeader = bundleModel.getBundle().getManifestHeader(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT); + IDocumentRange range = getSpecificRange(bundleModel, breeHeader, ee.getId()); + if (range != null && range.getOffset() != breeHeader.getOffset() + && range.getLength() != breeHeader.getName().length()) { + return range; + } + IManifestHeader requireCapaHeader = bundleModel.getBundle().getManifestHeader(Constants.REQUIRE_CAPABILITY); + + String attribute = ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE; + IDocumentRange attributeRange = findAttributeRange(bundleModel, requireCapaHeader, attribute); + if (attributeRange != null) { + return attributeRange; + } + // Not found, just mark the (probably) originating header + IManifestHeader header = breeHeader != null ? breeHeader : requireCapaHeader; + return newDocumentRange(header.getOffset(), header.getName().length()); + } + + private IDocumentRange findAttributeRange(IBundleModel bundleModel, IManifestHeader header, String attribute) { + try { + int start = header.getOffset() + header.getName().length(); + int length = header.getLength() - header.getName().length(); + String headerValue = ((IEditingModel) bundleModel).getDocument().get(start, length); + int attributeStart = headerValue.indexOf(attribute); + if (attributeStart > -1) { + boolean inQotes = false; + int i = attributeStart + attribute.length(); + for (; i < headerValue.length(); i++) { + char charAt = headerValue.charAt(i); + if (!inQotes && charAt == ',') { + break; + } + if (charAt == '"') { + inQotes = !inQotes; + } + } + return newDocumentRange(start + attributeStart, i - attributeStart - 1); + } + } catch (BadLocationException e) { + } + return null; + } + public static IDocumentRange getSpecificRange(IBundleModel model, IManifestHeader header, String element) { if (header == null || !(model instanceof IEditingModel)) { return null; @@ -509,20 +569,24 @@ public static IDocumentRange getSpecificRange(IBundleModel model, IManifestHeade // header value will be included in the selection range[1] = header.getName().length(); } + return newDocumentRange(range[0], range[1]); + } + + private static IDocumentRange newDocumentRange(int offset, int length) { return new IDocumentRange() { @Override public int getOffset() { - return range[0]; + return offset; } @Override public int getLength() { - return range[1]; + return length; } }; } - public static IDocumentRange getSpecificRange(IBundleModel model, String headerName, String search) { + private static IDocumentRange getSpecificRange(IBundleModel model, String headerName, String search) { IManifestHeader header = model.getBundle().getManifestHeader(headerName); return getSpecificRange(model, header, search); } diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/ExecutionEnvironmentSection.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/ExecutionEnvironmentSection.java index a32ef644a4..e6e3a63748 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/ExecutionEnvironmentSection.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/ExecutionEnvironmentSection.java @@ -16,14 +16,17 @@ *******************************************************************************/ package org.eclipse.pde.internal.ui.editor.plugin; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; +import java.util.function.Consumer; +import java.util.stream.Stream; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; @@ -41,6 +44,7 @@ import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.window.Window; +import org.eclipse.osgi.util.ManifestElement; import org.eclipse.pde.core.IModelChangedEvent; import org.eclipse.pde.core.plugin.IPluginModelBase; import org.eclipse.pde.core.plugin.PluginRegistry; @@ -51,6 +55,7 @@ import org.eclipse.pde.internal.core.natures.PluginProject; import org.eclipse.pde.internal.core.text.bundle.ExecutionEnvironment; import org.eclipse.pde.internal.core.text.bundle.RequiredExecutionEnvironmentHeader; +import org.eclipse.pde.internal.core.util.ManifestUtils; import org.eclipse.pde.internal.core.util.VMUtil; import org.eclipse.pde.internal.ui.IHelpContextIds; import org.eclipse.pde.internal.ui.PDEPlugin; @@ -79,7 +84,10 @@ import org.eclipse.ui.forms.widgets.Hyperlink; import org.eclipse.ui.forms.widgets.Section; import org.eclipse.ui.forms.widgets.TableWrapData; +import org.osgi.framework.BundleException; import org.osgi.framework.Constants; +import org.osgi.framework.namespace.ExecutionEnvironmentNamespace; +import org.osgi.resource.Namespace; public class ExecutionEnvironmentSection extends TableSection { @@ -144,12 +152,7 @@ protected void createClient(Section section, FormToolkit toolkit) { fEETable = tablePart.getTableViewer(); fEETable.setContentProvider((IStructuredContentProvider) inputElement -> { if (inputElement instanceof IBundleModel model) { - IBundle bundle = model.getBundle(); - @SuppressWarnings("deprecation") - IManifestHeader header = bundle.getManifestHeader(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT); - if (header instanceof RequiredExecutionEnvironmentHeader breeHeader) { - return breeHeader.getElements(); - } + return getRequiredEEs(model.getBundle()).toArray(); } return new Object[0]; }); @@ -261,8 +264,8 @@ private void handleRemove() { IStructuredSelection ssel = fEETable.getStructuredSelection(); if (!ssel.isEmpty()) { for (Object object : ssel) { - if (object instanceof ExecutionEnvironment ee) { - getHeader().removeExecutionEnvironment(ee.getName()); + if (object instanceof IExecutionEnvironment ee) { + getHeader().removeExecutionEnvironment(ee.getId()); } } } @@ -320,14 +323,8 @@ protected FilteredList createFilteredList(Composite parent) { @SuppressWarnings("deprecation") private void addExecutionEnvironments(Object[] result) { - List ees = Arrays.stream(result).map(resultObject -> { - if (resultObject instanceof IExecutionEnvironment ee) { - return ee.getId(); - } else if (resultObject instanceof ExecutionEnvironment ee) { - return ee.getName(); - } - return null; - }).filter(Objects::nonNull).toList(); + List ees = Arrays.stream(result).filter(IExecutionEnvironment.class::isInstance) + .map(IExecutionEnvironment.class::cast).map(IExecutionEnvironment::getId).toList(); IManifestHeader header = getHeader(); if (header == null) { @@ -348,14 +345,46 @@ private String getLineDelimiter() { } private IExecutionEnvironment[] getEnvironments() { - RequiredExecutionEnvironmentHeader header = getHeader(); IExecutionEnvironmentsManager eeManager = JavaRuntime.getExecutionEnvironmentsManager(); IExecutionEnvironment[] envs = eeManager.getExecutionEnvironments(); - if (header == null) { - return envs; + IBundle bundle = getBundle(); + if (bundle != null) { + List requiredEEs = getRequiredEEs(bundle).toList(); + if (!requiredEEs.isEmpty()) { + return Arrays.stream(envs).filter(ee -> !requiredEEs.contains(ee)) + .toArray(IExecutionEnvironment[]::new); + } + } + return envs; + } + + private Stream getRequiredEEs(IBundle bundle) { + List requiredEEs = new ArrayList<>(1); + RequiredExecutionEnvironmentHeader breeHeader = getHeader(); + if (breeHeader != null) { + requiredEEs.addAll(breeHeader.getEnvironments()); + } + IManifestHeader requiredCapabilitiesHeader = bundle.getManifestHeader(Constants.REQUIRE_CAPABILITY); + if (requiredCapabilitiesHeader != null) { + addRequiredEEs(requiredCapabilitiesHeader, requiredEEs::add); + } + return requiredEEs.stream().sorted(VMUtil.ASCENDING_EE_JAVA_VERSION) + .map(JavaRuntime.getExecutionEnvironmentsManager()::getEnvironment); + } + + static void addRequiredEEs(IManifestHeader requiredCapabilitiesHeader, Consumer eeCollector) { + String eeRequirement = requiredCapabilitiesHeader.getValue(); + try { + ManifestElement[] required = ManifestElement.parseHeader(Constants.REQUIRE_CAPABILITY, eeRequirement); + for (ManifestElement requiredCapability : required) { + if (ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE.equals(requiredCapability.getValue())) { + String filter = requiredCapability.getDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE); + ManifestUtils.parseRequiredEEsFromFilter(filter, eeCollector); + } + } + } catch (BundleException e) { + ILog.get().error("Failed to parse " + Constants.REQUIRE_CAPABILITY + " header: " + eeRequirement, e); //$NON-NLS-1$//$NON-NLS-2$ } - List ees = header.getElementNames().stream().map(eeManager::getEnvironment).toList(); - return Arrays.stream(envs).filter(ee -> !ees.contains(ee)).toArray(IExecutionEnvironment[]::new); } @Override