Bug 526011 - PDE should provide an abstraction above annotation-configuration in .classpath * introduce new manifest header Eclipse-ExportExternalAnnotations * if set to true, resolved entries in Required-Plugins will define their annotationPath equal to the plugin location This suffices to help JDT/Core to use these annotations for null analysis, if .eea files are shipped with the plug-in jar. We even have content assist for the new header :) Change-Id: I84643bc57d3da9f5a8f50d61da4ea82f8cd9cac9 Signed-off-by: Stephan Herrmann <stephan.herrmann@berlin.de> Reviewed-on: https://git.eclipse.org/r/c/pde/eclipse.pde.ui/+/175466 Tested-by: Vikas Chandra <Vikas.Chandra@in.ibm.com> Reviewed-by: Vikas Chandra <Vikas.Chandra@in.ibm.com>
diff --git a/ui/org.eclipse.pde.core/.settings/.api_filters b/ui/org.eclipse.pde.core/.settings/.api_filters index 5d17ac3..686677b 100644 --- a/ui/org.eclipse.pde.core/.settings/.api_filters +++ b/ui/org.eclipse.pde.core/.settings/.api_filters
@@ -1,5 +1,13 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <component id="org.eclipse.pde.core" version="2"> + <resource path="META-INF/MANIFEST.MF"> + <filter comment="experimental addition of a manifest option" id="924844039"> + <message_arguments> + <message_argument value="3.15.200"/> + <message_argument value="3.15.100"/> + </message_arguments> + </filter> + </resource> <resource path="src/org/eclipse/pde/internal/core/project/BundleProjectService.java" type="org.eclipse.pde.internal.core.project.BundleProjectService"> <filter comment="Platform Team allows use of bundle importers for PDE import from source repository" id="640712815"> <message_arguments>
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/plugin/IPluginBase.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/plugin/IPluginBase.java index 5f37e02..170f140 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/plugin/IPluginBase.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/plugin/IPluginBase.java
@@ -179,4 +179,16 @@ */ void setSchemaVersion(String schemaVersion) throws CoreException; + /** + * Returns whether this plugin exports its external annotations (.eea files) + * to be considered by clients performing annotation based null analysis. + * This is read from the manifest header + * {@code Eclipse-ExportExternalAnnotations}. + * + * @since 3.15 + */ + default boolean exportsExternalAnnotations() { + return false; + } + }
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/ICoreConstants.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/ICoreConstants.java index e28d771..d61f5e3 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/ICoreConstants.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/ICoreConstants.java
@@ -285,6 +285,7 @@ String ECLIPSE_SOURCE_REFERENCES = "Eclipse-SourceReferences"; //$NON-NLS-1$ String SERVICE_COMPONENT = "Service-Component"; //$NON-NLS-1$ String AUTOMATIC_MODULE_NAME = "Automatic-Module-Name"; //$NON-NLS-1$ + String ECLIPSE_EXPORT_EXTERNAL_ANNOTATIONS = "Eclipse-ExportExternalAnnotations"; //$NON-NLS-1$ // Equinox-specific system properties String OSGI_SYSTEM_BUNDLE = "osgi.system.bundle"; //$NON-NLS-1$
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEAuxiliaryState.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEAuxiliaryState.java index d668d56..41996da 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEAuxiliaryState.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEAuxiliaryState.java
@@ -95,6 +95,7 @@ String project; String localization; String bundleSourceEntry; + boolean exportsExternalAnnotations; } /** @@ -188,6 +189,11 @@ return info == null ? null : info.bundleSourceEntry; } + public boolean exportsExternalAnnotations(long bundleID) { + PluginInfo info = fPluginInfos.get(Long.toString(bundleID)); + return info == null ? false : info.exportsExternalAnnotations; + } + /** * Builds an xml document storing the auxiliary plugin info. * @param dir directory location to create the file @@ -375,6 +381,8 @@ info.localization = manifest.get(Constants.BUNDLE_LOCALIZATION); info.hasBundleStructure = hasBundleStructure; info.bundleSourceEntry = manifest.get(ICoreConstants.ECLIPSE_SOURCE_BUNDLE); + info.exportsExternalAnnotations = "true" //$NON-NLS-1$ + .equals(manifest.get(ICoreConstants.ECLIPSE_EXPORT_EXTERNAL_ANNOTATIONS)); fPluginInfos.put(Long.toString(desc.getBundleId()), info); }
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEClasspathContainer.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEClasspathContainer.java index a631457..1274af0 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEClasspathContainer.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEClasspathContainer.java
@@ -16,6 +16,7 @@ import java.io.File; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; @@ -24,6 +25,7 @@ import org.eclipse.jdt.core.IClasspathAttribute; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.osgi.service.resolver.BundleDescription; import org.eclipse.pde.core.plugin.IPluginLibrary; import org.eclipse.pde.core.plugin.IPluginModelBase; @@ -54,21 +56,28 @@ private static final IAccessRule EXCLUDE_ALL_RULE = JavaCore.newAccessRule(new Path("**/*"), IAccessRule.K_NON_ACCESSIBLE | IAccessRule.IGNORE_IF_BETTER); //$NON-NLS-1$ - protected void addProjectEntry(IProject project, Rule[] rules, ArrayList<IClasspathEntry> entries) throws CoreException { + protected void addProjectEntry(IProject project, Rule[] rules, boolean exportsExternalAnnotations, + ArrayList<IClasspathEntry> entries) throws CoreException { if (project.hasNature(JavaCore.NATURE_ID)) { - IClasspathEntry entry = null; - if (rules != null) { - IAccessRule[] accessRules = getAccessRules(rules); - entry = JavaCore.newProjectEntry(project.getFullPath(), accessRules, true, new IClasspathAttribute[0], false); - } else { - entry = JavaCore.newProjectEntry(project.getFullPath()); - } + IAccessRule[] accessRules = rules != null ? getAccessRules(rules) : null; + IClasspathAttribute[] extraAttribs = getClasspathAttributesForProject(project, exportsExternalAnnotations); + IClasspathEntry entry = JavaCore.newProjectEntry(project.getFullPath(), accessRules, true, extraAttribs, false); if (!entries.contains(entry)) { entries.add(entry); } } } + private IClasspathAttribute[] getClasspathAttributesForProject(IProject project, boolean exportsExternalAnnotations) + throws JavaModelException { + if (exportsExternalAnnotations) { + String annotationPath = JavaCore.create(project).getOutputLocation().toString(); + return new IClasspathAttribute[] { + JavaCore.newClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, annotationPath) }; + } + return new IClasspathAttribute[0]; + } + public static IClasspathEntry[] getExternalEntries(IPluginModelBase model) { ArrayList<IClasspathEntry> entries = new ArrayList<>(); addExternalPlugin(model, new Rule[0], entries); @@ -153,12 +162,20 @@ } private static IClasspathAttribute[] getClasspathAttributes(IPluginModelBase model) { + List<IClasspathAttribute> attributes = new ArrayList<>(); JavadocLocationManager manager = PDECore.getDefault().getJavadocLocationManager(); String location = manager.getJavadocLocation(model); - if (location == null) { - return new IClasspathAttribute[0]; + if (location != null) { + attributes + .add(JavaCore.newClasspathAttribute(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, location)); } - return new IClasspathAttribute[] {JavaCore.newClasspathAttribute(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, location)}; + if (model.getPluginBase().exportsExternalAnnotations()) { + String installLocation = model.getInstallLocation(); + attributes.add( + JavaCore.newClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, + installLocation)); + } + return attributes.toArray(IClasspathAttribute[]::new); } private static synchronized IAccessRule getDiscouragedRule(IPath path) {
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEState.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEState.java index 1a0cf66..c7ccc40 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEState.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/PDEState.java
@@ -306,4 +306,8 @@ return fAuxiliaryState.getBundleSourceEntry(bundleID); } + public boolean exportsExternalAnnotations(long bundleID) { + return fAuxiliaryState.exportsExternalAnnotations(bundleID); + } + }
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/RequiredPluginsClasspathContainer.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/RequiredPluginsClasspathContainer.java index 310abc4..3999a40 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/RequiredPluginsClasspathContainer.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/RequiredPluginsClasspathContainer.java
@@ -347,7 +347,7 @@ } if (resource != null) { - addProjectEntry(resource.getProject(), rules, entries); + addProjectEntry(resource.getProject(), rules, model.getPluginBase().exportsExternalAnnotations(), entries); } else { addExternalPlugin(model, rules, entries); }
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/WorkspacePluginModelManager.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/WorkspacePluginModelManager.java index a726211..30dd5e9 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/WorkspacePluginModelManager.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/WorkspacePluginModelManager.java
@@ -84,7 +84,8 @@ Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, // IPDEBuildConstants.ECLIPSE_PLATFORM_FILTER, // ICoreConstants.ECLIPSE_SYSTEM_BUNDLE, // - ICoreConstants.ECLIPSE_SOURCE_BUNDLE))); + ICoreConstants.ECLIPSE_SOURCE_BUNDLE, // + ICoreConstants.ECLIPSE_EXPORT_EXTERNAL_ANNOTATIONS))); private final ArrayList<IExtensionDeltaListener> fExtensionListeners = new ArrayList<>(); private ArrayList<ModelChange> fChangedExtensions = null;
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bundle/BundlePlugin.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bundle/BundlePlugin.java index 1b253ce..7965327 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bundle/BundlePlugin.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/bundle/BundlePlugin.java
@@ -52,4 +52,9 @@ return "true".equals(getValue(ICoreConstants.EXTENSIBLE_API, false)); //$NON-NLS-1$ } + @Override + public boolean exportsExternalAnnotations() { + return "true".equals(getValue(ICoreConstants.ECLIPSE_EXPORT_EXTERNAL_ANNOTATIONS, false)); //$NON-NLS-1$ + } + }
diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/plugin/PluginBase.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/plugin/PluginBase.java index faf0a78..046b963 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/plugin/PluginBase.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/plugin/PluginBase.java
@@ -51,6 +51,7 @@ private String fVersion; private boolean fHasBundleStructure; private String fBundleSourceEntry; + private boolean fExportsExternalAnnotations; public PluginBase(boolean readOnly) { super(readOnly); @@ -123,6 +124,7 @@ fProviderName = state.getProviderName(bundleDesc.getBundleId()); fHasBundleStructure = state.hasBundleStructure(bundleDesc.getBundleId()); fBundleSourceEntry = state.getBundleSourceEntry(bundleDesc.getBundleId()); + fExportsExternalAnnotations = state.exportsExternalAnnotations(bundleDesc.getBundleId()); loadRuntime(bundleDesc, state); loadImports(bundleDesc); } @@ -443,4 +445,8 @@ return fBundleSourceEntry; } + public boolean exportsExternalAnnotations() { + return fExportsExternalAnnotations; + } + }
diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/contentassist/ManifestContentAssistProcessor.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/contentassist/ManifestContentAssistProcessor.java index 806cede..ee4af33 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/contentassist/ManifestContentAssistProcessor.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/contentassist/ManifestContentAssistProcessor.java
@@ -48,7 +48,7 @@ private IJavaProject fJP; // if we order the headers alphabetically in the array, there is no need to sort and we can save time - private static final String[] fHeader = { ICoreConstants.AUTOMATIC_MODULE_NAME, Constants.BUNDLE_ACTIVATIONPOLICY, Constants.BUNDLE_ACTIVATOR, Constants.BUNDLE_CATEGORY, Constants.BUNDLE_CLASSPATH, Constants.BUNDLE_CONTACTADDRESS, Constants.BUNDLE_COPYRIGHT, Constants.BUNDLE_DESCRIPTION, Constants.BUNDLE_DOCURL, Constants.BUNDLE_LOCALIZATION, Constants.BUNDLE_MANIFESTVERSION, Constants.BUNDLE_NAME, Constants.BUNDLE_NATIVECODE, Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, Constants.BUNDLE_SYMBOLICNAME, Constants.BUNDLE_UPDATELOCATION, Constants.BUNDLE_VENDOR, Constants.BUNDLE_VERSION, Constants.DYNAMICIMPORT_PACKAGE, ICoreConstants.ECLIPSE_BUDDY_POLICY, ICoreConstants.ECLIPSE_BUNDLE_SHAPE, ICoreConstants.ECLIPSE_GENERIC_CAPABILITY, ICoreConstants.ECLIPSE_GENERIC_REQUIRED, ICoreConstants.ECLIPSE_LAZYSTART, + private static final String[] fHeader = { ICoreConstants.AUTOMATIC_MODULE_NAME, Constants.BUNDLE_ACTIVATIONPOLICY, Constants.BUNDLE_ACTIVATOR, Constants.BUNDLE_CATEGORY, Constants.BUNDLE_CLASSPATH, Constants.BUNDLE_CONTACTADDRESS, Constants.BUNDLE_COPYRIGHT, Constants.BUNDLE_DESCRIPTION, Constants.BUNDLE_DOCURL, Constants.BUNDLE_LOCALIZATION, Constants.BUNDLE_MANIFESTVERSION, Constants.BUNDLE_NAME, Constants.BUNDLE_NATIVECODE, Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT, Constants.BUNDLE_SYMBOLICNAME, Constants.BUNDLE_UPDATELOCATION, Constants.BUNDLE_VENDOR, Constants.BUNDLE_VERSION, Constants.DYNAMICIMPORT_PACKAGE, ICoreConstants.ECLIPSE_BUDDY_POLICY, ICoreConstants.ECLIPSE_BUNDLE_SHAPE, ICoreConstants.ECLIPSE_EXPORT_EXTERNAL_ANNOTATIONS, ICoreConstants.ECLIPSE_GENERIC_CAPABILITY, ICoreConstants.ECLIPSE_GENERIC_REQUIRED, ICoreConstants.ECLIPSE_LAZYSTART, ICoreConstants.PLATFORM_FILTER, ICoreConstants.ECLIPSE_REGISTER_BUDDY, ICoreConstants.ECLIPSE_SOURCE_REFERENCES, Constants.EXPORT_PACKAGE, ICoreConstants.EXPORT_SERVICE, Constants.FRAGMENT_HOST, Constants.IMPORT_PACKAGE, ICoreConstants.IMPORT_SERVICE, Constants.PROVIDE_CAPABILITY, Constants.REQUIRE_BUNDLE, Constants.REQUIRE_CAPABILITY}; @@ -229,6 +229,10 @@ return handleBuddyPolicyCompletion(value.substring(ICoreConstants.ECLIPSE_BUDDY_POLICY.length() + 1), offset); if (value.regionMatches(true, 0, ICoreConstants.ECLIPSE_BUNDLE_SHAPE, 0, Math.min(length, ICoreConstants.ECLIPSE_BUNDLE_SHAPE.length()))) return handleEclipseBundleShape(value.substring(ICoreConstants.ECLIPSE_BUNDLE_SHAPE.length() + 1), offset); + if (value.regionMatches(true, 0, ICoreConstants.ECLIPSE_EXPORT_EXTERNAL_ANNOTATIONS, 0, + Math.min(length, ICoreConstants.ECLIPSE_EXPORT_EXTERNAL_ANNOTATIONS.length()))) + return handleTrueFalseValue( + value.substring(ICoreConstants.ECLIPSE_EXPORT_EXTERNAL_ANNOTATIONS.length() + 1), offset); return new ICompletionProposal[0]; }