Skip to content

Commit

Permalink
Merge pull request #10038 from mkouba/qute-cdata
Browse files Browse the repository at this point in the history
Qute - add unparsed character data
  • Loading branch information
gastaldi authored Jun 16, 2020
2 parents 40f6944 + 6df596a commit 082dba8
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 5 deletions.
6 changes: 5 additions & 1 deletion docs/src/main/asciidoc/qute-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ NOTE: In Quarkus, the caching is done automatically.
The dynamic parts of a template include:

* *Comment*
** `{! This is a comment !}`,
** Starts with `{!` and ends with `!}`: `{! This is a comment !}`,
** Could be multi-line,
** May contain expressions and sections: `{! {#if true} !}`.
* *Expression*
Expand All @@ -93,6 +93,10 @@ The dynamic parts of a template include:
** The name in the closing tag is optional: `{#if active}ACTIVE!{/}`,
** Can be empty: `{#myTag image=true /}`,
** May declare nested section blocks: `{#if item.valid} Valid. {#else} Invalid. {/if}` and decide which block to render.
* *Unparsed Character Data*
** Starts with `{[` and ends with `]}`: `{[ <script>if(true){alert('Qute is cute!')};</script> ]}`,
** Could be multi-line,
** Used to mark the content that should be rendered but not parsed.

=== Identifiers

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class Parser implements Function<String, Expression> {
private static final char START_DELIMITER = '{';
private static final char END_DELIMITER = '}';
private static final char COMMENT_DELIMITER = '!';
private static final char CDATA_START_DELIMITER = '[';
private static final char CDATA_END_DELIMITER = ']';
private static final char UNDERSCORE = '_';
private static final char ESCAPE_CHAR = '\\';

Expand Down Expand Up @@ -151,6 +153,9 @@ private void processCharacter(char character) {
case COMMENT:
comment(character);
break;
case CDATA:
cdata(character);
break;
case TAG_CANDIDATE:
tagCandidate(character);
break;
Expand Down Expand Up @@ -193,6 +198,17 @@ private void comment(char character) {
}
}

private void cdata(char character) {
if (character == END_DELIMITER && buffer.length() > 0 && buffer.charAt(buffer.length() - 1) == CDATA_END_DELIMITER) {
// End of cdata
state = State.TEXT;
buffer.deleteCharAt(buffer.length() - 1);
flushText();
} else {
buffer.append(character);
}
}

private void tag(char character) {
if (character == END_DELIMITER) {
flushTag();
Expand All @@ -205,8 +221,15 @@ private void tagCandidate(char character) {
if (isValidIdentifierStart(character)) {
// Real tag start, flush text if any
flushText();
state = character == COMMENT_DELIMITER ? State.COMMENT : State.TAG_INSIDE;
buffer.append(character);
if (character == COMMENT_DELIMITER) {
buffer.append(character);
state = State.COMMENT;
} else if (character == CDATA_START_DELIMITER) {
state = State.CDATA;
} else {
buffer.append(character);
state = State.TAG_INSIDE;
}
} else {
// Ignore expressions/tags starting with an invalid identifier
buffer.append(START_DELIMITER).append(character);
Expand All @@ -219,8 +242,9 @@ private void tagCandidate(char character) {
}

private boolean isValidIdentifierStart(char character) {
// A valid identifier must start with a digit, alphabet, underscore, comment delimiter or a tag command (e.g. # for sections)
return Tag.isCommand(character) || character == COMMENT_DELIMITER || character == UNDERSCORE
// A valid identifier must start with a digit, alphabet, underscore, comment delimiter, cdata start delimiter or a tag command (e.g. # for sections)
return Tag.isCommand(character) || character == COMMENT_DELIMITER || character == CDATA_START_DELIMITER
|| character == UNDERSCORE
|| Character.isDigit(character)
|| Character.isAlphabetic(character);
}
Expand Down Expand Up @@ -570,6 +594,7 @@ enum State {
TAG_CANDIDATE,
COMMENT,
ESCAPE,
CDATA,

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,21 @@ public void testWhitespace() {
assertEquals("Hello world", engine.parse("Hello {name ?: 'world' }").render());
}

@Test
public void testCdata() {
Engine engine = Engine.builder().addDefaults().build();
String jsSnippet = "<script>const foo = function(){alert('bar');};</script>";
try {
engine.parse("Hello {name} " + jsSnippet);
fail();
} catch (Exception expected) {
}
assertEquals("Hello world <script>const foo = function(){alert('bar');};</script>", engine.parse("Hello {name} {["
+ jsSnippet
+ "]}").data("name", "world").render());
assertEquals("Hello world <strong>", engine.parse("Hello {name} {[<strong>]}").data("name", "world").render());
}

private void assertParserError(String template, String message, int line) {
Engine engine = Engine.builder().addDefaultSectionHelpers().build();
try {
Expand Down

0 comments on commit 082dba8

Please sign in to comment.