View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.text;
18  
19  import org.junit.jupiter.api.Test;
20  
21  import java.io.IOException;
22  import java.io.StringWriter;
23  import java.lang.reflect.Constructor;
24  import java.lang.reflect.Modifier;
25  import java.nio.charset.Charset;
26  import java.nio.charset.StandardCharsets;
27  import java.nio.file.Files;
28  import java.nio.file.Paths;
29  
30  import static org.apache.commons.text.StringEscapeUtils.escapeXSI;
31  import static org.apache.commons.text.StringEscapeUtils.unescapeXSI;
32  import static org.junit.jupiter.api.Assertions.assertEquals;
33  import static org.junit.jupiter.api.Assertions.assertFalse;
34  import static org.junit.jupiter.api.Assertions.assertNotNull;
35  import static org.junit.jupiter.api.Assertions.assertNull;
36  import static org.junit.jupiter.api.Assertions.assertTrue;
37  import static org.junit.jupiter.api.Assertions.fail;
38  
39  /**
40   * Unit tests for {@link StringEscapeUtils}.
41   *
42   * <p>
43   * This code has been adapted from Apache Commons Lang 3.5.
44   * </p>
45   *
46   */
47  public class StringEscapeUtilsTest {
48      private static final String FOO = "foo";
49  
50      @Test
51      public void testConstructor() {
52          assertNotNull(new StringEscapeUtils());
53          final Constructor<?>[] cons = StringEscapeUtils.class.getDeclaredConstructors();
54          assertEquals(1, cons.length);
55          assertTrue(Modifier.isPublic(cons[0].getModifiers()));
56          assertTrue(Modifier.isPublic(StringEscapeUtils.class.getModifiers()));
57          assertFalse(Modifier.isFinal(StringEscapeUtils.class.getModifiers()));
58      }
59  
60      @Test
61      public void testEscapeJava() throws IOException {
62          assertNull(StringEscapeUtils.escapeJava(null));
63          try {
64              StringEscapeUtils.ESCAPE_JAVA.translate(null, null);
65              fail("Exception expected!");
66          } catch (final IOException ex) {
67              fail("Exception expected!");
68          } catch (final IllegalArgumentException ex) {
69          }
70          try {
71              StringEscapeUtils.ESCAPE_JAVA.translate("", null);
72              fail("Exception expected!");
73          } catch (final IOException ex) {
74              fail("Exception expected!");
75          } catch (final IllegalArgumentException ex) {
76          }
77  
78          assertEscapeJava("", "", "empty string");
79          assertEscapeJava(FOO, FOO);
80          assertEscapeJava("\\t", "\t", "tab");
81          assertEscapeJava("\\\\", "\\", "backslash");
82          assertEscapeJava("'", "'", "single quote should not be escaped");
83          assertEscapeJava("\\\\\\b\\t\\r", "\\\b\t\r");
84          assertEscapeJava("\\u1234", "\u1234");
85          assertEscapeJava("\\u0234", "\u0234");
86          assertEscapeJava("\\u00EF", "\u00ef");
87          assertEscapeJava("\\u0001", "\u0001");
88          assertEscapeJava("\\uABCD", "\uabcd", "Should use capitalized Unicode hex");
89  
90          assertEscapeJava("He didn't say, \\\"stop!\\\"",
91                  "He didn't say, \"stop!\"");
92          assertEscapeJava("This space is non-breaking:" + "\\u00A0", "This space is non-breaking:\u00a0",
93                  "non-breaking space");
94          assertEscapeJava("\\uABCD\\u1234\\u012C",
95                  "\uABCD\u1234\u012C");
96      }
97  
98      /**
99       * Tests https://issues.apache.org/jira/browse/LANG-421
100      */
101     @Test
102     public void testEscapeJavaWithSlash() {
103         final String input = "String with a slash (/) in it";
104 
105         final String expected = input;
106         final String actual = StringEscapeUtils.escapeJava(input);
107 
108         /**
109          * In 2.4 StringEscapeUtils.escapeJava(String) escapes '/' characters, which are not a valid character
110          * to escape in a Java string.
111          */
112         assertEquals(expected, actual);
113     }
114 
115     private void assertEscapeJava(final String escaped, final String original) throws IOException {
116         assertEscapeJava(escaped, original, null);
117     }
118 
119     private void assertEscapeJava(final String expected, final String original, String message) throws IOException {
120         final String converted = StringEscapeUtils.escapeJava(original);
121         message = "escapeJava(String) failed" + (message == null ? "" : (": " + message));
122         assertEquals(expected, converted, message);
123 
124         final StringWriter writer = new StringWriter();
125         StringEscapeUtils.ESCAPE_JAVA.translate(original, writer);
126         assertEquals(expected, writer.toString());
127     }
128 
129     @Test
130     public void testUnescapeJava() throws IOException {
131         assertNull(StringEscapeUtils.unescapeJava(null));
132         try {
133             StringEscapeUtils.UNESCAPE_JAVA.translate(null, null);
134             fail("Exception expected!");
135         } catch (final IOException ex) {
136             fail("Exception expected!");
137         } catch (final IllegalArgumentException ex) {
138         }
139         try {
140             StringEscapeUtils.UNESCAPE_JAVA.translate("", null);
141             fail("Exception expected!");
142         } catch (final IOException ex) {
143             fail("Exception expected!");
144         } catch (final IllegalArgumentException ex) {
145         }
146         try {
147             StringEscapeUtils.unescapeJava("\\u02-3");
148             fail("Exception expected!");
149         } catch (final RuntimeException ex) {
150         }
151 
152         assertUnescapeJava("", "");
153         assertUnescapeJava("test", "test");
154         assertUnescapeJava("\ntest\b", "\\ntest\\b");
155         assertUnescapeJava("\u123425foo\ntest\b", "\\u123425foo\\ntest\\b");
156         assertUnescapeJava("'\foo\teste\r", "\\'\\foo\\teste\\r");
157         assertUnescapeJava("", "\\");
158         //foo
159         assertUnescapeJava("\uABCDx", "\\uabcdx", "lowercase Unicode");
160         assertUnescapeJava("\uABCDx", "\\uABCDx", "uppercase Unicode");
161         assertUnescapeJava("\uABCD", "\\uabcd", "Unicode as final character");
162     }
163 
164     private void assertUnescapeJava(final String unescaped, final String original) throws IOException {
165         assertUnescapeJava(unescaped, original, null);
166     }
167 
168     private void assertUnescapeJava(final String unescaped, final String original, final String message)
169             throws IOException {
170         final String expected = unescaped;
171         final String actual = StringEscapeUtils.unescapeJava(original);
172 
173         assertEquals(expected, actual, "unescape(String) failed"
174                         + (message == null ? "" : (": " + message))
175                         + ": expected '" + StringEscapeUtils.escapeJava(expected)
176                         // we escape this so we can see it in the error message
177                         + "' actual '" + StringEscapeUtils.escapeJava(actual) + "'");
178 
179         final StringWriter writer = new StringWriter();
180         StringEscapeUtils.UNESCAPE_JAVA.translate(original, writer);
181         assertEquals(unescaped, writer.toString());
182     }
183 
184     @Test
185     public void testEscapeEcmaScript() {
186         assertNull(StringEscapeUtils.escapeEcmaScript(null));
187         try {
188             StringEscapeUtils.ESCAPE_ECMASCRIPT.translate(null, null);
189             fail("Exception expected!");
190         } catch (final IOException ex) {
191             fail("Exception expected!");
192         } catch (final IllegalArgumentException ex) {
193         }
194         try {
195             StringEscapeUtils.ESCAPE_ECMASCRIPT.translate("", null);
196             fail("Exception expected!");
197         } catch (final IOException ex) {
198             fail("Exception expected!");
199         } catch (final IllegalArgumentException ex) {
200         }
201 
202         assertEquals("He didn\\'t say, \\\"stop!\\\"", StringEscapeUtils.escapeEcmaScript("He didn't say, \"stop!\""));
203         assertEquals("document.getElementById(\\\"test\\\").value = \\'<script>alert(\\'aaa\\');<\\/script>\\';",
204                 StringEscapeUtils.escapeEcmaScript(
205                         "document.getElementById(\"test\").value = '<script>alert('aaa');</script>';"));
206     }
207 
208 
209     // HTML and XML
210     //--------------------------------------------------------------
211 
212     private static final String[][] HTML_ESCAPES = {
213             {"no escaping", "plain text", "plain text"},
214             {"no escaping", "plain text", "plain text"},
215             {"empty string", "", ""},
216             {"null", null, null},
217             {"ampersand", "bread &amp; butter", "bread & butter"},
218             {"quotes", "&quot;bread&quot; &amp; butter", "\"bread\" & butter"},
219             {"final character only", "greater than &gt;", "greater than >"},
220             {"first character only", "&lt; less than", "< less than"},
221             {"apostrophe", "Huntington's chorea", "Huntington's chorea"},
222             {"languages", "English,Fran&ccedil;ais,\u65E5\u672C\u8A9E (nihongo)",
223                 "English,Fran\u00E7ais,\u65E5\u672C\u8A9E (nihongo)"},
224             {"8-bit ascii shouldn't number-escape", "\u0080\u009F", "\u0080\u009F"},
225     };
226 
227     @Test
228     public void testEscapeHtml3() {
229         for (final String[] element : HTML_ESCAPES) {
230             final String message = element[0];
231             final String expected = element[1];
232             final String original = element[2];
233             assertEquals(expected, StringEscapeUtils.escapeHtml4(original), message);
234             final StringWriter sw = new StringWriter();
235             try {
236                 StringEscapeUtils.ESCAPE_HTML3.translate(original, sw);
237             } catch (final IOException e) {
238             }
239             final String actual = original == null ? null : sw.toString();
240             assertEquals(expected, actual, message);
241         }
242     }
243 
244     @Test
245     public void testUnescapeHtml3() {
246         for (final String[] element : HTML_ESCAPES) {
247             final String message = element[0];
248             final String expected = element[2];
249             final String original = element[1];
250             assertEquals(expected, StringEscapeUtils.unescapeHtml3(original), message);
251 
252             final StringWriter sw = new StringWriter();
253             try {
254                 StringEscapeUtils.UNESCAPE_HTML3.translate(original, sw);
255             } catch (final IOException e) {
256             }
257             final String actual = original == null ? null : sw.toString();
258             assertEquals(expected, actual, message);
259         }
260         // \u00E7 is a cedilla (c with wiggle under)
261         // note that the test string must be 7-bit-clean (Unicode escaped) or else it will compile incorrectly
262         // on some locales
263         assertEquals("Fran\u00E7ais", StringEscapeUtils.unescapeHtml3("Fran\u00E7ais"), "funny chars pass through OK");
264 
265         assertEquals("Hello&;World", StringEscapeUtils.unescapeHtml3("Hello&;World"));
266         assertEquals("Hello&#;World", StringEscapeUtils.unescapeHtml3("Hello&#;World"));
267         assertEquals("Hello&# ;World", StringEscapeUtils.unescapeHtml3("Hello&# ;World"));
268         assertEquals("Hello&##;World", StringEscapeUtils.unescapeHtml3("Hello&##;World"));
269     }
270 
271 @Test
272     public void testEscapeHtml4() {
273         for (final String[] element : HTML_ESCAPES) {
274             final String message = element[0];
275             final String expected = element[1];
276             final String original = element[2];
277             assertEquals(expected, StringEscapeUtils.escapeHtml4(original), message);
278             final StringWriter sw = new StringWriter();
279             try {
280                 StringEscapeUtils.ESCAPE_HTML4.translate(original, sw);
281             } catch (final IOException e) {
282             }
283             final String actual = original == null ? null : sw.toString();
284             assertEquals(expected, actual, message);
285         }
286     }
287 
288     @Test
289     public void testUnescapeHtml4() {
290         for (final String[] element : HTML_ESCAPES) {
291             final String message = element[0];
292             final String expected = element[2];
293             final String original = element[1];
294             assertEquals(expected, StringEscapeUtils.unescapeHtml4(original), message);
295 
296             final StringWriter sw = new StringWriter();
297             try {
298                 StringEscapeUtils.UNESCAPE_HTML4.translate(original, sw);
299             } catch (final IOException e) {
300             }
301             final String actual = original == null ? null : sw.toString();
302             assertEquals(expected, actual, message);
303         }
304         // \u00E7 is a cedilla (c with wiggle under)
305         // note that the test string must be 7-bit-clean (Unicode escaped) or else it will compile incorrectly
306         // on some locales
307         assertEquals("Fran\u00E7ais", StringEscapeUtils.unescapeHtml4("Fran\u00E7ais"), "funny chars pass through OK");
308 
309         assertEquals("Hello&;World", StringEscapeUtils.unescapeHtml4("Hello&;World"));
310         assertEquals("Hello&#;World", StringEscapeUtils.unescapeHtml4("Hello&#;World"));
311         assertEquals("Hello&# ;World", StringEscapeUtils.unescapeHtml4("Hello&# ;World"));
312         assertEquals("Hello&##;World", StringEscapeUtils.unescapeHtml4("Hello&##;World"));
313     }
314 
315     @Test
316     public void testUnescapeHexCharsHtml() {
317         // Simple easy to grok test
318         assertEquals("\u0080\u009F", StringEscapeUtils.unescapeHtml4("&#x80;&#x9F;"), "hex number unescape");
319         assertEquals("\u0080\u009F", StringEscapeUtils.unescapeHtml4("&#X80;&#X9F;"), "hex number unescape");
320         // Test all Character values:
321         for (char i = Character.MIN_VALUE; i < Character.MAX_VALUE; i++) {
322             final Character c1 = i;
323             final Character c2 = (char) (i + 1);
324             final String expected = c1.toString() + c2.toString();
325             final String escapedC1 = "&#x" + Integer.toHexString(c1) + ";";
326             final String escapedC2 = "&#x" + Integer.toHexString(c2) + ";";
327             assertEquals(expected, StringEscapeUtils.unescapeHtml4(escapedC1 + escapedC2),
328                     "hex number unescape index " + i);
329         }
330     }
331 
332     @Test
333     public void testUnescapeUnknownEntity() {
334         assertEquals("&zzzz;", StringEscapeUtils.unescapeHtml4("&zzzz;"));
335     }
336 
337     @Test
338     public void testEscapeHtmlVersions() {
339         assertEquals("&Beta;", StringEscapeUtils.escapeHtml4("\u0392"));
340         assertEquals("\u0392", StringEscapeUtils.unescapeHtml4("&Beta;"));
341 
342         // TODO: refine API for escaping/unescaping specific HTML versions
343     }
344 
345 
346 
347     @Test
348     public void testEscapeXml10() {
349         assertEquals("a&lt;b&gt;c&quot;d&apos;e&amp;f", StringEscapeUtils.escapeXml10("a<b>c\"d'e&f"));
350         assertEquals("a\tb\rc\nd", StringEscapeUtils.escapeXml10("a\tb\rc\nd"),
351                 "XML 1.0 should not escape \t \n \r");
352         assertEquals("ab", StringEscapeUtils.escapeXml10("a\u0000\u0001\u0008\u000b\u000c\u000e\u001fb"),
353                 "XML 1.0 should omit most #x0-x8 | #xb | #xc | #xe-#x19");
354         assertEquals("a\ud7ff  \ue000b", StringEscapeUtils.escapeXml10("a\ud7ff\ud800 \udfff \ue000b"),
355                 "XML 1.0 should omit #xd800-#xdfff");
356         assertEquals("a\ufffdb", StringEscapeUtils.escapeXml10("a\ufffd\ufffe\uffffb"),
357                 "XML 1.0 should omit #xfffe | #xffff");
358         assertEquals("a\u007e&#127;&#132;\u0085&#134;&#159;\u00a0b",
359                 StringEscapeUtils.escapeXml10("a\u007e\u007f\u0084\u0085\u0086\u009f\u00a0b"),
360                 "XML 1.0 should escape #x7f-#x84 | #x86 - #x9f, for XML 1.1 compatibility");
361     }
362 
363     @Test
364     public void testEscapeXml11() {
365         assertEquals("a&lt;b&gt;c&quot;d&apos;e&amp;f", StringEscapeUtils.escapeXml11("a<b>c\"d'e&f"));
366         assertEquals("a\tb\rc\nd", StringEscapeUtils.escapeXml11("a\tb\rc\nd"),
367                 "XML 1.1 should not escape \t \n \r");
368         assertEquals("ab", StringEscapeUtils.escapeXml11("a\u0000b"),
369                 "XML 1.1 should omit #x0");
370         assertEquals("a&#1;&#8;&#11;&#12;&#14;&#31;b",
371                 StringEscapeUtils.escapeXml11("a\u0001\u0008\u000b\u000c\u000e\u001fb"),
372                 "XML 1.1 should escape #x1-x8 | #xb | #xc | #xe-#x19");
373         assertEquals("a\u007e&#127;&#132;\u0085&#134;&#159;\u00a0b",
374                 StringEscapeUtils.escapeXml11("a\u007e\u007f\u0084\u0085\u0086\u009f\u00a0b"),
375                 "XML 1.1 should escape #x7F-#x84 | #x86-#x9F");
376         assertEquals("a\ud7ff  \ue000b", StringEscapeUtils.escapeXml11("a\ud7ff\ud800 \udfff \ue000b"),
377                 "XML 1.1 should omit #xd800-#xdfff");
378         assertEquals("a\ufffdb", StringEscapeUtils.escapeXml11("a\ufffd\ufffe\uffffb"),
379                 "XML 1.1 should omit #xfffe | #xffff");
380     }
381 
382     /**
383      * Reverse of the above.
384      *
385      * @see <a href="https://issues.apache.org/jira/browse/LANG-729">LANG-729</a>
386      */
387     @Test
388     public void testUnescapeXmlSupplementaryCharacters() {
389         assertEquals("\uD84C\uDFB4", StringEscapeUtils.unescapeXml("&#144308;"),
390                 "Supplementary character must be represented using a single escape");
391 
392         assertEquals("a b c \uD84C\uDFB4", StringEscapeUtils.unescapeXml("a b c &#144308;"),
393                 "Supplementary characters mixed with basic characters should be decoded correctly");
394     }
395 
396     // Tests issue #38569
397     // http://issues.apache.org/bugzilla/show_bug.cgi?id=38569
398     @Test
399     public void testStandaloneAmphersand() {
400         assertEquals("<P&O>", StringEscapeUtils.unescapeHtml4("&lt;P&O&gt;"));
401         assertEquals("test & <", StringEscapeUtils.unescapeHtml4("test & &lt;"));
402         assertEquals("<P&O>", StringEscapeUtils.unescapeXml("&lt;P&O&gt;"));
403         assertEquals("test & <", StringEscapeUtils.unescapeXml("test & &lt;"));
404     }
405 
406     @Test
407     public void testLang313() {
408         assertEquals("& &", StringEscapeUtils.unescapeHtml4("& &amp;"));
409     }
410 
411     @Test
412     public void testEscapeCsvString() {
413         assertEquals("foo.bar", StringEscapeUtils.escapeCsv("foo.bar"));
414         assertEquals("\"foo,bar\"", StringEscapeUtils.escapeCsv("foo,bar"));
415         assertEquals("\"foo\nbar\"", StringEscapeUtils.escapeCsv("foo\nbar"));
416         assertEquals("\"foo\rbar\"", StringEscapeUtils.escapeCsv("foo\rbar"));
417         assertEquals("\"foo\"\"bar\"", StringEscapeUtils.escapeCsv("foo\"bar"));
418         assertEquals("foo\uD84C\uDFB4bar", StringEscapeUtils.escapeCsv("foo\uD84C\uDFB4bar"));
419         assertEquals("", StringEscapeUtils.escapeCsv(""));
420         assertNull(StringEscapeUtils.escapeCsv(null));
421     }
422 
423     @Test
424     public void testEscapeCsvWriter() {
425         checkCsvEscapeWriter("foo.bar", "foo.bar");
426         checkCsvEscapeWriter("\"foo,bar\"", "foo,bar");
427         checkCsvEscapeWriter("\"foo\nbar\"", "foo\nbar");
428         checkCsvEscapeWriter("\"foo\rbar\"", "foo\rbar");
429         checkCsvEscapeWriter("\"foo\"\"bar\"", "foo\"bar");
430         checkCsvEscapeWriter("foo\uD84C\uDFB4bar", "foo\uD84C\uDFB4bar");
431         checkCsvEscapeWriter("", null);
432         checkCsvEscapeWriter("", "");
433     }
434 
435     private void checkCsvEscapeWriter(final String expected, final String value) {
436         try {
437             final StringWriter writer = new StringWriter();
438             StringEscapeUtils.ESCAPE_CSV.translate(value, writer);
439             assertEquals(expected, writer.toString());
440         } catch (final IOException e) {
441             fail("Threw: " + e);
442         }
443     }
444 
445     @Test
446     public void testUnescapeCsvString() {
447         assertEquals("foo.bar", StringEscapeUtils.unescapeCsv("foo.bar"));
448         assertEquals("foo,bar", StringEscapeUtils.unescapeCsv("\"foo,bar\""));
449         assertEquals("foo\nbar", StringEscapeUtils.unescapeCsv("\"foo\nbar\""));
450         assertEquals("foo\rbar", StringEscapeUtils.unescapeCsv("\"foo\rbar\""));
451         assertEquals("foo\"bar", StringEscapeUtils.unescapeCsv("\"foo\"\"bar\""));
452         assertEquals("foo\uD84C\uDFB4bar", StringEscapeUtils.unescapeCsv("foo\uD84C\uDFB4bar"));
453         assertEquals("", StringEscapeUtils.unescapeCsv(""));
454         assertNull(StringEscapeUtils.unescapeCsv(null));
455 
456         assertEquals("\"foo.bar\"", StringEscapeUtils.unescapeCsv("\"foo.bar\""));
457     }
458 
459     @Test
460     public void testUnescapeCsvWriter() {
461         checkCsvUnescapeWriter("foo.bar", "foo.bar");
462         checkCsvUnescapeWriter("foo,bar", "\"foo,bar\"");
463         checkCsvUnescapeWriter("foo\nbar", "\"foo\nbar\"");
464         checkCsvUnescapeWriter("foo\rbar", "\"foo\rbar\"");
465         checkCsvUnescapeWriter("foo\"bar", "\"foo\"\"bar\"");
466         checkCsvUnescapeWriter("foo\uD84C\uDFB4bar", "foo\uD84C\uDFB4bar");
467         checkCsvUnescapeWriter("", null);
468         checkCsvUnescapeWriter("", "");
469 
470         checkCsvUnescapeWriter("\"foo.bar\"", "\"foo.bar\"");
471     }
472 
473     private void checkCsvUnescapeWriter(final String expected, final String value) {
474         try {
475             final StringWriter writer = new StringWriter();
476             StringEscapeUtils.UNESCAPE_CSV.translate(value, writer);
477             assertEquals(expected, writer.toString());
478         } catch (final IOException e) {
479             fail("Threw: " + e);
480         }
481     }
482 
483     /**
484      * Tests // https://issues.apache.org/jira/browse/LANG-480
485      */
486     @Test
487     public void testEscapeHtmlHighUnicode() {
488         // this is the utf8 representation of the character:
489         // COUNTING ROD UNIT DIGIT THREE
490         // in Unicode
491         // codepoint: U+1D362
492         final byte[] data = {(byte) 0xF0, (byte) 0x9D, (byte) 0x8D, (byte) 0xA2};
493 
494         final String original = new String(data, Charset.forName("UTF8"));
495 
496         final String escaped = StringEscapeUtils.escapeHtml4(original);
497         assertEquals(original, escaped, "High Unicode should not have been escaped");
498 
499         final String unescaped = StringEscapeUtils.unescapeHtml4(escaped);
500         assertEquals(original, unescaped, "High Unicode should have been unchanged");
501 
502         // TODO: I think this should hold, needs further investigation
503         //        String unescapedFromEntity = StringEscapeUtils.unescapeHtml4("&#119650;");
504         //        assertEquals("High Unicode should have been unescaped", original, unescapedFromEntity);
505     }
506 
507     /**
508      * Tests https://issues.apache.org/jira/browse/LANG-339
509      */
510     @Test
511     public void testEscapeHiragana() {
512         // Some random Japanese Unicode characters
513         final String original = "\u304B\u304C\u3068";
514         final String escaped = StringEscapeUtils.escapeHtml4(original);
515         assertEquals(original, escaped,
516                 "Hiragana character Unicode behaviour should not be being escaped by escapeHtml4");
517 
518         final String unescaped = StringEscapeUtils.unescapeHtml4(escaped);
519 
520         assertEquals(escaped, unescaped, "Hiragana character Unicode behaviour has changed - expected no unescaping");
521     }
522 
523     /**
524      * Tests https://issues.apache.org/jira/browse/LANG-708
525      *
526      * @throws IOException
527      *             if an I/O error occurs
528      */
529     @Test
530     public void testLang708() throws IOException {
531         final byte[] inputBytes = Files.readAllBytes(Paths.get("src/test/resources/stringEscapeUtilsTestData.txt"));
532         final String input = new String(inputBytes, StandardCharsets.UTF_8);
533         final String escaped = StringEscapeUtils.escapeEcmaScript(input);
534         // just the end:
535         assertTrue(escaped.endsWith("}]"), escaped);
536         // a little more:
537         assertTrue(escaped.endsWith("\"valueCode\\\":\\\"\\\"}]"), escaped);
538     }
539 
540     /**
541      * Tests https://issues.apache.org/jira/browse/LANG-911
542      */
543     @Test
544     public void testLang911() {
545         final String bellsTest = "\ud83d\udc80\ud83d\udd14";
546         final String value = StringEscapeUtils.escapeJava(bellsTest);
547         final String valueTest = StringEscapeUtils.unescapeJava(value);
548         assertEquals(bellsTest, valueTest);
549     }
550 
551     @Test
552     public void testEscapeJson() {
553         assertNull(StringEscapeUtils.escapeJson(null));
554         try {
555             StringEscapeUtils.ESCAPE_JSON.translate(null, null);
556             fail("Exception expected!");
557         } catch (final IOException ex) {
558             fail("Exception expected!");
559         } catch (final IllegalArgumentException ex) {
560         }
561         try {
562             StringEscapeUtils.ESCAPE_JSON.translate("", null);
563             fail("Exception expected!");
564         } catch (final IOException ex) {
565             fail("Exception expected!");
566         } catch (final IllegalArgumentException ex) {
567         }
568 
569         assertEquals("He didn't say, \\\"stop!\\\"", StringEscapeUtils.escapeJson("He didn't say, \"stop!\""));
570 
571         final String expected = "\\\"foo\\\" isn't \\\"bar\\\". specials: \\b\\r\\n\\f\\t\\\\\\/";
572         final String input = "\"foo\" isn't \"bar\". specials: \b\r\n\f\t\\/";
573 
574         assertEquals(expected, StringEscapeUtils.escapeJson(input));
575     }
576 
577     @Test
578     public void testBuilder() {
579         final String result =
580                 StringEscapeUtils.builder(StringEscapeUtils.ESCAPE_XML10).escape("<").append(">").toString();
581         assertEquals("&lt;>", result);
582     }
583 
584     @Test
585     public void testEscapeXSI() {
586         assertNull(null, escapeXSI(null));
587         assertEquals("He\\ didn\\'t\\ say,\\ \\\"Stop!\\\"", escapeXSI("He didn't say, \"Stop!\""));
588         assertEquals("\\\\", escapeXSI("\\"));
589         assertEquals("", escapeXSI("\n"));
590     }
591 
592     @Test
593     public void testUnscapeXSI() {
594         assertNull(null, unescapeXSI(null));
595         assertEquals("\"", unescapeXSI("\\\""));
596         assertEquals("He didn't say, \"Stop!\"", unescapeXSI("He\\ didn\\'t\\ say,\\ \\\"Stop!\\\""));
597         assertEquals("\\", unescapeXSI("\\\\"));
598         assertEquals("", unescapeXSI("\\"));
599     }
600 
601     @Test
602     public void testUnescapeEcmaScript() {
603         assertNull(StringEscapeUtils.unescapeEcmaScript(null));
604         assertEquals("8lvc1u+6B#-I", StringEscapeUtils.unescapeEcmaScript("8lvc1u+6B#-I"));
605         assertEquals("<script src=\"build/main.bundle.js\"></script>",
606                 StringEscapeUtils.unescapeEcmaScript("<script src=\"build/main.bundle.js\"></script>"));
607         assertEquals("<script src=\"build/main.bundle.js\"></script>>",
608                 StringEscapeUtils.unescapeEcmaScript("<script src=\"build/main.bundle.js\"></script>>"));
609     }
610 
611     @Test
612     public void testEscapeHtmlThree() {
613         assertNull(StringEscapeUtils.escapeHtml3(null));
614         assertEquals("a", StringEscapeUtils.escapeHtml3("a"));
615         assertEquals("&lt;b&gt;a", StringEscapeUtils.escapeHtml3("<b>a"));
616     }
617 
618     @Test
619     public void testUnescapeJson() {
620         final String jsonString =
621                 "{\"age\":100,\"name\":\"kyong.com\n\",\"messages\":[\"msg 1\",\"msg 2\",\"msg 3\"]}";
622 
623         assertEquals("", StringEscapeUtils.unescapeJson(""));
624         assertEquals(" ", StringEscapeUtils.unescapeJson(" "));
625         assertEquals("a:b", StringEscapeUtils.unescapeJson("a:b"));
626         assertEquals(jsonString, StringEscapeUtils.unescapeJson(jsonString));
627     }
628 
629     @Test // TEXT-120
630     public void testUnescapeJsonDoubleQuoteAndForwardSlash() {
631       final String escapedJsonString = "double quote: \\\" and a forward slash: \\/";
632       final String jsonString = "double quote: \" and a forward slash: /";
633 
634       assertEquals(jsonString, StringEscapeUtils.unescapeJson(escapedJsonString));
635     }
636 }