Skip to content

Commit

Permalink
Fallback to the running eclipse if annotation not found in target state
Browse files Browse the repository at this point in the history
If the annotations can't be found in the target platform currently no
automatic annotations are provided as part of the classpath container.

This now adds a fallback to the running eclipse instance to get the
annotations from there, this is similar on how Tycho works that loads
missing annotations from maven if they are not found in the target.
  • Loading branch information
laeubi committed Mar 17, 2024
1 parent 0d546e9 commit ab28042
Show file tree
Hide file tree
Showing 13 changed files with 182 additions and 74 deletions.
5 changes: 5 additions & 0 deletions apitools/org.eclipse.pde.api.tools/.project
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ds.core.builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dsVersion=V1_4
eclipse.preferences.version=1
enabled=true
generateBundleActivationPolicyLazy=true
path=OSGI-INF
validationErrorLevel=error
validationErrorLevel.missingImplicitUnbindMethod=error
1 change: 1 addition & 0 deletions apitools/org.eclipse.pde.api.tools/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)",
org.eclipse.core.filebuffers;bundle-version="[3.8.200,4.0.0)",
org.eclipse.equinox.frameworkadmin;bundle-version="[2.3.0,3.0.0)",
org.eclipse.core.variables;bundle-version="[3.6.200,4.0.0)"
Service-Component: OSGI-INF/org.eclipse.pde.api.tools.internal.ApiAnnotationsClasspathContributor.xml
Export-Package: org.eclipse.pde.api.tools.internal;x-friends:="org.eclipse.pde.api.tools.tests,org.eclipse.pde.api.tools.ui,org.eclipse.pde.api.tools.generator",
org.eclipse.pde.api.tools.internal.builder;x-friends:="org.eclipse.pde.api.tools.ui,org.eclipse.pde.api.tools.tests",
org.eclipse.pde.api.tools.internal.comparator;x-friends:="org.eclipse.pde.api.tools.ui,org.eclipse.pde.api.tools.tests",
Expand Down
1 change: 1 addition & 0 deletions apitools/org.eclipse.pde.api.tools/OSGI-INF/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/*.xml
3 changes: 2 additions & 1 deletion apitools/org.eclipse.pde.api.tools/build.properties
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ bin.includes = .,\
plugin.xml,\
lib/apitooling-ant.jar,\
scripts/,\
fixed_api_descriptions/
fixed_api_descriptions/,
OSGI-INF/
jars.extra.classpath=platform:/plugin/org.apache.ant/lib/ant.jar,platform:/plugin/org.objectweb.asm
jars.compile.order = .,\
lib/apitooling-ant.jar
Expand Down
6 changes: 0 additions & 6 deletions apitools/org.eclipse.pde.api.tools/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -328,10 +328,4 @@
</run>
</application>
</extension>
<extension
point="org.eclipse.pde.core.pluginClasspathContributors">
<contributor
class="org.eclipse.pde.api.tools.internal.ApiAnnotationsClasspathContributor">
</contributor>
</extension>
</plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,61 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Predicate;
import java.util.stream.Stream;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.osgi.service.resolver.BundleDelta;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.State;
import org.eclipse.osgi.service.resolver.StateDelta;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.core.IClasspathContributor;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.PluginRegistry;
import org.eclipse.pde.internal.core.ClasspathUtilCore;
import org.eclipse.pde.internal.core.IStateDeltaListener;
import org.eclipse.pde.internal.core.PDECore;
import org.osgi.resource.Resource;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;

public class ApiAnnotationsClasspathContributor implements IClasspathContributor {
@Component(service = IClasspathContributor.class)
public class ApiAnnotationsClasspathContributor implements IClasspathContributor, IStateDeltaListener {

private static final int CHANGE_FLAGS = BundleDelta.ADDED | BundleDelta.REMOVED | BundleDelta.UPDATED;

private static final Collection<String> API_TOOLS_ANNOTATIONS = List.of("org.eclipse.pde.api.tools.annotations"); //$NON-NLS-1$

private ConcurrentMap<String, Collection<IClasspathEntry>> entryMap = new ConcurrentHashMap<>();

@Override
public List<IClasspathEntry> getInitialEntries(BundleDescription project) {
IPluginModelBase projectModel = PluginRegistry.findModel((Resource) project);
if (hasApiNature(projectModel)) {
return ClasspathUtilCore.classpathEntries(annotations().filter(model -> !model.equals(projectModel)))
.collect(Collectors.toList());
return annotations()
.map(bundleId -> entryMap.computeIfAbsent(bundleId,
id -> ClasspathUtilCore.classpathEntriesForBundle(id).toList()))
.flatMap(Collection::stream)
.filter(Predicate.not(entry -> ClasspathUtilCore.isEntryForModel(entry, projectModel))).toList();
}
return Collections.emptyList();
return List.of();
}

@Activate
void registerListener() {
PDECore.getDefault().getModelManager().addStateDeltaListener(this);
}

@Deactivate
void undregisterListener() {
PDECore.getDefault().getModelManager().removeStateDeltaListener(this);
}


private boolean hasApiNature(IPluginModelBase model) {
Expand All @@ -67,14 +93,33 @@ private boolean hasApiNature(IPluginModelBase model) {
* @return s stream of all current available annotations in the current plugin
* registry
*/
public static Stream<IPluginModelBase> annotations() {
return API_TOOLS_ANNOTATIONS.stream().map(PluginRegistry::findModel).filter(Objects::nonNull)
.filter(IPluginModelBase::isEnabled);
public static Stream<String> annotations() {
return API_TOOLS_ANNOTATIONS.stream();
}

@Override
public List<IClasspathEntry> getEntriesForDependency(BundleDescription project, BundleDescription addedDependency) {
return Collections.emptyList();
}

@Override
public void stateResolved(StateDelta delta) {
if (delta == null) {
stateChanged(null);
} else {
// just refresh the items in the map if they have any changes...
for (BundleDelta bundleDelta : delta.getChanges(CHANGE_FLAGS, false)) {
entryMap.remove(bundleDelta.getBundle().getSymbolicName());
}
}

}

@Override
public void stateChanged(State newState) {
// we need to refresh everything
entryMap.clear();
}


}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
dsVersion=V1_3
dsVersion=V1_4
eclipse.preferences.version=1
enabled=true
generateBundleActivationPolicyLazy=true
Expand Down
3 changes: 2 additions & 1 deletion ui/org.eclipse.pde.core/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,5 @@ Require-Bundle:
Bundle-RequiredExecutionEnvironment: JavaSE-17
Bundle-ActivationPolicy: lazy
Automatic-Module-Name: org.eclipse.pde.core
Service-Component: OSGI-INF/org.eclipse.pde.internal.core.bnd.PdeBndAdapter.xml
Service-Component: OSGI-INF/org.eclipse.pde.internal.core.annotations.OSGiAnnotationsClasspathContributor.xml,
OSGI-INF/org.eclipse.pde.internal.core.bnd.PdeBndAdapter.xml
6 changes: 0 additions & 6 deletions ui/org.eclipse.pde.core/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -423,12 +423,6 @@
</targetLocation>
</extension>
<extension
point="org.eclipse.pde.core.pluginClasspathContributors">
<contributor
class="org.eclipse.pde.internal.core.annotations.OSGiAnnotationsClasspathContributor">
</contributor>
</extension>
<extension
point="org.eclipse.pde.core.dynamicSource">
<locator
class="org.eclipse.pde.internal.core.EclipsePluginSourcePathLocator"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@
import static java.util.Collections.singleton;

