Skip to content

Commit

Permalink
[FIXUP] Stream-line ClasspathComputer
Browse files Browse the repository at this point in the history
- Use a record instead of adding class fields to ClasspathComputer and
pass arguments directly where suitable/not too many/far.
  • Loading branch information
HannesWell committed Sep 17, 2023
1 parent d602646 commit b413b96
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -29,7 +31,6 @@
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaModelStatus;
Expand All @@ -54,135 +55,104 @@

public class ClasspathComputer {

private record ClasspathConfiguration(IPluginModelBase model, IJavaProject javaProject, boolean isTestPlugin,
IClasspathAttribute[] defaultAttrs, Map<IPath, IClasspathEntry> originalByPath,
List<IClasspathEntry> reloaded) {
}

private static Map<String, Integer> fSeverityTable = null;
private static final int SEVERITY_ERROR = 3;
private static final int SEVERITY_WARNING = 2;
private static final int SEVERITY_IGNORE = 1;

private final IProject project;
private final IPluginModelBase model;
private final Map<String, IPath> sourceLibraryMap;
private final boolean overrideCompliance;

private final IJavaProject javaProject;
private final IBuild build;
private final boolean isTestPlugin;
private final IClasspathAttribute[] defaultAttrs;
private final IClasspathEntry[] original;
private final Map<IPath, IClasspathEntry> originalByPath;
private final List<IClasspathEntry> reloaded = new ArrayList<>();

private ClasspathComputer(IProject project, IPluginModelBase model, Map<String, IPath> sourceLibraryMap,
boolean clear, boolean overrideCompliance) throws CoreException {
this.project = project;
this.model = model;
this.sourceLibraryMap = sourceLibraryMap != null ? sourceLibraryMap : new HashMap<>();
this.overrideCompliance = overrideCompliance;

this.javaProject = JavaCore.create(project);
this.build = getBuild(project);
this.isTestPlugin = hasTestPluginName(project);
this.defaultAttrs = getClasspathAttributes(project, model);
this.original = clear ? new IClasspathEntry[0] : javaProject.getRawClasspath();
// ignore duplicates eventually seen in the original classpath
this.originalByPath = mapFirstSeenByPath(Arrays.stream(this.original));
}

public static void setClasspath(IProject project, IPluginModelBase model) throws CoreException {
IClasspathEntry[] entries = getClasspath(project, model, null, false, true);
JavaCore.create(project).setRawClasspath(entries, null);
}

public static IClasspathEntry[] getClasspath(IProject project, IPluginModelBase model,
Map<String, IPath> sourceLibraryMap, boolean clear, boolean overrideCompliance) throws CoreException {
ClasspathComputer cpc = new ClasspathComputer(project, model, sourceLibraryMap, clear, overrideCompliance);
return cpc.compute();
}

private IClasspathEntry[] compute() throws CoreException {
public static IClasspathEntry[] getClasspath(IProject project, IPluginModelBase model, Map<String, IPath> sourceLibraryMap, boolean clear, boolean overrideCompliance) throws CoreException {
IJavaProject javaProject = JavaCore.create(project);
IClasspathEntry[] originalClasspath = clear ? new IClasspathEntry[0] : javaProject.getRawClasspath();
ClasspathConfiguration context = new ClasspathConfiguration(model, javaProject,
hasTestPluginName(project), getClasspathAttributes(project, model),
mapFirstSeenByPath(Arrays.stream(originalClasspath)), new ArrayList<>());

// add JRE and set compliance options
String ee = getExecutionEnvironment(model.getBundleDescription());
addContainerEntry(getEEPath(ee));
addContainerEntry(getEEPath(ee), context);
setComplianceOptions(javaProject, ee, overrideCompliance);

// add pde container
addContainerEntry(PDECore.REQUIRED_PLUGINS_CONTAINER_PATH);
addContainerEntry(PDECore.REQUIRED_PLUGINS_CONTAINER_PATH, context);

// add own libraries/source
addSourceAndLibraries();
addSourceAndLibraries(sourceLibraryMap != null ? sourceLibraryMap : Collections.emptyMap(), context);

IClasspathEntry[] entries = collectInOriginalOrder();

IJavaModelStatus validation = JavaConventions.validateClasspath(javaProject, entries,
javaProject.getOutputLocation());
IClasspathEntry[] entries = collectInOriginalOrder(originalClasspath, context.reloaded);
IJavaModelStatus validation = JavaConventions.validateClasspath(javaProject, entries, javaProject.getOutputLocation());
if (!validation.isOK()) {
PDECore.logErrorMessage(validation.getMessage());
throw new CoreException(validation);
}
return entries;
}

private IClasspathEntry[] collectInOriginalOrder() {
private static IClasspathEntry[] collectInOriginalOrder(IClasspathEntry[] originalClasspath,
List<IClasspathEntry> reloaded) {
// preserve original entries which eventually weren't reloaded
Stream<IClasspathEntry> reloadedPlusOriginal = Stream.concat(reloaded.stream(), Stream.of(original));
var reloadedPlusOriginal = Stream.concat(reloaded.stream(), Stream.of(originalClasspath));
Map<IPath, IClasspathEntry> resultByPath = mapFirstSeenByPath(reloadedPlusOriginal);
List<IClasspathEntry> result = new ArrayList<>(resultByPath.size());
// using the original order, collect the resulting entries, and remove
// from the map to prevent from any duplicates (even original ones)
Arrays.stream(original).map(e -> resultByPath.remove(pathWithoutEE(e.getPath()))).filter(e -> e != null)
.forEachOrdered(result::add);
Arrays.stream(originalClasspath).map(e -> resultByPath.remove(pathWithoutEE(e.getPath())))
.filter(Objects::nonNull).forEachOrdered(result::add);
// using the order of reloading, append new entries (in the map still)
reloaded.stream().filter(e -> resultByPath.remove(pathWithoutEE(e.getPath())) != null)
.forEachOrdered(result::add);
return result.toArray(new IClasspathEntry[result.size()]);
return result.toArray(IClasspathEntry[]::new);
}

private static Map<IPath, IClasspathEntry> mapFirstSeenByPath(Stream<IClasspathEntry> entryStream) {
return entryStream.collect(Collectors.toMap(e -> pathWithoutEE(e.getPath()), e -> e, (first, dupe) -> first));
}

private static IPath pathWithoutEE(IPath path) {
if (PDECore.JRE_CONTAINER_PATH.isPrefixOf(path)) {
// The path member of IClasspathEntry for JRE_CONTAINER_PATH may
// also declare an Execution Environment, which is an attribute.
return PDECore.JRE_CONTAINER_PATH;
}
return path;
// The path member of IClasspathEntry for JRE_CONTAINER_PATH may
// also declare an Execution Environment, which is an attribute.
return PDECore.JRE_CONTAINER_PATH.isPrefixOf(path) ? PDECore.JRE_CONTAINER_PATH : path;
}

private void addContainerEntry(IPath path) {
IClasspathEntry orig = originalByPath.get(pathWithoutEE(path));
if (orig != null) {
reloaded.add(JavaCore.newContainerEntry(path, orig.getAccessRules(), orig.getExtraAttributes(),
orig.isExported()));
} else {
reloaded.add(JavaCore.newContainerEntry(path, null, defaultAttrs, false));
}
private static void addContainerEntry(IPath path, ClasspathConfiguration context) {
IClasspathEntry original = context.originalByPath.get(pathWithoutEE(path));
context.reloaded.add(JavaCore.newContainerEntry(path, //
original != null ? original.getAccessRules() : null,
original != null ? original.getExtraAttributes() : context.defaultAttrs,
original != null ? original.isExported() : false));
}

private void addSourceAndLibraries() throws CoreException {
IPluginLibrary[] libraries = model.getPluginBase().getLibraries();
private static void addSourceAndLibraries(Map<String, IPath> sourceLibraryMap, ClasspathConfiguration context)
throws CoreException {
IPluginLibrary[] libraries = context.model.getPluginBase().getLibraries();
IBuild build = getBuild(context.javaProject.getProject());
for (IPluginLibrary library : libraries) {
IBuildEntry buildEntry = build == null ? null : build.getEntry("source." + library.getName()); //$NON-NLS-1$
if (buildEntry != null) {
addSourceFolders(buildEntry);
continue;
}
if (library.getName().equals(".")) { //$NON-NLS-1$
addJARdPlugin("."); //$NON-NLS-1$
addSourceFolders(buildEntry, context);
} else if (library.getName().equals(".")) { //$NON-NLS-1$
addJARdPlugin(".", sourceLibraryMap, context); //$NON-NLS-1$
} else {
addLibraryEntry(library);
addLibraryEntry(library, sourceLibraryMap, context);
}
}
if (libraries.length == 0) {
if (build != null) {
IBuildEntry buildEntry = build.getEntry("source.."); //$NON-NLS-1$
if (buildEntry != null) {
addSourceFolders(buildEntry);
addSourceFolders(buildEntry, context);
}
} else if (ClasspathUtilCore.hasBundleStructure(model)) {
addJARdPlugin("."); //$NON-NLS-1$
} else if (ClasspathUtilCore.hasBundleStructure(context.model)) {
addJARdPlugin(".", sourceLibraryMap, context); //$NON-NLS-1$
}
}
}
Expand Down Expand Up @@ -247,29 +217,31 @@ private static IClasspathAttribute[] getClasspathAttributes(IProject project, IP
return attributes;
}

private void addSourceFolders(IBuildEntry buildEntry) throws CoreException {
private static void addSourceFolders(IBuildEntry buildEntry, ClasspathConfiguration context)
throws CoreException {
String[] folders = buildEntry.getTokens();
IProject project = context.javaProject.getProject();
for (String folder : folders) {
IPath path = project.getFullPath().append(folder);
IClasspathEntry orig = originalByPath.get(pathWithoutEE(path));
IClasspathEntry orig = context.originalByPath.get(pathWithoutEE(path));
if (orig != null) {
reloaded.add(orig);
context.reloaded.add(orig);
continue;
}
if (project.findMember(folder) == null) {
CoreUtility.createFolder(project.getFolder(folder));
} else {
IPackageFragmentRoot root = javaProject.getPackageFragmentRoot(path.toString());
IPackageFragmentRoot root = context.javaProject.getPackageFragmentRoot(path.toString());
if (root.exists() && root.getKind() == IPackageFragmentRoot.K_BINARY) {
reloaded.add(root.getRawClasspathEntry());
context.reloaded.add(root.getRawClasspathEntry());
continue;
}
}
if (isTestPlugin) {
reloaded.add(JavaCore.newSourceEntry(path, null, null, null, new IClasspathAttribute[] {
if (context.isTestPlugin) {
context.reloaded.add(JavaCore.newSourceEntry(path, null, null, null, new IClasspathAttribute[] {
JavaCore.newClasspathAttribute(IClasspathAttribute.TEST, "true") })); //$NON-NLS-1$
} else {
reloaded.add(JavaCore.newSourceEntry(path));
context.reloaded.add(JavaCore.newSourceEntry(path));
}
}
}
Expand All @@ -284,17 +256,18 @@ protected static IBuild getBuild(IProject project) throws CoreException {
return (buildModel != null) ? buildModel.getBuild() : null;
}

private void addLibraryEntry(IPluginLibrary library) throws JavaModelException {
private static void addLibraryEntry(IPluginLibrary library, Map<String, IPath> sourceLibraryMap,
ClasspathConfiguration context) throws JavaModelException {
String name = ClasspathUtilCore.expandLibraryName(library.getName());
IResource jarFile = project.findMember(name);
IResource jarFile = context.javaProject.getProject().findMember(name);
if (jarFile == null) {
return;
}

IPath sourceAttachment = sourceLibraryMap.get(library.getName());
boolean isExported = library.isExported();

IPackageFragmentRoot root = JavaCore.create(project).getPackageFragmentRoot(jarFile);
IPackageFragmentRoot root = context.javaProject.getPackageFragmentRoot(jarFile);
if (root.exists() && root.getKind() == IPackageFragmentRoot.K_BINARY) {
IClasspathEntry oldEntry = root.getRawClasspathEntry();
// If we have the same binary root but new or different source,
Expand All @@ -304,43 +277,42 @@ private void addLibraryEntry(IPluginLibrary library) throws JavaModelException {
sourceAttachment = oldEntry.getSourceAttachmentPath();
}
if (sourceAttachment != null && sourceAttachment.equals(oldEntry.getSourceAttachmentPath())) {
reloaded.add(oldEntry);
context.reloaded.add(oldEntry);
return;
}
// Force recreation when the source attachment:
// - is not defined: the default could be available now, or
// - is overridden with a different value.
isExported = oldEntry.isExported();
}

reloadClasspathEntry(jarFile, name, sourceAttachment, isExported);
reloadClasspathEntry(jarFile, name, sourceAttachment, isExported, context);
}

private void addJARdPlugin(String libraryName) {
String filename = ClasspathUtilCore.getFilename(model);
private static void addJARdPlugin(String libraryName, Map<String, IPath> sourceLibraryMap,
ClasspathConfiguration context) {
String filename = ClasspathUtilCore.getFilename(context.model);
String name = ClasspathUtilCore.expandLibraryName(filename);
IResource jarFile = project.findMember(name);
IResource jarFile = context.javaProject.getProject().findMember(name);
if (jarFile != null) {
IPath sourceAttachment = sourceLibraryMap.get(libraryName);
reloadClasspathEntry(jarFile, filename, sourceAttachment, true);
reloadClasspathEntry(jarFile, filename, sourceAttachment, true, context);
}
}

private void reloadClasspathEntry(IResource library, String fileName, IPath sourceAttachment, boolean isExported) {
IClasspathEntry orig = originalByPath.get(pathWithoutEE(library.getFullPath()));
private static void reloadClasspathEntry(IResource library, String fileName, IPath sourceAttachment,
boolean isExported, ClasspathConfiguration context) {
IClasspathEntry orig = context.originalByPath.get(pathWithoutEE(library.getFullPath()));
if (orig != null && sourceAttachment == null) {
sourceAttachment = orig.getSourceAttachmentPath();
}
IProject project = context.javaProject.getProject();
IResource source = sourceAttachment != null ? project.findMember(sourceAttachment)
: project.findMember(ClasspathUtilCore.getSourceZipName(fileName));
sourceAttachment = source == null ? null : source.getFullPath();
if (orig != null) {
reloaded.add(JavaCore.newLibraryEntry(library.getFullPath(), sourceAttachment, null, orig.getAccessRules(),
orig.getExtraAttributes(), orig.isExported()));
} else {
reloaded.add(JavaCore.newLibraryEntry(library.getFullPath(), sourceAttachment, null, new IAccessRule[0],
defaultAttrs, isExported));
}
context.reloaded.add(JavaCore.newLibraryEntry(library.getFullPath(), sourceAttachment, null,
orig != null ? orig.getAccessRules() : null, //
orig != null ? orig.getExtraAttributes() : context.defaultAttrs,
orig != null ? orig.isExported() : isExported));
}

private static String getExecutionEnvironment(BundleDescription bundleDescription) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.eclipse.pde.ui.tests.build.properties.AllValidatorTests;
import org.eclipse.pde.ui.tests.classpathcontributor.ClasspathContributorTest;
import org.eclipse.pde.ui.tests.classpathresolver.ClasspathResolverTest;
import org.eclipse.pde.ui.tests.classpathupdater.ClasspathUpdaterTest;
import org.eclipse.pde.ui.tests.ee.ExportBundleTests;
import org.eclipse.pde.ui.tests.imports.AllImportTests;
import org.eclipse.pde.ui.tests.launcher.AllLauncherTests;
Expand Down Expand Up @@ -55,6 +56,7 @@
BundleRootTests.class,
PluginRegistryTests.class,
ClasspathResolverTest.class,
ClasspathUpdaterTest.class,
ClasspathContributorTest.class,
DynamicPluginProjectReferencesTest.class,
ClasspathResolutionTest.class,
Expand Down
Loading

0 comments on commit b413b96

Please sign in to comment.