From 9e3a7c642de98b636c18fd199f390ed445a8721e Mon Sep 17 00:00:00 2001
From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com.>
Date: Wed, 2 Jul 2025 14:06:03 +0100
Subject: [PATCH] test
---
.../service/CustomPDFDocumentFactory.java | 10 +--
.../software/common/util/ExceptionUtils.java | 17 +++++
.../software/common/util/PdfUtils.java | 3 +-
.../api/filters/FilterController.java | 9 +--
.../main/resources/messages_en_GB.properties | 1 +
.../main/resources/static/js/downloader.js | 20 ++----
.../resources/static/js/messageFormatter.js | 66 +++++++++++++++++++
.../resources/templates/fragments/common.html | 1 +
8 files changed, 103 insertions(+), 24 deletions(-)
create mode 100644 stirling-pdf/src/main/resources/static/js/messageFormatter.js
diff --git a/common/src/main/java/stirling/software/common/service/CustomPDFDocumentFactory.java b/common/src/main/java/stirling/software/common/service/CustomPDFDocumentFactory.java
index 24d1f4255..25b3d9501 100644
--- a/common/src/main/java/stirling/software/common/service/CustomPDFDocumentFactory.java
+++ b/common/src/main/java/stirling/software/common/service/CustomPDFDocumentFactory.java
@@ -83,7 +83,7 @@ public class CustomPDFDocumentFactory {
*/
public PDDocument load(File file, boolean readOnly) throws IOException {
if (file == null) {
- throw new IllegalArgumentException("File cannot be null");
+ throw ExceptionUtils.createNullArgumentException("File");
}
long fileSize = file.length();
@@ -110,7 +110,7 @@ public class CustomPDFDocumentFactory {
*/
public PDDocument load(Path path, boolean readOnly) throws IOException {
if (path == null) {
- throw new IllegalArgumentException("File cannot be null");
+ throw ExceptionUtils.createNullArgumentException("File");
}
long fileSize = Files.size(path);
@@ -131,7 +131,7 @@ public class CustomPDFDocumentFactory {
/** Load a PDF from byte array with automatic optimization and read-only option. */
public PDDocument load(byte[] input, boolean readOnly) throws IOException {
if (input == null) {
- throw new IllegalArgumentException("Input bytes cannot be null");
+ throw ExceptionUtils.createNullArgumentException("Input bytes");
}
long dataSize = input.length;
@@ -152,7 +152,7 @@ public class CustomPDFDocumentFactory {
/** Load a PDF from InputStream with automatic optimization and read-only option. */
public PDDocument load(InputStream input, boolean readOnly) throws IOException {
if (input == null) {
- throw new IllegalArgumentException("InputStream cannot be null");
+ throw ExceptionUtils.createNullArgumentException("InputStream");
}
// Since we don't know the size upfront, buffer to a temp file
@@ -175,7 +175,7 @@ public class CustomPDFDocumentFactory {
public PDDocument load(InputStream input, String password, boolean readOnly)
throws IOException {
if (input == null) {
- throw new IllegalArgumentException("InputStream cannot be null");
+ throw ExceptionUtils.createNullArgumentException("InputStream");
}
// Since we don't know the size upfront, buffer to a temp file
diff --git a/common/src/main/java/stirling/software/common/util/ExceptionUtils.java b/common/src/main/java/stirling/software/common/util/ExceptionUtils.java
index 5e5dabf86..331587b13 100644
--- a/common/src/main/java/stirling/software/common/util/ExceptionUtils.java
+++ b/common/src/main/java/stirling/software/common/util/ExceptionUtils.java
@@ -137,6 +137,23 @@ public class ExceptionUtils {
return new TranslatableException(message, messageKey, args);
}
+ /** Create common validation exceptions with translation support. */
+ public static IllegalArgumentException createInvalidArgumentException(String argumentName) {
+ return createIllegalArgumentException(
+ "error.invalidArgument", "Invalid argument: {0}", argumentName);
+ }
+
+ public static IllegalArgumentException createInvalidArgumentException(
+ String argumentName, String value) {
+ return createIllegalArgumentException(
+ "error.invalidFormat", "Invalid {0} format: {1}", argumentName, value);
+ }
+
+ public static IllegalArgumentException createNullArgumentException(String argumentName) {
+ return createIllegalArgumentException(
+ "error.argumentRequired", "{0} must not be null", argumentName);
+ }
+
/** Create file validation exceptions. */
public static IllegalArgumentException createHtmlFileRequiredException() {
return createIllegalArgumentException(
diff --git a/common/src/main/java/stirling/software/common/util/PdfUtils.java b/common/src/main/java/stirling/software/common/util/PdfUtils.java
index 3b1d901d7..5c5bda000 100644
--- a/common/src/main/java/stirling/software/common/util/PdfUtils.java
+++ b/common/src/main/java/stirling/software/common/util/PdfUtils.java
@@ -571,8 +571,7 @@ public class PdfUtils {
case "less":
return actualPageCount < pageCount;
default:
- throw new IllegalArgumentException(
- "Invalid comparator. Only 'greater', 'equal', and 'less' are supported.");
+ throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
}
}
diff --git a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java
index 96322ad80..ce9dab8c5 100644
--- a/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java
+++ b/stirling-pdf/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java
@@ -25,6 +25,7 @@ import stirling.software.SPDF.model.api.filter.FileSizeRequest;
import stirling.software.SPDF.model.api.filter.PageRotationRequest;
import stirling.software.SPDF.model.api.filter.PageSizeRequest;
import stirling.software.common.service.CustomPDFDocumentFactory;
+import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.PdfUtils;
import stirling.software.common.util.WebResponseUtils;
@@ -96,7 +97,7 @@ public class FilterController {
valid = actualPageCount < pageCount;
break;
default:
- throw new IllegalArgumentException("Invalid comparator: " + comparator);
+ throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
}
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
@@ -139,7 +140,7 @@ public class FilterController {
valid = actualArea < standardArea;
break;
default:
- throw new IllegalArgumentException("Invalid comparator: " + comparator);
+ throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
}
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
@@ -172,7 +173,7 @@ public class FilterController {
valid = actualFileSize < fileSize;
break;
default:
- throw new IllegalArgumentException("Invalid comparator: " + comparator);
+ throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
}
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
@@ -208,7 +209,7 @@ public class FilterController {
valid = actualRotation < rotation;
break;
default:
- throw new IllegalArgumentException("Invalid comparator: " + comparator);
+ throw ExceptionUtils.createInvalidArgumentException("comparator", comparator);
}
if (valid) return WebResponseUtils.multiPartFileToWebResponse(inputFile);
diff --git a/stirling-pdf/src/main/resources/messages_en_GB.properties b/stirling-pdf/src/main/resources/messages_en_GB.properties
index 2b1039e30..d95103f75 100644
--- a/stirling-pdf/src/main/resources/messages_en_GB.properties
+++ b/stirling-pdf/src/main/resources/messages_en_GB.properties
@@ -225,6 +225,7 @@ error.pathTraversalDetected=Path traversal detected for security reasons.
# Validation messages
error.invalidArgument=Invalid argument: {0}
+error.argumentRequired={0} must not be null
error.operationFailed=Operation failed: {0}
error.angleNotMultipleOf90=Angle must be a multiple of 90
error.pdfBookmarksNotFound=No PDF bookmarks/outline found in document
diff --git a/stirling-pdf/src/main/resources/static/js/downloader.js b/stirling-pdf/src/main/resources/static/js/downloader.js
index 5c13f9e23..a393ee8b3 100644
--- a/stirling-pdf/src/main/resources/static/js/downloader.js
+++ b/stirling-pdf/src/main/resources/static/js/downloader.js
@@ -348,19 +348,13 @@
// Handle structured error response with translation support
let displayMessage = json.message;
- // If translation info is available, use it to translate the message
- if (json.translationKey && window.stirlingPDF && window.stirlingPDF.translations) {
- const translatedTemplate = window.stirlingPDF.translations[json.translationKey];
- if (translatedTemplate) {
- displayMessage = translatedTemplate;
-
- // Replace placeholders with args if available
- if (json.translationArgs && Array.isArray(json.translationArgs)) {
- json.translationArgs.forEach((arg, index) => {
- displayMessage = displayMessage.replace(`{${index}}`, arg);
- });
- }
- }
+ // If translation info is available, use MessageFormatter to translate
+ if (json.translationKey && window.MessageFormatter) {
+ displayMessage = window.MessageFormatter.translate(
+ json.translationKey,
+ json.translationArgs,
+ json.message // fallback to original message
+ );
}
showErrorBanner((json.error || 'Error') + ': ' + displayMessage, json.trace || '');
diff --git a/stirling-pdf/src/main/resources/static/js/messageFormatter.js b/stirling-pdf/src/main/resources/static/js/messageFormatter.js
new file mode 100644
index 000000000..cf86b4aaf
--- /dev/null
+++ b/stirling-pdf/src/main/resources/static/js/messageFormatter.js
@@ -0,0 +1,66 @@
+/**
+ * Utility for formatting internationalized messages with placeholder replacement.
+ * Supports the {0}, {1}, {2}... placeholder format used by Java MessageFormat.
+ */
+window.MessageFormatter = (function() {
+ 'use strict';
+
+ /**
+ * Format a message template by replacing {0}, {1}, etc. placeholders with provided arguments.
+ *
+ * @param {string} template - The message template with {0}, {1}, etc. placeholders
+ * @param {Array|string} args - Arguments to replace placeholders with. Can be array or individual arguments
+ * @returns {string} The formatted message with placeholders replaced
+ *
+ * @example
+ * formatMessage("Hello {0}, you have {1} messages", ["John", 5])
+ * // Returns: "Hello John, you have 5 messages"
+ *
+ * formatMessage("Error {0}: {1}", "404", "Not Found")
+ * // Returns: "Error 404: Not Found"
+ */
+ function formatMessage(template, ...args) {
+ if (!template || typeof template !== 'string') {
+ return template || '';
+ }
+
+ // Handle case where first argument is an array
+ const argumentArray = Array.isArray(args[0]) ? args[0] : args;
+
+ // Replace {0}, {1}, {2}, etc. with corresponding arguments
+ return template.replace(/\{(\d+)\}/g, function(match, index) {
+ const argIndex = parseInt(index, 10);
+ return argumentArray[argIndex] !== undefined && argumentArray[argIndex] !== null
+ ? String(argumentArray[argIndex])
+ : match; // Keep original placeholder if no argument provided
+ });
+ }
+
+ /**
+ * Translate and format an error message using the global translation object.
+ * Falls back to the provided fallback message if translation not found.
+ *
+ * @param {string} translationKey - The translation key (e.g., "error.dpiExceedsLimit")
+ * @param {Array} translationArgs - Arguments for placeholder replacement
+ * @param {string} fallbackMessage - Fallback message if translation not found
+ * @returns {string} The translated and formatted message
+ */
+ function translateAndFormat(translationKey, translationArgs, fallbackMessage) {
+ if (!window.stirlingPDF || !window.stirlingPDF.translations) {
+ return fallbackMessage || translationKey;
+ }
+
+ const template = window.stirlingPDF.translations[translationKey];
+ if (!template) {
+ return fallbackMessage || translationKey;
+ }
+
+ return formatMessage(template, translationArgs || []);
+ }
+
+ // Public API
+ return {
+ format: formatMessage,
+ translate: translateAndFormat
+ };
+})();
\ No newline at end of file
diff --git a/stirling-pdf/src/main/resources/templates/fragments/common.html b/stirling-pdf/src/main/resources/templates/fragments/common.html
index c7b9b56bb..47cafff5c 100644
--- a/stirling-pdf/src/main/resources/templates/fragments/common.html
+++ b/stirling-pdf/src/main/resources/templates/fragments/common.html
@@ -86,6 +86,7 @@
+