import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.Optional;
import java.util.stream.Stream;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
Expand Down Expand Up @@ -85,41 +85,60 @@ private static Collection<ClasspathLibrary> collectLibraryEntries(IPluginModelBa
return entries;
}

public static Stream<IClasspathEntry> classpathEntries(Stream<IPluginModelBase> models) {
Map<Boolean, List<IPluginModelBase>> collect = models.collect(Collectors.partitioningBy(model -> {
IResource resource = model.getUnderlyingResource();
public static Stream<IClasspathEntry> classpathEntriesForBundle(String id) {
// first look if we have something in the workspace...
IPluginModelBase model = PluginRegistry.findModel(id);
if (model != null && model.isEnabled()) {
if (isWorkspaceProject(model)) {
IJavaProject javaProject = JavaCore.create(model.getUnderlyingResource().getProject());
return Stream.of(JavaCore.newProjectEntry(javaProject.getPath()));
}
String location = model.getInstallLocation();
if (location == null) {
return Stream.empty();
}
boolean isJarShape = new File(location).isFile();
IPluginLibrary[] libraries = model.getPluginBase().getLibraries();
if (isJarShape || libraries.length == 0) {
return Stream.of(getEntryForPath(IPath.fromOSString(location)));
}
return Arrays.stream(libraries).filter(library -> !IPluginLibrary.RESOURCE.equals(library.getType()))
.map(library -> {
String name = library.getName();
String expandedName = ClasspathUtilCore.expandLibraryName(name);
return ClasspathUtilCore.getPath(model, expandedName, isJarShape);
}).filter(Objects::nonNull).map(ClasspathUtilCore::getEntryForPath);
}
// if not found in the models, try to use one from the running eclipse
return Optional.ofNullable(Platform.getBundle(id)).map(bundle -> bundle.adapt(File.class)).filter(File::exists)
.map(File::toPath).map(Path::normalize).map(path -> IPath.fromOSString(path.toString()))
.map(ClasspathUtilCore::getEntryForPath).stream();
}

public static boolean isEntryForModel(IClasspathEntry entry, IPluginModelBase projectModel) {
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
IResource resource = projectModel.getUnderlyingResource();
if (resource != null) {
try {
return resource.getProject().hasNature(JavaCore.NATURE_ID);
} catch (CoreException e) {
// nothing we can do then...
}
return resource.getProject().getFullPath().equals(entry.getPath());
}
}
return false;
}

protected static boolean isWorkspaceProject(IPluginModelBase model) {
IResource resource = model.getUnderlyingResource();
if (resource != null) {
try {
return resource.getProject().hasNature(JavaCore.NATURE_ID);
} catch (CoreException e) {
// nothing we can do then...
}
return false;
}));
List<IPluginModelBase> javaModels = collect.get(true);
List<IPluginModelBase> externalModels = collect.get(false);
return Stream.concat(
javaModels.stream().map(m -> JavaCore.create(m.getUnderlyingResource().getProject()))
.map(IJavaProject::getPath).map(JavaCore::newProjectEntry),
externalModels.stream().flatMap(model -> {
String location = model.getInstallLocation();
if (location == null) {
return Stream.empty();
}
boolean isJarShape = new File(location).isFile();
IPluginLibrary[] libraries = model.getPluginBase().getLibraries();
if (isJarShape || libraries.length == 0) {
return Stream.of(IPath.fromOSString(location));
}
return Arrays.stream(libraries)
.filter(library -> !IPluginLibrary.RESOURCE.equals(library.getType())).map(library -> {
String name = library.getName();
String expandedName = ClasspathUtilCore.expandLibraryName(name);
return ClasspathUtilCore.getPath(model, expandedName, isJarShape);
}).filter(Objects::nonNull);
}).map(path -> JavaCore.newLibraryEntry(path, path, IPath.ROOT, new IAccessRule[0],
new IClasspathAttribute[0], false)));
}
return false;
}

private static IClasspathEntry getEntryForPath(IPath path) {
return JavaCore.newLibraryEntry(path, path, IPath.ROOT, new IAccessRule[0], new IClasspathAttribute[0], false);
}

private static void addLibraryEntry(IPluginLibrary library, Collection<ClasspathLibrary> entries) {
Expand Down

0 comments on commit ab28042

Please sign in to comment.