Merge branch 'master' of github.com:samskivert/jmustache
This commit is contained in:
@@ -48,6 +48,8 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<source.level>11</source.level>
|
<source.level>11</source.level>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<!-- This will be replaced by the release plugin -->
|
||||||
|
<project.build.outputTimestamp>2023-01-01T00:00:00Z</project.build.outputTimestamp>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -60,7 +62,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>org.yaml</groupId>
|
||||||
<artifactId>snakeyaml</artifactId>
|
<artifactId>snakeyaml</artifactId>
|
||||||
<version>2.0</version>
|
<version>2.2</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@@ -70,7 +72,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.11.0</version>
|
<version>3.12.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>${source.level}</source>
|
<source>${source.level}</source>
|
||||||
<target>${source.level}</target>
|
<target>${source.level}</target>
|
||||||
@@ -159,7 +161,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<version>3.2.2</version>
|
<version>3.2.5</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<includes><include>**/*Test.java</include></includes>
|
<includes><include>**/*Test.java</include></includes>
|
||||||
</configuration>
|
</configuration>
|
||||||
@@ -168,7 +170,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<version>3.6.2</version>
|
<version>3.6.3</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<quiet>true</quiet>
|
<quiet>true</quiet>
|
||||||
<show>public</show>
|
<show>public</show>
|
||||||
|
|||||||
@@ -4,11 +4,17 @@
|
|||||||
|
|
||||||
package com.samskivert.mustache;
|
package com.samskivert.mustache;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines some standard {@link Mustache.Escaper}s.
|
* Defines some standard {@link Mustache.Escaper}s.
|
||||||
*/
|
*/
|
||||||
public class Escapers
|
public class Escapers
|
||||||
{
|
{
|
||||||
|
// TODO for 2.0 this class should be final and have a private constructor but that
|
||||||
|
// would break semver on the off chance someone did extend it.
|
||||||
|
|
||||||
/** Escapes HTML entities. */
|
/** Escapes HTML entities. */
|
||||||
public static final Mustache.Escaper HTML = simple(new String[][] {
|
public static final Mustache.Escaper HTML = simple(new String[][] {
|
||||||
{ "&", "&" },
|
{ "&", "&" },
|
||||||
@@ -30,6 +36,11 @@ public class Escapers
|
|||||||
/** Returns an escaper that replaces a list of text sequences with canned replacements.
|
/** Returns an escaper that replaces a list of text sequences with canned replacements.
|
||||||
* @param repls a list of {@code (text, replacement)} pairs. */
|
* @param repls a list of {@code (text, replacement)} pairs. */
|
||||||
public static Mustache.Escaper simple (final String[]... repls) {
|
public static Mustache.Escaper simple (final String[]... repls) {
|
||||||
|
String[] lookupTable = Lookup7bitEscaper.createTable(repls);
|
||||||
|
if (lookupTable != null) {
|
||||||
|
return new Lookup7bitEscaper(lookupTable);
|
||||||
|
}
|
||||||
|
// our lookup replacements are not 7 bit ascii.
|
||||||
return new Mustache.Escaper() {
|
return new Mustache.Escaper() {
|
||||||
@Override public String escape (String text) {
|
@Override public String escape (String text) {
|
||||||
for (String[] escape : repls) {
|
for (String[] escape : repls) {
|
||||||
@@ -39,4 +50,98 @@ public class Escapers
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// This is based on benchmarking: https://github.com/jstachio/escape-benchmark
|
||||||
|
private static class Lookup7bitEscaper implements Mustache.Escaper {
|
||||||
|
/*
|
||||||
|
* This only works for replacing the lower 7 bit ascii
|
||||||
|
* characters
|
||||||
|
*/
|
||||||
|
private final String[] lookupTable;
|
||||||
|
|
||||||
|
private Lookup7bitEscaper(
|
||||||
|
String[] lookupTable) {
|
||||||
|
super();
|
||||||
|
this.lookupTable = lookupTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
static /* @Nullable */ String[] createTable (String[][] mappings) {
|
||||||
|
String[] table = new String[128];
|
||||||
|
for (String[] entry : mappings) {
|
||||||
|
String key = entry[0];
|
||||||
|
String value = entry[1];
|
||||||
|
if (key.length() != 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
char k = key.charAt(0);
|
||||||
|
if (k > 127) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
table[k] = value;
|
||||||
|
}
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void escape (Appendable a, CharSequence raw) throws IOException {
|
||||||
|
int end = raw.length();
|
||||||
|
for (int i = 0, start = 0; i < end; i++) {
|
||||||
|
char c = raw.charAt(i);
|
||||||
|
String found = escapeChar(lookupTable, c);
|
||||||
|
/*
|
||||||
|
* While this could be done with one loop it appears through
|
||||||
|
* benchmarking that by having the first loop assume the string
|
||||||
|
* to be not changed creates a fast path for strings with no escaping needed.
|
||||||
|
*/
|
||||||
|
if (found != null) {
|
||||||
|
a.append(raw, 0, i);
|
||||||
|
a.append(found);
|
||||||
|
start = i = i + 1;
|
||||||
|
for (; i < end; i++) {
|
||||||
|
c = raw.charAt(i);
|
||||||
|
found = escapeChar(lookupTable, c);
|
||||||
|
if (found != null) {
|
||||||
|
a.append(raw, start, i);
|
||||||
|
a.append(found);
|
||||||
|
start = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.append(raw, start, end);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.append(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static /* @Nullable */ String escapeChar (String[] lookupTable, char c) {
|
||||||
|
if (c > 127) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return lookupTable[c];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String escape (String raw) {
|
||||||
|
StringBuilder sb = new StringBuilder(raw.length());
|
||||||
|
try {
|
||||||
|
escape(sb, raw);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString () {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Escaper[");
|
||||||
|
for(char i = 0; i < lookupTable.length; i++) {
|
||||||
|
String value = lookupTable[i];
|
||||||
|
if (value != null) {
|
||||||
|
sb.append("{'").append(i).append("', '").append(value).append("'},");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append("]");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,6 +317,17 @@ public class Mustache {
|
|||||||
default CharSequence escape (CharSequence raw) {
|
default CharSequence escape (CharSequence raw) {
|
||||||
return escape(raw.toString());
|
return escape(raw.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes the raw characters with escape sequeneces if needed and appends to the appendable.
|
||||||
|
* The default implementation calls {@link #escape(CharSequence)}.
|
||||||
|
* @param a the stream like to append to.
|
||||||
|
* @param raw input string.
|
||||||
|
* @throws IOException if an error happens while writing to the appendable.
|
||||||
|
*/
|
||||||
|
default void escape (Appendable a, CharSequence raw) throws IOException {
|
||||||
|
a.append(escape(raw));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handles loading partial templates. */
|
/** Handles loading partial templates. */
|
||||||
@@ -1317,7 +1328,7 @@ public class Mustache {
|
|||||||
"No key, method or field with name '" + _name + "' on line " + _line;
|
"No key, method or field with name '" + _name + "' on line " + _line;
|
||||||
throw new MustacheException.Context(msg, _name, _line);
|
throw new MustacheException.Context(msg, _name, _line);
|
||||||
}
|
}
|
||||||
write(out, _escaper.escape(_formatter.format(value)));
|
escape(out, _formatter.format(value), _escaper);
|
||||||
}
|
}
|
||||||
@Override public void decompile (Delims delims, StringBuilder into) {
|
@Override public void decompile (Delims delims, StringBuilder into) {
|
||||||
delims.addTag(' ', _name, into);
|
delims.addTag(' ', _name, into);
|
||||||
|
|||||||
@@ -433,6 +433,14 @@ public class Template {
|
|||||||
throw new MustacheException(ioe);
|
throw new MustacheException(ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static void escape (Appendable out, CharSequence data, Mustache.Escaper escape) {
|
||||||
|
try {
|
||||||
|
escape.escape(out, data);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new MustacheException(ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used to cache variable fetchers for a given context class, name combination. */
|
/** Used to cache variable fetchers for a given context class, name combination. */
|
||||||
|
|||||||
Reference in New Issue
Block a user