forked from quarkusio/quarkus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- fixes quarkusio#43369
- Loading branch information
Showing
8 changed files
with
195 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
...pendent-projects/qute/core/src/main/java/io/quarkus/qute/CharReplacementResultMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package io.quarkus.qute; | ||
|
||
/** | ||
* Makes it possible to replace chars from Basic Multilingual Plane (BMP). | ||
* | ||
* @see Character#isBmpCodePoint(int) | ||
*/ | ||
abstract class CharReplacementResultMapper implements ResultMapper { | ||
|
||
@Override | ||
public String map(Object result, Expression expression) { | ||
return escape(result.toString()); | ||
} | ||
|
||
String escape(CharSequence value) { | ||
if (value.length() == 0) { | ||
return value.toString(); | ||
} | ||
for (int i = 0; i < value.length(); i++) { | ||
String replacement = replacementFor(value.charAt(i)); | ||
if (replacement != null) { | ||
// In most cases we will not need to escape the value at all | ||
return doEscape(value, i, new StringBuilder(value.subSequence(0, i)).append(replacement)); | ||
} | ||
} | ||
return value.toString(); | ||
} | ||
|
||
private String doEscape(CharSequence value, int index, StringBuilder builder) { | ||
int length = value.length(); | ||
while (++index < length) { | ||
char c = value.charAt(index); | ||
String replacement = replacementFor(c); | ||
if (replacement != null) { | ||
builder.append(replacement); | ||
} else { | ||
builder.append(c); | ||
} | ||
} | ||
return builder.toString(); | ||
} | ||
|
||
protected abstract String replacementFor(char c); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
independent-projects/qute/core/src/main/java/io/quarkus/qute/JsonEscaper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package io.quarkus.qute; | ||
|
||
import java.util.Optional; | ||
|
||
import io.quarkus.qute.TemplateNode.Origin; | ||
|
||
public class JsonEscaper extends CharReplacementResultMapper { | ||
|
||
@Override | ||
public boolean appliesTo(Origin origin, Object result) { | ||
if (result instanceof RawString) { | ||
return false; | ||
} | ||
Optional<Variant> variant = origin.getVariant(); | ||
if (variant.isPresent()) { | ||
String contentType = variant.get().getContentType(); | ||
if (contentType != null) { | ||
return contentType.startsWith(Variant.APPLICATION_JSON); | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
protected String replacementFor(char c) { | ||
// All Unicode characters may be placed within the quotation marks, | ||
// except for the characters that MUST be escaped: quotation mark, | ||
// reverse solidus, and the control characters (U+0000 through U+001F). | ||
// See also https://datatracker.ietf.org/doc/html/rfc8259#autoid-10 | ||
switch (c) { | ||
case '"': | ||
return "\\\""; | ||
case '\\': | ||
return "\\\\"; | ||
case '\r': | ||
return "\\r"; | ||
case '\b': | ||
return "\\b"; | ||
case '\n': | ||
return "\\n"; | ||
case '\t': | ||
return "\\t"; | ||
case '\f': | ||
return "\\f"; | ||
case '/': | ||
return "\\/"; | ||
default: | ||
return c < 32 ? String.format("\\u%04x", (int) c) : null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
independent-projects/qute/core/src/test/java/io/quarkus/qute/JsonEscaperTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package io.quarkus.qute; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import java.io.IOException; | ||
import java.util.Optional; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import io.quarkus.qute.TemplateNode.Origin; | ||
|
||
public class JsonEscaperTest { | ||
|
||
@Test | ||
public void testAppliesTo() { | ||
JsonEscaper json = new JsonEscaper(); | ||
Origin jsonOrigin = new Origin() { | ||
|
||
@Override | ||
public Optional<Variant> getVariant() { | ||
return Optional.of(Variant.forContentType(Variant.APPLICATION_JSON)); | ||
} | ||
|
||
@Override | ||
public String getTemplateId() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public String getTemplateGeneratedId() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public int getLineCharacterStart() { | ||
return 0; | ||
} | ||
|
||
@Override | ||
public int getLineCharacterEnd() { | ||
return 0; | ||
} | ||
|
||
@Override | ||
public int getLine() { | ||
return 0; | ||
} | ||
}; | ||
assertFalse(json.appliesTo(jsonOrigin, new RawString("foo"))); | ||
assertTrue(json.appliesTo(jsonOrigin, "foo")); | ||
} | ||
|
||
@Test | ||
public void testEscaping() throws IOException { | ||
JsonEscaper json = new JsonEscaper(); | ||
assertEquals("Čolek 1", json.escape("Čolek 1")); | ||
assertEquals("\\rČolek\\n", json.escape("\rČolek\n")); | ||
assertEquals("\\tČolek", json.escape("\tČolek")); | ||
assertEquals("\\\"tČolek", json.escape("\"tČolek")); | ||
assertEquals("\\\\tČolek", json.escape("\\tČolek")); | ||
assertEquals("\\\\u005C", json.escape("\\u005C")); | ||
assertEquals("\\u000bČolek", json.escape("\u000BČolek")); | ||
assertEquals("\\\\u000BČolek", json.escape("\\u000BČolek")); | ||
// Control char - start of Header | ||
assertEquals("\\u0001", json.escape("\u0001")); | ||
} | ||
|
||
} |