diff --git a/.github/workflows/ant.yml b/.github/workflows/ant.yml
index 07a77430..89d71dda 100644
--- a/.github/workflows/ant.yml
+++ b/.github/workflows/ant.yml
@@ -101,9 +101,11 @@ jobs:
- eclipse-202006-jdk8
- eclipse-202212
- eclipse-202309
+ - eclipse-202312
- eclipse-oxygen-full
- eclipse-2022-03-full
- eclipse-2023-09-full
+ - eclipse-2023-12-full
- ecj11
- ecj14
- ecj16
diff --git a/buildScripts/ivy.xml b/buildScripts/ivy.xml
index b165300b..6a5cd0eb 100644
--- a/buildScripts/ivy.xml
+++ b/buildScripts/ivy.xml
@@ -32,6 +32,7 @@
+
@@ -64,6 +65,7 @@
+
@@ -132,6 +134,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/buildScripts/setup.ant.xml b/buildScripts/setup.ant.xml
index f6329a3a..f8fb5e51 100644
--- a/buildScripts/setup.ant.xml
+++ b/buildScripts/setup.ant.xml
@@ -45,25 +45,21 @@ This buildfile is part of projectlombok.org. It sets up the build itself.
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Full eclipse testing requires downloading a native SWT binding. This script knows how to download for OS = [mac, linux, or windows] and architecture = [aarch64 or x86-64]. You have something different, you unique snowflake you. Your OS: "${os.name}", Your arch: "${os.arch}".
+ Full eclipse testing requires downloading a native SWT binding. This script knows how to download for OS = [mac, linux, or windows] and architecture = [aarch64 or x86-64]. You have something different, you unique snowflake you. Your OS: "${os.name}", Your arch: "${os.arch}".
@@ -160,15 +156,23 @@ This buildfile is part of projectlombok.org. It sets up the build itself.
-
+
-
+
-
+
+
+
+
+
+
+
+
+
@@ -192,13 +196,31 @@ This buildfile is part of projectlombok.org. It sets up the build itself.
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -206,133 +228,9 @@ This buildfile is part of projectlombok.org. It sets up the build itself.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
diff --git a/buildScripts/tests.ant.xml b/buildScripts/tests.ant.xml
index c7f7a865..ade6fbff 100644
--- a/buildScripts/tests.ant.xml
+++ b/buildScripts/tests.ant.xml
@@ -218,6 +218,36 @@ This buildfile is part of projectlombok.org. It takes care of compiling and runn
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -261,6 +291,14 @@ This buildfile is part of projectlombok.org. It takes care of compiling and runn
+
+
+
+
+
+
+
+
diff --git a/src/support/lombok/eclipse/dependencies/Child.java b/src/support/lombok/eclipse/dependencies/Child.java
new file mode 100644
index 00000000..06b93793
--- /dev/null
+++ b/src/support/lombok/eclipse/dependencies/Child.java
@@ -0,0 +1,8 @@
+package lombok.eclipse.dependencies;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+public class Child {
+ @XmlAttribute
+ String location;
+}
\ No newline at end of file
diff --git a/src/support/lombok/eclipse/dependencies/DownloadEclipseDependencies.java b/src/support/lombok/eclipse/dependencies/DownloadEclipseDependencies.java
index 2ff6839e..2391a1a9 100644
--- a/src/support/lombok/eclipse/dependencies/DownloadEclipseDependencies.java
+++ b/src/support/lombok/eclipse/dependencies/DownloadEclipseDependencies.java
@@ -7,71 +7,57 @@ import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.List;
+import java.util.Set;
/**
* Download eclipse bundles.
*/
public class DownloadEclipseDependencies {
- public static void main(String[] args) throws IOException {
+ public static void main(String[] args) throws Exception {
String target = args[0];
String eclipseVersion = args[1];
- String updatePage = args[2];
- String[] packages = Arrays.copyOfRange(args, 3, args.length);
+ String updateSiteUrl = args[2];
+ boolean resolveDependencies = Boolean.parseBoolean(args[3]);
+ List bundles = Arrays.asList(Arrays.copyOfRange(args, 4, args.length));
+
+ UpdateSite updateSite = new UpdateSite();
+ updateSite.read(updateSiteUrl);
+
+ final Set artifacts;
+ if (resolveDependencies) {
+ artifacts = updateSite.resolveWithDependencies(bundles);
+ } else {
+ artifacts = updateSite.resolveWithoutDependencies(bundles);
+ }
String pluginTarget = target + "/" + eclipseVersion + "/plugins/";
+ String pluginSource = updateSite.getResolvedUrl() + "/plugins/";
- String indexData = readUrlAsString(updatePage);
-
- for (String pkg : packages) {
- Matcher matcher = Pattern.compile("(" + pkg.replace(".", "\\.") + "_.*?\\.jar)").matcher(indexData);
- if (matcher.find()) {
- String path = matcher.group(1);
-
- try {
- downloadFile(path, updatePage, pluginTarget);
- } catch (Exception e) {
- }
-
- int index = path.lastIndexOf("_");
- String source = path.substring(0, index) + ".source" + path.substring(index);
- if (indexData.contains(source)) {
- try {
- downloadFile(source, updatePage, pluginTarget);
- } catch (Exception e) {
- }
- }
- } else {
- System.out.println("Bundle \"" + pkg + "\" not found");
+ for (String artifact : artifacts) {
+ try {
+ downloadFile(artifact, pluginSource, pluginTarget);
+ } catch (Exception e) {
+ }
+
+ int index = artifact.lastIndexOf("_");
+ String source = artifact.substring(0, index) + ".source" + artifact.substring(index);
+ try {
+ downloadFile(source, pluginSource, pluginTarget);
+ } catch (Exception e) {
}
}
writeEclipseLibrary(target, eclipseVersion);
}
- private static String readUrlAsString(String url) throws MalformedURLException, IOException {
- InputStream in = getStreamForUrl(url);
-
- StringBuilder sb = new StringBuilder();
-
- int bufferSize = 1024;
- char[] buffer = new char[bufferSize];
- InputStreamReader reader = new InputStreamReader(in, "UTF-8");
- for (int count = 0; (count = reader.read(buffer, 0, bufferSize)) > 0;) {
- sb.append(buffer, 0, count);
- }
- return sb.toString();
- }
-
private static void downloadFile(String filename, String repositoryUrl, String target) throws IOException {
new File(target).mkdirs();
File targetFile = new File(target, filename);
@@ -87,10 +73,13 @@ public class DownloadEclipseDependencies {
out = new FileOutputStream(targetFile);
copy(in, out);
System.out.println("[done]");
- } catch(IOException e) {
+ } catch (IOException e) {
System.out.println("[error]");
} finally {
- if (in != null) try { in.close(); } catch (Exception ignore) {}
+ if (in != null) try {
+ in.close();
+ } catch (Exception ignore) {
+ }
if (out != null) out.close();
}
}
@@ -111,7 +100,7 @@ public class DownloadEclipseDependencies {
InputStream in = new BufferedInputStream(connection.getInputStream());
return in;
}
-
+
private static void writeEclipseLibrary(String target, String eclipseVersion) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("\n");
@@ -121,7 +110,8 @@ public class DownloadEclipseDependencies {
sb.append("\" systemlibrary=\"false\">\n");
File[] files = new File(new File(target, eclipseVersion), "plugins").listFiles(new FilenameFilter() {
- @Override public boolean accept(File dir, String name) {
+ @Override
+ public boolean accept(File dir, String name) {
return name.endsWith(".jar") && !name.contains(".source_");
}
});
diff --git a/src/support/lombok/eclipse/dependencies/Provided.java b/src/support/lombok/eclipse/dependencies/Provided.java
new file mode 100644
index 00000000..6ca7d6f3
--- /dev/null
+++ b/src/support/lombok/eclipse/dependencies/Provided.java
@@ -0,0 +1,12 @@
+package lombok.eclipse.dependencies;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+public class Provided {
+ @XmlAttribute
+ String namespace;
+ @XmlAttribute
+ String name;
+ @XmlAttribute
+ String version;
+}
diff --git a/src/support/lombok/eclipse/dependencies/Repository.java b/src/support/lombok/eclipse/dependencies/Repository.java
new file mode 100644
index 00000000..8423d7ee
--- /dev/null
+++ b/src/support/lombok/eclipse/dependencies/Repository.java
@@ -0,0 +1,18 @@
+package lombok.eclipse.dependencies;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class Repository {
+ @XmlElementWrapper(name = "children")
+ @XmlElement(name="child")
+ List children;
+
+ @XmlElementWrapper(name = "units")
+ @XmlElement(name="unit")
+ List units;
+}
\ No newline at end of file
diff --git a/src/support/lombok/eclipse/dependencies/Required.java b/src/support/lombok/eclipse/dependencies/Required.java
new file mode 100644
index 00000000..5f1f895d
--- /dev/null
+++ b/src/support/lombok/eclipse/dependencies/Required.java
@@ -0,0 +1,17 @@
+package lombok.eclipse.dependencies;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+
+public class Required {
+ @XmlAttribute
+ String namespace;
+ @XmlAttribute
+ String name;
+ @XmlAttribute
+ String range;
+ @XmlAttribute
+ boolean optional;
+ @XmlElement
+ String filter;
+}
diff --git a/src/support/lombok/eclipse/dependencies/UniqueQueue.java b/src/support/lombok/eclipse/dependencies/UniqueQueue.java
new file mode 100644
index 00000000..7ec4a38c
--- /dev/null
+++ b/src/support/lombok/eclipse/dependencies/UniqueQueue.java
@@ -0,0 +1,21 @@
+package lombok.eclipse.dependencies;
+
+import java.util.ArrayDeque;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A incomplete implementation of a queue that maintains a history of all queued items and ensures unique elements.
+ * Elements cannot be added to the queue if they already exist in the history.
+ */
+public class UniqueQueue extends ArrayDeque {
+ private Set added = new HashSet<>();
+
+ @Override
+ public boolean add(T e) {
+ if (added.add(e)) {
+ return super.add(e);
+ }
+ return false;
+ }
+}
diff --git a/src/support/lombok/eclipse/dependencies/Unit.java b/src/support/lombok/eclipse/dependencies/Unit.java
new file mode 100644
index 00000000..51827cf0
--- /dev/null
+++ b/src/support/lombok/eclipse/dependencies/Unit.java
@@ -0,0 +1,27 @@
+package lombok.eclipse.dependencies;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+
+public class Unit {
+ @XmlAttribute
+ String id;
+ @XmlAttribute
+ String version;
+
+ @XmlElementWrapper(name = "provides")
+ @XmlElement(name="provided")
+ List provides;
+
+ @XmlElementWrapper(name = "requires")
+ @XmlElement(name="required")
+ List requires;
+
+ @Override
+ public String toString() {
+ return id + "_" + version;
+ }
+}
diff --git a/src/support/lombok/eclipse/dependencies/UpdateSite.java b/src/support/lombok/eclipse/dependencies/UpdateSite.java
new file mode 100644
index 00000000..93da4120
--- /dev/null
+++ b/src/support/lombok/eclipse/dependencies/UpdateSite.java
@@ -0,0 +1,194 @@
+package lombok.eclipse.dependencies;
+
+import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.sax.SAXSource;
+
+import org.xml.sax.InputSource;
+
+public class UpdateSite {
+ private static final String OS_NAME = System.getProperty("os.name").toLowerCase();
+ private static final String OSGI_OS = OS_NAME.contains("windows") ? "windows" : OS_NAME.contains("mac") ? "mac" : "linux";
+ private static final String OS_ARCH = System.getProperty("os.arch");
+ private static final String OSGI_ARCH = OS_ARCH.equals("aarch64") ? "aarch64" : "x86_64";
+
+ private JAXBContext jaxbContext;
+ private Repository repository;
+ private Map> providesIndex;
+ private String resolvedUrl;
+ private SAXParserFactory saxParserFactory;
+
+ public UpdateSite() throws Exception {
+ jaxbContext = JAXBContext.newInstance(Repository.class);
+ providesIndex = new HashMap<>();
+
+ SAXParserFactory spf = SAXParserFactory.newInstance();
+ spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ spf.setXIncludeAware(false);
+ saxParserFactory = spf;
+ }
+
+ public void read(String url) throws Exception {
+ String currentUrl = url;
+ try {
+ while (true) {
+ String child = resolveNextChild(currentUrl);
+ if (child.startsWith("https://")) {
+ currentUrl = child;
+ } else {
+ currentUrl += child;
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Found the real repository
+ }
+
+ resolvedUrl = currentUrl;
+
+ try (InputStream inputStream = readJarOrXml(resolvedUrl, "content")) {
+ repository = unmarshalRepository(inputStream);
+
+ // Build index
+ for (Unit unit : repository.units) {
+ for (Provided provides : unit.provides) {
+ providesIndex.computeIfAbsent(provides.namespace + ":" + provides.name, k -> new ArrayList<>()).add(unit);
+ }
+ }
+ }
+ ;
+ }
+
+ public Set resolveWithoutDependencies(List dependencies) {
+ return resolve(dependencies, false);
+ }
+
+ public Set resolveWithDependencies(List dependencies) {
+ return resolve(dependencies, true);
+ }
+
+ private Set resolve(List dependencies, boolean withDependencies) {
+ Queue toResolve = new UniqueQueue<>();
+ for (String dependency : dependencies) {
+ toResolve.add(dependency);
+ }
+ Set resolved = new HashSet<>();
+ while (!toResolve.isEmpty()) {
+ String next = toResolve.poll();
+
+ List providedUnits = providesIndex.get(next);
+ // Skip unknown
+ if (providedUnits == null) {
+ System.out.println("Skipping unknown unit " + next);
+ continue;
+ }
+ // Remove a.jre.javase dependency
+ List filteredProvidedUnits = providedUnits.stream()
+ .filter(u -> !u.id.equals("a.jre.javase")) // Remove
+ .collect(Collectors.toList());
+
+ if (filteredProvidedUnits.size() == 0) {
+ // This is a JDK only dependency, skip
+ continue;
+ }
+
+ // Skip ambiguous (we could use version ranges to solve that...)
+ if (filteredProvidedUnits.size() > 1) {
+ boolean alreadyResolved = filteredProvidedUnits.stream().anyMatch(resolved::contains);
+ if (!alreadyResolved) {
+ System.out.println("Ambiguous resolution for " + next + ": " + filteredProvidedUnits.toString());
+ continue;
+ }
+ }
+
+ Unit unit = filteredProvidedUnits.get(0);
+ resolved.add(unit);
+
+ if (withDependencies && unit.requires != null) {
+ for (Required required : unit.requires) {
+ if (required.optional) continue;
+ if (!matchesFilter(required.filter)) continue;
+
+ toResolve.add(required.namespace + ":" + required.name);
+ }
+ }
+ }
+
+ return resolved.stream().map(u -> u.toString() + ".jar").collect(Collectors.toSet());
+ }
+
+ // Dummy implementation
+ private boolean matchesFilter(String filter) {
+ if (filter == null) {
+ return true;
+ }
+ if (filter.contains("osgi.arch=") && !filter.contains("osgi.arch=" + OSGI_ARCH)) {
+ return false;
+ }
+ if (filter.contains("osgi.os=") && !filter.contains("osgi.os=" + OSGI_OS)) {
+ return false;
+ }
+ return true;
+ }
+
+ private String resolveNextChild(String currentUrl) throws Exception {
+ try (InputStream inputStream = readJarOrXml(currentUrl, "compositeContent")) {
+ Repository repository = unmarshalRepository(inputStream);
+ Child lastChild = repository.children.get(repository.children.size() - 1);
+ return lastChild.location;
+ }
+ }
+
+ private Repository unmarshalRepository(InputStream inputStream) throws Exception {
+ Source source = new SAXSource(saxParserFactory.newSAXParser().getXMLReader(), new InputSource(inputStream));
+ Repository repository = (Repository) jaxbContext.createUnmarshaller().unmarshal(source);
+ return repository;
+ }
+
+ private InputStream readJarOrXml(String url, String name) throws Exception {
+ try {
+ return getStreamForUrl(url + "/" + name + ".xml");
+ } catch (FileNotFoundException e) {
+ System.out.println("Not found, trying jar");
+ }
+ ZipInputStream zipInputStream = new ZipInputStream(getStreamForUrl(url + "/" + name + ".jar"));
+ ZipEntry entry;
+ while ((entry = zipInputStream.getNextEntry()) != null) {
+ if (entry.getName().equals(name + ".xml")) {
+ return zipInputStream;
+ }
+ }
+ throw new FileNotFoundException();
+ }
+
+ private static InputStream getStreamForUrl(String url) throws Exception {
+ System.out.println("Reading " + url);
+ HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
+ connection.setRequestProperty("User-Agent", "lombok");
+ connection.setRequestProperty("Accept", "*/*");
+ InputStream in = new BufferedInputStream(connection.getInputStream());
+ return in;
+ }
+
+ public String getResolvedUrl() {
+ return resolvedUrl;
+ }
+}