diff options
author | Kevin de Vlaming | 2018-12-14 00:06:26 +0000 |
---|---|---|
committer | Kevin de Vlaming | 2018-12-14 01:12:21 +0000 |
commit | b08f6820b35251ff6e2d784f77562dae27e1f409 (patch) | |
tree | ebac44a62b662744ca5e18748bb08cbc5033181d | |
parent | 372e02c209c8cf666f1cc93f96ab5a25be8c8979 (diff) | |
download | org.eclipse.mylyn.docs-b08f6820b35251ff6e2d784f77562dae27e1f409.tar.gz org.eclipse.mylyn.docs-b08f6820b35251ff6e2d784f77562dae27e1f409.tar.xz org.eclipse.mylyn.docs-b08f6820b35251ff6e2d784f77562dae27e1f409.zip |
542496: Fix escape behavior in Creole Document Builder
- The previous approach of replacing the tilde literal with its entity
reference when writing to Creole was incorrect, as it would not be
converted back to a literal when parsed via Creole Language
- A better approach is to escape any literal tildes by adding a
preceding tilde. Ie., '~' will be written to Creole as '~~'.
- CreoleLanguage is capable of detecting the first of these two tildes
as an escape then reading the second as a literal, thus ensuring that
during the round trip the tilde is kept intact
- Also added some additional special characters that should be escaped
with a tilde
Change-Id: If0ed1c10ed6ff961f1f15a02ec36fa693b2c6ee3
Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=542496
Signed-off-by: Kevin de Vlaming <kevin.devlaming@tasktop.com>
4 files changed, 73 insertions, 29 deletions
diff --git a/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/main/java/org/eclipse/mylyn/wikitext/creole/internal/CreoleDocumentBuilder.java b/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/main/java/org/eclipse/mylyn/wikitext/creole/internal/CreoleDocumentBuilder.java index fef3c23f2..ceb4463e8 100644 --- a/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/main/java/org/eclipse/mylyn/wikitext/creole/internal/CreoleDocumentBuilder.java +++ b/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/main/java/org/eclipse/mylyn/wikitext/creole/internal/CreoleDocumentBuilder.java @@ -50,17 +50,20 @@ public class CreoleDocumentBuilder extends AbstractMarkupDocumentBuilder { private final boolean emitWhenEmpty; + private final boolean escaping; + public ContentBlock(BlockType blockType, String prefix, String suffix, int precedingNewlineCount, - int trailingNewlineCount, boolean emitWhenEmpty) { + int trailingNewlineCount, boolean emitWhenEmpty, boolean escaping) { super(blockType, precedingNewlineCount, trailingNewlineCount); this.prefix = prefix; this.suffix = suffix; this.emitWhenEmpty = emitWhenEmpty; + this.escaping = escaping; } public ContentBlock(BlockType blockType, String prefix, String suffix, int precedingNewlineCount, int trailingNewlineCount) { - this(blockType, prefix, suffix, precedingNewlineCount, trailingNewlineCount, false); + this(blockType, prefix, suffix, precedingNewlineCount, trailingNewlineCount, false, true); } public ContentBlock(String prefix, String suffix, int precedingNewlineCount, int trailingNewlineCount) { @@ -73,12 +76,20 @@ public class CreoleDocumentBuilder extends AbstractMarkupDocumentBuilder { @Override public void write(int c) throws IOException { - CreoleDocumentBuilder.this.emitContent(c); + if (escaping) { + CreoleDocumentBuilder.this.emitEscapedContent(c); + } else { + CreoleDocumentBuilder.this.emitContent(c); + } } @Override public void write(String s) throws IOException { - CreoleDocumentBuilder.this.emitContent(s); + if (escaping) { + CreoleDocumentBuilder.this.emitEscapedContent(s); + } else { + CreoleDocumentBuilder.this.emitContent(s); + } } @Override @@ -128,7 +139,7 @@ public class CreoleDocumentBuilder extends AbstractMarkupDocumentBuilder { private final LinkAttributes attributes; LinkBlock(LinkAttributes attributes) { - super(null, "", "", 0, 0, true); + super(null, "", "", 0, 0, true, true); this.attributes = attributes; } @@ -154,7 +165,7 @@ public class CreoleDocumentBuilder extends AbstractMarkupDocumentBuilder { private class TableCellBlock extends ContentBlock { public TableCellBlock(BlockType blockType) { - super(blockType, blockType == BlockType.TABLE_CELL_NORMAL ? "|" : "|=", "", 0, 0, true); + super(blockType, blockType == BlockType.TABLE_CELL_NORMAL ? "|" : "|=", "", 0, 0, true, true); } @Override @@ -183,7 +194,7 @@ public class CreoleDocumentBuilder extends AbstractMarkupDocumentBuilder { return new ContentBlock(type, "", "", 2, 2); //$NON-NLS-1$ //$NON-NLS-2$ case PREFORMATTED: case CODE: - return new ContentBlock(type, "{{{\n", "\n}}}", 2, 2); //$NON-NLS-1$ //$NON-NLS-2$ + return new ContentBlock(type, "{{{\n", "\n}}}", 2, 2, false, false); //$NON-NLS-1$ //$NON-NLS-2$ case TABLE: return new SuffixBlock(type, "\n"); //$NON-NLS-1$ case TABLE_CELL_HEADER: @@ -226,7 +237,7 @@ public class CreoleDocumentBuilder extends AbstractMarkupDocumentBuilder { case DELETED: return new ContentBlock("--", "--"); //$NON-NLS-1$ //$NON-NLS-2$ case CODE: - return new ContentBlock("{{{", "}}}"); //$NON-NLS-1$ //$NON-NLS-2$ + return new ContentBlock(null, "{{{", "}}}", 0, 0, false, false); //$NON-NLS-1$ //$NON-NLS-2$ case UNDERLINED: return new ContentBlock("__", "__"); //$NON-NLS-1$ //$NON-NLS-2$ default: @@ -243,19 +254,14 @@ public class CreoleDocumentBuilder extends AbstractMarkupDocumentBuilder { @Override public void characters(String text) { - String escapedText = escapeTilde(text); assertOpenBlock(); try { - currentBlock.write(escapedText); + currentBlock.write(text); } catch (IOException e) { throw new RuntimeException(e); } } - private String escapeTilde(String text) { - return text != null ? text.replace("~", "˜") : null; - } - @Override public void entityReference(String entity) { assertOpenBlock(); @@ -275,24 +281,25 @@ public class CreoleDocumentBuilder extends AbstractMarkupDocumentBuilder { if (url != null) { assertOpenBlock(); try { - currentBlock.write("{{"); - currentBlock.write(url); - writeImageAttributes(attributes); - currentBlock.write("}}"); + Block imageBlock = new ContentBlock(null, "{{", "}}", 0, 0, false, false); + imageBlock.open(); + imageBlock.write(url); + writeImageAttributes(imageBlock, attributes); + imageBlock.close(); } catch (IOException e) { throw new RuntimeException(e); } } } - private void writeImageAttributes(Attributes attributes) throws IOException { + private void writeImageAttributes(Block imageBlock, Attributes attributes) throws IOException { if (attributes instanceof ImageAttributes) { if (!Strings.isNullOrEmpty(((ImageAttributes) attributes).getAlt())) { - currentBlock.write('|'); - currentBlock.write(((ImageAttributes) attributes).getAlt()); + imageBlock.write('|'); + imageBlock.write(((ImageAttributes) attributes).getAlt()); } else if (!Strings.isNullOrEmpty(((ImageAttributes) attributes).getTitle())) { - currentBlock.write('|'); - currentBlock.write(((ImageAttributes) attributes).getTitle()); + imageBlock.write('|'); + imageBlock.write(((ImageAttributes) attributes).getTitle()); } } } @@ -345,6 +352,21 @@ public class CreoleDocumentBuilder extends AbstractMarkupDocumentBuilder { } } + void emitEscapedContent(String s) throws IOException { + if (s != null) { + for (int x = 0; x < s.length(); ++x) { + emitEscapedContent(s.charAt(x)); + } + } + } + + void emitEscapedContent(int c) throws IOException { + if (c == '~' || c == '*' || c == '#' || c == '|' || c == '=') { + super.emitContent('~'); + } + super.emitContent(c); + } + @Override protected Block createImplicitParagraphBlock() { return new ImplicitParagraphBlock(); diff --git a/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/CreoleLanguageIntegrationTest.java b/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/CreoleLanguageIntegrationTest.java index 0e3c8a6b3..1a19e1261 100644 --- a/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/CreoleLanguageIntegrationTest.java +++ b/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/CreoleLanguageIntegrationTest.java @@ -55,11 +55,6 @@ public class CreoleLanguageIntegrationTest extends TestCase { assertRoundTripExact("# item 1\n## item 1.A.\n# item 2\n## item 2.A.\n### item 2.A.i.\n### item 2.A.ii.\n"); } - public void testImageLink() { - assertRoundTripExact( - "This is an image: {{/path/to/img.jpg|An image}}\nThis is an image link: [[http://example.net/|{{/path/to/img.jpg}}]]\nThis is a link: [[http://example.com/|inline link]]\n\n"); - } - public void testTable() { assertRoundTripExact("|=H Col 1|=H Col 2|\n|Cell 1 line 1\\\\Cell 1 line 2|Cell 2|\n\n"); } @@ -237,6 +232,27 @@ public class CreoleLanguageIntegrationTest extends TestCase { out.toString()); } + public void testEscapedCharacters() { + StringWriter out = new StringWriter(); + DocumentBuilder builder = new CreoleDocumentBuilder(out); + builder.beginDocument(); + builder.characters("~ Tilde ~ * Asterix * # Number # | Pipe |"); + builder.endDocument(); + assertParsedHtmlMatchesRoundTrip("<p>~ Tilde ~ * Asterix * # Number # | Pipe |</p>", out.toString()); + } + + public void testEscapedCharactersInCodeSpan() { + StringWriter out = new StringWriter(); + DocumentBuilder builder = new CreoleDocumentBuilder(out); + builder.beginDocument(); + builder.beginSpan(SpanType.CODE, new Attributes()); + builder.characters("~ Tilde ~ * Asterix * # Number # | Pipe |"); + builder.endSpan(); + builder.endDocument(); + assertParsedHtmlMatchesRoundTrip("<p><code>~ Tilde ~ * Asterix * # Number # | Pipe |</code></p>", + out.toString()); + } + private void assertRoundTripExact(String textile) { assertRoundTrip(textile, textile); } diff --git a/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/CreoleLanguageTest.java b/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/CreoleLanguageTest.java index 0bfa040aa..9de3f2b32 100644 --- a/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/CreoleLanguageTest.java +++ b/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/CreoleLanguageTest.java @@ -208,6 +208,12 @@ public class CreoleLanguageTest extends TestCase { content(html)); } + public void testTildeEscape() { + String html = parser.parseToHtml("~~ escape ~* escape ~= escape ~| escape ~# escape"); + + assertEquals("<p>~ escape * escape = escape | escape # escape</p>", content(html)); + } + private String repeat(int i, String string) { StringBuilder buf = new StringBuilder(string.length() * i); for (int x = 0; x < i; ++x) { diff --git a/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/documentbuilder/CreoleDocumentBuilderTest.java b/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/documentbuilder/CreoleDocumentBuilderTest.java index 752ce4c57..90d58821a 100644 --- a/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/documentbuilder/CreoleDocumentBuilderTest.java +++ b/wikitext/core/org.eclipse.mylyn.wikitext.creole/src/test/java/org/eclipse/mylyn/internal/wikitext/creole/tests/documentbuilder/CreoleDocumentBuilderTest.java @@ -74,7 +74,7 @@ public class CreoleDocumentBuilderTest extends AbstractCreoleDocumentBuilderTest builder.characters("this ~ is interpreted as an escape"); builder.endBlock(); builder.endDocument(); - assertMarkup("this ˜ is interpreted as an escape\n\n"); + assertMarkup("this ~~ is interpreted as an escape\n\n"); } public void testEntityReference() { |