From 363af559473674b7a4b08d86b150cfe9e78f46c8 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Wed, 15 Nov 2023 16:50:30 +0200 Subject: [PATCH 01/36] Remove deprecated integration script targets Signed-off-by: Jarno Elovirta --- src/main/integrator.xml | 47 ++--------------------------------------- 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/src/main/integrator.xml b/src/main/integrator.xml index 1f096fb1e1..92ee4f41b0 100644 --- a/src/main/integrator.xml +++ b/src/main/integrator.xml @@ -20,60 +20,17 @@ See the accompanying LICENSE file for applicable license. - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - WARN: The lax integration mode has been removed, using strict mode - - - - - - From b79b8bda6f9ee45c918220e46f993be956c467f4 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Wed, 15 Nov 2023 18:26:09 +0200 Subject: [PATCH 02/36] Add uninstall task Signed-off-by: Jarno Elovirta --- src/main/integrator.xml | 18 ++--- .../dita/dost/ant/PluginUninstallTask.java | 65 +++++++++++++++++++ 2 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 src/main/java/org/dita/dost/ant/PluginUninstallTask.java diff --git a/src/main/integrator.xml b/src/main/integrator.xml index 92ee4f41b0..38ce55f28a 100644 --- a/src/main/integrator.xml +++ b/src/main/integrator.xml @@ -21,27 +21,21 @@ See the accompanying LICENSE file for applicable license. - - + + - + - - - Plug-in ${dita.dir}/plugins/${plugin.id} doesn't exist. - - - - - + + - + diff --git a/src/main/java/org/dita/dost/ant/PluginUninstallTask.java b/src/main/java/org/dita/dost/ant/PluginUninstallTask.java new file mode 100644 index 0000000000..00e2c9ed63 --- /dev/null +++ b/src/main/java/org/dita/dost/ant/PluginUninstallTask.java @@ -0,0 +1,65 @@ +/* + * This file is part of the DITA Open Toolkit project. + * + * Copyright 2023 Jarno Elovirta + * + * See the accompanying LICENSE file for applicable license. + */ +package org.dita.dost.ant; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import org.apache.commons.io.FileUtils; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.dita.dost.log.DITAOTAntLogger; +import org.dita.dost.platform.Integrator; + +public final class PluginUninstallTask extends Task { + + private String id; + + @Override + public void execute() throws BuildException { + if (id == null) { + throw new BuildException(new IllegalStateException("id argument not set")); + } + + final File pluginDir = getPluginDir(id); + if (!pluginDir.exists()) { + throw new BuildException("Plug-in directory %s doesn't exist".formatted(pluginDir)); + } + + log("Delete plug-in directory %s".formatted(pluginDir), Project.MSG_DEBUG); + try { + FileUtils.deleteDirectory(pluginDir); + } catch (IOException e) { + throw new BuildException("Failed to delete plug-in directory %s".formatted(pluginDir), e); + } + + final DITAOTAntLogger logger = new DITAOTAntLogger(getProject()); + logger.setTarget(getOwningTarget()); + logger.setTask(this); + final Integrator integrator = new Integrator(getDitaDir()); + integrator.setLogger(logger); + try { + integrator.execute(); + } catch (final Exception e) { + throw new BuildException("Integration failed: " + e.getMessage(), e); + } + } + + private File getDitaDir() { + return Paths.get(getProject().getProperty("dita.dir")).toFile(); + } + + private File getPluginDir(final String id) { + return Paths.get(getDitaDir().getAbsolutePath(), "plugins", id).toFile(); + } + + public void setId(final String id) { + this.id = id; + } +} From 42cd7e0fcb0de7e8ce30603e5226232b8ad0f695 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Wed, 15 Nov 2023 19:01:24 +0200 Subject: [PATCH 03/36] Extract plug-in uninstall code to own class Signed-off-by: Jarno Elovirta --- .../dita/dost/ant/PluginUninstallTask.java | 34 +++------- .../dita/dost/platform/PluginUninstall.java | 67 +++++++++++++++++++ 2 files changed, 76 insertions(+), 25 deletions(-) create mode 100644 src/main/java/org/dita/dost/platform/PluginUninstall.java diff --git a/src/main/java/org/dita/dost/ant/PluginUninstallTask.java b/src/main/java/org/dita/dost/ant/PluginUninstallTask.java index 00e2c9ed63..feadcab000 100644 --- a/src/main/java/org/dita/dost/ant/PluginUninstallTask.java +++ b/src/main/java/org/dita/dost/ant/PluginUninstallTask.java @@ -8,14 +8,11 @@ package org.dita.dost.ant; import java.io.File; -import java.io.IOException; import java.nio.file.Paths; -import org.apache.commons.io.FileUtils; import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.dita.dost.log.DITAOTAntLogger; -import org.dita.dost.platform.Integrator; +import org.dita.dost.platform.PluginUninstall; public final class PluginUninstallTask extends Task { @@ -27,27 +24,18 @@ public void execute() throws BuildException { throw new BuildException(new IllegalStateException("id argument not set")); } - final File pluginDir = getPluginDir(id); - if (!pluginDir.exists()) { - throw new BuildException("Plug-in directory %s doesn't exist".formatted(pluginDir)); - } - - log("Delete plug-in directory %s".formatted(pluginDir), Project.MSG_DEBUG); - try { - FileUtils.deleteDirectory(pluginDir); - } catch (IOException e) { - throw new BuildException("Failed to delete plug-in directory %s".formatted(pluginDir), e); - } - + final PluginUninstall pluginUninstall = new PluginUninstall(); final DITAOTAntLogger logger = new DITAOTAntLogger(getProject()); logger.setTarget(getOwningTarget()); logger.setTask(this); - final Integrator integrator = new Integrator(getDitaDir()); - integrator.setLogger(logger); + pluginUninstall.setLogger(logger); + pluginUninstall.setId(id); + pluginUninstall.setDitaDir(getDitaDir()); + try { - integrator.execute(); - } catch (final Exception e) { - throw new BuildException("Integration failed: " + e.getMessage(), e); + pluginUninstall.execute(); + } catch (Exception e) { + throw new BuildException("Failed to uninstall %s: %s".formatted(id, e.getMessage()), e); } } @@ -55,10 +43,6 @@ private File getDitaDir() { return Paths.get(getProject().getProperty("dita.dir")).toFile(); } - private File getPluginDir(final String id) { - return Paths.get(getDitaDir().getAbsolutePath(), "plugins", id).toFile(); - } - public void setId(final String id) { this.id = id; } diff --git a/src/main/java/org/dita/dost/platform/PluginUninstall.java b/src/main/java/org/dita/dost/platform/PluginUninstall.java new file mode 100644 index 0000000000..4ec2541a37 --- /dev/null +++ b/src/main/java/org/dita/dost/platform/PluginUninstall.java @@ -0,0 +1,67 @@ +/* + * This file is part of the DITA Open Toolkit project. + * + * Copyright 2023 Jarno Elovirta + * + * See the accompanying LICENSE file for applicable license. + */ +package org.dita.dost.platform; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import org.apache.commons.io.FileUtils; +import org.apache.tools.ant.BuildException; +import org.dita.dost.log.DITAOTLogger; + +public final class PluginUninstall { + + private String id; + private DITAOTLogger logger; + private File ditaDir; + + private Integrator integrator; + + private void init() { + integrator = new Integrator(ditaDir); + integrator.setLogger(logger); + } + + public void execute() throws Exception { + init(); + if (id == null) { + throw new BuildException(new IllegalStateException("id argument not set")); + } + + final File pluginDir = Paths.get(ditaDir.getAbsolutePath(), "plugins", id).toFile(); + if (!pluginDir.exists()) { + throw new Exception("Plug-in directory %s doesn't exist".formatted(pluginDir)); + } + + logger.debug("Delete plug-in directory {0}", pluginDir); + try { + FileUtils.deleteDirectory(pluginDir); + } catch (IOException e) { + throw new BuildException("Failed to delete plug-in directory %s".formatted(pluginDir), e); + } + + integrator.setLogger(logger); + try { + integrator.execute(); + } catch (final Exception e) { + throw new BuildException("Integration failed: " + e.getMessage(), e); + } + } + + public void setLogger(DITAOTLogger logger) { + this.logger = logger; + } + + public void setDitaDir(File ditaDir) { + this.ditaDir = ditaDir; + } + + public void setId(final String id) { + this.id = id; + } +} From 66a5c35073d1cda30ef56237800d82eef6de6f7a Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Wed, 15 Nov 2023 19:17:55 +0200 Subject: [PATCH 04/36] Extract plug-in install code to own class Signed-off-by: Jarno Elovirta --- .../org/dita/dost/ant/PluginInstallTask.java | 311 ++-------------- .../org/dita/dost/platform/PluginInstall.java | 345 ++++++++++++++++++ .../PluginInstallTest.java} | 9 +- 3 files changed, 371 insertions(+), 294 deletions(-) create mode 100644 src/main/java/org/dita/dost/platform/PluginInstall.java rename src/test/java/org/dita/dost/{ant/PluginInstallTaskTest.java => platform/PluginInstallTest.java} (79%) diff --git a/src/main/java/org/dita/dost/ant/PluginInstallTask.java b/src/main/java/org/dita/dost/ant/PluginInstallTask.java index 8a9281f542..fb0b39d957 100644 --- a/src/main/java/org/dita/dost/ant/PluginInstallTask.java +++ b/src/main/java/org/dita/dost/ant/PluginInstallTask.java @@ -7,88 +7,25 @@ */ package org.dita.dost.ant; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.annotations.VisibleForTesting; -import java.io.*; -import java.net.MalformedURLException; +import java.io.File; import java.net.URI; import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; -import java.security.DigestInputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.*; -import java.util.stream.Collectors; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.io.output.NullOutputStream; import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; -import org.apache.tools.ant.taskdefs.Expand; -import org.apache.tools.ant.taskdefs.Get; import org.dita.dost.log.DITAOTAntLogger; -import org.dita.dost.platform.*; -import org.dita.dost.platform.Registry.Dependency; -import org.dita.dost.util.Configuration; -import org.w3c.dom.Document; -import org.xml.sax.SAXException; +import org.dita.dost.platform.PluginInstall; +import org.dita.dost.platform.SemVerMatch; public final class PluginInstallTask extends Task { - private List registries; - - private File tempDir; - private final ObjectMapper mapper = new ObjectMapper(); - private List installedPlugins; private Path pluginFile; - private URL pluginUrl; + private URI pluginUrl; private String pluginName; private SemVerMatch pluginVersion; private boolean force; - private Integrator integrator; - - @Override - public void init() { - registries = - Arrays - .stream(Configuration.configuration.get("registry").trim().split("\\s+")) - .map(registry -> registry.endsWith("/") ? registry : (registry + "/")) - .collect(Collectors.toList()); - try { - tempDir = Files.createTempDirectory(null).toFile(); - } catch (IOException e) { - throw new BuildException("Failed to create temporary directory: " + e.getMessage(), e); - } - installedPlugins = Plugins.getInstalledPlugins().stream().map(Map.Entry::getKey).toList(); - - final DITAOTAntLogger logger; - logger = new DITAOTAntLogger(getProject()); - logger.setTarget(getOwningTarget()); - logger.setTask(this); - - integrator = new Integrator(getProject().getBaseDir()); - integrator.setLogger(logger); - } - - private void cleanUp() { - if (tempDir != null) { - try { - FileUtils.deleteDirectory(tempDir); - } catch (IOException e) { - throw new BuildException(e); - } - } - } @Override public void execute() throws BuildException { @@ -96,233 +33,29 @@ public void execute() throws BuildException { throw new BuildException(new IllegalStateException("pluginName argument not set")); } - try { - final Map installs = new HashMap<>(); - if (pluginFile != null && Files.exists(pluginFile)) { - final File tempPluginDir = unzip(pluginFile.toFile()); - final String name = getPluginName(tempPluginDir); - installs.put(name, tempPluginDir); - } else if (pluginUrl != null) { - final File tempFile = get(pluginUrl, null); - final File tempPluginDir = unzip(tempFile); - final String name = getPluginName(tempPluginDir); - installs.put(name, tempPluginDir); - } else { - final Set plugins = readRegistry(this.pluginName, pluginVersion); - for (final Registry plugin : plugins) { - final File tempFile = get(plugin.url, plugin.cksum); - final File tempPluginDir = unzip(tempFile); - final String name = plugin.name; - installs.put(name, tempPluginDir); - } - } - for (final Map.Entry install : installs.entrySet()) { - final String name = install.getKey(); - final File tempPluginDir = install.getValue(); - final File pluginDir = getPluginDir(name); - if (pluginDir.exists()) { - if (force) { - log("Force install to " + pluginDir, Project.MSG_INFO); - integrator.addRemoved(name); - FileUtils.deleteDirectory(pluginDir); - } else { - throw new BuildException( - new IllegalStateException(String.format("Plug-in %s already installed: %s", name, pluginDir)) - ); - } - } - FileUtils.copyDirectory(tempPluginDir, pluginDir); - } - } catch (IOException e) { - throw new BuildException(e.getMessage(), e); - } finally { - cleanUp(); - } - try { - integrator.execute(); - } catch (final Exception e) { - throw new BuildException("Integration failed: " + e.getMessage(), e); - } - } - - private String getFileHash(final File file) { - try ( - DigestInputStream digestInputStream = new DigestInputStream( - new BufferedInputStream(new FileInputStream(file)), - MessageDigest.getInstance("SHA-256") - ) - ) { - IOUtils.copy(digestInputStream, new NullOutputStream()); - final MessageDigest digest = digestInputStream.getMessageDigest(); - final byte[] sha256 = digest.digest(); - return printHexBinary(sha256); - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException(e); - } catch (IOException e) { - throw new BuildException("Failed to calculate file checksum: " + e.getMessage(), e); - } - } - - private String printHexBinary(final byte[] md5) { - final StringBuilder sb = new StringBuilder(); - for (byte b : md5) { - sb.append(String.format("%02X", b)); - } - return sb.toString().toLowerCase(); - } + final PluginInstall pluginInstall = new PluginInstall(); + final DITAOTAntLogger logger = new DITAOTAntLogger(getProject()); + logger.setTarget(getOwningTarget()); + logger.setTask(this); + pluginInstall.setLogger(logger); + pluginInstall.setForce(force); + pluginInstall.setDitaDir(getDitaDir()); + pluginInstall.setPluginFile(pluginFile); + pluginInstall.setPluginUri(pluginUrl); + pluginInstall.setPluginName(pluginName); + pluginInstall.setPluginVersion(pluginVersion); - private String getPluginName(final File pluginDir) { - final File config = new File(pluginDir, "plugin.xml"); try { - final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(config); - return doc.getDocumentElement().getAttribute("id"); - } catch (SAXException | IOException | ParserConfigurationException e) { - throw new BuildException("Failed to read plugin name: " + e.getMessage(), e); + pluginInstall.execute(); + } catch (Exception e) { + throw new BuildException("Failed to install %s %s: %s".formatted(pluginName, pluginVersion, e.getMessage()), e); } } - private File getPluginDir(final String id) { - return Paths.get(getProject().getProperty("dita.dir"), "plugins", id).toFile(); + private File getDitaDir() { + return Paths.get(getProject().getProperty("dita.dir")).toFile(); } - private Set readRegistry(final String name, final SemVerMatch version) { - log(String.format("Reading registries for %s@%s", name, version), Project.MSG_INFO); - Registry res = null; - for (final String registry : registries) { - final URI registryUrl = URI.create(registry + name + ".json"); - log(String.format("Read registry %s", registry), Project.MSG_INFO); - try (BufferedInputStream in = new BufferedInputStream(registryUrl.toURL().openStream())) { - log("Parse registry", Project.MSG_INFO); - final JsonFactory factory = mapper.getFactory(); - final JsonParser parser = factory.createParser(in); - final JsonNode obj = mapper.readTree(parser); - final Collection regs; - if (obj.isArray()) { - regs = Arrays.asList(mapper.treeToValue(obj, Registry[].class)); - } else { - regs = resolveAlias(mapper.treeToValue(obj, Alias.class)); - } - final Optional reg = findPlugin(regs, version); - if (reg.isPresent()) { - final Registry plugin = reg.get(); - log(String.format("Plugin found at %s@%s", registryUrl, plugin.vers), Project.MSG_INFO); - res = plugin; - break; - } - } catch (MalformedURLException e) { - log(String.format("Invalid registry URL %s: %s", registryUrl, e.getMessage()), e, Project.MSG_ERR); - } catch (FileNotFoundException e) { - log(String.format("Registry configuration %s not found", registryUrl), e, Project.MSG_INFO); - } catch (IOException e) { - log( - String.format("Failed to read registry configuration %s: %s", registryUrl, e.getMessage()), - e, - Project.MSG_ERR - ); - } - } - if (res == null) { - throw new BuildException("Unable to find plugin " + pluginFile + " in any configured registry."); - } - - Set results = new HashSet<>(); - results.add(res); - res.deps - .stream() - .filter(dep -> !installedPlugins.contains(dep.name)) - .flatMap(dep -> readRegistry(dep.name, dep.req).stream()) - .forEach(results::add); - - return results; - } - - private Collection resolveAlias(Alias registry) { - return readRegistry(registry.alias(), null); - } - - private File get(final URL url, final String expectedChecksum) { - final File tempPluginFile = new File(tempDir, "plugin.zip"); - - final Get get = new Get(); - get.setProject(getProject()); - get.setTaskName("get"); - get.setSrc(url); - get.setDest(tempPluginFile); - get.setIgnoreErrors(false); - get.setVerbose(false); - get.execute(); - - if (expectedChecksum != null) { - final String checksum = getFileHash(tempPluginFile); - if (!checksum.equalsIgnoreCase(expectedChecksum)) { - throw new BuildException( - new IllegalArgumentException( - String.format( - "Downloaded plugin file checksum %s does not match expected value %s", - checksum, - expectedChecksum - ) - ) - ); - } - } - - return tempPluginFile; - } - - private File unzip(final File input) { - final File tempPluginDir = new File(tempDir, "plugin"); - - final Expand unzip = new Expand(); - unzip.setProject(getProject()); - unzip.setTaskName("unzip"); - unzip.setSrc(input); - unzip.setDest(tempPluginDir); - unzip.execute(); - - return findBaseDir(tempPluginDir); - } - - private File findBaseDir(final File tempPluginDir) { - final File config = new File(tempPluginDir, "plugin.xml"); - if (config.exists()) { - return tempPluginDir; - } else { - for (final File dir : tempPluginDir.listFiles(File::isDirectory)) { - final File res = findBaseDir(dir); - if (res != null) { - return res; - } - } - return null; - } - } - - private Optional findPlugin(final Collection regs, final SemVerMatch version) { - if (version == null) { - return regs.stream().filter(this::matchingPlatformVersion).max(Comparator.comparing(o -> o.vers)); - } else { - return regs.stream().filter(this::matchingPlatformVersion).filter(reg -> version.contains(reg.vers)).findFirst(); - } - } - - @VisibleForTesting - boolean matchingPlatformVersion(final Registry reg) { - final Optional platformDependency = reg.deps - .stream() - .filter(dep -> dep.name.equals("org.dita.base")) - .findFirst(); - if (platformDependency.isPresent()) { - final SemVer platform = new SemVer(Configuration.configuration.get("otversion")); - final Dependency dep = platformDependency.get(); - return dep.req.contains(platform); - } else { - return true; - } - } - - // Setters - public void setPluginFile(final String pluginFile) { try { this.pluginFile = Paths.get(pluginFile); @@ -332,9 +65,9 @@ public void setPluginFile(final String pluginFile) { try { final URI uri = new URI(pluginFile); if (uri.isAbsolute()) { - this.pluginUrl = uri.toURL(); + this.pluginUrl = uri; } - } catch (MalformedURLException | URISyntaxException e) { + } catch (URISyntaxException e) { // Ignore } if (pluginFile.contains("@")) { diff --git a/src/main/java/org/dita/dost/platform/PluginInstall.java b/src/main/java/org/dita/dost/platform/PluginInstall.java new file mode 100644 index 0000000000..f6188bca22 --- /dev/null +++ b/src/main/java/org/dita/dost/platform/PluginInstall.java @@ -0,0 +1,345 @@ +/* + * This file is part of the DITA Open Toolkit project. + * + * Copyright 2018 Jarno Elovirta + * + * See the accompanying LICENSE file for applicable license. + */ +package org.dita.dost.platform; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.annotations.VisibleForTesting; +import java.io.*; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.NullOutputStream; +import org.apache.tools.ant.BuildException; +import org.dita.dost.log.DITAOTLogger; +import org.dita.dost.platform.Registry.Dependency; +import org.dita.dost.util.Configuration; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +public final class PluginInstall { + + private List registries; + private File tempDir; + private final ObjectMapper mapper = new ObjectMapper(); + private List installedPlugins; + private Integrator integrator; + + private DITAOTLogger logger; + private File ditaDir; + + private Path pluginFile; + private URI pluginUri; + private String pluginName; + private SemVerMatch pluginVersion; + private boolean force; + + private void init() { + registries = + Arrays + .stream(Configuration.configuration.get("registry").trim().split("\\s+")) + .map(registry -> registry.endsWith("/") ? registry : (registry + "/")) + .collect(Collectors.toList()); + try { + tempDir = Files.createTempDirectory(null).toFile(); + } catch (IOException e) { + throw new BuildException("Failed to create temporary directory: " + e.getMessage(), e); + } + installedPlugins = Plugins.getInstalledPlugins().stream().map(Map.Entry::getKey).toList(); + + integrator = new Integrator(ditaDir); + integrator.setLogger(logger); + } + + public void execute() throws Exception { + init(); + if (pluginFile == null && pluginUri == null && pluginName == null) { + throw new BuildException(new IllegalStateException("pluginName argument not set")); + } + + try { + final Map installs = new HashMap<>(); + if (pluginFile != null && Files.exists(pluginFile)) { + final Path tempPluginDir = unzip(pluginFile.toFile()); + final String name = getPluginName(tempPluginDir); + installs.put(name, tempPluginDir); + } else if (pluginUri != null) { + final File tempFile = get(pluginUri, null); + final Path tempPluginDir = unzip(tempFile); + final String name = getPluginName(tempPluginDir); + installs.put(name, tempPluginDir); + } else { + final Set plugins = readRegistry(this.pluginName, pluginVersion); + for (final Registry plugin : plugins) { + final File tempFile = get(plugin.url.toURI(), plugin.cksum); + final Path tempPluginDir = unzip(tempFile); + final String name = plugin.name; + installs.put(name, tempPluginDir); + } + } + for (final Map.Entry install : installs.entrySet()) { + final String name = install.getKey(); + final Path tempPluginDir = install.getValue(); + final File pluginDir = getPluginDir(name); + if (pluginDir.exists()) { + if (force) { + logger.info("Force install to {0}", pluginDir); + integrator.addRemoved(name); + FileUtils.deleteDirectory(pluginDir); + } else { + throw new BuildException( + new IllegalStateException(String.format("Plug-in %s already installed: %s", name, pluginDir)) + ); + } + } + FileUtils.copyDirectory(tempPluginDir.toFile(), pluginDir); + } + } catch (IOException e) { + throw new BuildException(e.getMessage(), e); + } finally { + cleanUp(); + } + try { + integrator.execute(); + } catch (final Exception e) { + throw new BuildException("Integration failed: " + e.getMessage(), e); + } + } + + private void cleanUp() { + if (tempDir != null) { + try { + FileUtils.deleteDirectory(tempDir); + } catch (IOException e) { + throw new BuildException(e); + } + } + } + + private String getFileHash(final File file) { + try ( + DigestInputStream digestInputStream = new DigestInputStream( + new BufferedInputStream(new FileInputStream(file)), + MessageDigest.getInstance("SHA-256") + ) + ) { + IOUtils.copy(digestInputStream, new NullOutputStream()); + final MessageDigest digest = digestInputStream.getMessageDigest(); + final byte[] sha256 = digest.digest(); + return printHexBinary(sha256); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException(e); + } catch (IOException e) { + throw new BuildException("Failed to calculate file checksum: " + e.getMessage(), e); + } + } + + private String printHexBinary(final byte[] md5) { + final StringBuilder sb = new StringBuilder(); + for (byte b : md5) { + sb.append(String.format("%02X", b)); + } + return sb.toString().toLowerCase(); + } + + private String getPluginName(final Path pluginDir) { + final Path config = pluginDir.resolve("plugin.xml"); + try { + final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(config.toFile()); + return doc.getDocumentElement().getAttribute("id"); + } catch (SAXException | IOException | ParserConfigurationException e) { + throw new BuildException("Failed to read plugin name: " + e.getMessage(), e); + } + } + + private File getPluginDir(final String id) { + return Paths.get(ditaDir.getAbsolutePath(), "plugins", id).toFile(); + } + + private Set readRegistry(final String name, final SemVerMatch version) { + logger.info(String.format("Reading registries for %s@%s", name, version)); + Registry res = null; + for (final String registry : registries) { + final URI registryUrl = URI.create(registry + name + ".json"); + logger.info(String.format("Read registry %s", registry)); + try (BufferedInputStream in = new BufferedInputStream(registryUrl.toURL().openStream())) { + logger.info("Parse registry"); + final JsonFactory factory = mapper.getFactory(); + final JsonParser parser = factory.createParser(in); + final JsonNode obj = mapper.readTree(parser); + final Collection regs; + if (obj.isArray()) { + regs = Arrays.asList(mapper.treeToValue(obj, Registry[].class)); + } else { + regs = resolveAlias(mapper.treeToValue(obj, Alias.class)); + } + final Optional reg = findPlugin(regs, version); + if (reg.isPresent()) { + final Registry plugin = reg.get(); + logger.info("Plugin found at {0}@{1}", registryUrl, plugin.vers); + res = plugin; + break; + } + } catch (MalformedURLException e) { + logger.error("Invalid registry URL %s: %s", registryUrl, e.getMessage(), e); + } catch (FileNotFoundException e) { + logger.info("Registry configuration %s not found", registryUrl, e); + } catch (IOException e) { + logger.error("Failed to read registry configuration {0}: {1}", registryUrl, e.getMessage(), e); + } + } + if (res == null) { + throw new BuildException("Unable to find plugin " + pluginFile + " in any configured registry."); + } + + Set results = new HashSet<>(); + results.add(res); + res.deps + .stream() + .filter(dep -> !installedPlugins.contains(dep.name)) + .flatMap(dep -> readRegistry(dep.name, dep.req).stream()) + .forEach(results::add); + + return results; + } + + private Collection resolveAlias(Alias registry) { + return readRegistry(registry.alias(), null); + } + + private File get(final URI uri, final String expectedChecksum) throws Exception { + final File tempPluginFile = new File(tempDir, "plugin.zip"); + + final HttpClient client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build(); + final HttpRequest request = HttpRequest.newBuilder().GET().uri(uri).build(); + try { + logger.info("Download %s".formatted(request.uri())); + client.send(request, HttpResponse.BodyHandlers.ofFile(tempPluginFile.toPath())); + } catch (IOException | InterruptedException e) { + throw new Exception("Failed to download %s".formatted(uri), e); + } + + if (expectedChecksum != null) { + final String checksum = getFileHash(tempPluginFile); + if (!checksum.equalsIgnoreCase(expectedChecksum)) { + throw new BuildException( + new IllegalArgumentException( + "Downloaded plugin file checksum %s does not match expected value %s".formatted(checksum, expectedChecksum) + ) + ); + } + } + + return tempPluginFile; + } + + /** + * @see Zip Slip Vulnerability + */ + private Path unzip(final File input) throws Exception { + final Path tempPluginDir = new File(tempDir, "plugin").toPath(); + + logger.debug("Expanding %s to %s".formatted(input, tempPluginDir)); + try (ZipInputStream zipIn = new ZipInputStream(Files.newInputStream(input.toPath()))) { + for (ZipEntry ze; (ze = zipIn.getNextEntry()) != null;) { + Path resolvedPath = tempPluginDir.resolve(ze.getName()).normalize(); + if (!resolvedPath.startsWith(tempPluginDir)) { + throw new Exception("Entry with an illegal path: %s".formatted(ze.getName())); + } + if (ze.isDirectory()) { + Files.createDirectories(resolvedPath); + } else { + Files.createDirectories(resolvedPath.getParent()); + logger.debug("Write %s".formatted(resolvedPath)); + Files.copy(zipIn, resolvedPath); + } + } + } catch (IOException e) { + throw new Exception("Failed to expand %s to %s".formatted(input, tempPluginDir), e); + } + + return findBaseDir(tempPluginDir); + } + + private Path findBaseDir(final Path tempPluginDir) throws IOException { + return Files + .find(tempPluginDir, 256, (path, attributes) -> path.getFileName().toString().equals("plugin.xml")) + .findFirst() + .orElseThrow(() -> new IOException("plugin.xml not found")) + .getParent(); + } + + private Optional findPlugin(final Collection regs, final SemVerMatch version) { + if (version == null) { + return regs.stream().filter(this::matchingPlatformVersion).max(Comparator.comparing(o -> o.vers)); + } else { + return regs.stream().filter(this::matchingPlatformVersion).filter(reg -> version.contains(reg.vers)).findFirst(); + } + } + + @VisibleForTesting + boolean matchingPlatformVersion(final Registry reg) { + final Optional platformDependency = reg.deps + .stream() + .filter(dep -> dep.name.equals("org.dita.base")) + .findFirst(); + if (platformDependency.isPresent()) { + final SemVer platform = new SemVer(Configuration.configuration.get("otversion")); + final Dependency dep = platformDependency.get(); + return dep.req.contains(platform); + } else { + return true; + } + } + + public void setForce(final boolean force) { + this.force = force; + } + + public void setLogger(DITAOTLogger logger) { + this.logger = logger; + } + + public void setDitaDir(File ditaDir) { + this.ditaDir = ditaDir; + } + + public void setPluginFile(Path pluginFile) { + this.pluginFile = pluginFile; + } + + public void setPluginUri(URI pluginUri) { + this.pluginUri = pluginUri; + } + + public void setPluginName(String pluginName) { + this.pluginName = pluginName; + } + + public void setPluginVersion(SemVerMatch pluginVersion) { + this.pluginVersion = pluginVersion; + } +} diff --git a/src/test/java/org/dita/dost/ant/PluginInstallTaskTest.java b/src/test/java/org/dita/dost/platform/PluginInstallTest.java similarity index 79% rename from src/test/java/org/dita/dost/ant/PluginInstallTaskTest.java rename to src/test/java/org/dita/dost/platform/PluginInstallTest.java index d9860fea2a..aa52483890 100644 --- a/src/test/java/org/dita/dost/ant/PluginInstallTaskTest.java +++ b/src/test/java/org/dita/dost/platform/PluginInstallTest.java @@ -1,23 +1,22 @@ /* * This file is part of the DITA Open Toolkit project. * - * Copyright 2018 Jarno Elovirta + * Copyright 2023 Jarno Elovirta * * See the accompanying LICENSE file for applicable license. */ -package org.dita.dost.ant; +package org.dita.dost.platform; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import org.dita.dost.platform.Registry; import org.dita.dost.platform.Registry.Dependency; import org.junit.jupiter.api.Test; -public class PluginInstallTaskTest { +public class PluginInstallTest { - final PluginInstallTask registryTask = new PluginInstallTask(); + final PluginInstall registryTask = new PluginInstall(); @Test public void matchingPlatformVersion() { From f95d2c8bb148717a1d027563a60f02d757ee7a59 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Wed, 15 Nov 2023 21:33:31 +0200 Subject: [PATCH 05/36] Remove Ant layer from plug-in uninstall Signed-off-by: Jarno Elovirta --- src/main/java/org/dita/dost/invoker/Main.java | 20 +- .../org/dita/dost/log/StandardLogger.java | 205 ++++++++++++++++++ .../dita/dost/platform/PluginUninstall.java | 4 +- src/main/resources/cli_en_US.properties | 1 + 4 files changed, 224 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/dita/dost/log/StandardLogger.java diff --git a/src/main/java/org/dita/dost/invoker/Main.java b/src/main/java/org/dita/dost/invoker/Main.java index f54d29e402..a5ccfbaf26 100644 --- a/src/main/java/org/dita/dost/invoker/Main.java +++ b/src/main/java/org/dita/dost/invoker/Main.java @@ -54,6 +54,8 @@ import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.ProxySetup; import org.dita.dost.log.MessageUtils; +import org.dita.dost.log.StandardLogger; +import org.dita.dost.platform.PluginUninstall; import org.dita.dost.platform.Plugins; import org.dita.dost.project.Project.Context; import org.dita.dost.project.Project.Publication; @@ -128,7 +130,7 @@ public class Main extends org.apache.tools.ant.Main implements AntMain { private final ArgumentParser argumentParser = new ArgumentParser(); private Arguments args; - static final ResourceBundle locale = ResourceBundle.getBundle("cli", new Locale("en", "US")); + public static final ResourceBundle locale = ResourceBundle.getBundle("cli", new Locale("en", "US")); /** * Prints the message of the Throwable if it (the message) is not {@code null}. @@ -358,10 +360,18 @@ private void processArgs(final String[] arguments) { if (installArgs.uninstallId == null) { throw new CliException(locale.getString("uninstall.error.identifier_not_defined"), args.getUsage(true)); } - buildFile = findBuildFile(System.getProperty(SYSTEM_PROPERTY_DITA_HOME), "integrator.xml"); - targets.clear(); - targets.add("uninstall"); - definedProps.put(ANT_PLUGIN_ID, installArgs.uninstallId); + final PluginUninstall pluginUninstall = new PluginUninstall(); + pluginUninstall.setId(installArgs.uninstallId); + pluginUninstall.setDitaDir(new File(System.getProperty(SYSTEM_PROPERTY_DITA_HOME))); + pluginUninstall.setLogger(new StandardLogger(System.out, System.err, args.msgOutputLevel)); + try { + pluginUninstall.execute(); + } catch (CliException e) { + throw e; + } catch (Exception e) { + throw new CliException(e.getMessage(), e); + } + return; } else if (args instanceof final ConversionArguments conversionArgs) { final File ditaDir = new File(System.getProperty(SYSTEM_PROPERTY_DITA_HOME)); final File basePluginDir = new File(ditaDir, Configuration.pluginResourceDirs.get("org.dita.base").getPath()); diff --git a/src/main/java/org/dita/dost/log/StandardLogger.java b/src/main/java/org/dita/dost/log/StandardLogger.java new file mode 100644 index 0000000000..ae4b55e17e --- /dev/null +++ b/src/main/java/org/dita/dost/log/StandardLogger.java @@ -0,0 +1,205 @@ +/* + * This file is part of the DITA Open Toolkit project. + * + * Copyright 2023 Jarno Elovirta + * + * See the accompanying LICENSE file for applicable license. + */ +package org.dita.dost.log; + +import java.io.PrintStream; +import java.text.MessageFormat; +import org.apache.tools.ant.Project; +import org.slf4j.helpers.MarkerIgnoringBase; + +/** + * Logger to standard out and error. + */ +public final class StandardLogger extends MarkerIgnoringBase implements DITAOTLogger { + + private final PrintStream err; + private final PrintStream out; + private final int msgOutputLevel; + + public StandardLogger(final PrintStream out, final PrintStream err, final int msgOutputLevel) { + this.out = out; + this.err = err; + this.msgOutputLevel = msgOutputLevel; + } + + @Override + public void info(final String msg) { + log(msg, null, Project.MSG_INFO); + } + + @Override + public void info(String format, Object arg) { + log(MessageFormat.format(format, arg), null, Project.MSG_INFO); + } + + @Override + public void info(String format, Object arg1, Object arg2) { + log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_INFO); + } + + @Override + public void info(String format, Object... arguments) { + log(MessageFormat.format(format, arguments), null, Project.MSG_INFO); + } + + @Override + public void info(String msg, Throwable t) { + log(msg, t, Project.MSG_INFO); + } + + @Override + public boolean isWarnEnabled() { + return false; + } + + @Override + public void warn(final String msg) { + log(msg, null, Project.MSG_WARN); + } + + @Override + public void warn(String format, Object arg) { + log(MessageFormat.format(format, arg), null, Project.MSG_WARN); + } + + @Override + public void warn(String format, Object... arguments) { + log(MessageFormat.format(format, arguments), null, Project.MSG_WARN); + } + + @Override + public void warn(String format, Object arg1, Object arg2) { + log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_WARN); + } + + @Override + public void warn(String msg, Throwable t) { + log(msg, t, Project.MSG_WARN); + } + + @Override + public boolean isErrorEnabled() { + return true; + } + + @Override + public void error(final String msg) { + log(msg, null, Project.MSG_ERR); + } + + @Override + public void error(String format, Object arg) { + if (arg instanceof Throwable) { + log(format, (Throwable) arg, Project.MSG_ERR); + } else { + log(MessageFormat.format(format, arg), null, Project.MSG_ERR); + } + } + + @Override + public void error(String format, Object arg1, Object arg2) { + if (arg2 instanceof Throwable) { + log(MessageFormat.format(format, arg1), (Throwable) arg2, Project.MSG_ERR); + } else { + log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_ERR); + } + } + + @Override + public void error(String format, Object... arguments) { + final Object last = arguments[arguments.length - 1]; + if (last instanceof Throwable) { + final Object[] init = new Object[arguments.length - 1]; + System.arraycopy(arguments, 0, init, 0, init.length); + log(MessageFormat.format(format, init), (Throwable) last, Project.MSG_ERR); + } else { + log(MessageFormat.format(format, arguments), null, Project.MSG_ERR); + } + } + + @Override + public void error(final String msg, final Throwable t) { + log(msg, t, Project.MSG_ERR); + } + + @Override + public boolean isTraceEnabled() { + return false; + } + + @Override + public void trace(String msg) { + throw new UnsupportedOperationException(); + } + + @Override + public void trace(String format, Object arg) { + throw new UnsupportedOperationException(); + } + + @Override + public void trace(String format, Object arg1, Object arg2) { + throw new UnsupportedOperationException(); + } + + @Override + public void trace(String format, Object... arguments) { + throw new UnsupportedOperationException(); + } + + @Override + public void trace(String msg, Throwable t) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isDebugEnabled() { + return true; + } + + @Override + public void debug(final String msg) { + log(msg, null, Project.MSG_VERBOSE); + } + + @Override + public void debug(String format, Object arg) { + log(MessageFormat.format(format, arg), null, Project.MSG_VERBOSE); + } + + @Override + public void debug(String format, Object arg1, Object arg2) { + log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_VERBOSE); + } + + @Override + public void debug(String format, Object... arguments) { + log(MessageFormat.format(format, arguments), null, Project.MSG_VERBOSE); + } + + @Override + public void debug(String msg, Throwable t) { + log(msg, t, Project.MSG_VERBOSE); + } + + @Override + public boolean isInfoEnabled() { + return true; + } + + private void log(final String msg, final Throwable t, final int level) { + if (level > msgOutputLevel) { + return; + } + if (level == Project.MSG_ERR || level == Project.MSG_WARN) { + err.println(msg); + } else { + out.println(msg); + } + } +} diff --git a/src/main/java/org/dita/dost/platform/PluginUninstall.java b/src/main/java/org/dita/dost/platform/PluginUninstall.java index 4ec2541a37..4ff212099e 100644 --- a/src/main/java/org/dita/dost/platform/PluginUninstall.java +++ b/src/main/java/org/dita/dost/platform/PluginUninstall.java @@ -12,6 +12,8 @@ import java.nio.file.Paths; import org.apache.commons.io.FileUtils; import org.apache.tools.ant.BuildException; +import org.dita.dost.invoker.CliException; +import org.dita.dost.invoker.Main; import org.dita.dost.log.DITAOTLogger; public final class PluginUninstall { @@ -35,7 +37,7 @@ public void execute() throws Exception { final File pluginDir = Paths.get(ditaDir.getAbsolutePath(), "plugins", id).toFile(); if (!pluginDir.exists()) { - throw new Exception("Plug-in directory %s doesn't exist".formatted(pluginDir)); + throw new CliException(Main.locale.getString("uninstall.error.plugin_not_found").formatted(id)); } logger.debug("Delete plug-in directory {0}", pluginDir); diff --git a/src/main/resources/cli_en_US.properties b/src/main/resources/cli_en_US.properties index 2ebff410c9..b1b634bd30 100644 --- a/src/main/resources/cli_en_US.properties +++ b/src/main/resources/cli_en_US.properties @@ -29,6 +29,7 @@ install.option.force=Force-install plug-in (overwrite existing version) uninstall.usage=dita uninstall uninstall.argument.id=Uninstall plug-in with the specified ID uninstall.error.identifier_not_defined=You must specify plug-in identifier when using the uninstall subcommand +uninstall.error.plugin_not_found=Plug-in %s not found # Transtypes subcommand transtypes.usage=dita transtypes [options] # Conversion command From 899de31aac2b76d03051f7beccc55733b820e459 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Fri, 17 Nov 2023 11:15:23 +0200 Subject: [PATCH 06/36] Remove Ant layer from plug-in install Signed-off-by: Jarno Elovirta --- src/main/java/org/dita/dost/invoker/Main.java | 111 ++++++++++++------ 1 file changed, 74 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/dita/dost/invoker/Main.java b/src/main/java/org/dita/dost/invoker/Main.java index a5ccfbaf26..fb1a14a170 100644 --- a/src/main/java/org/dita/dost/invoker/Main.java +++ b/src/main/java/org/dita/dost/invoker/Main.java @@ -37,7 +37,9 @@ import com.google.common.collect.ImmutableMap; import java.io.*; import java.net.URI; +import java.net.URISyntaxException; import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDateTime; @@ -55,8 +57,10 @@ import org.apache.tools.ant.util.ProxySetup; import org.dita.dost.log.MessageUtils; import org.dita.dost.log.StandardLogger; +import org.dita.dost.platform.PluginInstall; import org.dita.dost.platform.PluginUninstall; import org.dita.dost.platform.Plugins; +import org.dita.dost.platform.SemVerMatch; import org.dita.dost.project.Project.Context; import org.dita.dost.project.Project.Publication; import org.dita.dost.project.ProjectFactory; @@ -337,40 +341,13 @@ private void processArgs(final String[] arguments) { printTranstypes(); return; } else if (args instanceof final DeliverablesArguments deliverablesArgs) { - if (deliverablesArgs.projectFile == null) { - throw new CliException(locale.getString("deliverables.error.project_not_defined"), args.getUsage(true)); - } - printDeliverables(deliverablesArgs.projectFile); + printDeliverables(deliverablesArgs); return; } else if (args instanceof final InstallArguments installArgs) { - buildFile = findBuildFile(System.getProperty(SYSTEM_PROPERTY_DITA_HOME), "integrator.xml"); - targets.clear(); - if (installArgs.installFile != null) { - targets.add("install"); - final File f = new File(installArgs.installFile.replace('/', File.separatorChar)).getAbsoluteFile(); - if (f.exists()) { - definedProps.put(ANT_PLUGIN_FILE, f.getAbsolutePath()); - } else { - definedProps.put(ANT_PLUGIN_FILE, installArgs.installFile); - } - } else { - targets.add("integrate"); - } + install(installArgs); + return; } else if (args instanceof final UninstallArguments installArgs) { - if (installArgs.uninstallId == null) { - throw new CliException(locale.getString("uninstall.error.identifier_not_defined"), args.getUsage(true)); - } - final PluginUninstall pluginUninstall = new PluginUninstall(); - pluginUninstall.setId(installArgs.uninstallId); - pluginUninstall.setDitaDir(new File(System.getProperty(SYSTEM_PROPERTY_DITA_HOME))); - pluginUninstall.setLogger(new StandardLogger(System.out, System.err, args.msgOutputLevel)); - try { - pluginUninstall.execute(); - } catch (CliException e) { - throw e; - } catch (Exception e) { - throw new CliException(e.getMessage(), e); - } + uninstall(installArgs); return; } else if (args instanceof final ConversionArguments conversionArgs) { final File ditaDir = new File(System.getProperty(SYSTEM_PROPERTY_DITA_HOME)); @@ -452,6 +429,66 @@ private void processArgs(final String[] arguments) { readyToRun = true; } + private void uninstall(UninstallArguments installArgs) { + if (installArgs.uninstallId == null) { + throw new CliException(locale.getString("uninstall.error.identifier_not_defined"), args.getUsage(true)); + } + final PluginUninstall pluginUninstall = new PluginUninstall(); + pluginUninstall.setId(installArgs.uninstallId); + pluginUninstall.setDitaDir(new File(System.getProperty(SYSTEM_PROPERTY_DITA_HOME))); + pluginUninstall.setLogger(new StandardLogger(System.out, System.err, args.msgOutputLevel)); + + try { + pluginUninstall.execute(); + } catch (CliException e) { + throw e; + } catch (Exception e) { + throw new CliException(e.getMessage(), e); + } + } + + private void install(InstallArguments installArgs) { + final PluginInstall pluginInstall = new PluginInstall(); + pluginInstall.setDitaDir(new File(System.getProperty(SYSTEM_PROPERTY_DITA_HOME))); + pluginInstall.setLogger(new StandardLogger(System.out, System.err, args.msgOutputLevel)); + if (installArgs.definedProps.containsKey("force")) { + pluginInstall.setForce(Boolean.parseBoolean(installArgs.definedProps.get("force").toString())); + } + if (installArgs.installFile != null) { + final File f = new File(installArgs.installFile.replace('/', File.separatorChar)).getAbsoluteFile(); + final String pluginFile = f.exists() ? f.getAbsolutePath() : installArgs.installFile; + try { + pluginInstall.setPluginFile(Paths.get(pluginFile)); + } catch (InvalidPathException e) { + // Ignore + } + try { + final URI uri = new URI(pluginFile); + if (uri.isAbsolute()) { + pluginInstall.setPluginUri(uri); + } + } catch (URISyntaxException e) { + // Ignore + } + if (pluginFile.contains("@")) { + final String[] tokens = pluginFile.split("@"); + pluginInstall.setPluginName(tokens[0]); + pluginInstall.setPluginVersion(new SemVerMatch(tokens[1])); + } else { + pluginInstall.setPluginName(pluginFile); + pluginInstall.setPluginVersion(null); + } + } + + try { + pluginInstall.execute(); + } catch (CliException e) { + throw e; + } catch (Exception e) { + throw new CliException(e.getMessage(), e); + } + } + private Map getLocalProperties(File ditaDir) { final Map res = new HashMap<>(); Stream @@ -629,16 +666,16 @@ private void printTranstypes() { } } - /** - * Handle the --deliverables argument - */ - private void printDeliverables(final File projectFile) { - final List> pairs = readProjectFile(projectFile) + private void printDeliverables(DeliverablesArguments deliverablesArgs) { + if (deliverablesArgs.projectFile == null) { + throw new CliException(locale.getString("deliverables.error.project_not_defined"), args.getUsage(true)); + } + final List> pairs = readProjectFile(deliverablesArgs.projectFile) .deliverables() .stream() .filter(deliverable -> deliverable.id() != null) .map(deliverable -> pair(deliverable.id(), deliverable.name())) - .collect(Collectors.toList()); + .toList(); final int length = pairs.stream().map(Map.Entry::getKey).map(String::length).reduce(Integer::max).orElse(0); for (Map.Entry pair : pairs) { System.out.println( From cc3d5821516fafbebb7b20e7877908c184319ebf Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Fri, 17 Nov 2023 19:58:48 +0200 Subject: [PATCH 07/36] Refactor Improve logging Signed-off-by: Jarno Elovirta --- .../dost/invoker/ConversionArguments.java | 2 +- .../dost/invoker/DeliverablesArguments.java | 5 +- .../dita/dost/invoker/InstallArguments.java | 2 +- src/main/java/org/dita/dost/invoker/Main.java | 51 +++++++++++-------- .../dita/dost/invoker/PluginsArguments.java | 4 +- .../dost/invoker/TranstypesArguments.java | 4 +- .../dita/dost/invoker/UninstallArguments.java | 2 +- .../org/dita/dost/invoker/UsageBuilder.java | 31 ++++++++--- .../dita/dost/invoker/VersionArguments.java | 4 +- .../org/dita/dost/log/StandardLogger.java | 43 ++++++++++++++-- src/main/resources/cli_en_US.properties | 1 + 11 files changed, 110 insertions(+), 39 deletions(-) diff --git a/src/main/java/org/dita/dost/invoker/ConversionArguments.java b/src/main/java/org/dita/dost/invoker/ConversionArguments.java index 31eb5151c1..41a404c130 100644 --- a/src/main/java/org/dita/dost/invoker/ConversionArguments.java +++ b/src/main/java/org/dita/dost/invoker/ConversionArguments.java @@ -395,7 +395,7 @@ private Map loadPropertyFiles() { @Override String getUsage(final boolean compact) { final UsageBuilder buf = UsageBuilder - .builder(compact) + .builder(compact, useColor) .usage(locale.getString("conversion.usage.input")) .usage(locale.getString("conversion.usage.project")) // .usage("dita --propertyfile= [options]") diff --git a/src/main/java/org/dita/dost/invoker/DeliverablesArguments.java b/src/main/java/org/dita/dost/invoker/DeliverablesArguments.java index fd62d935b8..fd2ceac8fb 100644 --- a/src/main/java/org/dita/dost/invoker/DeliverablesArguments.java +++ b/src/main/java/org/dita/dost/invoker/DeliverablesArguments.java @@ -16,6 +16,7 @@ import java.util.Deque; import java.util.Map; import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; public class DeliverablesArguments extends Arguments { @@ -23,7 +24,7 @@ public class DeliverablesArguments extends Arguments { @Override DeliverablesArguments parse(final String[] arguments) { - useColor = getUseColor(); + msgOutputLevel = Project.MSG_INFO; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); @@ -67,7 +68,7 @@ private void handleArgProject(final String arg, final Deque args) { @Override String getUsage(final boolean compact) { return UsageBuilder - .builder(compact) + .builder(compact, useColor) .usage(locale.getString("deliverables.usage")) .arguments(null, null, "file", locale.getString("deliverables.argument.file")) .build(); diff --git a/src/main/java/org/dita/dost/invoker/InstallArguments.java b/src/main/java/org/dita/dost/invoker/InstallArguments.java index 4485486109..1477640ba2 100644 --- a/src/main/java/org/dita/dost/invoker/InstallArguments.java +++ b/src/main/java/org/dita/dost/invoker/InstallArguments.java @@ -77,7 +77,7 @@ private void handleSubcommandInstall(final String arg, final Deque args) @Override String getUsage(final boolean compact) { return UsageBuilder - .builder(compact) + .builder(compact, useColor) .usage(locale.getString("install.usage")) .arguments(null, null, "id", locale.getString("install.argument.id")) .arguments(null, null, "url", locale.getString("install.argument.url")) diff --git a/src/main/java/org/dita/dost/invoker/Main.java b/src/main/java/org/dita/dost/invoker/Main.java index fb1a14a170..0b50aecf05 100644 --- a/src/main/java/org/dita/dost/invoker/Main.java +++ b/src/main/java/org/dita/dost/invoker/Main.java @@ -149,14 +149,15 @@ private void printMessage(final Throwable t) { } private void printErrorMessage(final String msg) { - if (args != null && args.useColor) { - System.err.print(DefaultLogger.ANSI_RED); - System.err.print(locale.getString("error_msg").formatted(msg)); - System.err.println(DefaultLogger.ANSI_RESET); - } else { - System.err.println(locale.getString("error_msg").formatted(msg)); - } - System.err.println(); + // if (args != null && args.useColor) { + // System.err.print(DefaultLogger.ANSI_RED); + // System.err.print(locale.getString("error_msg").formatted(msg)); + // System.err.println(DefaultLogger.ANSI_RESET); + // } else { + // System.err.println(locale.getString("error_msg").formatted(msg)); + // } + // System.err.println(); + logger.error(msg); } /** @@ -198,7 +199,7 @@ public void startAnt(final String[] args, final Properties additionalUserPropert handleLogfile(); printMessage(exc); if (exc.info != null) { - System.out.println(exc.info); + logger.info(exc.info); } exit(1); return; @@ -305,10 +306,14 @@ public static void main(final String[] args) { start(args, null, null); } + final StandardLogger logger; + /** * Constructor used when creating Main for later arg processing and startup */ - public Main() {} + public Main() { + logger = new StandardLogger(System.out, System.err, Project.MSG_INFO, true); + } /** * Process command line arguments. When ant is started from Launcher, @@ -322,9 +327,12 @@ private void processArgs(final String[] arguments) { final Map definedProps = new HashMap<>(args.definedProps); projectProps = Collections.singletonList(definedProps); buildFile = args.buildFile; + logger.setOutputLevel(args.msgOutputLevel); + logger.setUseColor(args.useColor); if (args.justPrintUsage) { - System.out.println(args.getUsage(false)); + logger.setOutputLevel(Project.MSG_INFO); + logger.info(args.getUsage(false)); return; } else if (args.justPrintDiagnostics) { Diagnostics.doReport(System.out, args.msgOutputLevel); @@ -436,7 +444,7 @@ private void uninstall(UninstallArguments installArgs) { final PluginUninstall pluginUninstall = new PluginUninstall(); pluginUninstall.setId(installArgs.uninstallId); pluginUninstall.setDitaDir(new File(System.getProperty(SYSTEM_PROPERTY_DITA_HOME))); - pluginUninstall.setLogger(new StandardLogger(System.out, System.err, args.msgOutputLevel)); + pluginUninstall.setLogger(logger); try { pluginUninstall.execute(); @@ -450,7 +458,7 @@ private void uninstall(UninstallArguments installArgs) { private void install(InstallArguments installArgs) { final PluginInstall pluginInstall = new PluginInstall(); pluginInstall.setDitaDir(new File(System.getProperty(SYSTEM_PROPERTY_DITA_HOME))); - pluginInstall.setLogger(new StandardLogger(System.out, System.err, args.msgOutputLevel)); + pluginInstall.setLogger(logger); if (installArgs.definedProps.containsKey("force")) { pluginInstall.setForce(Boolean.parseBoolean(installArgs.definedProps.get("force").toString())); } @@ -646,14 +654,14 @@ private void validateProject(org.dita.dost.project.Project project) throws IOExc * Handle the --plugins argument */ private void printPlugins() { + logger.setOutputLevel(Project.MSG_INFO); final List> installedPlugins = Plugins.getInstalledPlugins(); for (final Map.Entry entry : installedPlugins) { - System.out.print(entry.getKey()); if (entry.getValue() != null) { - System.out.print('@'); - System.out.print(entry.getValue()); + logger.info("{0}@{1}", entry.getKey(), entry.getValue()); + } else { + logger.info(entry.getKey()); } - System.out.println(); } } @@ -661,12 +669,14 @@ private void printPlugins() { * Handle the --transtypes argument */ private void printTranstypes() { + logger.setOutputLevel(Project.MSG_INFO); for (final String transtype : transtypes) { - System.out.println(transtype); + logger.info(transtype); } } private void printDeliverables(DeliverablesArguments deliverablesArgs) { + logger.setOutputLevel(Project.MSG_INFO); if (deliverablesArgs.projectFile == null) { throw new CliException(locale.getString("deliverables.error.project_not_defined"), args.getUsage(true)); } @@ -678,7 +688,7 @@ private void printDeliverables(DeliverablesArguments deliverablesArgs) { .toList(); final int length = pairs.stream().map(Map.Entry::getKey).map(String::length).reduce(Integer::max).orElse(0); for (Map.Entry pair : pairs) { - System.out.println( + logger.info( Strings.padEnd(pair.getKey(), length, ' ') + (pair.getValue() != null ? (" " + pair.getValue()) : "") ); } @@ -935,6 +945,7 @@ private BuildLogger createLogger() { * @throws BuildException if the version information is unavailable */ private void printVersion() throws BuildException { - System.out.println(locale.getString("version").formatted(Configuration.configuration.get("otversion"))); + logger.setOutputLevel(Project.MSG_INFO); + logger.info(locale.getString("version").formatted(Configuration.configuration.get("otversion"))); } } diff --git a/src/main/java/org/dita/dost/invoker/PluginsArguments.java b/src/main/java/org/dita/dost/invoker/PluginsArguments.java index 6cd7b20a6e..c9a2c0f9f2 100644 --- a/src/main/java/org/dita/dost/invoker/PluginsArguments.java +++ b/src/main/java/org/dita/dost/invoker/PluginsArguments.java @@ -13,11 +13,13 @@ import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; +import org.apache.tools.ant.Project; public class PluginsArguments extends Arguments { @Override PluginsArguments parse(final String[] arguments) { + msgOutputLevel = Project.MSG_WARN; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); @@ -32,6 +34,6 @@ PluginsArguments parse(final String[] arguments) { @Override String getUsage(final boolean compact) { - return UsageBuilder.builder(compact).usage(locale.getString("plugins.usage")).build(); + return UsageBuilder.builder(compact, useColor).usage(locale.getString("plugins.usage")).build(); } } diff --git a/src/main/java/org/dita/dost/invoker/TranstypesArguments.java b/src/main/java/org/dita/dost/invoker/TranstypesArguments.java index 0ef2488eb9..57fd6c13d9 100644 --- a/src/main/java/org/dita/dost/invoker/TranstypesArguments.java +++ b/src/main/java/org/dita/dost/invoker/TranstypesArguments.java @@ -13,11 +13,13 @@ import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; +import org.apache.tools.ant.Project; public class TranstypesArguments extends Arguments { @Override TranstypesArguments parse(final String[] arguments) { + msgOutputLevel = Project.MSG_INFO; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); @@ -32,6 +34,6 @@ TranstypesArguments parse(final String[] arguments) { @Override String getUsage(final boolean compact) { - return UsageBuilder.builder(compact).usage(locale.getString("transtypes.usage")).build(); + return UsageBuilder.builder(compact, useColor).usage(locale.getString("transtypes.usage")).build(); } } diff --git a/src/main/java/org/dita/dost/invoker/UninstallArguments.java b/src/main/java/org/dita/dost/invoker/UninstallArguments.java index 063ab6fbec..80d8a958c3 100644 --- a/src/main/java/org/dita/dost/invoker/UninstallArguments.java +++ b/src/main/java/org/dita/dost/invoker/UninstallArguments.java @@ -75,7 +75,7 @@ private void handleSubcommandUninstall(final String arg, final Deque arg @Override String getUsage(final boolean compact) { return UsageBuilder - .builder(compact) + .builder(compact, useColor) .usage(locale.getString("uninstall.usage")) .arguments(null, null, "id", locale.getString("uninstall.argument.id")) .build(); diff --git a/src/main/java/org/dita/dost/invoker/UsageBuilder.java b/src/main/java/org/dita/dost/invoker/UsageBuilder.java index cccd00d991..1802a5a6e0 100644 --- a/src/main/java/org/dita/dost/invoker/UsageBuilder.java +++ b/src/main/java/org/dita/dost/invoker/UsageBuilder.java @@ -22,8 +22,10 @@ public class UsageBuilder { private final Map options = new HashMap<>(); private final Map arguments = new LinkedHashMap<>(); private final List footers = new ArrayList<>(); + private final boolean useColor; - private UsageBuilder(final boolean compact) { + private UsageBuilder(final boolean compact, final boolean useColor) { + this.useColor = useColor; options("h", "help", null, locale.getString("help.option.help")); if (!compact) { options("d", "debug", null, locale.getString("help.option.debug")); @@ -32,8 +34,8 @@ private UsageBuilder(final boolean compact) { } } - public static UsageBuilder builder(final boolean compact) { - return new UsageBuilder(compact); + public static UsageBuilder builder(final boolean compact, final boolean useColor) { + return new UsageBuilder(compact, useColor); } public UsageBuilder usage(final String usage) { @@ -64,12 +66,15 @@ public UsageBuilder footer(final String desc) { public String build() { final String padding = getPadding(); - buf.append(ANSI_BOLD).append("Usage").append(ANSI_RESET).append(":\n"); + bold("Usage"); + buf.append(":\n"); for (String usage : usages) { buf.append(" ").append(usage).append("\n"); } if (!subcommands.isEmpty()) { - buf.append("\n").append(ANSI_BOLD).append("Subcommands").append(ANSI_RESET).append(":\n"); + buf.append("\n"); + bold("Subcommands"); + buf.append(":\n"); for (Map.Entry subcommand : sortSubCommands(subcommands)) { buf .append(" ") @@ -81,7 +86,9 @@ public String build() { buf.append("\n See 'dita --help' for details about a specific subcommand.\n"); } if (!arguments.isEmpty()) { - buf.append("\n").append(ANSI_BOLD).append("Arguments").append(ANSI_RESET).append(":\n"); + buf.append("\n"); + bold("Arguments"); + buf.append(":\n"); for (Map.Entry argument : arguments.entrySet()) { buf .append(" ") @@ -92,7 +99,9 @@ public String build() { } } if (!options.isEmpty()) { - buf.append("\n").append(ANSI_BOLD).append("Options").append(ANSI_RESET).append(":\n"); + buf.append("\n"); + bold("Options"); + buf.append(":\n"); for (Map.Entry option : sort(options)) { buf .append(" ") @@ -117,6 +126,14 @@ private List> sort(Map arguments) { return entries; } + private void bold(final String text) { + if (useColor) { + buf.append(ANSI_BOLD).append(text).append(ANSI_RESET); + } else { + buf.append(text); + } + } + private List> sortSubCommands(Map arguments) { final List> entries = new ArrayList<>(arguments.entrySet()); entries.sort(Map.Entry.comparingByKey()); diff --git a/src/main/java/org/dita/dost/invoker/VersionArguments.java b/src/main/java/org/dita/dost/invoker/VersionArguments.java index 22d54c30a2..2f37a9445d 100644 --- a/src/main/java/org/dita/dost/invoker/VersionArguments.java +++ b/src/main/java/org/dita/dost/invoker/VersionArguments.java @@ -13,11 +13,13 @@ import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; +import org.apache.tools.ant.Project; class VersionArguments extends Arguments { @Override VersionArguments parse(final String[] arguments) { + msgOutputLevel = Project.MSG_INFO; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); @@ -32,6 +34,6 @@ VersionArguments parse(final String[] arguments) { @Override String getUsage(final boolean compact) { - return UsageBuilder.builder(compact).usage(locale.getString("version.usage")).build(); + return UsageBuilder.builder(compact, useColor).usage(locale.getString("version.usage")).build(); } } diff --git a/src/main/java/org/dita/dost/log/StandardLogger.java b/src/main/java/org/dita/dost/log/StandardLogger.java index ae4b55e17e..839f94514a 100644 --- a/src/main/java/org/dita/dost/log/StandardLogger.java +++ b/src/main/java/org/dita/dost/log/StandardLogger.java @@ -10,6 +10,7 @@ import java.io.PrintStream; import java.text.MessageFormat; import org.apache.tools.ant.Project; +import org.dita.dost.invoker.Main; import org.slf4j.helpers.MarkerIgnoringBase; /** @@ -17,14 +18,40 @@ */ public final class StandardLogger extends MarkerIgnoringBase implements DITAOTLogger { + public static final String ANSI_RESET = "\u001B[0m"; + public static final String ANSI_BLACK = "\u001B[30m"; + public static final String ANSI_RED = "\u001B[31m"; + public static final String ANSI_GREEN = "\u001B[32m"; + public static final String ANSI_YELLOW = "\u001B[33m"; + public static final String ANSI_BLUE = "\u001B[34m"; + public static final String ANSI_PURPLE = "\u001B[35m"; + public static final String ANSI_CYAN = "\u001B[36m"; + public static final String ANSI_WHITE = "\u001B[37m"; + public static final String ANSI_BOLD = "\u001b[1m"; + private final PrintStream err; private final PrintStream out; - private final int msgOutputLevel; - - public StandardLogger(final PrintStream out, final PrintStream err, final int msgOutputLevel) { + private int msgOutputLevel; + private boolean useColor; + + public StandardLogger( + final PrintStream out, + final PrintStream err, + final int msgOutputLevel, + final boolean useColor + ) { this.out = out; this.err = err; this.msgOutputLevel = msgOutputLevel; + this.useColor = useColor; + } + + public void setOutputLevel(final int msgOutputLevel) { + this.msgOutputLevel = msgOutputLevel; + } + + public void setUseColor(final boolean useColor) { + this.useColor = useColor; } @Override @@ -196,8 +223,16 @@ private void log(final String msg, final Throwable t, final int level) { if (level > msgOutputLevel) { return; } - if (level == Project.MSG_ERR || level == Project.MSG_WARN) { + if (useColor && level == Project.MSG_ERR) { + err.print(ANSI_RED); + err.print(Main.locale.getString("error_msg").formatted("")); + err.println(msg); + err.print(ANSI_RESET); + } else if (useColor && level == Project.MSG_WARN) { + err.print(ANSI_YELLOW); + err.print(Main.locale.getString("warn_msg").formatted("")); err.println(msg); + err.print(ANSI_RESET); } else { out.println(msg); } diff --git a/src/main/resources/cli_en_US.properties b/src/main/resources/cli_en_US.properties index b1b634bd30..7bfd800c42 100644 --- a/src/main/resources/cli_en_US.properties +++ b/src/main/resources/cli_en_US.properties @@ -6,6 +6,7 @@ # See the accompanying LICENSE file for applicable license. # error_msg=Error: %s +warn_msg=Warning: %s help.option.help=Print help information help.option.debug=Enable debug logging help.option.verbose=Enable verbose logging From 011a1bec1b8343bc87ac1958f211a065a473175e Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Sat, 18 Nov 2023 09:22:26 +0200 Subject: [PATCH 08/36] Refactor to convert class to record Signed-off-by: Jarno Elovirta --- .../org/dita/dost/invoker/UsageBuilder.java | 38 ++----------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/dita/dost/invoker/UsageBuilder.java b/src/main/java/org/dita/dost/invoker/UsageBuilder.java index 1802a5a6e0..84ad720ed8 100644 --- a/src/main/java/org/dita/dost/invoker/UsageBuilder.java +++ b/src/main/java/org/dita/dost/invoker/UsageBuilder.java @@ -154,17 +154,9 @@ private String getPadding() { return " ".repeat(Math.max(0, max + 2)); } - private static class Key implements Comparable { - - final String shortKey; - final String longKey; - final String value; - final String string; - - private Key(String shortKey, String longKey, String value) { - this.shortKey = shortKey; - this.longKey = longKey; - this.value = value; + private record Key(String shortKey, String longKey, String value) implements Comparable { + @Override + public String toString() { final StringBuilder buf = new StringBuilder(); if (shortKey != null) { buf.append("-").append(shortKey); @@ -184,29 +176,7 @@ private Key(String shortKey, String longKey, String value) { if (shortKey == null && longKey == null & value != null) { buf.append("<").append(value).append(">"); } - string = buf.toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Key key = (Key) o; - return ( - Objects.equals(shortKey, key.shortKey) && - Objects.equals(longKey, key.longKey) && - Objects.equals(value, key.value) - ); - } - - @Override - public int hashCode() { - return Objects.hash(shortKey, longKey, value); - } - - @Override - public String toString() { - return string; + return buf.toString(); } @Override From 506ca33e9b6ef22e62fc474531a4bdc268bda18b Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Sat, 18 Nov 2023 11:09:46 +0200 Subject: [PATCH 09/36] Fix error messages Signed-off-by: Jarno Elovirta --- .../org/dita/dost/log/StandardLogger.java | 4 +-- .../org/dita/dost/platform/PluginInstall.java | 31 ++++++++++--------- src/main/resources/cli_en_US.properties | 5 +++ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/dita/dost/log/StandardLogger.java b/src/main/java/org/dita/dost/log/StandardLogger.java index 839f94514a..184d225d0a 100644 --- a/src/main/java/org/dita/dost/log/StandardLogger.java +++ b/src/main/java/org/dita/dost/log/StandardLogger.java @@ -226,13 +226,13 @@ private void log(final String msg, final Throwable t, final int level) { if (useColor && level == Project.MSG_ERR) { err.print(ANSI_RED); err.print(Main.locale.getString("error_msg").formatted("")); - err.println(msg); err.print(ANSI_RESET); + err.println(msg); } else if (useColor && level == Project.MSG_WARN) { err.print(ANSI_YELLOW); err.print(Main.locale.getString("warn_msg").formatted("")); - err.println(msg); err.print(ANSI_RESET); + err.println(msg); } else { out.println(msg); } diff --git a/src/main/java/org/dita/dost/platform/PluginInstall.java b/src/main/java/org/dita/dost/platform/PluginInstall.java index f6188bca22..9822ae4d6f 100644 --- a/src/main/java/org/dita/dost/platform/PluginInstall.java +++ b/src/main/java/org/dita/dost/platform/PluginInstall.java @@ -19,6 +19,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.security.DigestInputStream; @@ -34,6 +35,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.NullOutputStream; import org.apache.tools.ant.BuildException; +import org.dita.dost.invoker.Main; import org.dita.dost.log.DITAOTLogger; import org.dita.dost.platform.Registry.Dependency; import org.dita.dost.util.Configuration; @@ -110,9 +112,8 @@ public void execute() throws Exception { integrator.addRemoved(name); FileUtils.deleteDirectory(pluginDir); } else { - throw new BuildException( - new IllegalStateException(String.format("Plug-in %s already installed: %s", name, pluginDir)) - ); + logger.warn(String.format(Main.locale.getString("install.error.exists"), name)); + throw new BuildException(); } } FileUtils.copyDirectory(tempPluginDir.toFile(), pluginDir); @@ -212,7 +213,7 @@ private Set readRegistry(final String name, final SemVerMatch version) } } if (res == null) { - throw new BuildException("Unable to find plugin " + pluginFile + " in any configured registry."); + throw new BuildException(Main.locale.getString("install.error.not_found_from_registry").formatted(pluginFile)); } Set results = new HashSet<>(); @@ -239,16 +240,14 @@ private File get(final URI uri, final String expectedChecksum) throws Exception logger.info("Download %s".formatted(request.uri())); client.send(request, HttpResponse.BodyHandlers.ofFile(tempPluginFile.toPath())); } catch (IOException | InterruptedException e) { - throw new Exception("Failed to download %s".formatted(uri), e); + throw new Exception(Main.locale.getString("install.error.download_failure").formatted(uri), e); } if (expectedChecksum != null) { final String checksum = getFileHash(tempPluginFile); if (!checksum.equalsIgnoreCase(expectedChecksum)) { throw new BuildException( - new IllegalArgumentException( - "Downloaded plugin file checksum %s does not match expected value %s".formatted(checksum, expectedChecksum) - ) + Main.locale.getString("install.error.checksum_mismatch").formatted(checksum, expectedChecksum) ); } } @@ -284,12 +283,16 @@ private Path unzip(final File input) throws Exception { return findBaseDir(tempPluginDir); } - private Path findBaseDir(final Path tempPluginDir) throws IOException { - return Files - .find(tempPluginDir, 256, (path, attributes) -> path.getFileName().toString().equals("plugin.xml")) - .findFirst() - .orElseThrow(() -> new IOException("plugin.xml not found")) - .getParent(); + private Path findBaseDir(final Path tempPluginDir) throws Exception { + try { + return Files + .find(tempPluginDir, 256, (path, attributes) -> path.getFileName().toString().equals("plugin.xml")) + .findFirst() + .orElseThrow(() -> new IOException("plugin.xml not found")) + .getParent(); + } catch (NoSuchFileException e) { + throw new Exception(Main.locale.getString("install.error.plugin_xml_not_found")); + } } private Optional findPlugin(final Collection regs, final SemVerMatch version) { diff --git a/src/main/resources/cli_en_US.properties b/src/main/resources/cli_en_US.properties index 7bfd800c42..c4edad1681 100644 --- a/src/main/resources/cli_en_US.properties +++ b/src/main/resources/cli_en_US.properties @@ -26,6 +26,11 @@ install.argument.id=Install specified plug-in ID from registry install.argument.url=Install plug-in from a URL install.argument.file=Install plug-in from a local ZIP file install.option.force=Force-install plug-in (overwrite existing version) +install.error.exists=Plug-in %s already installed +install.error.not_found_from_registry=Unable to find plugin %s in any configured registry +install.error.plugin_xml_not_found=Unable to find plugin.xml +install.error.download_failure=Failed to download %s +install.error.checksum_mismatch=Downloaded plugin file checksum %s does not match expected value %s # Uninstall subcommand uninstall.usage=dita uninstall uninstall.argument.id=Uninstall plug-in with the specified ID From d8fe14c8fbd6a3ab7b5f39bd228c9050f237c2f8 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Sun, 19 Nov 2023 10:26:41 +0200 Subject: [PATCH 10/36] Fix logging levels Signed-off-by: Jarno Elovirta --- .../dita/dost/invoker/InstallArguments.java | 1 + .../dita/dost/invoker/UninstallArguments.java | 1 + .../org/dita/dost/platform/Integrator.java | 4 ++-- .../org/dita/dost/platform/PluginInstall.java | 18 +++++++++--------- .../dita/dost/platform/PluginUninstall.java | 2 +- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/dita/dost/invoker/InstallArguments.java b/src/main/java/org/dita/dost/invoker/InstallArguments.java index 1477640ba2..f9284b1b22 100644 --- a/src/main/java/org/dita/dost/invoker/InstallArguments.java +++ b/src/main/java/org/dita/dost/invoker/InstallArguments.java @@ -21,6 +21,7 @@ public class InstallArguments extends Arguments { @Override Arguments parse(final String[] arguments) { + msgOutputLevel = Project.MSG_INFO; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); diff --git a/src/main/java/org/dita/dost/invoker/UninstallArguments.java b/src/main/java/org/dita/dost/invoker/UninstallArguments.java index 80d8a958c3..3c088306ae 100644 --- a/src/main/java/org/dita/dost/invoker/UninstallArguments.java +++ b/src/main/java/org/dita/dost/invoker/UninstallArguments.java @@ -22,6 +22,7 @@ public class UninstallArguments extends Arguments { @Override UninstallArguments parse(final String[] arguments) { + msgOutputLevel = Project.MSG_INFO; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); diff --git a/src/main/java/org/dita/dost/platform/Integrator.java b/src/main/java/org/dita/dost/platform/Integrator.java index 228ac2ce14..42c128bda7 100644 --- a/src/main/java/org/dita/dost/platform/Integrator.java +++ b/src/main/java/org/dita/dost/platform/Integrator.java @@ -246,13 +246,13 @@ private void logChanges(final Set orig, final Set mod) { removed.removeAll(mod); removed.sort(Comparator.naturalOrder()); for (final String p : removed) { - logger.warn("Removed " + p); + logger.info("Removed " + p); } final List added = new ArrayList<>(mod); added.removeAll(orig); added.sort(Comparator.naturalOrder()); for (final String p : added) { - logger.warn("Added " + p); + logger.info("Added " + p); } } diff --git a/src/main/java/org/dita/dost/platform/PluginInstall.java b/src/main/java/org/dita/dost/platform/PluginInstall.java index 9822ae4d6f..5e9e89ccb3 100644 --- a/src/main/java/org/dita/dost/platform/PluginInstall.java +++ b/src/main/java/org/dita/dost/platform/PluginInstall.java @@ -181,13 +181,13 @@ private File getPluginDir(final String id) { } private Set readRegistry(final String name, final SemVerMatch version) { - logger.info(String.format("Reading registries for %s@%s", name, version)); + logger.info("Reading registries for {0} {1}", name, Objects.requireNonNullElse(version, "")); Registry res = null; for (final String registry : registries) { final URI registryUrl = URI.create(registry + name + ".json"); - logger.info(String.format("Read registry %s", registry)); + logger.debug("Read registry {0}", registry); try (BufferedInputStream in = new BufferedInputStream(registryUrl.toURL().openStream())) { - logger.info("Parse registry"); + logger.debug("Parse registry"); final JsonFactory factory = mapper.getFactory(); final JsonParser parser = factory.createParser(in); final JsonNode obj = mapper.readTree(parser); @@ -200,14 +200,14 @@ private Set readRegistry(final String name, final SemVerMatch version) final Optional reg = findPlugin(regs, version); if (reg.isPresent()) { final Registry plugin = reg.get(); - logger.info("Plugin found at {0}@{1}", registryUrl, plugin.vers); + logger.debug("Plugin found at {0}@{1}", registryUrl, plugin.vers); res = plugin; break; } } catch (MalformedURLException e) { - logger.error("Invalid registry URL %s: %s", registryUrl, e.getMessage(), e); + logger.error("Invalid registry URL {0}: {1}", registryUrl, e.getMessage(), e); } catch (FileNotFoundException e) { - logger.info("Registry configuration %s not found", registryUrl, e); + // Ignore } catch (IOException e) { logger.error("Failed to read registry configuration {0}: {1}", registryUrl, e.getMessage(), e); } @@ -237,7 +237,7 @@ private File get(final URI uri, final String expectedChecksum) throws Exception final HttpClient client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build(); final HttpRequest request = HttpRequest.newBuilder().GET().uri(uri).build(); try { - logger.info("Download %s".formatted(request.uri())); + logger.info("Download {0}", request.uri()); client.send(request, HttpResponse.BodyHandlers.ofFile(tempPluginFile.toPath())); } catch (IOException | InterruptedException e) { throw new Exception(Main.locale.getString("install.error.download_failure").formatted(uri), e); @@ -261,7 +261,7 @@ private File get(final URI uri, final String expectedChecksum) throws Exception private Path unzip(final File input) throws Exception { final Path tempPluginDir = new File(tempDir, "plugin").toPath(); - logger.debug("Expanding %s to %s".formatted(input, tempPluginDir)); + logger.debug("Expanding {0} to {1}", input, tempPluginDir); try (ZipInputStream zipIn = new ZipInputStream(Files.newInputStream(input.toPath()))) { for (ZipEntry ze; (ze = zipIn.getNextEntry()) != null;) { Path resolvedPath = tempPluginDir.resolve(ze.getName()).normalize(); @@ -272,7 +272,7 @@ private Path unzip(final File input) throws Exception { Files.createDirectories(resolvedPath); } else { Files.createDirectories(resolvedPath.getParent()); - logger.debug("Write %s".formatted(resolvedPath)); + logger.debug("Write {0}", resolvedPath); Files.copy(zipIn, resolvedPath); } } diff --git a/src/main/java/org/dita/dost/platform/PluginUninstall.java b/src/main/java/org/dita/dost/platform/PluginUninstall.java index 4ff212099e..369e17ffbe 100644 --- a/src/main/java/org/dita/dost/platform/PluginUninstall.java +++ b/src/main/java/org/dita/dost/platform/PluginUninstall.java @@ -40,7 +40,7 @@ public void execute() throws Exception { throw new CliException(Main.locale.getString("uninstall.error.plugin_not_found").formatted(id)); } - logger.debug("Delete plug-in directory {0}", pluginDir); + logger.info("Delete plug-in directory {0}", pluginDir); try { FileUtils.deleteDirectory(pluginDir); } catch (IOException e) { From 4e2488f0f9b0b3e08108b08da7ae03e50ecced14 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Sun, 19 Nov 2023 13:04:12 +0200 Subject: [PATCH 11/36] Refactor logger to handle more message formats Signed-off-by: Jarno Elovirta --- .../org/dita/dost/log/StandardLogger.java | 70 ++++++++---- .../org/dita/dost/log/StandardLoggerTest.java | 108 ++++++++++++++++++ 2 files changed, 158 insertions(+), 20 deletions(-) create mode 100644 src/test/java/org/dita/dost/log/StandardLoggerTest.java diff --git a/src/main/java/org/dita/dost/log/StandardLogger.java b/src/main/java/org/dita/dost/log/StandardLogger.java index 184d225d0a..03341d4fb8 100644 --- a/src/main/java/org/dita/dost/log/StandardLogger.java +++ b/src/main/java/org/dita/dost/log/StandardLogger.java @@ -9,6 +9,8 @@ import java.io.PrintStream; import java.text.MessageFormat; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.tools.ant.Project; import org.dita.dost.invoker.Main; import org.slf4j.helpers.MarkerIgnoringBase; @@ -61,17 +63,17 @@ public void info(final String msg) { @Override public void info(String format, Object arg) { - log(MessageFormat.format(format, arg), null, Project.MSG_INFO); + log(format, new Object[] { arg }, null, Project.MSG_INFO); } @Override public void info(String format, Object arg1, Object arg2) { - log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_INFO); + log(format, new Object[] { arg1, arg2 }, null, Project.MSG_INFO); } @Override public void info(String format, Object... arguments) { - log(MessageFormat.format(format, arguments), null, Project.MSG_INFO); + log(format, arguments, null, Project.MSG_INFO); } @Override @@ -91,17 +93,17 @@ public void warn(final String msg) { @Override public void warn(String format, Object arg) { - log(MessageFormat.format(format, arg), null, Project.MSG_WARN); + log(format, new Object[] { arg }, null, Project.MSG_WARN); } @Override public void warn(String format, Object... arguments) { - log(MessageFormat.format(format, arguments), null, Project.MSG_WARN); + log(format, arguments, null, Project.MSG_WARN); } @Override public void warn(String format, Object arg1, Object arg2) { - log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_WARN); + log(format, new Object[] { arg1, arg2 }, null, Project.MSG_WARN); } @Override @@ -124,16 +126,16 @@ public void error(String format, Object arg) { if (arg instanceof Throwable) { log(format, (Throwable) arg, Project.MSG_ERR); } else { - log(MessageFormat.format(format, arg), null, Project.MSG_ERR); + log(format, new Object[] { arg }, null, Project.MSG_ERR); } } @Override public void error(String format, Object arg1, Object arg2) { if (arg2 instanceof Throwable) { - log(MessageFormat.format(format, arg1), (Throwable) arg2, Project.MSG_ERR); + log(format, new Object[] { arg1 }, (Throwable) arg2, Project.MSG_ERR); } else { - log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_ERR); + log(format, new Object[] { arg1, arg2 }, null, Project.MSG_ERR); } } @@ -141,11 +143,11 @@ public void error(String format, Object arg1, Object arg2) { public void error(String format, Object... arguments) { final Object last = arguments[arguments.length - 1]; if (last instanceof Throwable) { - final Object[] init = new Object[arguments.length - 1]; - System.arraycopy(arguments, 0, init, 0, init.length); - log(MessageFormat.format(format, init), (Throwable) last, Project.MSG_ERR); + final Object[] args = new Object[arguments.length - 1]; + System.arraycopy(arguments, 0, args, 0, args.length); + log(format, args, (Throwable) last, Project.MSG_ERR); } else { - log(MessageFormat.format(format, arguments), null, Project.MSG_ERR); + log(format, arguments, null, Project.MSG_ERR); } } @@ -196,17 +198,17 @@ public void debug(final String msg) { @Override public void debug(String format, Object arg) { - log(MessageFormat.format(format, arg), null, Project.MSG_VERBOSE); + log(format, new Object[] { arg }, null, Project.MSG_VERBOSE); } @Override public void debug(String format, Object arg1, Object arg2) { - log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_VERBOSE); + log(format, new Object[] { arg1, arg2 }, null, Project.MSG_VERBOSE); } @Override public void debug(String format, Object... arguments) { - log(MessageFormat.format(format, arguments), null, Project.MSG_VERBOSE); + log(format, arguments, null, Project.MSG_VERBOSE); } @Override @@ -220,21 +222,49 @@ public boolean isInfoEnabled() { } private void log(final String msg, final Throwable t, final int level) { + log(msg, new Object[] {}, t, level); + } + + private void log(final String msg, final Object[] args, final Throwable t, final int level) { if (level > msgOutputLevel) { return; } if (useColor && level == Project.MSG_ERR) { err.print(ANSI_RED); - err.print(Main.locale.getString("error_msg").formatted("")); + err.printf(Main.locale.getString("error_msg"), ""); err.print(ANSI_RESET); - err.println(msg); } else if (useColor && level == Project.MSG_WARN) { err.print(ANSI_YELLOW); - err.print(Main.locale.getString("warn_msg").formatted("")); + err.printf(Main.locale.getString("warn_msg"), ""); err.print(ANSI_RESET); - err.println(msg); + } + if (args.length > 0) { + if (msg.contains("{}") || msg.contains("%s")) { + out.println(MessageFormat.format(addIndex(msg), args)); + } else { + out.println(MessageFormat.format(msg, args)); + } } else { out.println(msg); } } + + private static final Pattern ARGUMENT = Pattern.compile("\\{}|%s"); + + private String addIndex(String msg) { + final Matcher matcher = ARGUMENT.matcher(msg); + final StringBuilder buf = new StringBuilder(); + int start = 0; + for (int i = 0; matcher.find(start); i++) { + buf.append(msg, start, matcher.start()); + buf.append("{"); + buf.append(i); + buf.append("}"); + start = matcher.end(); + } + if (start < msg.length()) { + buf.append(msg, start, msg.length()); + } + return buf.toString(); + } } diff --git a/src/test/java/org/dita/dost/log/StandardLoggerTest.java b/src/test/java/org/dita/dost/log/StandardLoggerTest.java new file mode 100644 index 0000000000..7cc8bd2df9 --- /dev/null +++ b/src/test/java/org/dita/dost/log/StandardLoggerTest.java @@ -0,0 +1,108 @@ +/* + * This file is part of the DITA Open Toolkit project. + * + * Copyright 2023 Jarno Elovirta + * + * See the accompanying LICENSE file for applicable license. + */ + +package org.dita.dost.log; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import org.apache.tools.ant.Project; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class StandardLoggerTest { + + private static final String MSG = "message ä"; + + private StandardLogger logger; + private ByteArrayOutputStream out; + private ByteArrayOutputStream err; + + @BeforeEach + void setUp() { + out = new ByteArrayOutputStream(); + err = new ByteArrayOutputStream(); + logger = + new StandardLogger( + new PrintStream(out, true, StandardCharsets.UTF_8), + new PrintStream(err, true, StandardCharsets.UTF_8), + Project.MSG_DEBUG, + false + ); + } + + @Test + void debug() { + logger.debug(MSG); + + assertOut(MSG); + assertErr(); + } + + @ParameterizedTest + @CsvSource( + value = { + "Message {0} {1} {2}, Message first second third", + "Message {} {} {}, Message first second third", + "Message {} {} {} suffix, Message first second third suffix", + "{} {} {}, first second third", + "Message {} {} {}., Message first second third.", + "Message %s %s %s, Message first second third", + } + ) + void debug(final String msg, final String exp) { + logger.debug(msg, "first", "second", "third"); + + assertOut(exp); + assertErr(); + } + + @Test + void info() { + logger.info(MSG); + + assertOut(MSG); + assertErr(); + } + + @Test + void warn() { + logger.warn(MSG); + + assertOut(MSG); + assertErr(); + } + + @Test + void error() { + logger.error(MSG); + + assertOut(MSG); + assertErr(); + } + + private void assertOut() { + assertEquals(0, out.size()); + } + + private void assertOut(final String exp) { + assertEquals(exp + "\n", new String(out.toByteArray(), StandardCharsets.UTF_8)); + } + + private void assertErr() { + assertEquals(0, err.size()); + } + + private void assertErr(final String exp) { + assertEquals(exp + "\n", new String(err.toByteArray(), StandardCharsets.UTF_8)); + } +} From bdfa5f4dfcabd774a89ee00018b44863b5b3ea33 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Tue, 21 Nov 2023 18:49:41 +0200 Subject: [PATCH 12/36] Adjust logging levels Signed-off-by: Jarno Elovirta --- .../java/org/dita/dost/invoker/Arguments.java | 6 +- .../dost/invoker/ConversionArguments.java | 1 + .../dost/invoker/DeliverablesArguments.java | 2 - .../dita/dost/invoker/InstallArguments.java | 1 - src/main/java/org/dita/dost/invoker/Main.java | 8 - .../dita/dost/invoker/PluginsArguments.java | 2 - .../dost/invoker/TranstypesArguments.java | 2 - .../dita/dost/invoker/UninstallArguments.java | 1 - .../dita/dost/invoker/VersionArguments.java | 2 - .../org/dita/dost/log/AbstractLogger.java | 275 ++++++++++++++++++ .../org/dita/dost/log/DITAOTAntLogger.java | 171 +---------- .../org/dita/dost/log/StandardLogger.java | 238 +-------------- .../org/dita/dost/platform/Integrator.java | 31 +- .../org/dita/dost/platform/PluginInstall.java | 31 +- .../dita/dost/platform/PluginUninstall.java | 2 +- .../org/dita/dost/log/StandardLoggerTest.java | 8 +- 16 files changed, 323 insertions(+), 458 deletions(-) create mode 100644 src/main/java/org/dita/dost/log/AbstractLogger.java diff --git a/src/main/java/org/dita/dost/invoker/Arguments.java b/src/main/java/org/dita/dost/invoker/Arguments.java index 9cb7cd63c6..a8437cb110 100644 --- a/src/main/java/org/dita/dost/invoker/Arguments.java +++ b/src/main/java/org/dita/dost/invoker/Arguments.java @@ -33,7 +33,7 @@ abstract class Arguments { /** * Our current message output status. Follows Project.MSG_XXX. */ - int msgOutputLevel = Project.MSG_WARN; + int msgOutputLevel = Project.MSG_INFO; /** * File that we are using for configuration. */ @@ -107,9 +107,9 @@ void parseCommonOptions(final String arg, final Deque args) { if (isLongForm(arg, "-help") || arg.equals("-h")) { justPrintUsage = true; } else if (isLongForm(arg, "-verbose") || arg.equals("-v")) { - msgOutputLevel = Project.MSG_INFO; - } else if (isLongForm(arg, "-debug") || arg.equals("-d")) { msgOutputLevel = Project.MSG_VERBOSE; + } else if (isLongForm(arg, "-debug") || arg.equals("-d")) { + msgOutputLevel = Project.MSG_DEBUG; } else if (isLongForm(arg, "-emacs") || arg.equals("-e")) { emacsMode = true; } else if (isLongForm(arg, "-logfile") || arg.equals("-l")) { diff --git a/src/main/java/org/dita/dost/invoker/ConversionArguments.java b/src/main/java/org/dita/dost/invoker/ConversionArguments.java index 41a404c130..45bc079e09 100644 --- a/src/main/java/org/dita/dost/invoker/ConversionArguments.java +++ b/src/main/java/org/dita/dost/invoker/ConversionArguments.java @@ -105,6 +105,7 @@ public class ConversionArguments extends Arguments { @Override ConversionArguments parse(final String[] arguments) { + msgOutputLevel = Project.MSG_WARN; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); diff --git a/src/main/java/org/dita/dost/invoker/DeliverablesArguments.java b/src/main/java/org/dita/dost/invoker/DeliverablesArguments.java index fd2ceac8fb..9ecb913626 100644 --- a/src/main/java/org/dita/dost/invoker/DeliverablesArguments.java +++ b/src/main/java/org/dita/dost/invoker/DeliverablesArguments.java @@ -16,7 +16,6 @@ import java.util.Deque; import java.util.Map; import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.Project; public class DeliverablesArguments extends Arguments { @@ -24,7 +23,6 @@ public class DeliverablesArguments extends Arguments { @Override DeliverablesArguments parse(final String[] arguments) { - msgOutputLevel = Project.MSG_INFO; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); diff --git a/src/main/java/org/dita/dost/invoker/InstallArguments.java b/src/main/java/org/dita/dost/invoker/InstallArguments.java index f9284b1b22..1477640ba2 100644 --- a/src/main/java/org/dita/dost/invoker/InstallArguments.java +++ b/src/main/java/org/dita/dost/invoker/InstallArguments.java @@ -21,7 +21,6 @@ public class InstallArguments extends Arguments { @Override Arguments parse(final String[] arguments) { - msgOutputLevel = Project.MSG_INFO; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); diff --git a/src/main/java/org/dita/dost/invoker/Main.java b/src/main/java/org/dita/dost/invoker/Main.java index 0b50aecf05..78813189a1 100644 --- a/src/main/java/org/dita/dost/invoker/Main.java +++ b/src/main/java/org/dita/dost/invoker/Main.java @@ -149,14 +149,6 @@ private void printMessage(final Throwable t) { } private void printErrorMessage(final String msg) { - // if (args != null && args.useColor) { - // System.err.print(DefaultLogger.ANSI_RED); - // System.err.print(locale.getString("error_msg").formatted(msg)); - // System.err.println(DefaultLogger.ANSI_RESET); - // } else { - // System.err.println(locale.getString("error_msg").formatted(msg)); - // } - // System.err.println(); logger.error(msg); } diff --git a/src/main/java/org/dita/dost/invoker/PluginsArguments.java b/src/main/java/org/dita/dost/invoker/PluginsArguments.java index c9a2c0f9f2..b1413fac25 100644 --- a/src/main/java/org/dita/dost/invoker/PluginsArguments.java +++ b/src/main/java/org/dita/dost/invoker/PluginsArguments.java @@ -13,13 +13,11 @@ import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; -import org.apache.tools.ant.Project; public class PluginsArguments extends Arguments { @Override PluginsArguments parse(final String[] arguments) { - msgOutputLevel = Project.MSG_WARN; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); diff --git a/src/main/java/org/dita/dost/invoker/TranstypesArguments.java b/src/main/java/org/dita/dost/invoker/TranstypesArguments.java index 57fd6c13d9..bb1e0e1767 100644 --- a/src/main/java/org/dita/dost/invoker/TranstypesArguments.java +++ b/src/main/java/org/dita/dost/invoker/TranstypesArguments.java @@ -13,13 +13,11 @@ import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; -import org.apache.tools.ant.Project; public class TranstypesArguments extends Arguments { @Override TranstypesArguments parse(final String[] arguments) { - msgOutputLevel = Project.MSG_INFO; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); diff --git a/src/main/java/org/dita/dost/invoker/UninstallArguments.java b/src/main/java/org/dita/dost/invoker/UninstallArguments.java index 3c088306ae..80d8a958c3 100644 --- a/src/main/java/org/dita/dost/invoker/UninstallArguments.java +++ b/src/main/java/org/dita/dost/invoker/UninstallArguments.java @@ -22,7 +22,6 @@ public class UninstallArguments extends Arguments { @Override UninstallArguments parse(final String[] arguments) { - msgOutputLevel = Project.MSG_INFO; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); diff --git a/src/main/java/org/dita/dost/invoker/VersionArguments.java b/src/main/java/org/dita/dost/invoker/VersionArguments.java index 2f37a9445d..25c35d2680 100644 --- a/src/main/java/org/dita/dost/invoker/VersionArguments.java +++ b/src/main/java/org/dita/dost/invoker/VersionArguments.java @@ -13,13 +13,11 @@ import java.util.ArrayDeque; import java.util.Arrays; import java.util.Deque; -import org.apache.tools.ant.Project; class VersionArguments extends Arguments { @Override VersionArguments parse(final String[] arguments) { - msgOutputLevel = Project.MSG_INFO; final Deque args = new ArrayDeque<>(Arrays.asList(arguments)); while (!args.isEmpty()) { final String arg = args.pop(); diff --git a/src/main/java/org/dita/dost/log/AbstractLogger.java b/src/main/java/org/dita/dost/log/AbstractLogger.java new file mode 100644 index 0000000000..86e7211ba8 --- /dev/null +++ b/src/main/java/org/dita/dost/log/AbstractLogger.java @@ -0,0 +1,275 @@ +/* + * This file is part of the DITA Open Toolkit project. + * + * Copyright 2023 Jarno Elovirta + * + * See the accompanying LICENSE file for applicable license. + */ +package org.dita.dost.log; + +import java.text.MessageFormat; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.tools.ant.Project; +import org.dita.dost.invoker.Main; +import org.slf4j.helpers.MarkerIgnoringBase; + +/** + * Abstract logger implementation based on SLF4J. + */ +public abstract class AbstractLogger extends MarkerIgnoringBase implements DITAOTLogger { + + public static final String ANSI_RESET = "\u001B[0m"; + public static final String ANSI_BLACK = "\u001B[30m"; + public static final String ANSI_RED = "\u001B[31m"; + public static final String ANSI_GREEN = "\u001B[32m"; + public static final String ANSI_YELLOW = "\u001B[33m"; + public static final String ANSI_BLUE = "\u001B[34m"; + public static final String ANSI_PURPLE = "\u001B[35m"; + public static final String ANSI_CYAN = "\u001B[36m"; + public static final String ANSI_WHITE = "\u001B[37m"; + public static final String ANSI_BOLD = "\u001b[1m"; + + protected int msgOutputLevel; + protected boolean useColor; + + public void setOutputLevel(final int msgOutputLevel) { + this.msgOutputLevel = msgOutputLevel; + } + + public void setUseColor(final boolean useColor) { + this.useColor = useColor; + } + + @Override + public void info(final String msg) { + log(msg, new Object[] {}, null, Project.MSG_INFO); + } + + @Override + public void info(String format, Object arg) { + log(format, new Object[] { arg }, null, Project.MSG_INFO); + } + + @Override + public void info(String format, Object arg1, Object arg2) { + log(format, new Object[] { arg1, arg2 }, null, Project.MSG_INFO); + } + + @Override + public void info(String format, Object... arguments) { + log(format, arguments, null, Project.MSG_INFO); + } + + @Override + public void info(String msg, Throwable t) { + log(msg, new Object[] {}, t, Project.MSG_INFO); + } + + @Override + public boolean isWarnEnabled() { + return msgOutputLevel >= Project.MSG_WARN; + } + + @Override + public void warn(final String msg) { + log(msg, new Object[] {}, null, Project.MSG_WARN); + } + + @Override + public void warn(String format, Object arg) { + log(format, new Object[] { arg }, null, Project.MSG_WARN); + } + + @Override + public void warn(String format, Object... arguments) { + log(format, arguments, null, Project.MSG_WARN); + } + + @Override + public void warn(String format, Object arg1, Object arg2) { + log(format, new Object[] { arg1, arg2 }, null, Project.MSG_WARN); + } + + @Override + public void warn(String msg, Throwable t) { + log(msg, new Object[] {}, t, Project.MSG_WARN); + } + + @Override + public boolean isErrorEnabled() { + return msgOutputLevel >= Project.MSG_ERR; + } + + @Override + public void error(final String msg) { + log(msg, new Object[] {}, null, Project.MSG_ERR); + } + + @Override + public void error(String format, Object arg) { + if (arg instanceof Throwable) { + log(format, new Object[] {}, (Throwable) arg, Project.MSG_ERR); + } else { + log(format, new Object[] { arg }, null, Project.MSG_ERR); + } + } + + @Override + public void error(String format, Object arg1, Object arg2) { + if (arg2 instanceof Throwable) { + log(format, new Object[] { arg1 }, (Throwable) arg2, Project.MSG_ERR); + } else { + log(format, new Object[] { arg1, arg2 }, null, Project.MSG_ERR); + } + } + + @Override + public void error(String format, Object... arguments) { + final Object last = arguments[arguments.length - 1]; + if (last instanceof Throwable) { + final Object[] args = new Object[arguments.length - 1]; + System.arraycopy(arguments, 0, args, 0, args.length); + log(format, args, (Throwable) last, Project.MSG_ERR); + } else { + log(format, arguments, null, Project.MSG_ERR); + } + } + + @Override + public void error(final String msg, final Throwable t) { + log(msg, new Object[] {}, t, Project.MSG_ERR); + } + + @Override + public boolean isTraceEnabled() { + return msgOutputLevel >= Project.MSG_DEBUG; + } + + @Override + public void trace(String msg) { + log(msg, new Object[] {}, null, Project.MSG_DEBUG); + } + + @Override + public void trace(String format, Object arg) { + if (arg instanceof Throwable) { + log(format, new Object[] {}, (Throwable) arg, Project.MSG_DEBUG); + } else { + log(format, new Object[] { arg }, null, Project.MSG_DEBUG); + } + } + + @Override + public void trace(String format, Object arg1, Object arg2) { + if (arg2 instanceof Throwable) { + log(format, new Object[] { arg1 }, (Throwable) arg2, Project.MSG_DEBUG); + } else { + log(format, new Object[] { arg1, arg2 }, null, Project.MSG_DEBUG); + } + } + + @Override + public void trace(String format, Object... arguments) { + final Object last = arguments[arguments.length - 1]; + if (last instanceof Throwable) { + final Object[] args = new Object[arguments.length - 1]; + System.arraycopy(arguments, 0, args, 0, args.length); + log(format, args, (Throwable) last, Project.MSG_DEBUG); + } else { + log(format, arguments, null, Project.MSG_DEBUG); + } + } + + @Override + public void trace(String msg, Throwable t) { + log(msg, new Object[] {}, t, Project.MSG_DEBUG); + } + + @Override + public boolean isDebugEnabled() { + return msgOutputLevel >= Project.MSG_VERBOSE; + } + + @Override + public void debug(final String msg) { + log(msg, new Object[] {}, null, Project.MSG_VERBOSE); + } + + @Override + public void debug(String format, Object arg) { + log(format, new Object[] { arg }, null, Project.MSG_VERBOSE); + } + + @Override + public void debug(String format, Object arg1, Object arg2) { + log(format, new Object[] { arg1, arg2 }, null, Project.MSG_VERBOSE); + } + + @Override + public void debug(String format, Object... arguments) { + log(format, arguments, null, Project.MSG_VERBOSE); + } + + @Override + public void debug(String msg, Throwable t) { + log(msg, new Object[] {}, t, Project.MSG_VERBOSE); + } + + @Override + public boolean isInfoEnabled() { + return msgOutputLevel >= Project.MSG_INFO; + } + + abstract void log(final String msg, final Throwable t, final int level); + + protected void log(final String msg, final Object[] args, final Throwable t, final int level) { + if (level <= msgOutputLevel) { + return; + } + StringBuilder buf = null; + if (useColor && level == Project.MSG_ERR) { + buf = + new StringBuilder() + .append(ANSI_RED) + .append(Main.locale.getString("error_msg").formatted("")) + .append(ANSI_RESET); + } else if (useColor && level == Project.MSG_WARN) { + buf = + new StringBuilder() + .append(ANSI_YELLOW) + .append(Main.locale.getString("warn_msg").formatted("")) + .append(ANSI_RESET); + } + if (args.length > 0) { + if (buf == null) { + buf = new StringBuilder(); + } + if (msg.contains("{}") || msg.contains("%s")) { + buf.append(MessageFormat.format(addIndex(msg), args)); + } else { + buf.append(MessageFormat.format(msg, args)); + } + } + log(buf != null ? buf.toString() : msg, t, level); + } + + private static final Pattern ARGUMENT = Pattern.compile("\\{}|%s"); + + private String addIndex(String msg) { + final Matcher matcher = ARGUMENT.matcher(msg); + final StringBuilder buf = new StringBuilder(); + int start = 0; + for (int i = 0; matcher.find(start); i++) { + buf.append(msg, start, matcher.start()); + buf.append("{"); + buf.append(i); + buf.append("}"); + start = matcher.end(); + } + if (start < msg.length()) { + buf.append(msg, start, msg.length()); + } + return buf.toString(); + } +} diff --git a/src/main/java/org/dita/dost/log/DITAOTAntLogger.java b/src/main/java/org/dita/dost/log/DITAOTAntLogger.java index 2f6b305727..753f190bbf 100644 --- a/src/main/java/org/dita/dost/log/DITAOTAntLogger.java +++ b/src/main/java/org/dita/dost/log/DITAOTAntLogger.java @@ -7,18 +7,16 @@ */ package org.dita.dost.log; -import java.text.MessageFormat; import org.apache.tools.ant.Project; import org.apache.tools.ant.Target; import org.apache.tools.ant.Task; -import org.slf4j.helpers.MarkerIgnoringBase; /** * Logger proxy to Ant logger. * * @author Jarno Elovirta */ -public final class DITAOTAntLogger extends MarkerIgnoringBase implements DITAOTLogger { +public final class DITAOTAntLogger extends AbstractLogger { private final Project project; private Task task; @@ -30,6 +28,7 @@ public final class DITAOTAntLogger extends MarkerIgnoringBase implements DITAOTL * @throws NullPointerException if project is {@code null} */ public DITAOTAntLogger(final Project project) { + super(); if (project == null) { throw new NullPointerException(); } @@ -53,171 +52,7 @@ public void setTarget(final Target target) { } @Override - public void info(final String msg) { - log(msg, null, Project.MSG_INFO); - } - - @Override - public void info(String format, Object arg) { - log(MessageFormat.format(format, arg), null, Project.MSG_INFO); - } - - @Override - public void info(String format, Object arg1, Object arg2) { - log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_INFO); - } - - @Override - public void info(String format, Object... arguments) { - log(MessageFormat.format(format, arguments), null, Project.MSG_INFO); - } - - @Override - public void info(String msg, Throwable t) { - log(msg, t, Project.MSG_INFO); - } - - @Override - public boolean isWarnEnabled() { - return false; - } - - @Override - public void warn(final String msg) { - log(msg, null, Project.MSG_WARN); - } - - @Override - public void warn(String format, Object arg) { - log(MessageFormat.format(format, arg), null, Project.MSG_WARN); - } - - @Override - public void warn(String format, Object... arguments) { - log(MessageFormat.format(format, arguments), null, Project.MSG_WARN); - } - - @Override - public void warn(String format, Object arg1, Object arg2) { - log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_WARN); - } - - @Override - public void warn(String msg, Throwable t) { - log(msg, t, Project.MSG_WARN); - } - - @Override - public boolean isErrorEnabled() { - return true; - } - - @Override - public void error(final String msg) { - log(msg, null, Project.MSG_ERR); - } - - @Override - public void error(String format, Object arg) { - if (arg instanceof Throwable) { - log(format, (Throwable) arg, Project.MSG_ERR); - } else { - log(MessageFormat.format(format, arg), null, Project.MSG_ERR); - } - } - - @Override - public void error(String format, Object arg1, Object arg2) { - if (arg2 instanceof Throwable) { - log(MessageFormat.format(format, arg1), (Throwable) arg2, Project.MSG_ERR); - } else { - log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_ERR); - } - } - - @Override - public void error(String format, Object... arguments) { - final Object last = arguments[arguments.length - 1]; - if (last instanceof Throwable) { - final Object[] init = new Object[arguments.length - 1]; - System.arraycopy(arguments, 0, init, 0, init.length); - log(MessageFormat.format(format, init), (Throwable) last, Project.MSG_ERR); - } else { - log(MessageFormat.format(format, arguments), null, Project.MSG_ERR); - } - } - - @Override - public void error(final String msg, final Throwable t) { - log(msg, t, Project.MSG_ERR); - } - - @Override - public boolean isTraceEnabled() { - return false; - } - - @Override - public void trace(String msg) { - throw new UnsupportedOperationException(); - } - - @Override - public void trace(String format, Object arg) { - throw new UnsupportedOperationException(); - } - - @Override - public void trace(String format, Object arg1, Object arg2) { - throw new UnsupportedOperationException(); - } - - @Override - public void trace(String format, Object... arguments) { - throw new UnsupportedOperationException(); - } - - @Override - public void trace(String msg, Throwable t) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isDebugEnabled() { - return true; - } - - @Override - public void debug(final String msg) { - log(msg, null, Project.MSG_VERBOSE); - } - - @Override - public void debug(String format, Object arg) { - log(MessageFormat.format(format, arg), null, Project.MSG_VERBOSE); - } - - @Override - public void debug(String format, Object arg1, Object arg2) { - log(MessageFormat.format(format, arg1, arg2), null, Project.MSG_VERBOSE); - } - - @Override - public void debug(String format, Object... arguments) { - log(MessageFormat.format(format, arguments), null, Project.MSG_VERBOSE); - } - - @Override - public void debug(String msg, Throwable t) { - log(msg, t, Project.MSG_VERBOSE); - } - - @Override - public boolean isInfoEnabled() { - return true; - } - - private void log(final String msg, final Throwable t, final int level) { + void log(final String msg, final Throwable t, final int level) { if (task != null) { project.log(task, msg, level); } else if (target != null) { diff --git a/src/main/java/org/dita/dost/log/StandardLogger.java b/src/main/java/org/dita/dost/log/StandardLogger.java index 03341d4fb8..8f08a294b9 100644 --- a/src/main/java/org/dita/dost/log/StandardLogger.java +++ b/src/main/java/org/dita/dost/log/StandardLogger.java @@ -8,33 +8,15 @@ package org.dita.dost.log; import java.io.PrintStream; -import java.text.MessageFormat; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.apache.tools.ant.Project; -import org.dita.dost.invoker.Main; -import org.slf4j.helpers.MarkerIgnoringBase; /** * Logger to standard out and error. */ -public final class StandardLogger extends MarkerIgnoringBase implements DITAOTLogger { - - public static final String ANSI_RESET = "\u001B[0m"; - public static final String ANSI_BLACK = "\u001B[30m"; - public static final String ANSI_RED = "\u001B[31m"; - public static final String ANSI_GREEN = "\u001B[32m"; - public static final String ANSI_YELLOW = "\u001B[33m"; - public static final String ANSI_BLUE = "\u001B[34m"; - public static final String ANSI_PURPLE = "\u001B[35m"; - public static final String ANSI_CYAN = "\u001B[36m"; - public static final String ANSI_WHITE = "\u001B[37m"; - public static final String ANSI_BOLD = "\u001b[1m"; +public final class StandardLogger extends AbstractLogger { private final PrintStream err; private final PrintStream out; - private int msgOutputLevel; - private boolean useColor; public StandardLogger( final PrintStream out, @@ -42,229 +24,19 @@ public StandardLogger( final int msgOutputLevel, final boolean useColor ) { + super(); this.out = out; this.err = err; this.msgOutputLevel = msgOutputLevel; this.useColor = useColor; } - public void setOutputLevel(final int msgOutputLevel) { - this.msgOutputLevel = msgOutputLevel; - } - - public void setUseColor(final boolean useColor) { - this.useColor = useColor; - } - - @Override - public void info(final String msg) { - log(msg, null, Project.MSG_INFO); - } - - @Override - public void info(String format, Object arg) { - log(format, new Object[] { arg }, null, Project.MSG_INFO); - } - - @Override - public void info(String format, Object arg1, Object arg2) { - log(format, new Object[] { arg1, arg2 }, null, Project.MSG_INFO); - } - - @Override - public void info(String format, Object... arguments) { - log(format, arguments, null, Project.MSG_INFO); - } - - @Override - public void info(String msg, Throwable t) { - log(msg, t, Project.MSG_INFO); - } - - @Override - public boolean isWarnEnabled() { - return false; - } - - @Override - public void warn(final String msg) { - log(msg, null, Project.MSG_WARN); - } - - @Override - public void warn(String format, Object arg) { - log(format, new Object[] { arg }, null, Project.MSG_WARN); - } - - @Override - public void warn(String format, Object... arguments) { - log(format, arguments, null, Project.MSG_WARN); - } - - @Override - public void warn(String format, Object arg1, Object arg2) { - log(format, new Object[] { arg1, arg2 }, null, Project.MSG_WARN); - } - - @Override - public void warn(String msg, Throwable t) { - log(msg, t, Project.MSG_WARN); - } - - @Override - public boolean isErrorEnabled() { - return true; - } - - @Override - public void error(final String msg) { - log(msg, null, Project.MSG_ERR); - } - - @Override - public void error(String format, Object arg) { - if (arg instanceof Throwable) { - log(format, (Throwable) arg, Project.MSG_ERR); - } else { - log(format, new Object[] { arg }, null, Project.MSG_ERR); - } - } - - @Override - public void error(String format, Object arg1, Object arg2) { - if (arg2 instanceof Throwable) { - log(format, new Object[] { arg1 }, (Throwable) arg2, Project.MSG_ERR); - } else { - log(format, new Object[] { arg1, arg2 }, null, Project.MSG_ERR); - } - } - @Override - public void error(String format, Object... arguments) { - final Object last = arguments[arguments.length - 1]; - if (last instanceof Throwable) { - final Object[] args = new Object[arguments.length - 1]; - System.arraycopy(arguments, 0, args, 0, args.length); - log(format, args, (Throwable) last, Project.MSG_ERR); - } else { - log(format, arguments, null, Project.MSG_ERR); - } - } - - @Override - public void error(final String msg, final Throwable t) { - log(msg, t, Project.MSG_ERR); - } - - @Override - public boolean isTraceEnabled() { - return false; - } - - @Override - public void trace(String msg) { - throw new UnsupportedOperationException(); - } - - @Override - public void trace(String format, Object arg) { - throw new UnsupportedOperationException(); - } - - @Override - public void trace(String format, Object arg1, Object arg2) { - throw new UnsupportedOperationException(); - } - - @Override - public void trace(String format, Object... arguments) { - throw new UnsupportedOperationException(); - } - - @Override - public void trace(String msg, Throwable t) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isDebugEnabled() { - return true; - } - - @Override - public void debug(final String msg) { - log(msg, null, Project.MSG_VERBOSE); - } - - @Override - public void debug(String format, Object arg) { - log(format, new Object[] { arg }, null, Project.MSG_VERBOSE); - } - - @Override - public void debug(String format, Object arg1, Object arg2) { - log(format, new Object[] { arg1, arg2 }, null, Project.MSG_VERBOSE); - } - - @Override - public void debug(String format, Object... arguments) { - log(format, arguments, null, Project.MSG_VERBOSE); - } - - @Override - public void debug(String msg, Throwable t) { - log(msg, t, Project.MSG_VERBOSE); - } - - @Override - public boolean isInfoEnabled() { - return true; - } - - private void log(final String msg, final Throwable t, final int level) { - log(msg, new Object[] {}, t, level); - } - - private void log(final String msg, final Object[] args, final Throwable t, final int level) { - if (level > msgOutputLevel) { - return; - } - if (useColor && level == Project.MSG_ERR) { - err.print(ANSI_RED); - err.printf(Main.locale.getString("error_msg"), ""); - err.print(ANSI_RESET); - } else if (useColor && level == Project.MSG_WARN) { - err.print(ANSI_YELLOW); - err.printf(Main.locale.getString("warn_msg"), ""); - err.print(ANSI_RESET); - } - if (args.length > 0) { - if (msg.contains("{}") || msg.contains("%s")) { - out.println(MessageFormat.format(addIndex(msg), args)); - } else { - out.println(MessageFormat.format(msg, args)); - } + public void log(final String msg, final Throwable t, final int level) { + if (level == Project.MSG_ERR || level == Project.MSG_WARN) { + err.println(msg); } else { out.println(msg); } } - - private static final Pattern ARGUMENT = Pattern.compile("\\{}|%s"); - - private String addIndex(String msg) { - final Matcher matcher = ARGUMENT.matcher(msg); - final StringBuilder buf = new StringBuilder(); - int start = 0; - for (int i = 0; matcher.find(start); i++) { - buf.append(msg, start, matcher.start()); - buf.append("{"); - buf.append(i); - buf.append("}"); - start = matcher.end(); - } - if (start < msg.length()) { - buf.append(msg, start, msg.length()); - } - return buf.toString(); - } } diff --git a/src/main/java/org/dita/dost/platform/Integrator.java b/src/main/java/org/dita/dost/platform/Integrator.java index 42c128bda7..edce401791 100644 --- a/src/main/java/org/dita/dost/platform/Integrator.java +++ b/src/main/java/org/dita/dost/platform/Integrator.java @@ -246,13 +246,13 @@ private void logChanges(final Set orig, final Set mod) { removed.removeAll(mod); removed.sort(Comparator.naturalOrder()); for (final String p : removed) { - logger.info("Removed " + p); + logger.info("Removed {}", p); } final List added = new ArrayList<>(mod); added.removeAll(orig); added.sort(Comparator.naturalOrder()); for (final String p : added) { - logger.info("Added " + p); + logger.info("Added {}", p); } } @@ -272,7 +272,7 @@ private void integrate() throws Exception { // generate the files from template for (final Entry template : templateSet.entrySet()) { final File templateFile = new File(ditaDir, template.getKey()); - logger.debug("Process template " + templateFile.getPath()); + logger.trace("Process template " + templateFile.getPath()); // fileGen.setPluginId(template.getValue().id); fileGen.generate(templateFile); } @@ -355,7 +355,7 @@ private void integrate() throws Exception { if (!(outFile.getParentFile().exists()) && !outFile.getParentFile().mkdirs()) { throw new RuntimeException("Failed to make directory " + outFile.getParentFile().getAbsolutePath()); } - logger.debug("Generate configuration properties " + outFile.getPath()); + logger.trace("Generate configuration properties {}", outFile.getPath()); out = new BufferedOutputStream(new FileOutputStream(outFile)); configuration.store(out, "DITA-OT runtime configuration, do not edit manually"); } catch (final Exception e) { @@ -459,7 +459,7 @@ private void customIntegration() { try { customIntegrator.process(); } catch (final Exception e) { - logger.error("Custom integrator " + customIntegrator.getClass().getName() + " failed: " + e.getMessage(), e); + logger.error("Custom integrator {} failed: {}", customIntegrator.getClass().getName(), e.getMessage(), e); } } } @@ -526,7 +526,7 @@ private void writeEnvShell(final Collection jars) { if (!(outFile.getParentFile().exists()) && !outFile.getParentFile().mkdirs()) { throw new RuntimeException("Failed to make directory " + outFile.getParentFile().getAbsolutePath()); } - logger.debug("Generate environment shell " + outFile.getPath()); + logger.trace("Generate environment shell " + outFile.getPath()); out = new BufferedWriter(new FileWriter(outFile)); out.write("#!/bin/sh\n"); @@ -557,7 +557,7 @@ private void writeEnvBatch(final Collection jars) { if (!(outFile.getParentFile().exists()) && !outFile.getParentFile().mkdirs()) { throw new RuntimeException("Failed to make directory " + outFile.getParentFile().getAbsolutePath()); } - logger.debug("Generate environment batch " + outFile.getPath()); + logger.trace("Generate environment batch " + outFile.getPath()); out = new BufferedWriter(new FileWriter(outFile)); for (final File relativeLib : jars) { @@ -583,7 +583,7 @@ private void writeStartcmdShell(final Collection jars) { if (!(outFile.getParentFile().exists()) && !outFile.getParentFile().mkdirs()) { throw new RuntimeException("Failed to make directory " + outFile.getParentFile().getAbsolutePath()); } - logger.debug("Generate start command shell " + outFile.getPath()); + logger.trace("Generate start command shell {}", outFile.getPath()); out = new BufferedWriter(new FileWriter(outFile)); out.write( @@ -656,7 +656,7 @@ private void writeStartcmdBatch(final Collection jars) { if (!(outFile.getParentFile().exists()) && !outFile.getParentFile().mkdirs()) { throw new RuntimeException("Failed to make directory " + outFile.getParentFile().getAbsolutePath()); } - logger.debug("Generate start command batch " + outFile.getPath()); + logger.trace("Generate start command batch {}", outFile.getPath()); out = new BufferedWriter(new FileWriter(outFile)); out.write( @@ -732,8 +732,7 @@ private boolean loadPlugin(final String plugin) { .map(val -> new Value(plugin, val)) .collect(Collectors.toList()); if (!extensionPoints.contains(key)) { - final String msg = "Plug-in " + plugin + " uses an undefined extension point " + key; - throw new RuntimeException(msg); + throw new RuntimeException("Plug-in %s uses an undefined extension point %s".formatted(plugin, key)); } if (featureTable.containsKey(key)) { final List value = featureTable.get(key); @@ -827,7 +826,7 @@ private void mergePlugins() { if (!descSet.isEmpty()) { final URI b = new File(ditaDir, CONFIG_DIR + File.separator + "plugins.xml").toURI(); for (final File descFile : descSet) { - logger.debug("Read plug-in configuration " + descFile.getPath()); + logger.trace("Read plug-in configuration {}", descFile.getPath()); final Element plugin = parseDesc(descFile); if (plugin != null) { final URI base = getRelativePath(b, descFile.toURI()); @@ -840,7 +839,7 @@ private void mergePlugins() { private void writePlugins() throws TransformerException { final File plugins = new File(ditaDir, CONFIG_DIR + File.separator + "plugins.xml"); - logger.debug("Writing " + plugins); + logger.trace("Writing {}", plugins); try { new XMLUtils().writeDocument(pluginsDoc, plugins); } catch (final IOException e) { @@ -904,13 +903,11 @@ private Element parseDesc(final File descFile) { private void validatePlugin(final Features f) { final String id = f.getPluginId(); if (!ID_PATTERN.matcher(id).matches()) { - final String msg = "Plug-in ID '" + id + "' doesn't follow syntax rules."; - throw new IllegalArgumentException(msg); + throw new IllegalArgumentException("Plug-in ID '%s' doesn't follow syntax rules.".formatted(id)); } final List version = f.getFeature("package.version"); if (version != null && !version.isEmpty() && !VERSION_PATTERN.matcher(version.get(0)).matches()) { - final String msg = "Plug-in version '" + version.get(0) + "' doesn't follow syntax rules."; - throw new IllegalArgumentException(msg); + throw new IllegalArgumentException("Plug-in version '%s' doesn't follow syntax rules.".formatted(version.get(0))); } } diff --git a/src/main/java/org/dita/dost/platform/PluginInstall.java b/src/main/java/org/dita/dost/platform/PluginInstall.java index 5e9e89ccb3..2919ec86b3 100644 --- a/src/main/java/org/dita/dost/platform/PluginInstall.java +++ b/src/main/java/org/dita/dost/platform/PluginInstall.java @@ -78,10 +78,17 @@ private void init() { public void execute() throws Exception { init(); - if (pluginFile == null && pluginUri == null && pluginName == null) { - throw new BuildException(new IllegalStateException("pluginName argument not set")); + if (pluginFile != null || pluginUri != null || pluginName != null) { + installPlugin(); } + try { + integrator.execute(); + } catch (final Exception e) { + throw new BuildException("Integration failed: " + e.getMessage(), e); + } + } + private void installPlugin() throws Exception { try { final Map installs = new HashMap<>(); if (pluginFile != null && Files.exists(pluginFile)) { @@ -112,7 +119,7 @@ public void execute() throws Exception { integrator.addRemoved(name); FileUtils.deleteDirectory(pluginDir); } else { - logger.warn(String.format(Main.locale.getString("install.error.exists"), name)); + logger.warn(Main.locale.getString("install.error.exists"), name); throw new BuildException(); } } @@ -123,16 +130,12 @@ public void execute() throws Exception { } finally { cleanUp(); } - try { - integrator.execute(); - } catch (final Exception e) { - throw new BuildException("Integration failed: " + e.getMessage(), e); - } } private void cleanUp() { if (tempDir != null) { try { + logger.trace("Delete {}", tempDir); FileUtils.deleteDirectory(tempDir); } catch (IOException e) { throw new BuildException(e); @@ -181,13 +184,13 @@ private File getPluginDir(final String id) { } private Set readRegistry(final String name, final SemVerMatch version) { - logger.info("Reading registries for {0} {1}", name, Objects.requireNonNullElse(version, "")); + logger.trace("Reading registries for {0} {1}", name, Objects.requireNonNullElse(version, "")); Registry res = null; for (final String registry : registries) { final URI registryUrl = URI.create(registry + name + ".json"); logger.debug("Read registry {0}", registry); try (BufferedInputStream in = new BufferedInputStream(registryUrl.toURL().openStream())) { - logger.debug("Parse registry"); + logger.trace("Parse registry"); final JsonFactory factory = mapper.getFactory(); final JsonParser parser = factory.createParser(in); final JsonNode obj = mapper.readTree(parser); @@ -200,7 +203,7 @@ private Set readRegistry(final String name, final SemVerMatch version) final Optional reg = findPlugin(regs, version); if (reg.isPresent()) { final Registry plugin = reg.get(); - logger.debug("Plugin found at {0}@{1}", registryUrl, plugin.vers); + logger.trace("Plugin found at {0}@{1}", registryUrl, plugin.vers); res = plugin; break; } @@ -237,7 +240,7 @@ private File get(final URI uri, final String expectedChecksum) throws Exception final HttpClient client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build(); final HttpRequest request = HttpRequest.newBuilder().GET().uri(uri).build(); try { - logger.info("Download {0}", request.uri()); + logger.debug("Download {}", request.uri()); client.send(request, HttpResponse.BodyHandlers.ofFile(tempPluginFile.toPath())); } catch (IOException | InterruptedException e) { throw new Exception(Main.locale.getString("install.error.download_failure").formatted(uri), e); @@ -261,7 +264,7 @@ private File get(final URI uri, final String expectedChecksum) throws Exception private Path unzip(final File input) throws Exception { final Path tempPluginDir = new File(tempDir, "plugin").toPath(); - logger.debug("Expanding {0} to {1}", input, tempPluginDir); + logger.trace("Expanding {0} to {1}", input, tempPluginDir); try (ZipInputStream zipIn = new ZipInputStream(Files.newInputStream(input.toPath()))) { for (ZipEntry ze; (ze = zipIn.getNextEntry()) != null;) { Path resolvedPath = tempPluginDir.resolve(ze.getName()).normalize(); @@ -272,7 +275,7 @@ private Path unzip(final File input) throws Exception { Files.createDirectories(resolvedPath); } else { Files.createDirectories(resolvedPath.getParent()); - logger.debug("Write {0}", resolvedPath); + logger.trace("Write {0}", resolvedPath); Files.copy(zipIn, resolvedPath); } } diff --git a/src/main/java/org/dita/dost/platform/PluginUninstall.java b/src/main/java/org/dita/dost/platform/PluginUninstall.java index 369e17ffbe..4ff212099e 100644 --- a/src/main/java/org/dita/dost/platform/PluginUninstall.java +++ b/src/main/java/org/dita/dost/platform/PluginUninstall.java @@ -40,7 +40,7 @@ public void execute() throws Exception { throw new CliException(Main.locale.getString("uninstall.error.plugin_not_found").formatted(id)); } - logger.info("Delete plug-in directory {0}", pluginDir); + logger.debug("Delete plug-in directory {0}", pluginDir); try { FileUtils.deleteDirectory(pluginDir); } catch (IOException e) { diff --git a/src/test/java/org/dita/dost/log/StandardLoggerTest.java b/src/test/java/org/dita/dost/log/StandardLoggerTest.java index 7cc8bd2df9..3d85f4b12f 100644 --- a/src/test/java/org/dita/dost/log/StandardLoggerTest.java +++ b/src/test/java/org/dita/dost/log/StandardLoggerTest.java @@ -78,16 +78,16 @@ void info() { void warn() { logger.warn(MSG); - assertOut(MSG); - assertErr(); + assertOut(); + assertErr(MSG); } @Test void error() { logger.error(MSG); - assertOut(MSG); - assertErr(); + assertOut(); + assertErr(MSG); } private void assertOut() { From 616615b39623f04c3682514046bdfa9ef81a39cf Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Fri, 24 Nov 2023 11:19:35 +0200 Subject: [PATCH 13/36] Adjust log message colour and level prefix Signed-off-by: Jarno Elovirta --- .../org/dita/dost/invoker/DefaultLogger.java | 12 ++++++ src/main/java/org/dita/dost/invoker/Main.java | 2 + .../org/dita/dost/log/AbstractLogger.java | 37 ++++++++++++++++++- .../org/dita/dost/log/DITAOTAntLogger.java | 3 ++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dita/dost/invoker/DefaultLogger.java b/src/main/java/org/dita/dost/invoker/DefaultLogger.java index ab23ca12d6..82037b4e97 100644 --- a/src/main/java/org/dita/dost/invoker/DefaultLogger.java +++ b/src/main/java/org/dita/dost/invoker/DefaultLogger.java @@ -361,6 +361,18 @@ private void printMessage(final String message, final PrintStream stream, final } else { stream.println(message); } + // if (useColor && priority == Project.MSG_ERR) { + // stream.print(ANSI_RED); + // stream.print("Error"); + // stream.println(ANSI_RESET); + // stream.print(": "); + // } else if (useColor && priority == Project.MSG_WARN) { + // stream.print(ANSI_YELLOW); + // stream.print("Warning"); + // stream.println(ANSI_RESET); + // stream.print(": "); + // } + // stream.println(message); } /** diff --git a/src/main/java/org/dita/dost/invoker/Main.java b/src/main/java/org/dita/dost/invoker/Main.java index 78813189a1..014c6adafa 100644 --- a/src/main/java/org/dita/dost/invoker/Main.java +++ b/src/main/java/org/dita/dost/invoker/Main.java @@ -27,6 +27,7 @@ package org.dita.dost.invoker; import static org.dita.dost.invoker.Arguments.*; +import static org.dita.dost.log.DITAOTAntLogger.USE_COLOR; import static org.dita.dost.util.Configuration.transtypes; import static org.dita.dost.util.Constants.ANT_TEMP_DIR; import static org.dita.dost.util.LangUtils.pair; @@ -354,6 +355,7 @@ private void processArgs(final String[] arguments) { final File basePluginDir = new File(ditaDir, Configuration.pluginResourceDirs.get("org.dita.base").getPath()); buildFile = findBuildFile(basePluginDir.getAbsolutePath(), "build.xml"); definedProps.putAll(getLocalProperties(ditaDir)); + definedProps.put(USE_COLOR, Boolean.toString(conversionArgs.useColor)); if (conversionArgs.projectFile == null) { projectProps = Collections.singletonList(definedProps); } else { diff --git a/src/main/java/org/dita/dost/log/AbstractLogger.java b/src/main/java/org/dita/dost/log/AbstractLogger.java index 86e7211ba8..709de108b5 100644 --- a/src/main/java/org/dita/dost/log/AbstractLogger.java +++ b/src/main/java/org/dita/dost/log/AbstractLogger.java @@ -250,8 +250,43 @@ protected void log(final String msg, final Object[] args, final Throwable t, fin } else { buf.append(MessageFormat.format(msg, args)); } + } else if (buf != null) { + buf.append(msg); } - log(buf != null ? buf.toString() : msg, t, level); + final String res; + if (!useColor) { + res = buf != null ? buf.toString() : msg; + } else if (buf != null) { + res = removeLevelPrefix(buf).toString(); + } else { + res = removeLevelPrefix(msg); + } + log(res, t, level); + } + + private static String removeLevelPrefix(String msg) { + var start = msg.indexOf("]["); + if (start == -1) { + return msg; + } + var end = msg.indexOf("] ", start); + if (end == -1) { + return msg; + } + return msg.substring(0, start) + msg.substring(end); + } + + private static StringBuilder removeLevelPrefix(StringBuilder msg) { + var start = msg.indexOf("]["); + if (start == -1) { + return msg; + } + var end = msg.indexOf("] ", start); + if (end == -1) { + return msg; + } + msg.replace(start, end, ""); + return msg; } private static final Pattern ARGUMENT = Pattern.compile("\\{}|%s"); diff --git a/src/main/java/org/dita/dost/log/DITAOTAntLogger.java b/src/main/java/org/dita/dost/log/DITAOTAntLogger.java index 753f190bbf..ddf349f8d2 100644 --- a/src/main/java/org/dita/dost/log/DITAOTAntLogger.java +++ b/src/main/java/org/dita/dost/log/DITAOTAntLogger.java @@ -18,6 +18,8 @@ */ public final class DITAOTAntLogger extends AbstractLogger { + public static final String USE_COLOR = "dita.use-color"; + private final Project project; private Task task; private Target target; @@ -33,6 +35,7 @@ public DITAOTAntLogger(final Project project) { throw new NullPointerException(); } this.project = project; + this.useColor = Boolean.parseBoolean(project.getProperty(USE_COLOR)); } /** From 717801ae4e0f9578c5a376b0331cc6014af53af3 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Fri, 24 Nov 2023 14:02:38 +0200 Subject: [PATCH 14/36] Simplify CLI logger Signed-off-by: Jarno Elovirta --- .../dita/dost/ant/ExtensibleAntInvoker.java | 2 +- .../org/dita/dost/invoker/DefaultLogger.java | 52 +++++++++++-------- src/main/java/org/dita/dost/invoker/Main.java | 16 +++--- .../org/dita/dost/log/AbstractLogger.java | 4 +- .../org/dita/dost/log/DITAOTAntLogger.java | 2 +- 5 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/dita/dost/ant/ExtensibleAntInvoker.java b/src/main/java/org/dita/dost/ant/ExtensibleAntInvoker.java index 37696d8cb5..52be8ba005 100644 --- a/src/main/java/org/dita/dost/ant/ExtensibleAntInvoker.java +++ b/src/main/java/org/dita/dost/ant/ExtensibleAntInvoker.java @@ -212,7 +212,7 @@ public void execute() throws BuildException { logger.debug("{0} processing took {1} ms", mod.getClass().getSimpleName(), end - start); } } catch (final DITAOTException e) { - throw new BuildException("Failed to run pipeline: " + e.getMessage(), e); + throw new BuildException(e.getMessage(), e); } } diff --git a/src/main/java/org/dita/dost/invoker/DefaultLogger.java b/src/main/java/org/dita/dost/invoker/DefaultLogger.java index 82037b4e97..42a4f16474 100644 --- a/src/main/java/org/dita/dost/invoker/DefaultLogger.java +++ b/src/main/java/org/dita/dost/invoker/DefaultLogger.java @@ -37,24 +37,14 @@ import org.apache.tools.ant.util.DateUtils; import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.StringUtils; +import org.dita.dost.log.AbstractLogger; /** * Writes build events to a PrintStream. Currently, it only writes which targets * are being executed, and any messages that get logged. * */ -class DefaultLogger implements BuildLogger { - - public static final String ANSI_RESET = "\u001B[0m"; - public static final String ANSI_BLACK = "\u001B[30m"; - public static final String ANSI_RED = "\u001B[31m"; - public static final String ANSI_GREEN = "\u001B[32m"; - public static final String ANSI_YELLOW = "\u001B[33m"; - public static final String ANSI_BLUE = "\u001B[34m"; - public static final String ANSI_PURPLE = "\u001B[35m"; - public static final String ANSI_CYAN = "\u001B[36m"; - public static final String ANSI_WHITE = "\u001B[37m"; - public static final String ANSI_BOLD = "\u001b[1m"; +class DefaultLogger extends AbstractLogger implements BuildLogger { /** * Size of left-hand column for right-justified task name. @@ -71,26 +61,29 @@ class DefaultLogger implements BuildLogger { private PrintStream err; /** Lowest level of message to write out */ - private int msgOutputLevel = Project.MSG_ERR; + // private int msgOutputLevel = Project.MSG_ERR; /** Time of the start of the build */ private long startTime = System.currentTimeMillis(); // CheckStyle:ConstantNameCheck OFF - bc /** Line separator */ - protected static final String lSep = StringUtils.LINE_SEP; + // protected static final String lSep = StringUtils.LINE_SEP; // CheckStyle:ConstantNameCheck ON /** Whether or not to use emacs-style output */ private boolean emacsMode = false; - private boolean useColor = false; + + // private boolean useColor = false; // CheckStyle:VisibilityModifier ON /** * Sole constructor. */ - public DefaultLogger() {} + public DefaultLogger() { + msgOutputLevel = Project.MSG_ERR; + } /** * Sets the highest level of message this logger should respond to. @@ -187,7 +180,13 @@ public void buildFinished(final BuildEvent event) { // message.append(StringUtils.LINE_SEP); // message.append(getBuildFailedMessage()); // message.append(StringUtils.LINE_SEP); + if (useColor) { + message.append(ANSI_RED); + } message.append("Error: "); + if (useColor) { + message.append(ANSI_RESET); + } throwableMessage(message, error, Project.MSG_VERBOSE <= msgOutputLevel); } // message.append(StringUtils.LINE_SEP); @@ -198,7 +197,11 @@ public void buildFinished(final BuildEvent event) { if (error == null && !msg.trim().isEmpty()) { printMessage(msg, out, Project.MSG_VERBOSE); } else if (!msg.isEmpty()) { - printMessage(msg, err, Project.MSG_ERR); + if (useColor) { + printMessage(removeLevelPrefix(message).toString(), err, Project.MSG_ERR); + } else { + printMessage(msg, err, Project.MSG_ERR); + } } log(msg); } @@ -325,7 +328,7 @@ public void messageLogged(final BuildEvent event) { if (priority != Project.MSG_ERR) { printMessage(msg, out, priority); } else { - printMessage(msg, err, priority); + printMessage(removeLevelPrefix(new StringBuilder(msg)).toString(), err, priority); } log(msg); } @@ -355,21 +358,21 @@ protected static String formatTime(final long millis) { */ private void printMessage(final String message, final PrintStream stream, final int priority) { if (useColor && priority == Project.MSG_ERR) { - stream.print(ANSI_RED); + // stream.print(ANSI_RED); stream.print(message); - stream.println(ANSI_RESET); + // stream.println(ANSI_RESET); } else { stream.println(message); } // if (useColor && priority == Project.MSG_ERR) { // stream.print(ANSI_RED); // stream.print("Error"); - // stream.println(ANSI_RESET); + // stream.print(ANSI_RESET); // stream.print(": "); // } else if (useColor && priority == Project.MSG_WARN) { // stream.print(ANSI_YELLOW); // stream.print("Warning"); - // stream.println(ANSI_RESET); + // stream.print(ANSI_RESET); // stream.print(": "); // } // stream.println(message); @@ -406,4 +409,9 @@ protected String extractProjectName(final BuildEvent event) { final Project project = event.getProject(); return (project != null) ? project.getName() : null; } + + @Override + public void log(String msg, Throwable t, int level) { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/org/dita/dost/invoker/Main.java b/src/main/java/org/dita/dost/invoker/Main.java index 014c6adafa..1ddc6c36cc 100644 --- a/src/main/java/org/dita/dost/invoker/Main.java +++ b/src/main/java/org/dita/dost/invoker/Main.java @@ -145,12 +145,12 @@ public class Main extends org.apache.tools.ant.Main implements AntMain { private void printMessage(final Throwable t) { final String message = t.getMessage(); if (message != null && !message.trim().isEmpty()) { - printErrorMessage(message); + printErrorMessage(message, t); } } - private void printErrorMessage(final String msg) { - logger.error(msg); + private void printErrorMessage(final String msg, final Throwable t) { + logger.error(msg, t); } /** @@ -637,7 +637,8 @@ private void validateProject(org.dita.dost.project.Project project) throws IOExc for (Publication.Param param : deliverable.publication().params()) { if (RESERVED_PARAMS.containsKey(param.name())) { printErrorMessage( - MessageUtils.getMessage("DOTJ085E", param.name(), RESERVED_PARAMS.get(param.name())).toString() + MessageUtils.getMessage("DOTJ085E", param.name(), RESERVED_PARAMS.get(param.name())).toString(), + null ); } } @@ -845,10 +846,10 @@ private void runBuild(final ClassLoader coreLoader, Map definedP } catch (final Throwable t) { // yes, I know it is bad style to catch Throwable, // but if we don't, we lose valuable information - printErrorMessage("Caught an exception while logging the end of the build. Exception was:"); + printErrorMessage("Caught an exception while logging the end of the build. Exception was:", null); t.printStackTrace(); if (error != null) { - printErrorMessage("There has been an error prior to that:"); + printErrorMessage("There has been an error prior to that:", null); error.printStackTrace(); } throw new BuildException(t); @@ -916,7 +917,8 @@ private BuildLogger createLogger() { logger = ClasspathUtils.newInstance(args.loggerClassname, Main.class.getClassLoader(), BuildLogger.class); } catch (final BuildException e) { printErrorMessage( - "The specified logger class " + args.loggerClassname + " could not be used because " + e.getMessage() + "The specified logger class " + args.loggerClassname + " could not be used because " + e.getMessage(), + e ); throw new RuntimeException(); } diff --git a/src/main/java/org/dita/dost/log/AbstractLogger.java b/src/main/java/org/dita/dost/log/AbstractLogger.java index 709de108b5..24d4cce48f 100644 --- a/src/main/java/org/dita/dost/log/AbstractLogger.java +++ b/src/main/java/org/dita/dost/log/AbstractLogger.java @@ -221,7 +221,7 @@ public boolean isInfoEnabled() { return msgOutputLevel >= Project.MSG_INFO; } - abstract void log(final String msg, final Throwable t, final int level); + public abstract void log(final String msg, final Throwable t, final int level); protected void log(final String msg, final Object[] args, final Throwable t, final int level) { if (level <= msgOutputLevel) { @@ -276,7 +276,7 @@ private static String removeLevelPrefix(String msg) { return msg.substring(0, start) + msg.substring(end); } - private static StringBuilder removeLevelPrefix(StringBuilder msg) { + protected static StringBuilder removeLevelPrefix(StringBuilder msg) { var start = msg.indexOf("]["); if (start == -1) { return msg; diff --git a/src/main/java/org/dita/dost/log/DITAOTAntLogger.java b/src/main/java/org/dita/dost/log/DITAOTAntLogger.java index ddf349f8d2..04f383b927 100644 --- a/src/main/java/org/dita/dost/log/DITAOTAntLogger.java +++ b/src/main/java/org/dita/dost/log/DITAOTAntLogger.java @@ -55,7 +55,7 @@ public void setTarget(final Target target) { } @Override - void log(final String msg, final Throwable t, final int level) { + public void log(final String msg, final Throwable t, final int level) { if (task != null) { project.log(task, msg, level); } else if (target != null) { From 0e9e05d0cd38958245f2ace533641fc8d07bf955 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Sat, 25 Nov 2023 11:21:42 +0200 Subject: [PATCH 15/36] Log Ant targets with prefix and using target description Signed-off-by: Jarno Elovirta --- .../org/dita/dost/invoker/DefaultLogger.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dita/dost/invoker/DefaultLogger.java b/src/main/java/org/dita/dost/invoker/DefaultLogger.java index 42a4f16474..b00b1b4cc7 100644 --- a/src/main/java/org/dita/dost/invoker/DefaultLogger.java +++ b/src/main/java/org/dita/dost/invoker/DefaultLogger.java @@ -236,9 +236,29 @@ protected String getBuildSuccessfulMessage() { @Override public void targetStarted(final BuildEvent event) { if (Project.MSG_INFO <= msgOutputLevel && !event.getTarget().getName().equals("")) { - final String msg = StringUtils.LINE_SEP + event.getTarget().getName() + ":"; - printMessage(msg, out, event.getPriority()); - log(msg); + final String msg; + if (useColor) { + if (event.getTarget().getDescription() == null) { + msg = null; + } else { + msg = + new StringBuilder() + .append(StringUtils.LINE_SEP) + .append(ANSI_BLUE) + .append("==> ") + .append(ANSI_RESET) + .append(ANSI_BOLD) + .append(event.getTarget().getDescription()) + .append(ANSI_RESET) + .toString(); + } + } else { + msg = StringUtils.LINE_SEP + event.getTarget().getName() + ":"; + } + if (msg != null) { + printMessage(msg, out, event.getPriority()); + log(msg); + } } } From f8eb237a5a07bc51962d8b6820e4749c0c31e165 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Sat, 25 Nov 2023 14:40:32 +0200 Subject: [PATCH 16/36] Fix message cleaner Signed-off-by: Jarno Elovirta --- .../org/dita/dost/log/AbstractLogger.java | 8 +- .../org/dita/dost/log/AbstractLoggerTest.java | 79 +++++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 src/test/java/org/dita/dost/log/AbstractLoggerTest.java diff --git a/src/main/java/org/dita/dost/log/AbstractLogger.java b/src/main/java/org/dita/dost/log/AbstractLogger.java index 24d4cce48f..aaecf896f3 100644 --- a/src/main/java/org/dita/dost/log/AbstractLogger.java +++ b/src/main/java/org/dita/dost/log/AbstractLogger.java @@ -224,7 +224,7 @@ public boolean isInfoEnabled() { public abstract void log(final String msg, final Throwable t, final int level); protected void log(final String msg, final Object[] args, final Throwable t, final int level) { - if (level <= msgOutputLevel) { + if (level > msgOutputLevel) { return; } StringBuilder buf = null; @@ -264,12 +264,12 @@ protected void log(final String msg, final Object[] args, final Throwable t, fin log(res, t, level); } - private static String removeLevelPrefix(String msg) { + protected static String removeLevelPrefix(String msg) { var start = msg.indexOf("]["); if (start == -1) { return msg; } - var end = msg.indexOf("] ", start); + var end = msg.indexOf("]", start + 1); if (end == -1) { return msg; } @@ -281,7 +281,7 @@ protected static StringBuilder removeLevelPrefix(StringBuilder msg) { if (start == -1) { return msg; } - var end = msg.indexOf("] ", start); + var end = msg.indexOf("]", start + 1); if (end == -1) { return msg; } diff --git a/src/test/java/org/dita/dost/log/AbstractLoggerTest.java b/src/test/java/org/dita/dost/log/AbstractLoggerTest.java new file mode 100644 index 0000000000..cf381422bf --- /dev/null +++ b/src/test/java/org/dita/dost/log/AbstractLoggerTest.java @@ -0,0 +1,79 @@ +/* + * This file is part of the DITA Open Toolkit project. + * + * Copyright 2023 Jarno Elovirta + * + * See the accompanying LICENSE file for applicable license. + */ + +package org.dita.dost.log; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; +import org.apache.tools.ant.Project; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +class AbstractLoggerTest extends AbstractLogger { + + static List removeLevelPrefix() { + return List.of( + Arguments.of("[DOTJ037W][INFO] Message", "[DOTJ037W] Message"), + Arguments.of("[DOTJ037W][INFO]: Message", "[DOTJ037W]: Message"), + Arguments.of("[DOTJ037W] Message", "[DOTJ037W] Message") + ); + } + + @ParameterizedTest + @MethodSource("removeLevelPrefix") + void removeLevelPrefix_StringBuilder(String src, String exp) { + assertEquals(exp, AbstractLogger.removeLevelPrefix(new StringBuilder(src)).toString()); + } + + @ParameterizedTest + @MethodSource("removeLevelPrefix") + void removeLevelPrefix_String(String src, String exp) { + assertEquals(exp, AbstractLogger.removeLevelPrefix(src)); + } + + private String expMessage; + private Throwable expThrowable; + private Integer expLevel; + + @BeforeEach + void setUp() { + expMessage = null; + expThrowable = null; + expLevel = null; + } + + @Test + void logArguments_filter() { + msgOutputLevel = Project.MSG_WARN; + log("Message", null, null, Project.MSG_INFO); + } + + @ParameterizedTest + @ValueSource(strings = { "Message {} {}", "Message {0} {1}", "Message %s %s" }) + void logArguments(String msg) { + useColor = true; + msgOutputLevel = Project.MSG_INFO; + expMessage = "Message first second"; + expThrowable = null; + expLevel = Project.MSG_INFO; + + log(msg, new Object[] { "first", "second" }, null, Project.MSG_INFO); + } + + @Override + public void log(String msg, Throwable t, int level) { + assertEquals(expMessage, msg); + assertEquals(expThrowable, t); + assertEquals(expLevel, level); + } +} From 492c2862dfe61f42f3c79a7a7cae51daa3bf5eb2 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Sun, 26 Nov 2023 17:01:30 +0200 Subject: [PATCH 17/36] Add cli.log-format configuration Default to new fancy format, but allow using the old legacy format when needed Signed-off-by: Jarno Elovirta --- src/main/config/configuration.properties | 1 + .../java/org/dita/dost/invoker/Arguments.java | 2 +- .../org/dita/dost/invoker/DefaultLogger.java | 13 ++++---- src/main/java/org/dita/dost/invoker/Main.java | 2 ++ .../org/dita/dost/log/AbstractLogger.java | 32 +++++++++++-------- .../dita/dost/module/SourceReaderModule.java | 4 +-- .../module/reader/AbstractReaderModule.java | 4 +-- .../org/dita/dost/log/AbstractLoggerTest.java | 12 +++++++ 8 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/main/config/configuration.properties b/src/main/config/configuration.properties index c112819479..a4c03228d0 100644 --- a/src/main/config/configuration.properties +++ b/src/main/config/configuration.properties @@ -9,6 +9,7 @@ temp-file-name-scheme = org.dita.dost.module.reader.DefaultTempFileScheme #filter-attributes = #flag-attributes = cli.color = true +cli.log-format = fancy default.coderef-charset=UTF-8 # Integration diff --git a/src/main/java/org/dita/dost/invoker/Arguments.java b/src/main/java/org/dita/dost/invoker/Arguments.java index a8437cb110..5f2f3a3af1 100644 --- a/src/main/java/org/dita/dost/invoker/Arguments.java +++ b/src/main/java/org/dita/dost/invoker/Arguments.java @@ -63,7 +63,7 @@ abstract class Arguments { /** * Whether or not output to the log is to be unadorned. */ - boolean emacsMode; + boolean emacsMode = Configuration.configuration.getOrDefault("cli.log-format", "fancy").equals("fancy"); /** * optional thread priority */ diff --git a/src/main/java/org/dita/dost/invoker/DefaultLogger.java b/src/main/java/org/dita/dost/invoker/DefaultLogger.java index b00b1b4cc7..8f2d18a564 100644 --- a/src/main/java/org/dita/dost/invoker/DefaultLogger.java +++ b/src/main/java/org/dita/dost/invoker/DefaultLogger.java @@ -197,10 +197,10 @@ public void buildFinished(final BuildEvent event) { if (error == null && !msg.trim().isEmpty()) { printMessage(msg, out, Project.MSG_VERBOSE); } else if (!msg.isEmpty()) { - if (useColor) { - printMessage(removeLevelPrefix(message).toString(), err, Project.MSG_ERR); - } else { + if (legacyFormat) { printMessage(msg, err, Project.MSG_ERR); + } else { + printMessage(removeLevelPrefix(message).toString(), err, Project.MSG_ERR); } } log(msg); @@ -345,10 +345,11 @@ public void messageLogged(final BuildEvent event) { } final String msg = message.toString(); - if (priority != Project.MSG_ERR) { - printMessage(msg, out, priority); + final PrintStream dst = priority == Project.MSG_ERR ? err : out; + if (legacyFormat) { + printMessage(msg, dst, priority); } else { - printMessage(removeLevelPrefix(new StringBuilder(msg)).toString(), err, priority); + printMessage(removeLevelPrefix(new StringBuilder(msg)).toString(), dst, priority); } log(msg); } diff --git a/src/main/java/org/dita/dost/invoker/Main.java b/src/main/java/org/dita/dost/invoker/Main.java index 1ddc6c36cc..3a0f67b7ef 100644 --- a/src/main/java/org/dita/dost/invoker/Main.java +++ b/src/main/java/org/dita/dost/invoker/Main.java @@ -922,6 +922,8 @@ private BuildLogger createLogger() { ); throw new RuntimeException(); } + } else if (Configuration.configuration.getOrDefault("cli.log-format", "fancy").equals("legacy")) { + logger = new org.apache.tools.ant.DefaultLogger(); } else { logger = new DefaultLogger(); ((DefaultLogger) logger).useColor(args.useColor); diff --git a/src/main/java/org/dita/dost/log/AbstractLogger.java b/src/main/java/org/dita/dost/log/AbstractLogger.java index aaecf896f3..4fea4c0846 100644 --- a/src/main/java/org/dita/dost/log/AbstractLogger.java +++ b/src/main/java/org/dita/dost/log/AbstractLogger.java @@ -12,6 +12,7 @@ import java.util.regex.Pattern; import org.apache.tools.ant.Project; import org.dita.dost.invoker.Main; +import org.dita.dost.util.Configuration; import org.slf4j.helpers.MarkerIgnoringBase; /** @@ -30,8 +31,9 @@ public abstract class AbstractLogger extends MarkerIgnoringBase implements DITAO public static final String ANSI_WHITE = "\u001B[37m"; public static final String ANSI_BOLD = "\u001b[1m"; - protected int msgOutputLevel; + protected int msgOutputLevel = Project.MSG_DEBUG; protected boolean useColor; + protected boolean legacyFormat = Configuration.configuration.getOrDefault("cli.log-format", "fancy").equals("legacy"); public void setOutputLevel(final int msgOutputLevel) { this.msgOutputLevel = msgOutputLevel; @@ -228,18 +230,20 @@ protected void log(final String msg, final Object[] args, final Throwable t, fin return; } StringBuilder buf = null; - if (useColor && level == Project.MSG_ERR) { - buf = - new StringBuilder() - .append(ANSI_RED) - .append(Main.locale.getString("error_msg").formatted("")) - .append(ANSI_RESET); - } else if (useColor && level == Project.MSG_WARN) { - buf = - new StringBuilder() - .append(ANSI_YELLOW) - .append(Main.locale.getString("warn_msg").formatted("")) - .append(ANSI_RESET); + if (!legacyFormat) { + if (useColor && level == Project.MSG_ERR) { + buf = + new StringBuilder() + .append(ANSI_RED) + .append(Main.locale.getString("error_msg").formatted("")) + .append(ANSI_RESET); + } else if (useColor && level == Project.MSG_WARN) { + buf = + new StringBuilder() + .append(ANSI_YELLOW) + .append(Main.locale.getString("warn_msg").formatted("")) + .append(ANSI_RESET); + } } if (args.length > 0) { if (buf == null) { @@ -254,7 +258,7 @@ protected void log(final String msg, final Object[] args, final Throwable t, fin buf.append(msg); } final String res; - if (!useColor) { + if (legacyFormat) { res = buf != null ? buf.toString() : msg; } else if (buf != null) { res = removeLevelPrefix(buf).toString(); diff --git a/src/main/java/org/dita/dost/module/SourceReaderModule.java b/src/main/java/org/dita/dost/module/SourceReaderModule.java index 7f2a9fcf91..542af08446 100644 --- a/src/main/java/org/dita/dost/module/SourceReaderModule.java +++ b/src/main/java/org/dita/dost/module/SourceReaderModule.java @@ -90,9 +90,9 @@ void initXmlReader() throws SAXException { final XMLGrammarPool grammarPool = GrammarPoolManager.getGrammarPool(); try { reader.setProperty(FEATURE_GRAMMAR_POOL, grammarPool); - logger.info("Using Xerces grammar pool for DTD and schema caching."); + logger.debug("Using Xerces grammar pool for DTD and schema caching."); } catch (final NoClassDefFoundError e) { - logger.debug("Xerces not available, not using grammar caching"); + logger.warn("Xerces not available, not using grammar caching"); } catch (final SAXNotRecognizedException | SAXNotSupportedException e) { logger.warn("Failed to set Xerces grammar pool for parser: " + e.getMessage()); } diff --git a/src/main/java/org/dita/dost/module/reader/AbstractReaderModule.java b/src/main/java/org/dita/dost/module/reader/AbstractReaderModule.java index b421ee1ee6..d27676f6dd 100644 --- a/src/main/java/org/dita/dost/module/reader/AbstractReaderModule.java +++ b/src/main/java/org/dita/dost/module/reader/AbstractReaderModule.java @@ -228,9 +228,9 @@ void initXMLReader(final boolean validate) throws DITAOTException { final XMLGrammarPool grammarPool = GrammarPoolManager.getGrammarPool(); try { reader.setProperty("http://apache.org/xml/properties/internal/grammar-pool", grammarPool); - logger.info("Using Xerces grammar pool for DTD and schema caching."); + logger.debug("Using Xerces grammar pool for DTD and schema caching."); } catch (final NoClassDefFoundError e) { - logger.debug("Xerces not available, not using grammar caching"); + logger.warn("Xerces not available, not using grammar caching"); } catch (final SAXNotRecognizedException | SAXNotSupportedException e) { logger.warn("Failed to set Xerces grammar pool for parser: " + e.getMessage()); } diff --git a/src/test/java/org/dita/dost/log/AbstractLoggerTest.java b/src/test/java/org/dita/dost/log/AbstractLoggerTest.java index cf381422bf..7a34e9b45d 100644 --- a/src/test/java/org/dita/dost/log/AbstractLoggerTest.java +++ b/src/test/java/org/dita/dost/log/AbstractLoggerTest.java @@ -47,6 +47,8 @@ void removeLevelPrefix_String(String src, String exp) { @BeforeEach void setUp() { + useColor = true; + msgOutputLevel = Project.MSG_DEBUG; expMessage = null; expThrowable = null; expLevel = null; @@ -55,9 +57,19 @@ void setUp() { @Test void logArguments_filter() { msgOutputLevel = Project.MSG_WARN; + log("Message", null, null, Project.MSG_INFO); } + @Test + void logArguments_filter_default() { + expMessage = "Message"; + expThrowable = null; + expLevel = Project.MSG_DEBUG; + + log("Message", new Object[] {}, null, Project.MSG_DEBUG); + } + @ParameterizedTest @ValueSource(strings = { "Message {} {}", "Message {0} {1}", "Message %s %s" }) void logArguments(String msg) { From 0badee8a2f204f83f7dfc7b77ef010543b891342 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Sun, 26 Nov 2023 17:39:11 +0200 Subject: [PATCH 18/36] Reduce args logging in fancy log format Signed-off-by: Jarno Elovirta --- src/main/plugins/org.dita.base/build_init.xml | 47 +++++++++++-------- .../build_preprocess2_template.xml | 6 +-- .../build_preprocess_template.xml | 11 ++--- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/main/plugins/org.dita.base/build_init.xml b/src/main/plugins/org.dita.base/build_init.xml index 0727e687a0..abd22f82c8 100644 --- a/src/main/plugins/org.dita.base/build_init.xml +++ b/src/main/plugins/org.dita.base/build_init.xml @@ -200,55 +200,62 @@ See the accompanying LICENSE file for applicable license. - + + + + + + + + - + - + - + - + - + - ***************************************************************** - * basedir = ${basedir} - * dita.dir = ${dita.dir} + ***************************************************************** + * basedir = ${basedir} + * dita.dir = ${dita.dir} - * transtype = ${transtype} - * tempdir = ${dita.temp.dir} - * outputdir = ${output.dir} - * clean.temp = ${clean.temp} - * DITA-OT version = ${otversion} - * XML parser = ${xml.parser} - * XSLT processor = ${xslt.parser} - * collator = ${collator} - ***************************************************************** - - ***************************************************************** + ${log-prefix}transtype = ${transtype} + ${log-prefix}tempdir = ${dita.temp.dir} + ${log-prefix}outputdir = ${output.dir} + * clean.temp = ${clean.temp} + ${log-prefix}DITA-OT version = ${otversion} + * XML parser = ${xml.parser} + * XSLT processor = ${xslt.parser} + * collator = ${collator} + ***************************************************************** + + ***************************************************************** diff --git a/src/main/plugins/org.dita.base/build_preprocess2_template.xml b/src/main/plugins/org.dita.base/build_preprocess2_template.xml index 3db5b8a896..2701743b7b 100644 --- a/src/main/plugins/org.dita.base/build_preprocess2_template.xml +++ b/src/main/plugins/org.dita.base/build_preprocess2_template.xml @@ -94,10 +94,10 @@ See the accompanying LICENSE file for applicable license. - ***************************************************************** - * input = ${args.input} + ***************************************************************** + ${log-prefix}input = ${args.input} * resources = ${args.resources} - ***************************************************************** + ***************************************************************** diff --git a/src/main/plugins/org.dita.base/build_preprocess_template.xml b/src/main/plugins/org.dita.base/build_preprocess_template.xml index 2fb172f46b..1f7072f97c 100644 --- a/src/main/plugins/org.dita.base/build_preprocess_template.xml +++ b/src/main/plugins/org.dita.base/build_preprocess_template.xml @@ -42,8 +42,7 @@ See the accompanying LICENSE file for applicable license. clean-preprocess, copy-files, {depend.preprocess.post}" - dita:extension="depends org.dita.dost.platform.InsertDependsAction" - description="Preprocessing ended" /> + dita:extension="depends org.dita.dost.platform.InsertDependsAction" /> @@ -90,10 +89,10 @@ See the accompanying LICENSE file for applicable license. - ***************************************************************** - * input = ${args.input} - * resources = ${args.resources} - ***************************************************************** + ***************************************************************** + ${log-prefix}input = ${args.input} + * resources = ${args.resources} + ***************************************************************** - + diff --git a/src/test/resources/bookmap1/src/chapter3.ditamap b/src/test/resources/bookmap1/src/chapter3.ditamap index 56518e859f..c1c8d8fb1c 100644 --- a/src/test/resources/bookmap1/src/chapter3.ditamap +++ b/src/test/resources/bookmap1/src/chapter3.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap1/src/chapter4.ditamap b/src/test/resources/bookmap1/src/chapter4.ditamap index 782cea9a2f..560d6d74a4 100644 --- a/src/test/resources/bookmap1/src/chapter4.ditamap +++ b/src/test/resources/bookmap1/src/chapter4.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap1/src/chapter5.ditamap b/src/test/resources/bookmap1/src/chapter5.ditamap index b3f4210ecd..5452695571 100644 --- a/src/test/resources/bookmap1/src/chapter5.ditamap +++ b/src/test/resources/bookmap1/src/chapter5.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> Batty Things diff --git a/src/test/resources/bookmap2/src/chapter2.ditamap b/src/test/resources/bookmap2/src/chapter2.ditamap index bd8ea84fa2..817575b509 100644 --- a/src/test/resources/bookmap2/src/chapter2.ditamap +++ b/src/test/resources/bookmap2/src/chapter2.ditamap @@ -3,7 +3,7 @@ Sourceforge.net. See the accompanying LICENSE file for applicable licenses.--> - + diff --git a/src/test/resources/bookmap2/src/chapter3.ditamap b/src/test/resources/bookmap2/src/chapter3.ditamap index 56518e859f..c1c8d8fb1c 100644 --- a/src/test/resources/bookmap2/src/chapter3.ditamap +++ b/src/test/resources/bookmap2/src/chapter3.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap2/src/chapter4.ditamap b/src/test/resources/bookmap2/src/chapter4.ditamap index 782cea9a2f..560d6d74a4 100644 --- a/src/test/resources/bookmap2/src/chapter4.ditamap +++ b/src/test/resources/bookmap2/src/chapter4.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap2/src/chapter5.ditamap b/src/test/resources/bookmap2/src/chapter5.ditamap index b3f4210ecd..5452695571 100644 --- a/src/test/resources/bookmap2/src/chapter5.ditamap +++ b/src/test/resources/bookmap2/src/chapter5.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> Batty Things diff --git a/src/test/resources/bookmap3/src/chapter2.ditamap b/src/test/resources/bookmap3/src/chapter2.ditamap index bd8ea84fa2..817575b509 100644 --- a/src/test/resources/bookmap3/src/chapter2.ditamap +++ b/src/test/resources/bookmap3/src/chapter2.ditamap @@ -3,7 +3,7 @@ Sourceforge.net. See the accompanying LICENSE file for applicable licenses.--> - + diff --git a/src/test/resources/bookmap3/src/chapter3.ditamap b/src/test/resources/bookmap3/src/chapter3.ditamap index 56518e859f..c1c8d8fb1c 100644 --- a/src/test/resources/bookmap3/src/chapter3.ditamap +++ b/src/test/resources/bookmap3/src/chapter3.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap3/src/chapter4.ditamap b/src/test/resources/bookmap3/src/chapter4.ditamap index 782cea9a2f..560d6d74a4 100644 --- a/src/test/resources/bookmap3/src/chapter4.ditamap +++ b/src/test/resources/bookmap3/src/chapter4.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap3/src/chapter5.ditamap b/src/test/resources/bookmap3/src/chapter5.ditamap index b3f4210ecd..5452695571 100644 --- a/src/test/resources/bookmap3/src/chapter5.ditamap +++ b/src/test/resources/bookmap3/src/chapter5.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> Batty Things diff --git a/src/test/resources/bookmap4/src/chapter2.ditamap b/src/test/resources/bookmap4/src/chapter2.ditamap index bd8ea84fa2..817575b509 100644 --- a/src/test/resources/bookmap4/src/chapter2.ditamap +++ b/src/test/resources/bookmap4/src/chapter2.ditamap @@ -3,7 +3,7 @@ Sourceforge.net. See the accompanying LICENSE file for applicable licenses.--> - + diff --git a/src/test/resources/bookmap4/src/chapter3.ditamap b/src/test/resources/bookmap4/src/chapter3.ditamap index 56518e859f..c1c8d8fb1c 100644 --- a/src/test/resources/bookmap4/src/chapter3.ditamap +++ b/src/test/resources/bookmap4/src/chapter3.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap4/src/chapter4.ditamap b/src/test/resources/bookmap4/src/chapter4.ditamap index 782cea9a2f..560d6d74a4 100644 --- a/src/test/resources/bookmap4/src/chapter4.ditamap +++ b/src/test/resources/bookmap4/src/chapter4.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap4/src/chapter5.ditamap b/src/test/resources/bookmap4/src/chapter5.ditamap index b3f4210ecd..5452695571 100644 --- a/src/test/resources/bookmap4/src/chapter5.ditamap +++ b/src/test/resources/bookmap4/src/chapter5.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> Batty Things diff --git a/src/test/resources/bookmap5/src/chapter2.ditamap b/src/test/resources/bookmap5/src/chapter2.ditamap index bd8ea84fa2..817575b509 100644 --- a/src/test/resources/bookmap5/src/chapter2.ditamap +++ b/src/test/resources/bookmap5/src/chapter2.ditamap @@ -3,7 +3,7 @@ Sourceforge.net. See the accompanying LICENSE file for applicable licenses.--> - + diff --git a/src/test/resources/bookmap5/src/chapter3.ditamap b/src/test/resources/bookmap5/src/chapter3.ditamap index 56518e859f..c1c8d8fb1c 100644 --- a/src/test/resources/bookmap5/src/chapter3.ditamap +++ b/src/test/resources/bookmap5/src/chapter3.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap5/src/chapter4.ditamap b/src/test/resources/bookmap5/src/chapter4.ditamap index 782cea9a2f..560d6d74a4 100644 --- a/src/test/resources/bookmap5/src/chapter4.ditamap +++ b/src/test/resources/bookmap5/src/chapter4.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap5/src/chapter5.ditamap b/src/test/resources/bookmap5/src/chapter5.ditamap index b3f4210ecd..5452695571 100644 --- a/src/test/resources/bookmap5/src/chapter5.ditamap +++ b/src/test/resources/bookmap5/src/chapter5.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> Batty Things diff --git a/src/test/resources/bookmap6/src/chapter2.ditamap b/src/test/resources/bookmap6/src/chapter2.ditamap index bd8ea84fa2..817575b509 100644 --- a/src/test/resources/bookmap6/src/chapter2.ditamap +++ b/src/test/resources/bookmap6/src/chapter2.ditamap @@ -3,7 +3,7 @@ Sourceforge.net. See the accompanying LICENSE file for applicable licenses.--> - + diff --git a/src/test/resources/bookmap6/src/chapter3.ditamap b/src/test/resources/bookmap6/src/chapter3.ditamap index 56518e859f..c1c8d8fb1c 100644 --- a/src/test/resources/bookmap6/src/chapter3.ditamap +++ b/src/test/resources/bookmap6/src/chapter3.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap6/src/chapter4.ditamap b/src/test/resources/bookmap6/src/chapter4.ditamap index 782cea9a2f..560d6d74a4 100644 --- a/src/test/resources/bookmap6/src/chapter4.ditamap +++ b/src/test/resources/bookmap6/src/chapter4.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap6/src/chapter5.ditamap b/src/test/resources/bookmap6/src/chapter5.ditamap index b3f4210ecd..5452695571 100644 --- a/src/test/resources/bookmap6/src/chapter5.ditamap +++ b/src/test/resources/bookmap6/src/chapter5.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> Batty Things diff --git a/src/test/resources/bookmap7/src/chapter2.ditamap b/src/test/resources/bookmap7/src/chapter2.ditamap index bd8ea84fa2..817575b509 100644 --- a/src/test/resources/bookmap7/src/chapter2.ditamap +++ b/src/test/resources/bookmap7/src/chapter2.ditamap @@ -3,7 +3,7 @@ Sourceforge.net. See the accompanying LICENSE file for applicable licenses.--> - + diff --git a/src/test/resources/bookmap7/src/chapter4.ditamap b/src/test/resources/bookmap7/src/chapter4.ditamap index 782cea9a2f..560d6d74a4 100644 --- a/src/test/resources/bookmap7/src/chapter4.ditamap +++ b/src/test/resources/bookmap7/src/chapter4.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> My little book diff --git a/src/test/resources/bookmap7/src/chapter5.ditamap b/src/test/resources/bookmap7/src/chapter5.ditamap index b3f4210ecd..5452695571 100644 --- a/src/test/resources/bookmap7/src/chapter5.ditamap +++ b/src/test/resources/bookmap7/src/chapter5.ditamap @@ -1,7 +1,7 @@ + "bookmap.dtd"> Batty Things From faa3ac5c5bdaa1f67f902e92a9b06a85376e52a9 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Fri, 1 Dec 2023 14:33:27 +0200 Subject: [PATCH 27/36] Add target descriptions for CLI Signed-off-by: Jarno Elovirta --- .../org.dita.base/build_preprocess2_template.xml | 2 -- src/main/plugins/org.dita.pdf2/build_template.xml | 15 +++++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/plugins/org.dita.base/build_preprocess2_template.xml b/src/main/plugins/org.dita.base/build_preprocess2_template.xml index 2701743b7b..403044ee5e 100644 --- a/src/main/plugins/org.dita.base/build_preprocess2_template.xml +++ b/src/main/plugins/org.dita.base/build_preprocess2_template.xml @@ -13,7 +13,6 @@ See the accompanying LICENSE file for applicable license. - + @@ -129,7 +130,8 @@ with those set forth herein. - + @@ -146,7 +148,8 @@ with those set forth herein. - + @@ -387,7 +390,11 @@ with those set forth herein. + dita:depends="transform.fo2pdf.init, + {depend.org.dita.pdf2.format}"/> + + From 750befddc2bbbd3bcd960ada3047d30e9aeaee71 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Fri, 1 Dec 2023 14:40:45 +0200 Subject: [PATCH 28/36] Adjust test log filter Signed-off-by: Jarno Elovirta --- .../java/org/dita/dost/AbstractIntegrationTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/dita/dost/AbstractIntegrationTest.java b/src/test/java/org/dita/dost/AbstractIntegrationTest.java index 1839e54404..f43f0e6e1a 100644 --- a/src/test/java/org/dita/dost/AbstractIntegrationTest.java +++ b/src/test/java/org/dita/dost/AbstractIntegrationTest.java @@ -611,11 +611,11 @@ private void rewriteId( static class TestListener implements BuildListener { - private final Pattern fatalPattern = Pattern.compile("\\[\\w+F\\]|\\[FATAL\\]"); - private final Pattern errorPattern = Pattern.compile("\\[\\w+E\\]|\\[ERROR\\]"); - private final Pattern warnPattern = Pattern.compile("\\[\\w+W\\]|\\[WARN\\]"); - private final Pattern infoPattern = Pattern.compile("\\[\\w+I\\]|\\[INFO\\]"); - private final Pattern debugPattern = Pattern.compile("\\[\\w+D\\]|\\[DEBUG\\]"); + private final Pattern fatalPattern = Pattern.compile("\\[\\w+F\\]"); + private final Pattern errorPattern = Pattern.compile("\\[\\w+E\\]"); + private final Pattern warnPattern = Pattern.compile("\\[\\w+W\\]"); + private final Pattern infoPattern = Pattern.compile("\\[\\w+I\\]"); + private final Pattern debugPattern = Pattern.compile("\\[\\w+D\\]"); public final List messages = new ArrayList<>(); final PrintStream out; From 680e4b3de802aa5070a60b8451ebb55946c175e9 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Sun, 3 Dec 2023 13:49:41 +0200 Subject: [PATCH 29/36] Change info logging to debug Signed-off-by: Jarno Elovirta --- src/main/java/org/dita/dost/module/XsltModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dita/dost/module/XsltModule.java b/src/main/java/org/dita/dost/module/XsltModule.java index 90725d6817..48e3f17b7f 100644 --- a/src/main/java/org/dita/dost/module/XsltModule.java +++ b/src/main/java/org/dita/dost/module/XsltModule.java @@ -94,7 +94,7 @@ public AbstractPipelineOutput execute(AbstractPipelineInput input) throws DITAOT } if (destDir != null) { - logger.info("Transforming into " + destDir.getAbsolutePath()); + logger.debug("Transforming into " + destDir.getAbsolutePath()); } processor = xmlUtils.getProcessor(); final XsltCompiler xsltCompiler = processor.newXsltCompiler(); From fa137ca2e1b9765ee92d835c5762479688f605a8 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Wed, 6 Dec 2023 11:21:21 +0200 Subject: [PATCH 30/36] Make legacy format default and force CLI to be fancy Signed-off-by: Jarno Elovirta --- src/main/bin/dita | 2 +- src/main/bin/dita.bat | 2 +- src/main/config/configuration.properties | 4 +-- .../dost/invoker/ConversionArguments.java | 3 ++ .../org/dita/dost/invoker/DefaultLogger.java | 31 +++++++++---------- src/main/java/org/dita/dost/invoker/Main.java | 2 ++ .../org/dita/dost/util/Configuration.java | 10 +++--- 7 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/main/bin/dita b/src/main/bin/dita index 0f5435af5c..a10dcd9192 100755 --- a/src/main/bin/dita +++ b/src/main/bin/dita @@ -175,5 +175,5 @@ ant_sys_opts= if [ -n "$CYGHOME" ]; then ant_sys_opts="-Dcygwin.user.home=\"$CYGHOME\"" fi -ant_exec_command="exec \"$JAVACMD\" $ANT_OPTS -Djava.awt.headless=true -classpath \"$LOCALCLASSPATH\" -Dant.home=\"$DITA_HOME\" -Ddita.dir=\"$DITA_HOME\" -Dant.library.dir=\"$ANT_LIB\" $ant_sys_opts org.apache.tools.ant.launch.Launcher $ANT_ARGS -cp \"$CLASSPATH\"" +ant_exec_command="exec \"$JAVACMD\" $ANT_OPTS -Djava.awt.headless=true -Dcli.log-format=fancy -Dcli.color=true -classpath \"$LOCALCLASSPATH\" -Dant.home=\"$DITA_HOME\" -Ddita.dir=\"$DITA_HOME\" -Dant.library.dir=\"$ANT_LIB\" $ant_sys_opts org.apache.tools.ant.launch.Launcher $ANT_ARGS -cp \"$CLASSPATH\"" eval $ant_exec_command "$ant_exec_args" diff --git a/src/main/bin/dita.bat b/src/main/bin/dita.bat index 54377dd8a0..f2a8a5eb8f 100755 --- a/src/main/bin/dita.bat +++ b/src/main/bin/dita.bat @@ -58,7 +58,7 @@ if "%_JAVACMD%" == "" set _JAVACMD=java.exe :runAnt rem sun.io.useCanonCaches improves performance in Java 12+ (see https://bugs.openjdk.java.net/browse/JDK-8207005) -"%_JAVACMD%" %ANT_OPTS% -Djava.awt.headless=true -Dsun.io.useCanonCaches=true -classpath "%DITA_HOME%\lib\ant-launcher.jar;%DITA_HOME%\config" "-Dant.home=%DITA_HOME%" "-Ddita.dir=%DITA_HOME%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% -cp "%CLASSPATH%" %DITA_CMD_LINE_ARGS% -buildfile "%DITA_HOME%\build.xml" -main "org.dita.dost.invoker.Main" +"%_JAVACMD%" %ANT_OPTS% -Djava.awt.headless=true -Dcli.log-format=fancy -Dcli.color=true -Dsun.io.useCanonCaches=true -classpath "%DITA_HOME%\lib\ant-launcher.jar;%DITA_HOME%\config" "-Dant.home=%DITA_HOME%" "-Ddita.dir=%DITA_HOME%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% -cp "%CLASSPATH%" %DITA_CMD_LINE_ARGS% -buildfile "%DITA_HOME%\build.xml" -main "org.dita.dost.invoker.Main" rem Check the error code of the Ant build if not "%OS%"=="Windows_NT" goto onError set ANT_ERROR=%ERRORLEVEL% diff --git a/src/main/config/configuration.properties b/src/main/config/configuration.properties index a4c03228d0..dcf3dc7f9a 100644 --- a/src/main/config/configuration.properties +++ b/src/main/config/configuration.properties @@ -8,8 +8,8 @@ default.cascade = merge temp-file-name-scheme = org.dita.dost.module.reader.DefaultTempFileScheme #filter-attributes = #flag-attributes = -cli.color = true -cli.log-format = fancy +cli.color = false +cli.log-format = legacy default.coderef-charset=UTF-8 # Integration diff --git a/src/main/java/org/dita/dost/invoker/ConversionArguments.java b/src/main/java/org/dita/dost/invoker/ConversionArguments.java index 45bc079e09..97d24ebe83 100644 --- a/src/main/java/org/dita/dost/invoker/ConversionArguments.java +++ b/src/main/java/org/dita/dost/invoker/ConversionArguments.java @@ -10,6 +10,7 @@ import static org.dita.dost.invoker.ArgumentParser.getPluginArguments; import static org.dita.dost.invoker.Main.locale; +import static org.dita.dost.util.Configuration.configuration; import static org.dita.dost.util.Constants.ANT_TEMP_DIR; import static org.dita.dost.util.XMLUtils.toList; @@ -185,6 +186,8 @@ ConversionArguments parse(final String[] arguments) { } definedProps.putAll(loadPropertyFiles()); + definedProps.put("cli.log-format", configuration.get("cli.log-format")); + return this; } diff --git a/src/main/java/org/dita/dost/invoker/DefaultLogger.java b/src/main/java/org/dita/dost/invoker/DefaultLogger.java index dfa81d6b54..68f688f686 100644 --- a/src/main/java/org/dita/dost/invoker/DefaultLogger.java +++ b/src/main/java/org/dita/dost/invoker/DefaultLogger.java @@ -273,23 +273,22 @@ public void targetStarted(final BuildEvent event) { } if (Project.MSG_INFO <= msgOutputLevel && !event.getTarget().getName().equals("")) { final String msg; - if (useColor) { - if (event.getTarget().getDescription() == null) { - msg = null; - } else { - msg = - new StringBuilder() - .append(StringUtils.LINE_SEP) - .append(ANSI_BLUE) - .append("==> ") - .append(ANSI_RESET) - .append(ANSI_BOLD) - .append(event.getTarget().getDescription()) - .append(ANSI_RESET) - .toString(); - } + if (event.getTarget().getDescription() == null) { + msg = null; } else { - msg = StringUtils.LINE_SEP + event.getTarget().getName() + ":"; + var buf = new StringBuilder().append(StringUtils.LINE_SEP); + if (useColor) { + buf.append(ANSI_BLUE); + } + buf.append("==> "); + if (useColor) { + buf.append(ANSI_RESET).append(ANSI_BOLD); + } + buf.append(event.getTarget().getDescription()); + if (useColor) { + buf.append(ANSI_RESET); + } + msg = buf.toString(); } if (msg != null) { out.println(msg); diff --git a/src/main/java/org/dita/dost/invoker/Main.java b/src/main/java/org/dita/dost/invoker/Main.java index 9b99a96564..1926b57a29 100644 --- a/src/main/java/org/dita/dost/invoker/Main.java +++ b/src/main/java/org/dita/dost/invoker/Main.java @@ -166,6 +166,7 @@ private void printErrorMessage(final String msg, final Throwable t) { * null in which case the system classloader is * used. */ + @Deprecated public static void start( final String[] args, final Properties additionalUserProperties, @@ -295,6 +296,7 @@ private void handleLogfile() { * * @param args Command line arguments. Must not be null. */ + @Deprecated public static void main(final String[] args) { start(args, null, null); } diff --git a/src/main/java/org/dita/dost/util/Configuration.java b/src/main/java/org/dita/dost/util/Configuration.java index 17a519f036..bb875911e2 100644 --- a/src/main/java/org/dita/dost/util/Configuration.java +++ b/src/main/java/org/dita/dost/util/Configuration.java @@ -10,11 +10,7 @@ import static org.dita.dost.platform.Integrator.CONF_PARSER_FORMAT; import static org.dita.dost.util.Constants.*; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.util.*; import org.dita.dost.platform.Integrator; @@ -115,6 +111,10 @@ public final class Configuration { for (final Map.Entry e : properties.entrySet()) { c.put(e.getKey().toString(), e.getValue().toString()); } + // Override with system properties + for (var systemProperty : new String[] { "cli.log-format", "cli.color" }) { + c.put(systemProperty, System.getProperty(systemProperty, c.get(systemProperty))); + } configuration = Collections.unmodifiableMap(c); } From c6d5c40368b013b336d7714b57b094af12d4f5c6 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Wed, 6 Dec 2023 12:15:03 +0200 Subject: [PATCH 31/36] Adjust exception logging Signed-off-by: Jarno Elovirta --- .../org/dita/dost/invoker/DefaultLogger.java | 25 +++++++++++++------ src/main/resources/cli_en_US.properties | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/dita/dost/invoker/DefaultLogger.java b/src/main/java/org/dita/dost/invoker/DefaultLogger.java index 68f688f686..dc3c3b7043 100644 --- a/src/main/java/org/dita/dost/invoker/DefaultLogger.java +++ b/src/main/java/org/dita/dost/invoker/DefaultLogger.java @@ -34,6 +34,7 @@ import org.apache.tools.ant.util.DateUtils; import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.StringUtils; +import org.dita.dost.exception.DITAOTException; import org.dita.dost.log.AbstractLogger; /** @@ -168,7 +169,13 @@ private static void throwableMessage(final StringBuilder m, final Throwable erro */ @Override public void buildFinished(final BuildEvent event) { - final Throwable error = event.getException(); + Throwable error = event.getException(); + for (var e = error; e != null; e = e.getCause()) { + if (e instanceof DITAOTException) { + error = e; + break; + } + } final StringBuilder message = new StringBuilder(); if (error == null) { if (msgOutputLevel >= Project.MSG_INFO) { @@ -190,12 +197,16 @@ public void buildFinished(final BuildEvent event) { if (useColor) { message.append(ANSI_RESET); } - try (var buf = new StringWriter(); var printWriter = new PrintWriter(buf)) { - error.printStackTrace(printWriter); - printWriter.flush(); - message.append(Main.locale.getString("exception_msg").formatted(buf)); - } catch (IOException e) { - // Failed to print stack trace + if (error instanceof DITAOTException && msgOutputLevel < Project.MSG_INFO) { + message.append(Main.locale.getString("exception_msg").formatted(error.getMessage())); + } else { + try (var buf = new StringWriter(); var printWriter = new PrintWriter(buf)) { + error.printStackTrace(printWriter); + printWriter.flush(); + message.append(Main.locale.getString("exception_msg").formatted(buf)); + } catch (IOException e) { + // Failed to print stack trace + } } if (msgOutputLevel >= Project.MSG_INFO) { diff --git a/src/main/resources/cli_en_US.properties b/src/main/resources/cli_en_US.properties index 67ec76fbbf..c5887de720 100644 --- a/src/main/resources/cli_en_US.properties +++ b/src/main/resources/cli_en_US.properties @@ -7,7 +7,7 @@ # error_msg=Error: %s warn_msg=Warning: %s -exception_msg=Build failed with and exception: %s +exception_msg=Build failed with an exception: %s help.option.help=Print help information help.option.debug=Enable debug logging help.option.verbose=Enable verbose logging From 52fe77003284d434ac41b6e8f5582420dbe2205e Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Thu, 7 Dec 2023 14:46:08 +0200 Subject: [PATCH 32/36] Generate configuration JAR in integrator Signed-off-by: Jarno Elovirta --- .../org/dita/dost/platform/Integrator.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/main/java/org/dita/dost/platform/Integrator.java b/src/main/java/org/dita/dost/platform/Integrator.java index edce401791..26af5fb5ed 100644 --- a/src/main/java/org/dita/dost/platform/Integrator.java +++ b/src/main/java/org/dita/dost/platform/Integrator.java @@ -26,12 +26,17 @@ import java.io.*; import java.net.URI; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.attribute.PosixFilePermission; import java.util.*; import java.util.Map.Entry; +import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import javax.xml.XMLConstants; import javax.xml.parsers.SAXParserFactory; import javax.xml.stream.XMLInputFactory; @@ -386,10 +391,51 @@ private void integrate() throws Exception { final Collection libJars = ImmutableList.builder().addAll(getLibJars()).addAll(jars).build(); writeStartcmdShell(libJars); writeStartcmdBatch(libJars); + writeConfigurationJar(); customIntegration(); } + /** + * Create legacy configuration JAR. The configuration JAR is used by e.g. DITA-OT Gradle plug-in so we have to keep on + * generating it. + */ + private void writeConfigurationJar() throws IOException { + final Path outFile = ditaDir.toPath().resolve("lib").resolve("dost-configuration.jar"); + logger.trace("Generate configuration JAR {}", outFile); + try (OutputStream out = Files.newOutputStream(outFile); final ZipOutputStream zip = new ZipOutputStream(out)) { + var config = ditaDir.toPath().resolve("config"); + Consumer copy = (Path path) -> { + final Path file = config.resolve(path); + if (!Files.exists(file)) { + return; + } + try { + ZipEntry entry = new ZipEntry(path.toString().replace('\\', '/')); + zip.putNextEntry(entry); + Files.copy(file, zip); + zip.flush(); + zip.closeEntry(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }; + + copy.accept(Paths.get("messages.xml")); + Files + .list(config) + .map(Path::getFileName) + .filter(path -> path.toString().startsWith("messages_") && path.toString().endsWith(".properties")) + .forEach(copy); + copy.accept(Paths.get("plugins.xml")); + copy.accept(Paths.get("configuration.properties")); + copy.accept(Paths.get("CatalogManager.properties")); + copy.accept(Paths.get("org.dita.dost.platform", "plugin.properties")); + } catch (IOException e) { + throw new IOException("Failed to write configuration JAR", e); + } + } + private Properties readMessageBundle() throws IOException, XMLStreamException { final Properties messages = new Properties(); // final Path basePluginDir = pluginTable.get("org.dita.base").getPluginDir().toPath(); From 672127bf3212165e47d428ae2f964422d376f8dd Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Thu, 7 Dec 2023 22:44:52 +0200 Subject: [PATCH 33/36] Fix defaulting to legacy and fix E2E tests Signed-off-by: Jarno Elovirta --- src/main/java/org/dita/dost/invoker/Arguments.java | 2 +- src/main/java/org/dita/dost/invoker/Main.java | 2 +- src/main/java/org/dita/dost/log/AbstractLogger.java | 4 +++- src/main/java/org/dita/dost/util/Configuration.java | 5 ++++- src/test/resources/configuration.properties | 2 ++ 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/dita/dost/invoker/Arguments.java b/src/main/java/org/dita/dost/invoker/Arguments.java index 5f2f3a3af1..2f8e8c6039 100644 --- a/src/main/java/org/dita/dost/invoker/Arguments.java +++ b/src/main/java/org/dita/dost/invoker/Arguments.java @@ -63,7 +63,7 @@ abstract class Arguments { /** * Whether or not output to the log is to be unadorned. */ - boolean emacsMode = Configuration.configuration.getOrDefault("cli.log-format", "fancy").equals("fancy"); + boolean emacsMode = Configuration.configuration.getOrDefault("cli.log-format", "legacy").equals("fancy"); /** * optional thread priority */ diff --git a/src/main/java/org/dita/dost/invoker/Main.java b/src/main/java/org/dita/dost/invoker/Main.java index 1926b57a29..03eaed6b57 100644 --- a/src/main/java/org/dita/dost/invoker/Main.java +++ b/src/main/java/org/dita/dost/invoker/Main.java @@ -917,7 +917,7 @@ private BuildLogger createLogger() { ); throw new RuntimeException(); } - } else if (Configuration.configuration.getOrDefault("cli.log-format", "fancy").equals("legacy")) { + } else if (Configuration.configuration.getOrDefault("cli.log-format", "legacy").equals("legacy")) { logger = new org.apache.tools.ant.DefaultLogger(); } else { logger = new DefaultLogger(); diff --git a/src/main/java/org/dita/dost/log/AbstractLogger.java b/src/main/java/org/dita/dost/log/AbstractLogger.java index 4fea4c0846..fa4c2f50a9 100644 --- a/src/main/java/org/dita/dost/log/AbstractLogger.java +++ b/src/main/java/org/dita/dost/log/AbstractLogger.java @@ -33,7 +33,9 @@ public abstract class AbstractLogger extends MarkerIgnoringBase implements DITAO protected int msgOutputLevel = Project.MSG_DEBUG; protected boolean useColor; - protected boolean legacyFormat = Configuration.configuration.getOrDefault("cli.log-format", "fancy").equals("legacy"); + protected boolean legacyFormat = Configuration.configuration + .getOrDefault("cli.log-format", "legacy") + .equals("legacy"); public void setOutputLevel(final int msgOutputLevel) { this.msgOutputLevel = msgOutputLevel; diff --git a/src/main/java/org/dita/dost/util/Configuration.java b/src/main/java/org/dita/dost/util/Configuration.java index bb875911e2..4e0a17dc96 100644 --- a/src/main/java/org/dita/dost/util/Configuration.java +++ b/src/main/java/org/dita/dost/util/Configuration.java @@ -113,7 +113,10 @@ public final class Configuration { } // Override with system properties for (var systemProperty : new String[] { "cli.log-format", "cli.color" }) { - c.put(systemProperty, System.getProperty(systemProperty, c.get(systemProperty))); + var value = System.getProperty(systemProperty); + if (value != null) { + c.put(systemProperty, value); + } } configuration = Collections.unmodifiableMap(c); diff --git a/src/test/resources/configuration.properties b/src/test/resources/configuration.properties index 0f0e089f4e..906f05284d 100644 --- a/src/test/resources/configuration.properties +++ b/src/test/resources/configuration.properties @@ -4,6 +4,8 @@ otversion = 1.2.3 chunk.id-generation-scheme = counter default.cascade = nomerge temp-file-name-scheme = org.dita.dost.module.reader.DefaultTempFileScheme +cli.color = false +cli.log-format = legacy # Integration plugindirs = plugins;demo From 725b4b629923b8f8ad7a36953049fbde0b579103 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Fri, 8 Dec 2023 10:05:16 +0200 Subject: [PATCH 34/36] Refactor to clean up Signed-off-by: Jarno Elovirta --- .../org/dita/dost/platform/Integrator.java | 137 ++++++++---------- 1 file changed, 61 insertions(+), 76 deletions(-) diff --git a/src/main/java/org/dita/dost/platform/Integrator.java b/src/main/java/org/dita/dost/platform/Integrator.java index 26af5fb5ed..0c890ee8da 100644 --- a/src/main/java/org/dita/dost/platform/Integrator.java +++ b/src/main/java/org/dita/dost/platform/Integrator.java @@ -174,20 +174,10 @@ public void execute() throws Exception { // Read the properties file, if it exists. properties = new Properties(); if (propertiesFile != null) { - FileInputStream propertiesStream = null; - try { - propertiesStream = new FileInputStream(propertiesFile); + try (InputStream propertiesStream = Files.newInputStream(propertiesFile.toPath())) { properties.load(propertiesStream); } catch (final Exception e) { throw new RuntimeException(e); - } finally { - if (propertiesStream != null) { - try { - propertiesStream.close(); - } catch (final IOException e) { - logger.error(e.getMessage(), e); - } - } } } else { properties.putAll(Configuration.configuration); @@ -351,39 +341,11 @@ private void integrate() throws Exception { } configuration.putAll(getParserConfiguration()); - OutputStream out = null; - try { - final File outFile = new File( - ditaDir, - CONFIG_DIR + File.separator + getClass().getPackage().getName() + File.separator + GEN_CONF_PROPERTIES - ); - if (!(outFile.getParentFile().exists()) && !outFile.getParentFile().mkdirs()) { - throw new RuntimeException("Failed to make directory " + outFile.getParentFile().getAbsolutePath()); - } - logger.trace("Generate configuration properties {}", outFile.getPath()); - out = new BufferedOutputStream(new FileOutputStream(outFile)); - configuration.store(out, "DITA-OT runtime configuration, do not edit manually"); - } catch (final Exception e) { - throw new RuntimeException("Failed to write configuration properties: " + e.getMessage(), e); - } finally { - if (out != null) { - try { - out.close(); - } catch (final IOException e) { - logger.error(e.getMessage(), e); - } - } - } - - // Write messages properties - final Properties messages = readMessageBundle(); - final File messagesFile = ditaDir.toPath().resolve(CONFIG_DIR).resolve("messages_en_US.properties").toFile(); - try (final OutputStream messagesOut = new FileOutputStream(messagesFile)) { - messages.store(messagesOut, null); - } + writePluginProperties(configuration); + writeMessageBundle(); final Collection jars = featureTable.containsKey(FEAT_LIB_EXTENSIONS) - ? relativize(new LinkedHashSet<>(featureTable.get(FEAT_LIB_EXTENSIONS))) + ? relativize(featureTable.get(FEAT_LIB_EXTENSIONS)) : Collections.emptySet(); writeEnvShell(jars); writeEnvBatch(jars); @@ -396,6 +358,34 @@ private void integrate() throws Exception { customIntegration(); } + private void writeMessageBundle() throws IOException, XMLStreamException { + // Write messages properties + final Properties messages = readMessageBundle(); + final Path messagesFile = ditaDir.toPath().resolve(CONFIG_DIR).resolve("messages_en_US.properties"); + try (final OutputStream messagesOut = Files.newOutputStream(messagesFile)) { + messages.store(messagesOut, null); + } + } + + private void writePluginProperties(Properties configuration) { + final Path outFile = ditaDir + .toPath() + .resolve(CONFIG_DIR) + .resolve(getClass().getPackage().getName()) + .resolve(GEN_CONF_PROPERTIES); + try { + Files.createDirectories(outFile.getParent()); + } catch (IOException e) { + throw new RuntimeException("Failed to make directory " + outFile.getParent()); + } + logger.trace("Generate configuration properties {}", outFile); + try (OutputStream out = Files.newOutputStream(outFile)) { + configuration.store(out, "DITA-OT runtime configuration, do not edit manually"); + } catch (final Exception e) { + throw new RuntimeException("Failed to write configuration properties: " + e.getMessage(), e); + } + } + /** * Create legacy configuration JAR. The configuration JAR is used by e.g. DITA-OT Gradle plug-in so we have to keep on * generating it. @@ -440,9 +430,9 @@ private Properties readMessageBundle() throws IOException, XMLStreamException { final Properties messages = new Properties(); // final Path basePluginDir = pluginTable.get("org.dita.base").getPluginDir().toPath(); // final File messagesXmlFile = basePluginDir.resolve(CONFIG_DIR).resolve("messages.xml").toFile(); - final File messagesXmlFile = ditaDir.toPath().resolve(CONFIG_DIR).resolve("messages.xml").toFile(); - if (messagesXmlFile.exists()) { - try (final InputStream in = new FileInputStream(messagesXmlFile)) { + final Path messagesXmlFile = ditaDir.toPath().resolve(CONFIG_DIR).resolve("messages.xml"); + if (Files.exists(messagesXmlFile)) { + try (final InputStream in = Files.newInputStream(messagesXmlFile)) { final XMLStreamReader src = XMLInputFactory.newInstance().createXMLStreamReader(new StreamSource(in)); String id = null; final StringBuilder buf = new StringBuilder(); @@ -553,28 +543,28 @@ private Map getParserConfiguration() { } private Collection relativize(final Collection src) { - final Collection res = new ArrayList<>(src.size()); final File base = new File(ditaDir, "dummy"); - for (final Value lib : src) { - final File libFile = toFile(lib.value()); - if (!libFile.exists()) { - throw new IllegalArgumentException("Library file not found: " + libFile.getAbsolutePath()); - } - res.add(FileUtils.getRelativePath(base, libFile)); - } - return res; + return src + .stream() + .map(lib -> toFile(lib.value())) + .map(libFile -> { + if (!libFile.exists()) { + throw new IllegalArgumentException("Library file not found: " + libFile.getAbsolutePath()); + } + return FileUtils.getRelativePath(base, libFile); + }) + .toList(); } private void writeEnvShell(final Collection jars) { - Writer out = null; + final Path outFile = ditaDir.toPath().resolve(CONFIG_DIR).resolve("env.sh"); try { - final File outFile = new File(ditaDir, CONFIG_DIR + File.separator + "env.sh"); - if (!(outFile.getParentFile().exists()) && !outFile.getParentFile().mkdirs()) { - throw new RuntimeException("Failed to make directory " + outFile.getParentFile().getAbsolutePath()); - } - logger.trace("Generate environment shell " + outFile.getPath()); - out = new BufferedWriter(new FileWriter(outFile)); - + Files.createDirectories(outFile.getParent()); + } catch (IOException e) { + throw new RuntimeException("Failed to make directory " + outFile.getParent()); + } + logger.trace("Generate environment shell {}", outFile); + try (Writer out = Files.newBufferedWriter(outFile)) { out.write("#!/bin/sh\n"); for (final File relativeLib : jars) { out.write("CLASSPATH=\"$CLASSPATH:"); @@ -585,27 +575,24 @@ private void writeEnvShell(final Collection jars) { out.write("\"\n"); } try { - Files.setPosixFilePermissions(outFile.toPath(), PERMISSIONS); + Files.setPosixFilePermissions(outFile, PERMISSIONS); } catch (final UnsupportedOperationException e) { // not supported } } catch (final IOException e) { throw new RuntimeException("Failed to write environment shell: " + e.getMessage(), e); - } finally { - closeQuietly(out); } } private void writeEnvBatch(final Collection jars) { - Writer out = null; + final Path outFile = ditaDir.toPath().resolve(CONFIG_DIR).resolve("env.bat"); try { - final File outFile = new File(ditaDir, CONFIG_DIR + File.separator + "env.bat"); - if (!(outFile.getParentFile().exists()) && !outFile.getParentFile().mkdirs()) { - throw new RuntimeException("Failed to make directory " + outFile.getParentFile().getAbsolutePath()); - } - logger.trace("Generate environment batch " + outFile.getPath()); - out = new BufferedWriter(new FileWriter(outFile)); - + Files.createDirectories(outFile.getParent()); + } catch (IOException e) { + throw new RuntimeException("Failed to make directory " + outFile.getParent()); + } + logger.trace("Generate environment batch {}", outFile); + try (Writer out = Files.newBufferedWriter(outFile)) { for (final File relativeLib : jars) { out.write("set \"CLASSPATH=%CLASSPATH%;"); if (!relativeLib.isAbsolute()) { @@ -614,11 +601,9 @@ private void writeEnvBatch(final Collection jars) { out.write(relativeLib.toString().replace(File.separator, WINDOWS_SEPARATOR)); out.write("\"\r\n"); } - outFile.setExecutable(true); + outFile.toFile().setExecutable(true); } catch (final IOException e) { throw new RuntimeException("Failed to write environment batch: " + e.getMessage(), e); - } finally { - closeQuietly(out); } } From 0af6898ab0522af67ec96e24d79db206e6e41150 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Fri, 8 Dec 2023 11:05:58 +0200 Subject: [PATCH 35/36] Add deprecation warning to integration.xml Signed-off-by: Jarno Elovirta --- src/main/integrator.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/integrator.xml b/src/main/integrator.xml index dc16e6015d..4e7bfead5c 100644 --- a/src/main/integrator.xml +++ b/src/main/integrator.xml @@ -7,6 +7,8 @@ Copyright 2006 IBM Corporation See the accompanying LICENSE file for applicable license. --> + + WARN: Ant integrator has been deprecated. Use dita command instead. From 2391482197a55ebe76fc912fd8f32e12e19af9a8 Mon Sep 17 00:00:00 2001 From: Jarno Elovirta Date: Sun, 10 Dec 2023 11:15:54 +0200 Subject: [PATCH 36/36] Apply suggestions from code review Co-authored-by: Roger Sheen Signed-off-by: Jarno Elovirta --- src/main/integrator.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/integrator.xml b/src/main/integrator.xml index 4e7bfead5c..85f61c329a 100644 --- a/src/main/integrator.xml +++ b/src/main/integrator.xml @@ -8,7 +8,7 @@ See the accompanying LICENSE file for applicable license. --> - WARN: Ant integrator has been deprecated. Use dita command instead. + WARN: The Ant integration process is deprecated. Use the 'dita install' command instead.