From 1c2491fd51f3c518f2ff3491e1bb7f3164af71dd Mon Sep 17 00:00:00 2001 From: Werner Dietl Date: Sat, 10 Oct 2026 04:46:55 -0400 Subject: [PATCH] Refresh the Annotation File Utilities documents from the latest zip The AFU page links to the file format specification, the changelog, and two figures in afu/. The generator copied only the specification, only when afu/ did not have it yet, and only from /annotation-file-utilities/ inside the zip. The annotation-tools zips have annotation-file-utilities/ at the top, so nothing was copied: the specification on the site is from an old release, and afu/changelog.html and afu/figures/*.svg are not on the site at all (both return 404). The extraction also swallowed a ZipException. Copy the specification (HTML and PDF), changelog.html and figures/ from the latest zip on every run, whether annotation-file-utilities/ is at the top of the zip or inside a release directory, as in the zips built by the Checker Framework. A document that the zip lacks keeps its earlier copy. Read errors now fail the run. Co-Authored-By: Claude Opus 5.5 --- .../eisop/website/EisopSiteGenerator.java | 90 ++++++++++++------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/src/main/java/io/github/eisop/website/EisopSiteGenerator.java b/src/main/java/io/github/eisop/website/EisopSiteGenerator.java index f66a291e..03b12b9c 100644 --- a/src/main/java/io/github/eisop/website/EisopSiteGenerator.java +++ b/src/main/java/io/github/eisop/website/EisopSiteGenerator.java @@ -3,6 +3,7 @@ import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.exception.ZipException; +import net.lingala.zip4j.model.FileHeader; import org.apache.commons.io.FileUtils; import org.json.simple.JSONArray; @@ -22,6 +23,8 @@ import java.util.Date; import java.util.List; import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class EisopSiteGenerator { @@ -567,40 +570,10 @@ static void getAFU(boolean onlyLatest) throws IOException { String latestReleaseName = latestReleaseZipName.substring(0, latestReleaseZipName.length() - 4); - // Extract format documentation from the latest AFU release zip if not already present - File latestFormatHTML = new File(directoryPath, "annotation-file-format.html"); - File latestFormatPDF = new File(directoryPath, "annotation-file-format.pdf"); - if (!latestFormatHTML.exists() || !latestFormatPDF.exists()) { - File latestZip = new File(directoryPath, latestReleaseZipName); - if (latestZip.exists()) { - File tempDir = File.createTempFile("afu-extract", ""); - tempDir.delete(); - tempDir.mkdirs(); - try { - ZipFile zipFile = new ZipFile(latestZip); - zipFile.extractAll(tempDir.getAbsolutePath()); - File formatHTML = - new File( - tempDir, - latestReleaseName - + "/annotation-file-utilities/annotation-file-format.html"); - if (formatHTML.exists()) { - FileUtils.copyFileToDirectory(formatHTML, directoryPath); - } - File formatPDF = - new File( - tempDir, - latestReleaseName - + "/annotation-file-utilities/annotation-file-format.pdf"); - if (formatPDF.exists()) { - FileUtils.copyFileToDirectory(formatPDF, directoryPath); - } - } catch (ZipException e) { - e.printStackTrace(); - } finally { - FileUtils.deleteQuietly(tempDir); - } - } + // Refresh the documents that the AFU page links to from the latest release's zip + File latestZip = new File(directoryPath, latestReleaseZipName); + if (latestZip.exists()) { + extractAfuDocs(latestZip, directoryPath); } System.out.println("Latest release: " + latestReleaseName); @@ -658,6 +631,55 @@ static void getAFU(boolean onlyLatest) throws IOException { FileUtils.writeStringToFile(newMD, mdString, StandardCharsets.UTF_8); } + /** + * The documents of an Annotation File Utilities zip that the AFU page links to: the file format + * specification, the changelog, and the figures. They are in the zip's + * annotation-file-utilities directory, which is at the top of older zips and inside a + * directory named after the release in newer ones. + */ + private static final Pattern AFU_DOC_ENTRY = + Pattern.compile( + "(?:[^/]+/)?annotation-file-utilities/" + + "(annotation-file-format\\.(?:html|pdf)|changelog\\.html" + + "|figures/[^/]+\\.(?:svg|png))"); + + /** + * Returns where a zip entry goes relative to the afu directory of the website, if it is one of + * the documents that the AFU page links to. + * + * @param entryName the name of an entry of an Annotation File Utilities zip + * @return the path relative to the afu directory, or null if the entry is not such a document + */ + static String afuDocPath(String entryName) { + Matcher m = AFU_DOC_ENTRY.matcher(entryName); + return m.matches() ? m.group(1) : null; + } + + /** + * Copies the documents that the AFU page links to out of an Annotation File Utilities zip, + * replacing the copies of an earlier release. A document that the zip does not have, such as + * the changelog in releases that moved it into the Checker Framework changelog, keeps its + * earlier copy. + * + * @param zip the zip of the latest Annotation File Utilities release + * @param afuDir the website's afu directory + * @throws IOException if the zip cannot be read or a document cannot be written + */ + static void extractAfuDocs(File zip, File afuDir) throws IOException { + try (ZipFile zipFile = new ZipFile(zip)) { + for (FileHeader header : zipFile.getFileHeaders()) { + String path = header.isDirectory() ? null : afuDocPath(header.getFileName()); + if (path == null) { + continue; + } + try (InputStream in = zipFile.getInputStream(header)) { + FileUtils.copyInputStreamToFile(in, new File(afuDir, path)); + } + System.out.println("Extracted " + path + " from " + zip.getName()); + } + } + } + /** * Fills in the Annotation File Utilities placeholders on every page generated from * cf-template.md.