This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new d63455807a StringFormatter class
d63455807a is described below
commit d63455807acab26ea88bdf27153730009ac2681d
Author: James Bognar <[email protected]>
AuthorDate: Sat Nov 29 09:47:17 2025 -0500
StringFormatter class
---
.../java/org/apache/juneau/bean/LinkString.java | 2 +-
.../org/apache/juneau/assertions/Assertion.java | 2 +-
.../juneau/common/reflect/ExecutableInfo.java | 4 +-
.../apache/juneau/common/utils/FormatString.java | 724 ---------------------
.../apache/juneau/common/utils/StringFormat.java | 8 +-
.../apache/juneau/common/utils/ThrowableUtils.java | 16 +-
.../java/org/apache/juneau/BasicException.java | 3 +-
.../org/apache/juneau/BasicRuntimeException.java | 7 +-
.../main/java/org/apache/juneau/BeanSession.java | 2 +-
.../java/org/apache/juneau/ContextSession.java | 2 +-
.../main/java/org/apache/juneau/cp/Messages.java | 2 +-
.../org/apache/juneau/parser/ParseException.java | 2 +-
.../org/apache/juneau/parser/ParserListener.java | 5 +-
.../juneau/serializer/SerializeException.java | 2 +-
.../juneau/serializer/SerializerListener.java | 3 +-
.../juneau/serializer/SerializerSession.java | 2 +-
.../apache/juneau/microservice/Microservice.java | 2 +-
.../juneau/microservice/jetty/JettyLogger.java | 18 +-
.../org/apache/juneau/rest/client/RestClient.java | 12 +-
.../juneau/rest/mock/MockServletRequest.java | 2 +-
.../org/apache/juneau/rest/arg/ArgException.java | 3 +-
.../org/apache/juneau/rest/servlet/RestObject.java | 4 +-
.../apache/juneau/rest/servlet/RestServlet.java | 4 +-
.../juneau/rest/swagger/SwaggerException.java | 3 +-
.../test/java/org/apache/juneau/ComboInput.java | 2 +-
.../a/rttests/RoundTripLargeObjects_Test.java | 3 +-
.../juneau/common/utils/FormatString_Test.java | 416 ------------
.../org/apache/juneau/common/utils/Utils_Test.java | 4 +-
.../apache/juneau/rest/annotation/Path_Test.java | 15 +-
.../client/RestClient_Config_RestClient_Test.java | 2 +-
30 files changed, 59 insertions(+), 1217 deletions(-)
diff --git
a/juneau-bean/juneau-bean-common/src/main/java/org/apache/juneau/bean/LinkString.java
b/juneau-bean/juneau-bean-common/src/main/java/org/apache/juneau/bean/LinkString.java
index 2cc51dec96..fb23f0218e 100644
---
a/juneau-bean/juneau-bean-common/src/main/java/org/apache/juneau/bean/LinkString.java
+++
b/juneau-bean/juneau-bean-common/src/main/java/org/apache/juneau/bean/LinkString.java
@@ -152,7 +152,7 @@ public class LinkString implements Comparable<LinkString> {
} catch (SchemaValidationException | SerializeException
e) {
throw toRex(e);
}
- this.uri = java.net.URI.create(mformat(value, args));
+ this.uri = java.net.URI.create(f(value, args));
return this;
}
diff --git
a/juneau-core/juneau-assertions/src/main/java/org/apache/juneau/assertions/Assertion.java
b/juneau-core/juneau-assertions/src/main/java/org/apache/juneau/assertions/Assertion.java
index e884dda1fc..17b10eacf7 100644
---
a/juneau-core/juneau-assertions/src/main/java/org/apache/juneau/assertions/Assertion.java
+++
b/juneau-core/juneau-assertions/src/main/java/org/apache/juneau/assertions/Assertion.java
@@ -215,7 +215,7 @@ public class Assertion {
protected BasicAssertionError error(Throwable cause, String msg,
Object...args) {
msg = mformat(msg, args);
if (nn(this.msg))
- msg = mformat(this.msg,
this.msgArgs).replace("<<<MSG>>>", msg);
+ msg = f(this.msg, this.msgArgs).replace("<<<MSG>>>",
msg);
if (nn(out))
out.println(msg);
if (nn(throwable)) {
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
index 967d3541e0..515af99dc0 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/reflect/ExecutableInfo.java
@@ -753,9 +753,9 @@ public abstract class ExecutableInfo extends AccessibleInfo
{
private void checkIndex(int index) {
int pc = getParameterCount();
if (pc == 0)
- throw new IndexOutOfBoundsException(mformat("Invalid
index ''{0}''. No parameters.", index));
+ throw new IndexOutOfBoundsException(f("Invalid index
''{0}''. No parameters.", index));
if (index < 0 || index >= pc)
- throw new IndexOutOfBoundsException(mformat("Invalid
index ''{0}''. Parameter count: {1}", index, pc));
+ throw new IndexOutOfBoundsException(f("Invalid index
''{0}''. Parameter count: {1}", index, pc));
}
private List<ParameterInfo> findParameters() {
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/FormatString.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/FormatString.java
deleted file mode 100644
index d3a2e9dc69..0000000000
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/FormatString.java
+++ /dev/null
@@ -1,724 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.common.utils;
-
-import static org.apache.juneau.common.utils.AssertionUtils.*;
-import static org.apache.juneau.common.utils.CollectionUtils.*;
-import static org.apache.juneau.common.utils.StateEnum.*;
-import static org.apache.juneau.common.utils.Utils.*;
-
-import java.text.*;
-import java.util.*;
-import java.util.stream.*;
-import java.util.MissingFormatArgumentException;
-
-import org.apache.juneau.common.collections.*;
-
-/**
- * Unified string formatter supporting both MessageFormat-style and
printf-style formatting in the same pattern.
- *
- * <p>
- * This class provides a thread-safe, cacheable formatter that can handle
mixed format styles in a single pattern.
- * It supports both MessageFormat syntax (<js>"{0}"</js>,
<js>"{1,number}"</js>) and printf syntax (<js>"%s"</js>, <js>"%d"</js>)
- * within the same string.
- *
- * <h5 class='section'>Features:</h5>
- * <ul>
- * <li><b>Dual Format Support:</b> Mix MessageFormat and printf-style
placeholders in the same pattern</li>
- * <li><b>Thread-Safe:</b> Immutable class, safe for concurrent use</li>
- * <li><b>Cacheable:</b> Use {@link #of(String)} for cached instances</li>
- * <li><b>Argument Sharing:</b> Both format styles share the same argument
array</li>
- * </ul>
- *
- * <h5 class='section'>Format Style Detection:</h5>
- * <p>
- * The formatter automatically detects which style to use for each placeholder:
- * <ul>
- * <li><b>MessageFormat style:</b> <js>"{0}"</js>, <js>"{1,number}"</js>,
<js>"{2,date}"</js>, etc.</li>
- * <li><b>Printf style:</b> <js>"%s"</js>, <js>"%d"</js>, <js>"%.2f"</js>,
<js>"%1$s"</js>, etc.</li>
- * </ul>
- *
- * <h5 class='section'>Argument Mapping:</h5>
- * <p>
- * Arguments are processed in order of appearance:
- * <ul>
- * <li><b>MessageFormat placeholders:</b> Use explicit indices (e.g.,
<js>"{0}"</js> uses <c>args[0]</c>)</li>
- * <li><b>Printf placeholders:</b> Use sequential indices starting after the
highest MessageFormat index</li>
- * </ul>
- *
- * <h5 class='section'>Examples:</h5>
- * <p class='bjava'>
- * <jc>// Mixed format styles</jc>
- * FormatString <jv>fmt</jv> = FormatString.<jsm>of</jsm>(<js>"Hello {0},
you have %d items"</js>);
- * String <jv>result</jv> =
<jv>fmt</jv>.<jsm>format</jsm>(<js>"John"</js>, 5);
- * <jc>// Returns: "Hello John, you have 5 items"</jc>
- *
- * <jc>// MessageFormat with explicit indices, printf with sequential</jc>
- * FormatString <jv>fmt2</jv> = FormatString.<jsm>of</jsm>(<js>"User {0}
has %s and {1} items"</js>);
- * String <jv>result2</jv> =
<jv>fmt2</jv>.<jsm>format</jsm>(<js>"Alice"</js>, 10, <js>"admin"</js>);
- * <jc>// Returns: "User Alice has admin and 10 items"</jc>
- * <jc>// {0} -> "Alice", {1} -> 10, %s -> "admin"</jc>
- *
- * <jc>// Printf with explicit indices</jc>
- * FormatString <jv>fmt3</jv> = FormatString.<jsm>of</jsm>(<js>"%1$s loves
%2$s, and {0} also loves %3$s"</js>);
- * String <jv>result3</jv> =
<jv>fmt3</jv>.<jsm>format</jsm>(<js>"Alice"</js>, <js>"Bob"</js>,
<js>"Charlie"</js>);
- * <jc>// Returns: "Alice loves Bob, and Alice also loves Charlie"</jc>
- * </p>
- *
- * <h5 class='section'>Caching:</h5>
- * <p>
- * Use {@link #of(String)} to get cached instances. The cache is thread-safe
and limited to 1000 entries.
- * For uncached instances, use the constructor directly.
- * </p>
- *
- * <h5 class='section'>Thread Safety:</h5>
- * <p>
- * This class is immutable and thread-safe. Multiple threads can safely use
the same instance concurrently.
- * </p>
- *
- * @see StringUtils#format(String, Object...)
- * @see StringUtils#mformat(String, Object...)
- */
-public final class FormatString {
-
- private static final Cache<String,FormatString> CACHE =
Cache.of(String.class, FormatString.class).maxSize(1000).weak().build();
-
- private static final Cache2<Locale,String,MessageFormat>
MESSAGE_FORMAT_CACHE = Cache2.of(Locale.class, String.class,
MessageFormat.class).maxSize(100).threadLocal().weak()
- .supplier((locale, content) -> new MessageFormat(content,
locale)).build();
-
- private static final Cache<Locale,NumberFormat> NUMBER_FORMAT_CACHE =
Cache.of(Locale.class,
NumberFormat.class).maxSize(50).threadLocal().weak().supplier(NumberFormat::getInstance).build();
-
- private static final Cache<Locale,DateFormat> DATE_FORMAT_CACHE =
Cache.of(Locale.class, DateFormat.class).maxSize(50).threadLocal().weak()
- .supplier(locale ->
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT,
locale)).build();
-
- private static final AsciiSet PRINTF_CONVERSION_CHARS =
AsciiSet.of("bBhHsScCdoxXeEfgGaAtTn%");
- private static final AsciiSet PRINTF_FORMAT_CHARS = AsciiSet.of("-+
0(#.*$");
- private static final AsciiSet FORMAT_CHARS = AsciiSet.of("%{'");
-
- /**
- * Formats a pattern string with the given arguments using the default
locale.
- *
- * <p>
- * This is a convenience method that creates a FormatString instance
and formats it.
- * If the pattern contains no format specifiers (no '%', '{', or '''),
the pattern is returned as-is.
- *
- * @param pattern The format pattern.
- * @param args The arguments to format.
- * @return The formatted string.
- * @throws IllegalArgumentException If the pattern is <jk>null</jk> or
format specifiers are invalid.
- */
- public static String format(String pattern, Object...args) {
- if (!hasArgs(pattern))
- return pattern;
- return of(pattern).format(args);
- }
-
- /**
- * Formats a pattern string with the given arguments using the
specified locale.
- *
- * <p>
- * This is a convenience method that creates a FormatString instance
and formats it.
- * If the pattern contains no format specifiers (no '%', '{', or '''),
the pattern is returned as-is.
- *
- * @param pattern The format pattern.
- * @param locale The locale to use for formatting. If <jk>null</jk>,
uses the default locale.
- * @param args The arguments to format.
- * @return The formatted string.
- * @throws IllegalArgumentException If the pattern is <jk>null</jk> or
format specifiers are invalid.
- */
- public static String format(String pattern, Locale locale,
Object...args) {
- if (!hasArgs(pattern))
- return pattern;
- return of(pattern).format(locale, args);
- }
-
- /**
- * Checks if a pattern contains any format specifiers.
- *
- * <p>
- * Returns <jk>true</jk> if the pattern contains any of the following
characters:
- * <ul>
- * <li><js>'%'</js> - Printf-style format specifier</li>
- * <li><js>'{'</js> - MessageFormat-style placeholder</li>
- * <li><js>'''</js> - MessageFormat quote</li>
- * </ul>
- *
- * <p>
- * Uses {@link AsciiSet} for efficient single-pass scanning of the
string.
- *
- * @param pattern The pattern to check.
- * @return <jk>true</jk> if the pattern contains format specifiers,
<jk>false</jk> otherwise.
- */
- private static boolean hasArgs(String pattern) {
- if (pattern == null || pattern.isEmpty())
- return false;
- for (var i = 0; i < pattern.length(); i++) {
- if (FORMAT_CHARS.contains(pattern.charAt(i)))
- return true;
- }
- return false;
- }
-
- /**
- * Base class for format tokens.
- */
- private abstract static class Token {
- /**
- * Appends the formatted content to the StringBuilder.
- *
- * @param sb The StringBuilder to append to.
- * @param args The arguments array.
- * @param locale The locale for formatting (can be null for
default).
- */
- abstract void append(StringBuilder sb, Object[] args, Locale
locale);
- }
-
- /**
- * Literal text token.
- */
- private static final class LiteralToken extends Token {
- private final String text;
-
- LiteralToken(String text) {
- this.text = text;
- }
-
- @Override
- void append(StringBuilder sb, Object[] args, Locale locale) {
- sb.append(text);
- }
-
- @Override
- public String toString() {
- return "[L:" + text + "]";
- }
- }
-
- /**
- * MessageFormat-style token (e.g., {0}, {1,number}).
- */
- private static final class MessageFormatToken extends Token {
- private final char format;
- private final String content; // null for simple tokens,
normalized pattern like "{0,number}" for complex tokens
- private final int index; // 0-based index
- private final String placeholder; // Original placeholder text
like "{0}" or "{0,number}"
-
- /**
- * @param content - The variable content such as "{}" or "{0}",
or "{0,number}"
- * The content should have the curly-braces already
removed so that we're only looking at the inner parts.
- * @param index - The zero-based index of the variable in the
message (used for sequential {} placeholders).
- */
- MessageFormatToken(String content, int index) {
- if (content.isBlank()) {
- this.content = null;
- this.index = index;
- this.format = 's';
- this.placeholder = "{" + index + "}";
- } else if (content.indexOf(',') == -1) {
- this.content = null;
- this.index = parseIndexMF(content);
- this.format = 's';
- this.placeholder = "{" + this.index + "}";
- } else {
- var tokens = content.split(",", 2);
- this.index = parseIndexMF(tokens[0]);
- this.content = "{0," + tokens[1] + "}";
- this.format = 'o';
- this.placeholder = "{" + this.index + "," +
tokens[1] + "}";
- }
- }
-
- @Override
- void append(StringBuilder sb, Object[] args, Locale locale) {
- // MessageFormat inserts the placeholder text if
argument is missing
- if (args == null || index >= args.length || index < 0) {
- sb.append(placeholder);
- return;
- }
- var o = args[index];
- var l = locale == null ? Locale.getDefault() : locale;
- switch (format) {
- case 's':
- if (o == null) {
- sb.append("null");
- } else if (o instanceof Number o2) {
-
sb.append(NUMBER_FORMAT_CACHE.get(l).format(o2));
- } else if (o instanceof Date o2) {
-
sb.append(DATE_FORMAT_CACHE.get(l).format(o2));
- } else {
- sb.append(o.toString());
- }
- break;
- default:
- // Use Cache2 with Locale and content
as separate keys to avoid string concatenation
- var mf = MESSAGE_FORMAT_CACHE.get(l,
content);
- sb.append(mf.format(a(o)));
- break;
- }
- }
-
- @Override
- public String toString() {
- return "[M:" + format + index + (content == null ? "" :
(':' + content)) + "]";
- }
- }
-
- /**
- * Printf-style token (e.g., %s, %d, %.2f).
- */
- private static final class StringFormatToken extends Token {
- private final char format; // 's' = simple (handle directly),
'o' = other (use String.format)
- private final String content; // The format string to pass to
String.format (null for simple formats)
- private final int index; // 0-based index
-
- StringFormatToken(String content, int index) {
- // content is everything after '%' (e.g., "s", "1$s",
"d", ".2f", "1$.2f")
- var $ = content.indexOf('$');
- if ($ >= 0) {
- index = parseIndexSF(content.substring(0, $)) -
1;
- content = content.substring($ + 1);
- }
- this.format = content.length() == 1 ?
content.charAt(content.length() - 1) : 'z';
- this.index = index;
- this.content = "%" + content;
- }
-
- @Override
- void append(StringBuilder sb, Object[] args, Locale locale) {
- // String.format() throws
MissingFormatArgumentException when argument is missing
- if (args == null || index >= args.length || index < 0) {
- throw new
MissingFormatArgumentException(content);
- }
- var o = args[index];
- var l = locale == null ? Locale.getDefault() : locale;
- var dl = locale == null ||
locale.equals(Locale.getDefault());
- switch (format) {
- case 'b':
- // String.format() with %b converts:
- // - null -> "false"
- // - Boolean -> toString()
- // - Any other non-null value -> "true"
- if (o == null) {
- sb.append("false");
- } else if (o instanceof Boolean) {
- sb.append(o.toString());
- } else {
- sb.append("true");
- }
- return;
- case 'B':
- // String.format() with %B converts:
- // - null -> "FALSE"
- // - Boolean -> toString().toUpperCase()
- // - Any other non-null value -> "TRUE"
- if (o == null) {
- sb.append("FALSE");
- } else if (o instanceof Boolean) {
-
sb.append(o.toString().toUpperCase());
- } else {
- sb.append("TRUE");
- }
- return;
- case 's':
- if (o == null) {
- sb.append("null");
- return;
- }
- sb.append(o.toString());
- return;
- case 'S':
- if (o == null) {
- sb.append("NULL");
- return;
- }
- sb.append(o.toString().toUpperCase());
- return;
- case 'd':
- if (o == null) {
- sb.append("null");
- return;
- }
- if (o instanceof Number o2) {
- if (dl) {
- if (o instanceof
Integer || o instanceof Long || o instanceof Byte || o instanceof Short) {
- sb.append(o);
- } else {
- // For other
Number types (BigDecimal, BigInteger, etc.), convert to long
-
sb.append(o2.longValue());
- }
- return;
- }
- // For non-default locales, use
String.format to ensure printf-style consistency
- sb.append(sf(l, "%d", o));
- return;
- }
- break;
- case 'x':
- if (o == null) {
- sb.append("null");
- return;
- }
- if (o instanceof Integer o2) {
-
sb.append(Integer.toHexString(o2));
- return;
- } else if (o instanceof Long o2) {
- sb.append(Long.toHexString(o2));
- return;
- }
- break;
- case 'X':
- if (o == null) {
- sb.append("NULL");
- return;
- }
- if (o instanceof Integer o2) {
-
sb.append(Integer.toHexString(o2).toUpperCase());
- return;
- } else if (o instanceof Long o2) {
-
sb.append(Long.toHexString(o2).toUpperCase());
- return;
- }
- break;
- case 'o':
- if (o == null) {
- sb.append("null");
- return;
- }
- if (o instanceof Integer o2) {
-
sb.append(Integer.toOctalString(o2));
- return;
- } else if (o instanceof Long o2) {
-
sb.append(Long.toOctalString(o2));
- return;
- }
- break;
- case 'c':
- if (o == null) {
- sb.append("null");
- return;
- }
- if (o instanceof Character) {
- sb.append(o);
- return;
- } else if (o instanceof Integer o2) {
- sb.append((char)o2.intValue());
- return;
- }
- break;
- case 'C':
- if (o == null) {
- sb.append("NULL");
- return;
- }
- if (o instanceof Character o2) {
-
sb.append(Character.toUpperCase(o2));
- return;
- } else if (o instanceof Integer o2) {
-
sb.append(Character.toUpperCase((char)o2.intValue()));
- return;
- }
- break;
- case 'f':
- if (o == null) {
- sb.append("null");
- return;
- }
- // Always use String.format() to match
exact behavior (precision, etc.)
- if (o instanceof Number) {
- sb.append(sf(l, "%f", o));
- return;
- }
- break;
- default:
- break;
- }
-
- // Fallback to String.format for any other simple format
- sb.append(sf(l, content, o));
- }
-
- @Override
- public String toString() {
- return "[S:" + format + index + ":" + content + "]";
- }
- }
-
- private static String sf(Locale l, String s, Object o) {
- return String.format(l, s, a(o));
- }
-
- private final String pattern;
- private final Token[] tokens;
-
- /**
- * Creates a new FormatString instance.
- *
- * @param pattern The format pattern. Can contain both MessageFormat
and printf-style placeholders.
- * @throws IllegalArgumentException If the pattern is <jk>null</jk>.
- */
- public FormatString(String pattern) {
- this.pattern = assertArgNotNull("pattern", pattern);
- this.tokens = parseTokens(pattern).toArray(Token[]::new);
- }
-
- /**
- * Returns a cached FormatString instance for the given pattern.
- *
- * <p>
- * This method uses a thread-safe cache to avoid recreating
FormatString instances for the same pattern.
- * The cache is limited to 1000 entries.
- *
- * @param pattern The format pattern.
- * @return A cached or new FormatString instance.
- * @throws IllegalArgumentException If the pattern is <jk>null</jk>.
- */
- public static FormatString of(String pattern) {
- assertArgNotNull("pattern", pattern);
- return CACHE.get(pattern, () -> new FormatString(pattern));
- }
-
- /**
- * Formats the pattern with the given arguments using the default
locale.
- *
- * @param args The arguments to format.
- * @return The formatted string.
- * @throws IllegalArgumentException If format specifiers are invalid or
arguments don't match.
- */
- public String format(Object...args) {
- return format(Locale.getDefault(), args);
- }
-
- /**
- * Formats the pattern with the given arguments using the specified
locale.
- *
- * <p>
- * The locale affects both MessageFormat and printf-style formatting:
- * <ul>
- * <li><b>MessageFormat:</b> Locale-specific number, date, and time
formatting</li>
- * <li><b>Printf:</b> Locale-specific number formatting (decimal
separators, etc.)</li>
- * </ul>
- *
- * @param locale The locale to use for formatting. If <jk>null</jk>,
uses the default locale.
- * @param args The arguments to format.
- * @return The formatted string.
- * @throws IllegalArgumentException If format specifiers are invalid or
arguments don't match.
- */
- public String format(Locale locale, Object...args) {
- var sb = new StringBuilder(pattern.length() + 64);
- for (var token : tokens) {
- token.append(sb, args, locale);
- }
- return sb.toString();
- }
-
- /**
- * Parses the pattern into a list of tokens.
- */
- private static List<Token> parseTokens(String pattern) {
- var tokens = new ArrayList<Token>();
- var length = pattern.length();
- var i = 0;
- var sequentialIndex = 0; // 0-based index for sequential
placeholders
-
- // Possible String.format variable formats:
- // %[argument_index$][flags][width][.precision]conversion
- // %[argument_index$][flags][width]conversion
- // %[flags][width]conversion
-
- // Possible MessageFormat variable formats:
- // {}
- // {#,formatType}
- // {#,formatType,formatStyle}
-
- // S1 - In literal, looking for %, {, or '
- // S2 - Found %, looking for conversion char or t or T
- // S3 - Found {, looking for }
- // S4 - Found ', in quoted section (MessageFormat single quotes
escape special chars), looking for '
- var state = S1;
-
- var nestedBracketDepth = 0;
-
- var mark = 0;
- while (i < length) {
- var ch = pattern.charAt(i++);
-
- if (state == S1) {
- if (ch == '%') {
- lit(tokens, pattern, mark, i - 1);
- state = S2;
- mark = i;
- } else if (ch == '{') {
- lit(tokens, pattern, mark, i - 1);
- state = S3;
- mark = i - 1;
- } else if (ch == '\'') {
- lit(tokens, pattern, mark, i - 1);
- state = S4;
- mark = i;
- }
- } else if (state == S2) {
- if (ch == '%') {
- tokens.add(new LiteralToken("%"));
- state = S1;
- mark = i;
- } else if (ch == 't' || ch == 'T') {
- // Do nothing. Part of 2-character
time conversion.
- } else if
(PRINTF_CONVERSION_CHARS.contains(ch)) {
- sf(tokens, pattern, mark, i,
sequentialIndex++);
- state = S1;
- mark = i;
- } else if (PRINTF_FORMAT_CHARS.contains(ch) ||
Character.isDigit(ch)) {
- // Do nothing.
- } else {
- // Unknown character - could be invalid
conversion or end of format
- // Create StringFormatToken and let
String.format() validate it
- // This allows String.format() to throw
IllegalFormatException for invalid conversions like %F
- // printfStart is position after '%',
so substring from printfStart-1 (the '%') to i (after the char)
- sf(tokens, pattern, mark, i,
sequentialIndex++);
- state = S1;
- mark = i;
- }
- } else if (state == S3) {
- if (ch == '{') {
- nestedBracketDepth++;
- } else if (ch == '}') {
- if (nestedBracketDepth > 0) {
- nestedBracketDepth--;
- } else {
- mf(tokens, pattern, mark + 1, i
- 1, sequentialIndex++);
- state = S1;
- mark = i;
- }
- }
- } else /* if (state == S4) */ {
- if (ch == '\'') {
- if (mark == i - 1) {
- lit(tokens, pattern, mark, i);
// '' becomes '
- state = S1;
- mark = i;
- } else {
- lit(tokens, pattern, mark, i -
1);
- state = S1;
- mark = i;
- }
- }
- }
- }
-
- // Process remaining content based on final state
- if (state == S1) {
- lit(tokens, pattern, mark);
- } else if (state == S2) {
- // Dangling '%' without conversion - throw exception to
match String.format() behavior
- // UnknownFormatConversionException constructor takes
just the conversion character
- throw new
java.util.UnknownFormatConversionException("%");
- } else if (state == S3) {
- // Unmatched '{' - throw exception to match
MessageFormat behavior
- throw new IllegalArgumentException("Unmatched braces in
the pattern.");
- } else /* if (state == S4) */ {
- // Unmatched quote - MessageFormat treats it as ending
the quoted section
- // Add the quoted content as literal (from mark to end
of pattern)
- lit(tokens, pattern, mark);
- }
-
- return tokens;
- }
-
- private static void lit(List<Token> tokens, String pattern, int start,
int end) {
- if (start == end)
- return;
- tokens.add(new LiteralToken(pattern.substring(start, end)));
- }
-
- private static void lit(List<Token> tokens, String pattern, int start) {
- if (start == pattern.length())
- return;
- tokens.add(new LiteralToken(pattern.substring(start)));
- }
-
- private static void sf(List<Token> tokens, String pattern, int start,
int end, int index) {
- tokens.add(new StringFormatToken(pattern.substring(start, end),
index));
- }
-
- private static void mf(List<Token> tokens, String pattern, int start,
int end, int index) {
- tokens.add(new MessageFormatToken(pattern.substring(start,
end), index));
- }
-
- private static int parseIndexMF(String s) {
- try {
- return Integer.parseInt(s.trim());
- } catch (@SuppressWarnings("unused") NumberFormatException e) {
- throw new IllegalArgumentException("can't parse
argument number: " + s);
- }
- }
-
- private static int parseIndexSF(String s) {
- return Integer.parseInt(s.trim());
- }
-
- @Override
- public String toString() {
- return pattern;
- }
-
- /**
- * Returns a debug representation of the parsed pattern showing the
token structure.
- *
- * <p>
- * This method is useful for debugging and understanding how a pattern
was parsed.
- * It returns a string showing each token in the format:
- * <ul>
- * <li><b>Literal tokens:</b> <js>"[L:text]"</js> - Literal text</li>
- * <li><b>MessageFormat tokens (simple):</b> <js>"[M:s0]"</js> -
Simple MessageFormat placeholder (format='s', index=0)</li>
- * <li><b>MessageFormat tokens (complex):</b>
<js>"[M:o0:{0,number,currency}]"</js> - Complex MessageFormat placeholder
(format='o', index=0, content)</li>
- * <li><b>StringFormat tokens (simple):</b> <js>"[S:s0:%s]"</js> -
Simple printf placeholder (format='s', index=0, content)</li>
- * <li><b>StringFormat tokens (complex):</b> <js>"[S:z0:%.2f]"</js> -
Complex printf placeholder (format='z', index=0, content)</li>
- * </ul>
- *
- * <h5 class='section'>Token Format:</h5>
- * <ul>
- * <li><b>L</b> = Literal token</li>
- * <li><b>M</b> = MessageFormat token</li>
- * <li><b>S</b> = StringFormat (printf) token</li>
- * <li><b>Format character:</b> 's' = simple, 'o' = other/complex,
'z' = complex printf</li>
- * <li><b>Index:</b> 0-based argument index</li>
- * <li><b>Content:</b> The format string content (for complex
tokens)</li>
- * </ul>
- *
- * <h5 class='section'>Examples:</h5>
- * <p class='bjava'>
- * FormatString <jv>fmt</jv> =
FormatString.<jsm>of</jsm>(<js>"Hello {0}, you have %d items"</js>);
- * <jv>fmt</jv>.<jsm>toPattern</jsm>();
- * <jc>// Returns: "[L:Hello ][M:s0][L:, you have ][S:d1:%d][L:
items]"</jc>
- * </p>
- *
- * @return A debug string showing the parsed token structure.
- */
- public String toPattern() {
- return
Arrays.stream(tokens).map(Object::toString).collect(Collectors.joining());
- }
-
- @Override
- public boolean equals(Object o) {
- return o instanceof FormatString o2 && eq(this, o2, (x, y) ->
eq(x.pattern, y.pattern));
- }
-
- @Override
- public int hashCode() {
- return pattern.hashCode();
- }
-}
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringFormat.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringFormat.java
index 145d8e6a58..9cefec0cd9 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringFormat.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/StringFormat.java
@@ -114,7 +114,7 @@ public final class StringFormat {
*
* <p>
* This is a convenience method that creates a StringFormat instance
and formats it.
- * If the pattern contains no format specifiers (no '%', '{', or '''),
the pattern is returned as-is.
+ * If no arguments are passed in, the pattern is simply returned as-is.
*
* @param pattern The format pattern.
* @param args The arguments to format.
@@ -122,7 +122,7 @@ public final class StringFormat {
* @throws IllegalArgumentException If the pattern is <jk>null</jk> or
format specifiers are invalid.
*/
public static String format(String pattern, Object...args) {
- if (!hasArgs(pattern))
+ if (args.length == 0)
return pattern;
return of(pattern).format(args);
}
@@ -132,7 +132,7 @@ public final class StringFormat {
*
* <p>
* This is a convenience method that creates a StringFormat instance
and formats it.
- * If the pattern contains no format specifiers (no '%', '{', or '''),
the pattern is returned as-is.
+ * If no arguments are passed in, the pattern is returned as-is.
*
* @param pattern The format pattern.
* @param locale The locale to use for formatting. If <jk>null</jk>,
uses the default locale.
@@ -141,7 +141,7 @@ public final class StringFormat {
* @throws IllegalArgumentException If the pattern is <jk>null</jk> or
format specifiers are invalid.
*/
public static String format(String pattern, Locale locale,
Object...args) {
- if (!hasArgs(pattern))
+ if (args.length == 0)
return pattern;
return of(pattern).format(locale, args);
}
diff --git
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/ThrowableUtils.java
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/ThrowableUtils.java
index 482949cb9f..6b6a492f00 100644
---
a/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/ThrowableUtils.java
+++
b/juneau-core/juneau-common/src/main/java/org/apache/juneau/common/utils/ThrowableUtils.java
@@ -138,7 +138,7 @@ public class ThrowableUtils {
* @return A new IllegalArgumentException with the formatted message.
*/
public static IllegalArgumentException illegalArg(String msg,
Object...args) {
- return new IllegalArgumentException(args.length == 0 ? msg :
f(msg, args));
+ return new IllegalArgumentException(f(msg, args));
}
/**
@@ -160,7 +160,7 @@ public class ThrowableUtils {
* @return A new IllegalArgumentException with the formatted message
and cause.
*/
public static IllegalArgumentException illegalArg(Throwable cause,
String msg, Object...args) {
- return new IllegalArgumentException(args.length == 0 ? msg :
f(msg, args), cause);
+ return new IllegalArgumentException(f(msg, args), cause);
}
/**
@@ -171,7 +171,7 @@ public class ThrowableUtils {
* @return A new IOException with the formatted message.
*/
public static java.io.IOException ioex(String msg, Object...args) {
- return new java.io.IOException(args.length == 0 ? msg : f(msg,
args));
+ return new java.io.IOException(f(msg, args));
}
/**
@@ -193,7 +193,7 @@ public class ThrowableUtils {
* @return A new IOException with the formatted message and cause.
*/
public static java.io.IOException ioex(Throwable cause, String msg,
Object...args) {
- return new java.io.IOException(args.length == 0 ? msg : f(msg,
args), cause);
+ return new java.io.IOException(f(msg, args), cause);
}
/**
@@ -214,7 +214,7 @@ public class ThrowableUtils {
* @return A new RuntimeException with the formatted message.
*/
public static RuntimeException rex(String msg, Object...args) {
- return new RuntimeException(args.length == 0 ? msg : f(msg,
args));
+ return new RuntimeException(f(msg, args));
}
/**
@@ -294,7 +294,7 @@ public class ThrowableUtils {
* @return A new RuntimeException with the formatted message and cause.
*/
public static BeanRuntimeException bex(Throwable cause, String msg,
Object...args) {
- return new BeanRuntimeException(args.length == 0 ? msg : f(msg,
args), cause);
+ return new BeanRuntimeException(f(msg, args), cause);
}
/**
@@ -324,7 +324,7 @@ public class ThrowableUtils {
* @return A new UnsupportedOperationException with the formatted
message.
*/
public static UnsupportedOperationException unsupportedOp(String msg,
Object...args) {
- return new UnsupportedOperationException(args.length == 0 ? msg
: f(msg, args));
+ return new UnsupportedOperationException(f(msg, args));
}
/**
@@ -346,7 +346,7 @@ public class ThrowableUtils {
* @return A new UnsupportedOperationException with the formatted
message and cause.
*/
public static UnsupportedOperationException unsupportedOp(Throwable
cause, String msg, Object...args) {
- return new UnsupportedOperationException(args.length == 0 ? msg
: f(msg, args), cause);
+ return new UnsupportedOperationException(f(msg, args), cause);
}
/**
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicException.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicException.java
index ee7c55e3c8..662f8b7763 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicException.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicException.java
@@ -18,6 +18,7 @@ package org.apache.juneau;
import static org.apache.juneau.common.utils.StringUtils.*;
import static org.apache.juneau.common.utils.ThrowableUtils.*;
+import static org.apache.juneau.common.utils.Utils.*;
import java.text.*;
@@ -38,7 +39,7 @@ public abstract class BasicException extends Exception {
* @param args Optional {@link MessageFormat}-style arguments.
*/
public BasicException(String message, Object...args) {
- super(mformat(message, args));
+ super(f(message, args));
}
/**
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicRuntimeException.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicRuntimeException.java
index 72e10564f4..4ed62867f4 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicRuntimeException.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BasicRuntimeException.java
@@ -16,7 +16,6 @@
*/
package org.apache.juneau;
-import static org.apache.juneau.common.utils.StringUtils.*;
import static org.apache.juneau.common.utils.ThrowableUtils.*;
import static org.apache.juneau.common.utils.Utils.*;
@@ -40,7 +39,7 @@ public class BasicRuntimeException extends RuntimeException {
* @param args Optional {@link MessageFormat}-style arguments.
*/
public BasicRuntimeException(String message, Object...args) {
- super(mformat(message, args));
+ super(f(message, args));
}
/**
@@ -60,7 +59,7 @@ public class BasicRuntimeException extends RuntimeException {
* @param args Optional {@link MessageFormat}-style arguments.
*/
public BasicRuntimeException(Throwable cause, String message,
Object...args) {
- super(mformat(message, args), cause);
+ super(f(message, args), cause);
}
@Override /* Overridden from Throwable */
@@ -112,7 +111,7 @@ public class BasicRuntimeException extends RuntimeException
{
*/
public BasicRuntimeException setMessage(String message, Object...args) {
assertModifiable();
- this.message = mformat(message, args);
+ this.message = f(message, args);
return this;
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
index 1218f6ee22..e35e0ee03a 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
@@ -309,7 +309,7 @@ public class BeanSession extends ContextSession {
@Override
public void addWarning(String msg, Object...args) {
if (isDebug())
- LOG.log(Level.WARNING, () -> args.length == 0 ? msg :
MessageFormat.format(msg, args));
+ LOG.log(Level.WARNING, () -> args.length == 0 ? msg :
f(msg, args));
super.addWarning(msg, args);
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextSession.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextSession.java
index d24cca367d..fd4a9aabfc 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextSession.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextSession.java
@@ -189,7 +189,7 @@ public abstract class ContextSession {
return;
if (warnings == null)
warnings = new LinkedList<>();
- warnings.add((warnings.size() + 1) + ": " + mformat(msg, args));
+ warnings.add((warnings.size() + 1) + ": " + f(msg, args));
}
/**
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/Messages.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/Messages.java
index 1bb254649e..cf18850fd4 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/Messages.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/cp/Messages.java
@@ -441,7 +441,7 @@ public class Messages extends ResourceBundle {
var s = getString(key);
if (s.startsWith("{!"))
return s;
- return mformat(s, args);
+ return f(s, args);
}
@Override /* Overridden from ResourceBundle */
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParseException.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParseException.java
index 9f9ffc280f..4fe799fdaf 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParseException.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParseException.java
@@ -60,7 +60,7 @@ public class ParseException extends BasicRuntimeException {
private static String getMessage(ParserSession session, String msg,
Object...args) {
if (args.length != 0)
- msg = mformat(msg, args);
+ msg = f(msg, args);
if (nn(session)) {
Position p = session.getPosition();
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserListener.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserListener.java
index b77f267701..fd32671ace 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserListener.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/parser/ParserListener.java
@@ -18,6 +18,7 @@ package org.apache.juneau.parser;
import static org.apache.juneau.common.utils.StringUtils.*;
import static org.apache.juneau.common.utils.ThrowableUtils.*;
+import static org.apache.juneau.common.utils.Utils.*;
import org.apache.juneau.*;
@@ -44,7 +45,7 @@ public class ParserListener {
* @param p The bean property we had an issue on.
*/
public void onBeanSetterException(ParserSession session, Throwable t,
BeanPropertyMeta p) {
- onError(session, t, mformat("Could not call setValue() on
property ''{0}'' of class ''{1}'', exception = {2}", p.getName(),
p.getBeanMeta().getClassMeta(), lm(t)));
+ onError(session, t, f("Could not call setValue() on property
''{0}'' of class ''{1}'', exception = {2}", p.getName(),
p.getBeanMeta().getClassMeta(), lm(t)));
}
/**
@@ -72,6 +73,6 @@ public class ParserListener {
* @param bean The bean.
*/
public <T> void onUnknownBeanProperty(ParserSession session, String
propertyName, Class<T> beanClass, T bean) {
- onError(session, null, mformat("Unknown property ''{0}''
encountered while trying to parse into class ''{1}'' at location {2}",
propertyName, beanClass, session.getPosition()));
+ onError(session, null, f("Unknown property ''{0}'' encountered
while trying to parse into class ''{1}'' at location {2}", propertyName,
beanClass, session.getPosition()));
}
}
\ No newline at end of file
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializeException.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializeException.java
index c62da17c53..fe5f51a2bc 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializeException.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializeException.java
@@ -60,7 +60,7 @@ public class SerializeException extends BasicRuntimeException
{
}
private static String getMessage(SerializerSession session, String msg,
Object...args) {
- msg = mformat(msg, args);
+ msg = f(msg, args);
if (nn(session)) {
Map<String,Object> m = session.getLastLocation();
if (nn(m) && ! m.isEmpty())
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerListener.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerListener.java
index 5cd8588cfd..f20825027c 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerListener.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerListener.java
@@ -18,6 +18,7 @@ package org.apache.juneau.serializer;
import static org.apache.juneau.common.utils.StringUtils.*;
import static org.apache.juneau.common.utils.ThrowableUtils.*;
+import static org.apache.juneau.common.utils.Utils.*;
import org.apache.juneau.*;
@@ -44,7 +45,7 @@ public class SerializerListener {
* @param p The bean property we had an issue on.
*/
public void onBeanGetterException(SerializerSession session, Throwable
t, BeanPropertyMeta p) {
- onError(session, t, mformat("Could not call getValue() on
property ''{0}'' of class ''{1}'', exception = {2}", p.getName(),
p.getBeanMeta().getClassMeta(), lm(t)));
+ onError(session, t, f("Could not call getValue() on property
''{0}'' of class ''{1}'', exception = {2}", p.getName(),
p.getBeanMeta().getClassMeta(), lm(t)));
}
/**
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
index cf4f5667d3..4d5231ea30 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/serializer/SerializerSession.java
@@ -980,7 +980,7 @@ public class SerializerSession extends BeanTraverseSession {
@Override
protected void onError(Throwable t, String msg, Object...args) {
if (nn(listener))
- listener.onError(this, t, mformat(msg, args));
+ listener.onError(this, t, f(msg, args));
super.onError(t, msg, args);
}
diff --git
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/Microservice.java
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/Microservice.java
index b261575b56..2117ced2e8 100755
---
a/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/Microservice.java
+++
b/juneau-microservice/juneau-microservice-core/src/main/java/org/apache/juneau/microservice/Microservice.java
@@ -1155,7 +1155,7 @@ public class Microservice implements ConfigEventListener {
* @param args Optional {@link MessageFormat}-style arguments.
*/
protected void log(Level level, String message, Object...args) {
- var msg = args.length == 0 ? message :
MessageFormat.format(message, args);
+ var msg = args.length == 0 ? message : f(message, args);
getLogger().log(level, msg);
}
diff --git
a/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyLogger.java
b/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyLogger.java
index 948e934c68..7e9b190725 100644
---
a/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyLogger.java
+++
b/juneau-microservice/juneau-microservice-jetty/src/main/java/org/apache/juneau/microservice/jetty/JettyLogger.java
@@ -57,23 +57,7 @@ public class JettyLogger implements LocationAwareLogger {
* @return The formatted message string.
*/
private static String format(String msg, Object...args) {
- msg = String.valueOf(msg);
- if (args.length == 0)
- return msg;
- var sb = new StringBuilder();
- int start = 0;
- for (var arg : args) {
- int bi = msg.indexOf("{}", start);
- if (bi < 0) {
- sb.append(msg.substring(start)).append("
").append(arg);
- start = msg.length();
- } else {
- sb.append(msg.substring(start,
bi)).append(String.valueOf(arg));
- start = bi + 2;
- }
- }
- sb.append(msg.substring(start));
- return sb.toString();
+ return f(msg, args);
}
/**
diff --git
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index 68de91f3f9..21d24257ab 100644
---
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -7537,10 +7537,6 @@ public class RestClient extends BeanContextable
implements HttpClient, Closeable
return request(op(method, uri, body));
}
- private static Supplier<String> msg(String msg, Object...args) {
- return () -> args.length == 0 ? msg : MessageFormat.format(msg,
args);
- }
-
private static RestOperation op(String method, Object url, Object body)
{
return RestOperation.of(method, url, body);
}
@@ -7691,9 +7687,9 @@ public class RestClient extends BeanContextable
implements HttpClient, Closeable
* @param args The arguments.
*/
protected void log(Level level, String msg, Object...args) {
- logger.log(level, msg(msg, args));
+ logger.log(level, f(msg, args));
if (logToConsole)
- console.println(msg(msg, args).get());
+ console.println(f(msg, args));
}
/**
@@ -7705,9 +7701,9 @@ public class RestClient extends BeanContextable
implements HttpClient, Closeable
* @param args Optional message arguments.
*/
protected void log(Level level, Throwable t, String msg, Object...args)
{
- logger.log(level, t, msg(msg, args));
+ logger.log(level, t, fs(msg, args));
if (logToConsole) {
- console.println(msg(msg, args).get());
+ console.println(f(msg, args));
if (nn(t))
t.printStackTrace(console);
}
diff --git
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java
index 57af897cf8..2b4ba16015 100644
---
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java
+++
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockServletRequest.java
@@ -76,7 +76,7 @@ public class MockServletRequest implements HttpServletRequest
{
* @return A new request.
*/
public static MockServletRequest create(String method, String uri,
Object...pathArgs) {
- return create().method(method).uri(mformat(uri, pathArgs));
+ return create().method(method).uri(f(uri, pathArgs));
}
private String method = "GET";
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/ArgException.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/ArgException.java
index 108ed3ee6f..12244c265a 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/ArgException.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/arg/ArgException.java
@@ -17,6 +17,7 @@
package org.apache.juneau.rest.arg;
import static org.apache.juneau.common.utils.StringUtils.*;
+import static org.apache.juneau.common.utils.Utils.*;
import java.util.*;
@@ -46,7 +47,7 @@ public class ArgException extends InternalServerError {
* @param args The message args.
*/
public ArgException(ParameterInfo pi, String msg, Object...args) {
- super(mformat(msg, args) + " on parameter " + pi.getIndex() + "
of method " + pi.getMethod().getFullName() + ".");
+ super(f(msg, args) + " on parameter " + pi.getIndex() + " of
method " + pi.getMethod().getFullName() + ".");
}
/**
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/servlet/RestObject.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/servlet/RestObject.java
index d9b42a2080..c2742a7334 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/servlet/RestObject.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/servlet/RestObject.java
@@ -70,7 +70,7 @@ public abstract class RestObject {
* @param args Optional {@link MessageFormat}-style arguments.
*/
public void log(Level level, String msg, Object...args) {
- doLog(level, null, () -> mformat(msg, args));
+ doLog(level, null, fs(msg, args));
}
/**
@@ -85,7 +85,7 @@ public abstract class RestObject {
* @param args Optional {@link MessageFormat}-style arguments.
*/
public void log(Level level, Throwable cause, String msg,
Object...args) {
- doLog(level, cause, () -> mformat(msg, args));
+ doLog(level, cause, fs(msg, args));
}
/**
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/servlet/RestServlet.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/servlet/RestServlet.java
index bbeabfb3c9..2ba034f2a0 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/servlet/RestServlet.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/servlet/RestServlet.java
@@ -169,7 +169,7 @@ public abstract class RestServlet extends HttpServlet {
* @param args Optional {@link MessageFormat}-style arguments.
*/
public void log(Level level, String msg, Object...args) {
- doLog(level, null, () -> StringUtils.mformat(msg, args));
+ doLog(level, null, fs(msg, args));
}
/**
@@ -184,7 +184,7 @@ public abstract class RestServlet extends HttpServlet {
* @param args Optional {@link MessageFormat}-style arguments.
*/
public void log(Level level, Throwable cause, String msg,
Object...args) {
- doLog(level, cause, () -> StringUtils.mformat(msg, args));
+ doLog(level, cause, fs(msg, args));
}
/**
diff --git
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/swagger/SwaggerException.java
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/swagger/SwaggerException.java
index 35ff5feade..5bbed92ff1 100644
---
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/swagger/SwaggerException.java
+++
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/swagger/SwaggerException.java
@@ -17,6 +17,7 @@
package org.apache.juneau.rest.swagger;
import static org.apache.juneau.common.utils.StringUtils.*;
+import static org.apache.juneau.common.utils.Utils.*;
import org.apache.juneau.parser.*;
@@ -24,6 +25,6 @@ class SwaggerException extends ParseException {
private static final long serialVersionUID = 1L;
SwaggerException(Exception e, String location, Object...locationArgs) {
- super(e, "Swagger exception: at " + mformat(location,
locationArgs));
+ super(e, "Swagger exception: at " + f(location, locationArgs));
}
}
\ No newline at end of file
diff --git a/juneau-utest/src/test/java/org/apache/juneau/ComboInput.java
b/juneau-utest/src/test/java/org/apache/juneau/ComboInput.java
index 68a1de1da6..10a4fa4952 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/ComboInput.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/ComboInput.java
@@ -75,7 +75,7 @@ public class ComboInput<T> {
}
public ComboInput<T> verify(Predicate<T> verify, String msg,
Object...args) {
- this.verify.add(x -> verify.test(x) ? null : mformat(msg,
args));
+ this.verify.add(x -> verify.test(x) ? null : f(msg, args));
return this;
}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjects_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjects_Test.java
index 4b96ef8f14..7b52a59294 100755
---
a/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjects_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/a/rttests/RoundTripLargeObjects_Test.java
@@ -17,6 +17,7 @@
package org.apache.juneau.a.rttests;
import static org.apache.juneau.common.utils.StringUtils.*;
+import static org.apache.juneau.common.utils.Utils.*;
import java.util.*;
@@ -111,7 +112,7 @@ class RoundTripLargeObjects_Test extends TestBase {
// Initialization run.
r = s.serialize(a);
- System.err.println(mformat("Serialized size: {0,number} ", (r
instanceof String ? r.toString().length() : ((byte[])r).length))); // NOT DEBUG
+ System.err.println(f("Serialized size: {0,number} ", (r
instanceof String ? r.toString().length() : ((byte[])r).length))); // NOT DEBUG
p.parse(r, A.class);
startTime = System.currentTimeMillis();
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/FormatString_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/FormatString_Test.java
deleted file mode 100644
index a8a4445544..0000000000
---
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/FormatString_Test.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.common.utils;
-
-import static org.apache.juneau.common.utils.Utils.*;
-import static org.junit.jupiter.api.Assertions.*;
-import static java.util.stream.Collectors.*;
-
-import java.math.*;
-import java.text.*;
-import java.util.*;
-import java.util.stream.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.common.function.*;
-import org.junit.jupiter.api.*;
-
-class FormatString_Test extends TestBase {
-
- private static StringFormat fs(String pattern) {
- return StringFormat.of(pattern);
- }
-
- private static String stringify(ThrowingSupplier<String> supplier) {
- try {
- return supplier.get();
- } catch (Throwable t) {
- return t.getClass().getSimpleName() + ": " +
t.getLocalizedMessage();
- }
- }
-
- private static void assertStringFormat(String pattern, Locale locale,
Object... args) {
- var expected = stringify(()->String.format(locale, pattern,
args));
- var actual = "";
- var fmt = (StringFormat)null;
- try {
- var fmt2 = fs(pattern);
- fmt = fmt2;
- actual = stringify(()->fmt2.format(locale, args));
- } catch (Throwable t) {
- actual = t.getClass().getSimpleName() + ": " +
t.getLocalizedMessage();
- }
- if (!expected.equals(actual)) {
- System.out.println("Pattern: " + pattern);
- var toPattern = opt(fmt).map(x ->
x.toPattern()).orElse(null);
- System.out.println("toPattern(): " + toPattern);
- fail("Pattern: " + pattern + ", toPattern(): " +
toPattern + ", expected: <" + expected + "> but was: <" + actual + ">");
- }
- }
-
- private static void assertStringFormat(String pattern, Object... args) {
- var expected = stringify(()->String.format(pattern, args));
- var actual = "";
- var fmt = (StringFormat)null;
- try {
- var fmt2 = fs(pattern);
- fmt = fmt2;
- actual = stringify(()->fmt2.format(args));
- } catch (Throwable t) {
- actual = t.getClass().getSimpleName() + ": " +
t.getLocalizedMessage();
- }
- if (!expected.equals(actual)) {
- System.out.println("Pattern: " + pattern);
- var toPattern = opt(fmt).map(x ->
x.toPattern()).orElse(null);
- System.out.println("toPattern(): " + toPattern);
- fail("Pattern: " + pattern + ", toPattern(): " +
toPattern + ", expected: <" + expected + "> but was: <" + actual + ">");
- }
- }
-
- private static void assertMessageFormat(String pattern, Locale locale,
Object... args) {
- var expected = stringify(()->new MessageFormat(pattern,
locale).format(args));
- var actual = "";
- var fmt = (StringFormat)null;
- try {
- var fmt2 = fs(pattern);
- fmt = fmt2;
- actual = stringify(()->fmt2.format(locale, args));
- } catch (Throwable t) {
- actual = t.getClass().getSimpleName() + ": " +
t.getLocalizedMessage();
- }
- if (!expected.equals(actual)) {
- System.out.println("Pattern: " + pattern);
- var toPattern = opt(fmt).map(x ->
x.toPattern()).orElse(null);
- System.out.println("toPattern(): " + toPattern);
- fail("Pattern: " + pattern + ", toPattern(): " +
toPattern + ", expected: <" + expected + "> but was: <" + actual + ">");
- }
- }
-
- private static void assertMessageFormat(String pattern, Object... args)
{
- var expected = stringify(()->MessageFormat.format(pattern,
args));
- var actual = "";
- var fmt = (StringFormat)null;
- try {
- var fmt2 = fs(pattern);
- fmt = fmt2;
- actual = stringify(()->fmt2.format(args));
- } catch (Throwable t) {
- actual = t.getClass().getSimpleName() + ": " +
t.getLocalizedMessage();
- }
- if (!expected.equals(actual)) {
- System.out.println("Pattern: " + pattern);
- var toPattern = opt(fmt).map(x ->
x.toPattern()).orElse(null);
- System.out.println("toPattern(): " + toPattern);
- fail("Pattern: " + pattern + ", toPattern(): " +
toPattern + ", expected: <" + expected + "> but was: <" + actual + ">");
- }
- }
-
- private static void assertMixedFormat(String expected, String pattern,
Locale locale, Object... args) {
- var actual = "";
- var fmt = (StringFormat)null;
- try {
- var fmt2 = fs(pattern);
- fmt = fmt2;
- actual = stringify(()->fmt2.format(locale, args));
- } catch (Throwable t) {
- actual = t.getClass().getSimpleName() + ": " +
t.getLocalizedMessage();
- }
- if (!expected.equals(actual)) {
- System.out.println("Pattern: " + pattern);
- var toPattern = opt(fmt).map(x ->
x.toPattern()).orElse(null);
- System.out.println("toPattern(): " + toPattern);
- fail("Pattern: " + pattern + ", toPattern(): " +
toPattern + ", expected: <" + expected + "> but was: <" + actual + ">");
- }
- }
-
- private static void assertMixedFormat(String expected, String pattern,
Object... args) {
- var actual = "";
- var fmt = (StringFormat)null;
- try {
- var fmt2 = fs(pattern);
- fmt = fmt2;
- actual = stringify(()->fmt2.format(args));
- } catch (Throwable t) {
- actual = t.getClass().getSimpleName() + ": " +
t.getLocalizedMessage();
- }
- if (!expected.equals(actual)) {
- System.out.println("Pattern: " + pattern);
- var toPattern = opt(fmt).map(x ->
x.toPattern()).orElse(null);
- System.out.println("toPattern(): " + toPattern);
- fail("Pattern: " + pattern + ", toPattern(): " +
toPattern + ", expected: <" + expected + "> but was: <" + actual + ">");
- }
- }
-
-
//====================================================================================================
- // MessageFormat tests
-
//====================================================================================================
- @Test void a01_messageFormat() {
- assertMessageFormat("Hello {0}", "John");
- assertMessageFormat("Price: {0,number,currency}", 19.99);
- assertMessageFormat("{0} has {1} items and {2} friends",
"John", 5, 3);
- assertMessageFormat("Hello {0} world", "John");
- assertMessageFormat("Count: {0,number,integer}", 1234);
- assertMessageFormat("Date: {0,date,short}", new Date(0));
- assertMessageFormat("Time: {0,time,short}", new Date(0));
- // Simple {0} with Date - uses DATE_FORMAT_CACHE for formatting
- assertMessageFormat("Date: {0}", new Date(0));
- assertMessageFormat("Value: {0}", (String)null);
- assertMessageFormat("Name: {0}", "");
- assertMessageFormat("Text: {0}\nNewline\tTab", "Hello");
- assertMessageFormat("Unicode: {0} 中文", "Test");
- assertMessageFormat("{0}{1}", "A", "B");
- assertMessageFormat("{0} and {0} again", "Hello");
- assertMessageFormat("Price: {0,number,currency}, Count:
{1,number,integer}, Date: {2,date,short}", 19.99, 42, new java.util.Date());
- assertMessageFormat("Price: {0,number,currency}", Locale.US,
19.99);
- assertMessageFormat("Price: {0,number,currency}",
Locale.FRANCE, 19.99);
- assertMessageFormat("a '{0}' b");
- assertMessageFormat("a ''{0}'' b", 1);
- assertMessageFormat("'{0}'");
- assertMessageFormat("''{0}''", 1);
-
- // Errors
- assertMessageFormat("Set: {{0}}", 50);
- assertMessageFormat("Set: {{0}} and {{1}}", "A", "B");
- assertMessageFormat("Hello {0}");
- assertMessageFormat("{0} has {1} items and {2} friends",
"John", 5);
- assertMessageFormat("Hello {");
- assertMessageFormat("Hello {0");
- assertMessageFormat("Hello '");
- assertMessageFormat("Hello 'x");
- }
-
-
//====================================================================================================
- // StringFormat (printf) tests
-
//====================================================================================================
- @Test void a02_stringFormat() {
- assertStringFormat("Hello %s", "John");
- assertStringFormat("Price: $%.2f", 19.99);
- assertStringFormat("Name: %-10s Age: %3d", "John", 25);
- assertStringFormat("Color: #%06X", 0xFF5733);
- assertStringFormat("Hello world");
- assertStringFormat("Progress: %d%%", 50);
- assertStringFormat("");
- assertStringFormat("%1$s loves %2$s, and %1$s also loves %3$s",
"Alice", "Bob", "Charlie");
- assertStringFormat("Hello %1$s", "John");
- assertStringFormat("Price: %1$.2f", 19.99);
- assertStringFormat("Octal: %o", 64);
- assertStringFormat("Octal: %o", 255);
- assertStringFormat("Octal: %o", (Number)null);
- assertStringFormat("Flag: %b", true);
- assertStringFormat("Flag: %b", false);
- assertStringFormat("Flag: %b", (Boolean)null);
- // %B uppercase boolean formatting
- assertStringFormat("Flag: %B", true);
- assertStringFormat("Flag: %B", false);
- assertStringFormat("Flag: %B", (Boolean)null); // Line 281:
null -> "FALSE"
- assertStringFormat("Flag: %B", "hello"); // Line 285:
non-Boolean -> "TRUE"
- assertStringFormat("Flag: %B", 42); // Line 285: non-Boolean
-> "TRUE"
- assertStringFormat("Char: %c", 'A');
- assertStringFormat("Char: %c", "A");
- assertStringFormat("Char: %c", (String)null);
- assertStringFormat("Value: %.2e", 1234567.0);
- assertStringFormat("Value: %.2e", (Number)null);
- assertStringFormat("Number: %+10.2f", 19.99);
- assertStringFormat("ID: %05d", 42);
- assertStringFormat("Value: %d", (Number)null); // Line 304:
null -> "null"
- assertStringFormat("Value: %s", (String)null);
- assertStringFormat("Name: %s", "");
- assertStringFormat("%s%s", "A", "B");
- assertStringFormat("Progress: %d%% Complete: %d%%", 50, 75);
- assertStringFormat("%1$s and %1$s again", "Hello");
- assertStringFormat("Hex: 0x%08X, Decimal: %+d, Float: %10.3f",
255, 42, 3.14159);
- assertStringFormat("Price: %.2f", Locale.US, 19.99);
- assertStringFormat("Price: %.2f", Locale.FRANCE, 19.99);
- assertStringFormat("Value: %s", (Object)null);
- assertStringFormat("Int: %d", 42);
- assertStringFormat("Long: %d", 1234567890L);
- assertStringFormat("Byte: %d", (byte)127);
- assertStringFormat("Short: %d", (short)32767);
- assertStringFormat("Int: %d", Locale.FRANCE, 1234);
- assertStringFormat("Long: %d", Locale.GERMANY, 1234567L);
- assertStringFormat("Value: %d", "not-a-number");
- assertStringFormat("Hex: %x", 255); // Line 328: Integer ->
Integer.toHexString()
- assertStringFormat("Hex: %x", 255L);
- assertStringFormat("Hex: %x", (byte)255);
- assertStringFormat("Hex: %x", (Number)null); // Line 324: null
-> "null"
- assertStringFormat("Hex: %X", 255);
- assertStringFormat("Hex: %X", 0xABCL);
- assertStringFormat("Hex: %X", (short)255);
- assertStringFormat("Hex: %X", (Number)null); // Line 337: null
-> "null"
- assertStringFormat("Octal: %o", 255L);
- assertStringFormat("Octal: %o", (byte)64);
- assertStringFormat("Value: %b", "hello");
- assertStringFormat("Value: %b", 42);
- assertStringFormat("Char: %c", 65);
- assertStringFormat("Char: %c", 65L);
- assertStringFormat("Char: %c", "X");
- assertStringFormat("Char: %C", (Character)null); // Line 376:
null -> "null"
- assertStringFormat("Char: %C", 66); // Line 382-383: Number
(Integer) -> Character.toUpperCase((char)o2.intValue())
- assertStringFormat("Char: %C", 66L); // Line 382-383: Number
(Long) -> Character.toUpperCase((char)o2.intValue())
- assertStringFormat("Float: %f", 3.14f);
- assertStringFormat("Double: %f", 3.14159);
- assertStringFormat("Float: %f", Locale.FRANCE, 3.14f);
- assertStringFormat("Double: %f", Locale.GERMANY, 1234.56);
- assertStringFormat("Value: %f", (Number)null); // Line 389:
null -> "null"
- assertStringFormat("Value: %f", "not-a-number");
- assertStringFormat("Value: %.2e", 1234.56);
- assertStringFormat("Value: %S", "hello");
- assertStringFormat("Value: %S", (String)null); // Line 297:
null -> "null"
- assertStringFormat("Value: %B", true);
- assertStringFormat("Char: %C", 'a');
- assertStringFormat("Float: %F", 3.14);
-
- // Errors
- assertStringFormat("Hello %s");
- assertStringFormat("Hello %s and %s", "John");
- assertStringFormat("Hello %");
- assertStringFormat("Hello %s and %", "John");
- assertStringFormat("Hello %x$s", "John");
- }
-
-
//====================================================================================================
- // Mixed format tests
-
//====================================================================================================
- @Test void a03_mixedFormat() {
- assertMixedFormat("Hello John, you have 5 items", "Hello {0},
you have %d items", "John", 5);
- assertMixedFormat("User Alice has admin and 10 items", "User
{0} has %s and {2} items", "Alice", "admin", 10);
- assertMixedFormat("Alice loves Bob, and Alice also loves
Charlie", "%1$s loves %2$s, and {0} also loves %3$s", "Alice", "Bob",
"Charlie");
- assertMixedFormat("Alice has 5 items, Bob has 3 items, total:
8", "{0} has %d items, {2} has %d items, total: %d", "Alice", 5, "Bob", 3, 8);
- assertMixedFormat("Alice Bob Charlie", "{0} %2$s {2}", "Alice",
"Bob", "Charlie");
- assertMixedFormat("Hello John, you have 5 items", "Hello {0},
you have %d items", "John", 5);
- assertMixedFormat("A B B D C", "{0} %s {1} %s {2}", "A", "B",
"C", "D");
- assertMixedFormat("ABB", "{0}%s{1}", "A", "B", "C");
- assertMixedFormat("Hello and Hello are the same", "{0} and %1$s
are the same", "Hello");
-
- // Errors
- assertMixedFormat("MissingFormatArgumentException: Format
specifier '%s'", "Hello {0} and %s", "John");
- assertMixedFormat("John has 5 items and {2} friends", "{0} has
%d items and {2} friends", "John", 5);
- assertMixedFormat("MissingFormatArgumentException: Format
specifier '%s'", "%1$s loves %2$s, and {0} also loves %3$s", "Alice", "Bob");
- }
-
-
//====================================================================================================
- // Supported but deviates from MessageFormat/String.format
-
//====================================================================================================
- @Test void a04_supportedButDeviatesFromMessageFormat() {
- // {} is not supported by MessageFormat, only by StringFormat
as an extension
- assertMixedFormat("Hello John world", "Hello {} world", "John");
- assertMixedFormat("A B C", "{} {} {}", "A", "B", "C");
- // BigDecimal with %d - String.format throws exception, but our
optimized code handles it
- assertMixedFormat("Number: 42", "Number: %d", new
BigDecimal("42"));
- // MessageFormat throws NullPointerException when locale is
null, but StringFormat handles it
- // So we test StringFormat's behavior directly instead of
comparing with MessageFormat
- assertMixedFormat("Price: $19.99", "Price:
{0,number,currency}", (Locale)null, 19.99);
- }
-
-
//====================================================================================================
- // Error handling
-
//====================================================================================================
- @Test void a05_errors() {
- assertThrows(IllegalArgumentException.class, () -> new
StringFormat(null));
- assertThrows(IllegalArgumentException.class, () -> fs(null));
- }
-
- @Test void a06_caching() {
- // Should return the same instance due to caching
- assertSame(fs("Hello {0}"), fs("Hello {0}"));
-
- // Different patterns should return different instances
- assertNotSame(fs("Hello {0}"), fs("Hello %s"));
-
- // Constructor doesn't use cache, so instances should be
different
- var fmt1 = new StringFormat("Hello {0}");
- var fmt2 = new StringFormat("Hello {0}");
- assertNotSame(fmt1, fmt2);
- assertEquals(fmt1, fmt2); // But they should be equal
- }
-
- @Test void a07_equalsAndHashCode() {
- var fmt1 = StringFormat.of("Hello {0}");
- var fmt2 = StringFormat.of("Hello {0}");
- var fmt3 = StringFormat.of("Hello %s");
-
- assertEquals(fmt1, fmt2);
- assertNotEquals(fmt1, fmt3);
- assertEquals(fmt1.hashCode(), fmt2.hashCode());
- }
-
- @Test void a08_toString() {
- assertEquals("Hello {0}", fs("Hello {0}").toString());
- }
-
- @Test void a09_toPattern() {
- // Literal tokens
- assertEquals("[L:Hello ]", fs("Hello ").toPattern());
- assertEquals("[L:a ][L:{0}][L: b]", fs("a '{0}'
b").toPattern()); // Single quotes don't escape MessageFormat
-
- // MessageFormat tokens - simple (content == null) - Line 228:
content == null branch
- assertEquals("[L:Hello ][M:s0]", fs("Hello {0}").toPattern());
- assertEquals("[L:Hello ][M:s0][L: ][M:s1]", fs("Hello {0}
{1}").toPattern());
-
- // MessageFormat tokens - complex (content != null) - Line 228:
content != null branch
- assertEquals("[L:Price: ][M:o0:{0,number,currency}]",
fs("Price: {0,number,currency}").toPattern());
- assertEquals("[L:Count: ][M:o0:{0,number,integer}]", fs("Count:
{0,number,integer}").toPattern());
- assertEquals("[L:Date: ][M:o0:{0,date,short}]", fs("Date:
{0,date,short}").toPattern());
-
- // StringFormat tokens - Line 406: StringFormatToken.toString()
- assertEquals("[L:Hello ][S:s0:%s]", fs("Hello
%s").toPattern()); // Simple format: 's'
- assertEquals("[L:Number: ][S:d0:%d]", fs("Number:
%d").toPattern()); // Simple format: 'd'
- assertEquals("[L:Hex: ][S:x0:%x]", fs("Hex: %x").toPattern());
// Simple format: 'x'
- assertEquals("[L:Float: ][S:z0:%.2f]", fs("Float:
%.2f").toPattern()); // Complex format: 'z' (other)
- assertEquals("[L:ID: ][S:z0:%05d]", fs("ID:
%05d").toPattern()); // Complex format: 'z' (other)
-
- // Mixed formats
- assertEquals("[L:Hello ][M:s0][L:, you have ][S:d1:%d][L:
items]", fs("Hello {0}, you have %d items").toPattern());
- assertEquals("[L:Price: ][M:o0:{0,number,currency}][L: and
][S:s1:%s]", fs("Price: {0,number,currency} and %s").toPattern());
-
- // Time conversions (2-character) - Line 529: 't' or 'T'
handling
- assertEquals("[L:Month: ][S:z0:%tm]", fs("Month:
%tm").toPattern()); // %tm is 2-character time conversion
- assertEquals("[L:Year: ][S:z0:%tY]", fs("Year:
%tY").toPattern()); // %tY is 2-character time conversion
- assertEquals("[L:Date: ][S:z0:%TD]", fs("Date:
%TD").toPattern()); // %TD is 2-character time conversion
- }
-
- @Test void a10_veryLongPattern() {
- var pattern = "Start: " + IntStream.range(0, 10).mapToObj(i ->
"{" + i + "}").collect(joining(" ")) + " ";
- var args = IntStream.range(0, 10).boxed().toArray();
- assertMessageFormat(pattern, args);
- }
-
- @Test void a11_parseIndexErrors() {
- assertThrows(IllegalArgumentException.class, () -> fs("Hello
{abc}"));
- }
-
- @Test void a12_localeHandling() {
- // Lines 259-260: Test locale null checks and default locale
detection in StringFormatToken
- // Line 259: var l = locale == null ? Locale.getDefault() :
locale;
- // Line 260: var dl = locale == null ||
locale.equals(Locale.getDefault());
-
- // Test with null locale (covers locale == null on both lines)
- assertStringFormat("Hello %s", (Locale)null, "John");
- assertStringFormat("Number: %d", (Locale)null, 42);
- assertStringFormat("Float: %.2f", (Locale)null, 3.14); // Use
.2f for consistent formatting
-
- // Test with default locale (covers
locale.equals(Locale.getDefault()) on line 260)
- assertStringFormat("Hello %s", Locale.getDefault(), "John");
- assertStringFormat("Number: %d", Locale.getDefault(), 42);
- assertStringFormat("Float: %.2f", Locale.getDefault(), 3.14);
// Use .2f for consistent formatting
-
- // Test with non-default locale (covers else branch on line 259
and false case on line 260)
- assertStringFormat("Hello %s", Locale.FRANCE, "John");
- assertStringFormat("Number: %d", Locale.GERMANY, 42);
- assertStringFormat("Float: %.2f", Locale.JAPAN, 3.14); // Use
.2f for consistent formatting
- }
-}
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/common/utils/Utils_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/common/utils/Utils_Test.java
index 42438b8e5f..39ed92c678 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/common/utils/Utils_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/common/utils/Utils_Test.java
@@ -58,7 +58,7 @@ class Utils_Test extends TestBase {
// Null handling
assertEquals("Value: null", f("Value: %s", (String)null));
- assertNull(f(null, "test"));
+ assertThrows(IllegalArgumentException.class, ()->f(null,
"test"));
assertEquals("test", f("test"));
}
@@ -83,7 +83,7 @@ class Utils_Test extends TestBase {
// Null handling
var nullSupplier = fs(null, "test");
- assertNull(nullSupplier.get());
+ assertThrows(IllegalArgumentException.class,
()->nullSupplier.get());
// Empty pattern
var emptySupplier = fs("");
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Path_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Path_Test.java
index 6a119feb91..6df2ebe31b 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Path_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/Path_Test.java
@@ -383,31 +383,28 @@ class Path_Test extends TestBase {
public static class F {
@RestGet(path="/")
public String a(RequestPathParams path) {
- return format("a: {0}", path.toString());
+ return Utils.f("a: {0}", path.toString());
}
@RestGet(path="/*")
public String b(RequestPathParams path) {
- return format("b: {0}", path.toString());
+ return Utils.f("b: {0}", path.toString());
}
@RestGet(path="/fc")
public String c(RequestPathParams path) {
- return format("c: {0}", path.toString());
+ return Utils.f("c: {0}", path.toString());
}
@RestGet(path="/fd/{c}/{d}")
public String d(RequestPathParams path) {
- return format("d: {0}", path.toString());
+ return Utils.f("d: {0}", path.toString());
}
@RestGet(path="/fe/{a}/{b}")
public String e(RequestPathParams path) {
- return format("e: {0}", path.toString());
+ return Utils.f("e: {0}", path.toString());
}
@RestGet(path="/ff/{c}/{d}/*")
public String f(RequestPathParams path) {
- return format("f: {0}", path.toString());
+ return Utils.f("f: {0}", path.toString());
}
- private static String format(String msg, Object...args) {
- return StringUtils.mformat(msg, args);
- }
}
@Test void f01_pathVariablesOnClass() throws Exception {
diff --git
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_RestClient_Test.java
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_RestClient_Test.java
index e7cfd068b2..dfc9ff237b 100644
---
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_RestClient_Test.java
+++
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Config_RestClient_Test.java
@@ -316,7 +316,7 @@ class RestClient_Config_RestClient_Test extends TestBase {
super(builder);
}
@Override
- public void log(Level level,String msg,Object...args) {
+ public void log(Level level,String msg, Object...args) {
lastMessage = msg;
}
}