From d1d72031f209d864ee721c40cd9ce1e174383577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralfs=20G=C5=ABtmanis?= Date: Mon, 18 Sep 2023 14:04:46 +0300 Subject: [PATCH] Add ability to pass CharSequence instead of forcing String for context variable. java.lang.String is immutable and cannot be overwritten by user. When passing sensitive data as context variable String remains in memory until GC removes it. CharSequence allows user to provide his own implementation and overwrite data as needed. --- .../com/samskivert/super/java/io/Writer.java | 2 ++ .../com/samskivert/mustache/Mustache.java | 26 ++++++++++++++++--- .../com/samskivert/mustache/Template.java | 4 +-- .../com/samskivert/mustache/MustacheTest.java | 11 +++++--- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/main/gwt/com/samskivert/super/java/io/Writer.java b/src/main/gwt/com/samskivert/super/java/io/Writer.java index e9cd6df..c399fe6 100644 --- a/src/main/gwt/com/samskivert/super/java/io/Writer.java +++ b/src/main/gwt/com/samskivert/super/java/io/Writer.java @@ -9,4 +9,6 @@ package java.io; public abstract class Writer { public abstract void write (String text) throws IOException; + + public abstract Writer append (CharSequence text) throws IOException; } diff --git a/src/main/java/com/samskivert/mustache/Mustache.java b/src/main/java/com/samskivert/mustache/Mustache.java index 2b9d078..bc38ecf 100644 --- a/src/main/java/com/samskivert/mustache/Mustache.java +++ b/src/main/java/com/samskivert/mustache/Mustache.java @@ -203,10 +203,25 @@ public class Mustache { * then empty strings are considered falsey. If {@link #zeroIsFalse} is true, then zero * values are considered falsey. */ public boolean isFalsey (Object value) { - return ((emptyStringIsFalse && "".equals(formatter.format(value))) || + return ((emptyStringIsFalse && isEmptyCharSequence(formatter.format(value))) || (zeroIsFalse && (value instanceof Number) && ((Number)value).longValue() == 0)); } + /** + * Replaces "".equals(value). E.g. only not null values with length 0 + */ + private boolean isEmptyCharSequence(Object value) { + if (value == null) { + return false; + } + + if(value instanceof CharSequence) { + return ((CharSequence) value).length() == 0; + } + + return false; + } + /** Loads and compiles the template {@code name} using this compiler's configured template * loader. Note that this does no caching: the caller should cache the loaded template if * they expect to use it multiple times. @@ -255,8 +270,8 @@ public class Mustache { /** Handles converting objects to strings when rendering templates. */ public interface Formatter { - /** Converts {@code value} to a string for inclusion in a template. */ - String format (Object value); + /** Converts {@code value} to a CharSequence for inclusion in a template. */ + CharSequence format (Object value); } /** Handles lambdas. */ @@ -295,6 +310,11 @@ public class Mustache { /** Returns {@code raw} with the appropriate characters replaced with escape sequences. */ String escape (String raw); + + /** Returns {@code raw} with the appropriate characters replaced with escape sequences. **/ + default CharSequence escape (CharSequence raw) { + return escape(raw.toString()); + } } /** Handles loading partial templates. */ diff --git a/src/main/java/com/samskivert/mustache/Template.java b/src/main/java/com/samskivert/mustache/Template.java index bae9935..4dfc520 100644 --- a/src/main/java/com/samskivert/mustache/Template.java +++ b/src/main/java/com/samskivert/mustache/Template.java @@ -384,9 +384,9 @@ public class Template { abstract void visit (Mustache.Visitor visitor); - protected static void write (Writer out, String data) { + protected static void write (Writer out, CharSequence data) { try { - out.write(data); + out.append(data); } catch (IOException ioe) { throw new MustacheException(ioe); } diff --git a/src/test/java/com/samskivert/mustache/MustacheTest.java b/src/test/java/com/samskivert/mustache/MustacheTest.java index cb1b208..d2a2f23 100644 --- a/src/test/java/com/samskivert/mustache/MustacheTest.java +++ b/src/test/java/com/samskivert/mustache/MustacheTest.java @@ -17,9 +17,6 @@ import java.util.Optional; import java.util.TimeZone; import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; /** * Mustache tests that can only be run on the JVM. Most tests should go in BaseMustacheTest so @@ -54,6 +51,14 @@ public class MustacheTest extends SharedTests }); } + @Test public void testCharSequenceVariable() { + Map ctx = new HashMap<>(); + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append("bar"); + ctx.put("foo", stringBuffer); + test("bar", "{{foo}}", ctx); + } + public interface HasDefault { default String getFoo () { return "bar"; } }