    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.text;
    018
    019import java.io.IOException;
    020import java.io.Writer;
    021import java.util.Collections;
    022import java.util.HashMap;
    023import java.util.Map;
    024
    025import org.apache.commons.lang3.StringUtils;
    026import org.apache.commons.text.translate.AggregateTranslator;
    027import org.apache.commons.text.translate.CharSequenceTranslator;
    028import org.apache.commons.text.translate.CsvTranslators;
    029import org.apache.commons.text.translate.EntityArrays;
    030import org.apache.commons.text.translate.JavaUnicodeEscaper;
    031import org.apache.commons.text.translate.LookupTranslator;
    032import org.apache.commons.text.translate.NumericEntityEscaper;
    033import org.apache.commons.text.translate.NumericEntityUnescaper;
    034import org.apache.commons.text.translate.OctalUnescaper;
    035import org.apache.commons.text.translate.UnicodeUnescaper;
    036import org.apache.commons.text.translate.UnicodeUnpairedSurrogateRemover;
    037
    038/**
    039 * <p>
    040 * Escapes and unescapes {@code String}s for Java, Java Script, HTML and XML.
    041 * </p>
    042 *
    043 * <p>
    044 * #ThreadSafe#
    045 * </p>
    046 *
    047 * <p>
    048 * This code has been adapted from Apache Commons Lang 3.5.
    049 * </p>
    050 *
    051 * @since 1.0
    052 */
    053public class StringEscapeUtils {
    054
    055    /* ESCAPE TRANSLATORS */
    056
    057    /**
    058     * Convenience wrapper for {@link StringBuilder} providing escape methods.
    059     *
    060     * <p>Example:</p>
    061     * <pre>
    062     * new Builder(ESCAPE_HTML4)
    063     *      .append("&lt;p&gt;")
    064     *      .escape("This is paragraph 1 and special chars like &amp; get escaped.")
    065     *      .append("&lt;/p&gt;&lt;p&gt;")
    066     *      .escape("This is paragraph 2 &amp; more...")
    067     *      .append("&lt;/p&gt;")
    068     *      .toString()
    069     * </pre>
    070     */
    071    public static final class Builder {
    072
    073        /**
    074         * StringBuilder to be used in the Builder class.
    075         */
    076        private final StringBuilder sb;
    077
    078        /**
    079         * CharSequenceTranslator to be used in the Builder class.
    080         */
    081        private final CharSequenceTranslator translator;
    082
    083        /**
    084         * Builder constructor.
    085         *
    086         * @param translator a CharSequenceTranslator.
    087         */
    088        private Builder(final CharSequenceTranslator translator) {
    089            this.sb = new StringBuilder();
    090            this.translator = translator;
    091        }
    092
    093        /**
    094         * Literal append, no escaping being done.
    095         *
    096         * @param input the String to append
    097         * @return {@code this}, to enable chaining
    098         */
    099        public Builder append(final String input) {
    100            sb.append(input);
    101            return this;
    102        }
    103
    104        /**
    105         * Escape {@code input} according to the given {@link CharSequenceTranslator}.
    106         *
    107         * @param input the String to escape
    108         * @return {@code this}, to enable chaining
    109         */
    110        public Builder escape(final String input) {
    111            sb.append(translator.translate(input));
    112            return this;
    113        }
    114
    115        /**
    116         * Return the escaped string.
    117         *
    118         * @return The escaped string
    119         */
    120        @Override
    121        public String toString() {
    122            return sb.toString();
    123        }
    124    }
    125    /**
    126     * Translator object for unescaping backslash escaped entries.
    127     */
    128    static class XsiUnescaper extends CharSequenceTranslator {
    129
    130        /**
    131         * Escaped backslash constant.
    132         */
    133        private static final char BACKSLASH = '\\';
    134
    135        @Override
    136        public int translate(final CharSequence input, final int index, final Writer writer) throws IOException {
    137
    138            if (index != 0) {
    139                throw new IllegalStateException("XsiUnescaper should never reach the [1] index");
    140            }
    141
    142            final String s = input.toString();
    143
    144            int segmentStart = 0;
    145            int searchOffset = 0;
    146            while (true) {
    147                final int pos = s.indexOf(BACKSLASH, searchOffset);
    148                if (pos == -1) {
    149                    if (segmentStart < s.length()) {
    150                        writer.write(s.substring(segmentStart));
    151                    }
    152                    break;
    153                }
    154                if (pos > segmentStart) {
    155                    writer.write(s.substring(segmentStart, pos));
    156                }
    157                segmentStart = pos + 1;
    158                searchOffset = pos + 2;
    159            }
    160
    161            return Character.codePointCount(input, 0, input.length());
    162        }
    163    }
    164
    165    /**
    166     * Translator object for escaping Java.
    167     *
    168     * While {@link #escapeJava(String)} is the expected method of use, this
    169     * object allows the Java escaping functionality to be used
    170     * as the foundation for a custom translator.
    171     */
    172    public static final CharSequenceTranslator ESCAPE_JAVA;
    173    static {
    174        final Map<CharSequence, CharSequence> escapeJavaMap = new HashMap<>();
    175        escapeJavaMap.put("\"", "\\\"");
    176        escapeJavaMap.put("\\", "\\\\");
    177        ESCAPE_JAVA = new AggregateTranslator(
    178                new LookupTranslator(Collections.unmodifiableMap(escapeJavaMap)),
    179                new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE),
    180                JavaUnicodeEscaper.outsideOf(32, 0x7f)
    181        );
    182    }
    183
    184    /**
    185     * Translator object for escaping EcmaScript/JavaScript.
    186     *
    187     * While {@link #escapeEcmaScript(String)} is the expected method of use, this
    188     * object allows the EcmaScript escaping functionality to be used
    189     * as the foundation for a custom translator.
    190     */
    191    public static final CharSequenceTranslator ESCAPE_ECMASCRIPT;
    192    static {
    193        final Map<CharSequence, CharSequence> escapeEcmaScriptMap = new HashMap<>();
    194        escapeEcmaScriptMap.put("'", "\\'");
    195        escapeEcmaScriptMap.put("\"", "\\\"");
    196        escapeEcmaScriptMap.put("\\", "\\\\");
    197        escapeEcmaScriptMap.put("/", "\\/");
    198        ESCAPE_ECMASCRIPT = new AggregateTranslator(
    199                new LookupTranslator(Collections.unmodifiableMap(escapeEcmaScriptMap)),
    200                new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE),
    201                JavaUnicodeEscaper.outsideOf(32, 0x7f)
    202        );
    203    }
    204
    205    /**
    206     * Translator object for escaping Json.
    207     *
    208     * While {@link #escapeJson(String)} is the expected method of use, this
    209     * object allows the Json escaping functionality to be used
    210     * as the foundation for a custom translator.
    211     */
    212    public static final CharSequenceTranslator ESCAPE_JSON;
    213    static {
    214        final Map<CharSequence, CharSequence> escapeJsonMap = new HashMap<>();
    215        escapeJsonMap.put("\"", "\\\"");
    216        escapeJsonMap.put("\\", "\\\\");
    217        escapeJsonMap.put("/", "\\/");
    218        ESCAPE_JSON = new AggregateTranslator(
    219                new LookupTranslator(Collections.unmodifiableMap(escapeJsonMap)),
    220                new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE),
    221                JavaUnicodeEscaper.outsideOf(32, 0x7e)
    222        );
    223    }
    224
    225    /**
    226     * Translator object for escaping XML 1.0.
    227     *
    228     * While {@link #escapeXml10(String)} is the expected method of use, this
    229     * object allows the XML escaping functionality to be used
    230     * as the foundation for a custom translator.
    231     */
    232    public static final CharSequenceTranslator ESCAPE_XML10;
    233    static {
    234        final Map<CharSequence, CharSequence> escapeXml10Map = new HashMap<>();
    235        escapeXml10Map.put("\u0000", StringUtils.EMPTY);
    236        escapeXml10Map.put("\u0001", StringUtils.EMPTY);
    237        escapeXml10Map.put("\u0002", StringUtils.EMPTY);
    238        escapeXml10Map.put("\u0003", StringUtils.EMPTY);
    239        escapeXml10Map.put("\u0004", StringUtils.EMPTY);
    240        escapeXml10Map.put("\u0005", StringUtils.EMPTY);
    241        escapeXml10Map.put("\u0006", StringUtils.EMPTY);
    242        escapeXml10Map.put("\u0007", StringUtils.EMPTY);
    243        escapeXml10Map.put("\u0008", StringUtils.EMPTY);
    244        escapeXml10Map.put("\u000b", StringUtils.EMPTY);
    245        escapeXml10Map.put("\u000c", StringUtils.EMPTY);
    246        escapeXml10Map.put("\u000e", StringUtils.EMPTY);
    247        escapeXml10Map.put("\u000f", StringUtils.EMPTY);
    248        escapeXml10Map.put("\u0010", StringUtils.EMPTY);
    249        escapeXml10Map.put("\u0011", StringUtils.EMPTY);
    250        escapeXml10Map.put("\u0012", StringUtils.EMPTY);
    251        escapeXml10Map.put("\u0013", StringUtils.EMPTY);
    252        escapeXml10Map.put("\u0014", StringUtils.EMPTY);
    253        escapeXml10Map.put("\u0015", StringUtils.EMPTY);
    254        escapeXml10Map.put("\u0016", StringUtils.EMPTY);
    255        escapeXml10Map.put("\u0017", StringUtils.EMPTY);
    256        escapeXml10Map.put("\u0018", StringUtils.EMPTY);
    257        escapeXml10Map.put("\u0019", StringUtils.EMPTY);
    258        escapeXml10Map.put("\u001a", StringUtils.EMPTY);
    259        escapeXml10Map.put("\u001b", StringUtils.EMPTY);
    260        escapeXml10Map.put("\u001c", StringUtils.EMPTY);
    261        escapeXml10Map.put("\u001d", StringUtils.EMPTY);
    262        escapeXml10Map.put("\u001e", StringUtils.EMPTY);
    263        escapeXml10Map.put("\u001f", StringUtils.EMPTY);
    264        escapeXml10Map.put("\ufffe", StringUtils.EMPTY);
    265        escapeXml10Map.put("\uffff", StringUtils.EMPTY);
    266        ESCAPE_XML10 = new AggregateTranslator(
    267                new LookupTranslator(EntityArrays.BASIC_ESCAPE),
    268                new LookupTranslator(EntityArrays.APOS_ESCAPE),
    269                new LookupTranslator(Collections.unmodifiableMap(escapeXml10Map)),
    270                NumericEntityEscaper.between(0x7f, 0x84),
    271                NumericEntityEscaper.between(0x86, 0x9f),
    272                new UnicodeUnpairedSurrogateRemover()
    273        );
    274    }
    275
    276    /**
    277     * Translator object for escaping XML 1.1.
    278     *
    279     * While {@link #escapeXml11(String)} is the expected method of use, this
    280     * object allows the XML escaping functionality to be used
    281     * as the foundation for a custom translator.
    282     */
    283    public static final CharSequenceTranslator ESCAPE_XML11;
    284
    285    static {
    286        final Map<CharSequence, CharSequence> escapeXml11Map = new HashMap<>();
    287        escapeXml11Map.put("\u0000", StringUtils.EMPTY);
    288        escapeXml11Map.put("\u000b", "&#11;");
    289        escapeXml11Map.put("\u000c", "&#12;");
    290        escapeXml11Map.put("\ufffe", StringUtils.EMPTY);
    291        escapeXml11Map.put("\uffff", StringUtils.EMPTY);
    292        ESCAPE_XML11 = new AggregateTranslator(
    293                new LookupTranslator(EntityArrays.BASIC_ESCAPE),
    294                new LookupTranslator(EntityArrays.APOS_ESCAPE),
    295                new LookupTranslator(Collections.unmodifiableMap(escapeXml11Map)),
    296                NumericEntityEscaper.between(0x1, 0x8),
    297                NumericEntityEscaper.between(0xe, 0x1f),
    298                NumericEntityEscaper.between(0x7f, 0x84),
    299                NumericEntityEscaper.between(0x86, 0x9f),
    300                new UnicodeUnpairedSurrogateRemover()
    301        );
    302    }
    303
    304    /**
    305     * Translator object for escaping HTML version 3.0.
    306     *
    307     * While {@link #escapeHtml3(String)} is the expected method of use, this
    308     * object allows the HTML escaping functionality to be used
    309     * as the foundation for a custom translator.
    310     */
    311    public static final CharSequenceTranslator ESCAPE_HTML3 =
    312            new AggregateTranslator(
    313                    new LookupTranslator(EntityArrays.BASIC_ESCAPE),
    314                    new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE)
    315            );
    316
    317    /**
    318     * Translator object for escaping HTML version 4.0.
    319     *
    320     * While {@link #escapeHtml4(String)} is the expected method of use, this
    321     * object allows the HTML escaping functionality to be used
    322     * as the foundation for a custom translator.
    323     */
    324    public static final CharSequenceTranslator ESCAPE_HTML4 =
    325            new AggregateTranslator(
    326                    new LookupTranslator(EntityArrays.BASIC_ESCAPE),
    327                    new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE),
    328                    new LookupTranslator(EntityArrays.HTML40_EXTENDED_ESCAPE)
    329            );
    330    /**
    331     * Translator object for escaping individual Comma Separated Values.
    332     *
    333     * While {@link #escapeCsv(String)} is the expected method of use, this
    334     * object allows the CSV escaping functionality to be used
    335     * as the foundation for a custom translator.
    336     */
    337    public static final CharSequenceTranslator ESCAPE_CSV = new CsvTranslators.CsvEscaper();
    338
    339    /* UNESCAPE TRANSLATORS */
    340
    341    /**
    342     * Translator object for escaping Shell command language.
    343     *
    344     * @see <a href="https://pubs.opengroup.org/onlinepubs/7908799/xcu/chap2.html">Shell Command Language</a>
    345     */
    346    public static final CharSequenceTranslator ESCAPE_XSI;
    347    static {
    348        final Map<CharSequence, CharSequence> escapeXsiMap = new HashMap<>();
    349        escapeXsiMap.put("|", "\\|");
    350        escapeXsiMap.put("&", "\\&");
    351        escapeXsiMap.put(";", "\\;");
    352        escapeXsiMap.put("<", "\\<");
    353        escapeXsiMap.put(">", "\\>");
    354        escapeXsiMap.put("(", "\\(");
    355        escapeXsiMap.put(")", "\\)");
    356        escapeXsiMap.put("$", "\\$");
    357        escapeXsiMap.put("`", "\\`");
    358        escapeXsiMap.put("\\", "\\\\");
    359        escapeXsiMap.put("\"", "\\\"");
    360        escapeXsiMap.put("'", "\\'");
    361        escapeXsiMap.put(" ", "\\ ");
    362        escapeXsiMap.put("\t", "\\\t");
    363        escapeXsiMap.put("\r\n", StringUtils.EMPTY);
    364        escapeXsiMap.put("\n", StringUtils.EMPTY);
    365        escapeXsiMap.put("*", "\\*");
    366        escapeXsiMap.put("?", "\\?");
    367        escapeXsiMap.put("[", "\\[");
    368        escapeXsiMap.put("#", "\\#");
    369        escapeXsiMap.put("~", "\\~");
    370        escapeXsiMap.put("=", "\\=");
    371        escapeXsiMap.put("%", "\\%");
    372        ESCAPE_XSI = new LookupTranslator(
    373                Collections.unmodifiableMap(escapeXsiMap)
    374        );
    375    }
    376
    377    /**
    378     * Translator object for unescaping escaped Java.
    379     *
    380     * While {@link #unescapeJava(String)} is the expected method of use, this
    381     * object allows the Java unescaping functionality to be used
    382     * as the foundation for a custom translator.
    383     */
    384    public static final CharSequenceTranslator UNESCAPE_JAVA;
    385
    386    static {
    387        final Map<CharSequence, CharSequence> unescapeJavaMap = new HashMap<>();
    388        unescapeJavaMap.put("\\\\", "\\");
    389        unescapeJavaMap.put("\\\"", "\"");
    390        unescapeJavaMap.put("\\'", "'");
    391        unescapeJavaMap.put("\\", StringUtils.EMPTY);
    392        UNESCAPE_JAVA = new AggregateTranslator(
    393                new OctalUnescaper(),     // .between('\1', '\377'),
    394                new UnicodeUnescaper(),
    395                new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_UNESCAPE),
    396                new LookupTranslator(Collections.unmodifiableMap(unescapeJavaMap))
    397        );
    398    }
    399
    400    /**
    401     * Translator object for unescaping escaped EcmaScript.
    402     *
    403     * While {@link #unescapeEcmaScript(String)} is the expected method of use, this
    404     * object allows the EcmaScript unescaping functionality to be used
    405     * as the foundation for a custom translator.
    406     */
    407    public static final CharSequenceTranslator UNESCAPE_ECMASCRIPT = UNESCAPE_JAVA;
    408
    409    /**
    410     * Translator object for unescaping escaped Json.
    411     *
    412     * While {@link #unescapeJson(String)} is the expected method of use, this
    413     * object allows the Json unescaping functionality to be used
    414     * as the foundation for a custom translator.
    415     */
    416    public static final CharSequenceTranslator UNESCAPE_JSON = UNESCAPE_JAVA;
    417
    418    /**
    419     * Translator object for unescaping escaped HTML 3.0.
    420     *
    421     * While {@link #unescapeHtml3(String)} is the expected method of use, this
    422     * object allows the HTML unescaping functionality to be used
    423     * as the foundation for a custom translator.
    424     */
    425    public static final CharSequenceTranslator UNESCAPE_HTML3 =
    426            new AggregateTranslator(
    427                    new LookupTranslator(EntityArrays.BASIC_UNESCAPE),
    428                    new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE),
    429                    new NumericEntityUnescaper()
    430            );
    431
    432    /**
    433     * Translator object for unescaping escaped HTML 4.0.
    434     *
    435     * While {@link #unescapeHtml4(String)} is the expected method of use, this
    436     * object allows the HTML unescaping functionality to be used
    437     * as the foundation for a custom translator.
    438     */
    439    public static final CharSequenceTranslator UNESCAPE_HTML4 =
    440            new AggregateTranslator(
    441                    new LookupTranslator(EntityArrays.BASIC_UNESCAPE),
    442                    new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE),
    443                    new LookupTranslator(EntityArrays.HTML40_EXTENDED_UNESCAPE),
    444                    new NumericEntityUnescaper()
    445            );
    446
    447    /**
    448     * Translator object for unescaping escaped XML.
    449     *
    450     * While {@link #unescapeXml(String)} is the expected method of use, this
    451     * object allows the XML unescaping functionality to be used
    452     * as the foundation for a custom translator.
    453     */
    454    public static final CharSequenceTranslator UNESCAPE_XML =
    455            new AggregateTranslator(
    456                    new LookupTranslator(EntityArrays.BASIC_UNESCAPE),
    457                    new LookupTranslator(EntityArrays.APOS_UNESCAPE),
    458                    new NumericEntityUnescaper()
    459            );
    460
    461    /**
    462     * Translator object for unescaping escaped Comma Separated Value entries.
    463     *
    464     * While {@link #unescapeCsv(String)} is the expected method of use, this
    465     * object allows the CSV unescaping functionality to be used
    466     * as the foundation for a custom translator.
    467     */
    468    public static final CharSequenceTranslator UNESCAPE_CSV = new CsvTranslators.CsvUnescaper();
    469
    470    /* Helper functions */
    471
    472    /**
    473     * Translator object for unescaping escaped XSI Value entries.
    474     *
    475     * While {@link #unescapeXSI(String)}  is the expected method of use, this
    476     * object allows the XSI unescaping functionality to be used
    477     * as the foundation for a custom translator.
    478     */
    479    public static final CharSequenceTranslator UNESCAPE_XSI = new XsiUnescaper();
    480
    481    /**
    482     * Gets a {@link Builder}.
    483     * @param translator the text translator
    484     * @return {@link Builder}
    485     */
    486    public static StringEscapeUtils.Builder builder(final CharSequenceTranslator translator) {
    487        return new Builder(translator);
    488    }
    489
    490    /**
    491     * Returns a {@code String} value for a CSV column enclosed in double quotes,
    492     * if required.
    493     *
    494     * <p>If the value contains a comma, newline or double quote, then the
    495     *    String value is returned enclosed in double quotes.</p>
    496     *
    497     * <p>Any double quote characters in the value are escaped with another double quote.</p>
    498     *
    499     * <p>If the value does not contain a comma, newline or double quote, then the
    500     *    String value is returned unchanged.</p>
    501     *
    502     * see <a href="https://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
    503     * <a href="https://tools.ietf.org/html/rfc4180">RFC 4180</a>.
    504     *
    505     * @param input the input CSV column String, may be null
    506     * @return The input String, enclosed in double quotes if the value contains a comma,
    507     * newline or double quote, {@code null} if null string input
    508     */
    509    public static final String escapeCsv(final String input) {
    510        return ESCAPE_CSV.translate(input);
    511    }
    512
    513    /**
    514     * Escapes the characters in a {@code String} using EcmaScript String rules.
    515     *
    516     * <p>Escapes any values it finds into their EcmaScript String form.
    517     * Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
    518     *
    519     * <p>So a tab becomes the characters {@code '\\'} and
    520     * {@code 't'}.</p>
    521     *
    522     * <p>The only difference between Java strings and EcmaScript strings
    523     * is that in EcmaScript, a single quote and forward-slash (/) are escaped.</p>
    524     *
    525     * <p>Note that EcmaScript is best known by the JavaScript and ActionScript dialects.</p>
    526     *
    527     * <p>Example:</p>
    528     * <pre>
    529     * input string: He didn't say, "Stop!"
    530     * output string: He didn\'t say, \"Stop!\"
    531     * </pre>
    532     *
    533     * <b>Security Note.</b> We only provide backslash escaping in this method. For example, {@code '\"'} has the output
    534     * {@code '\\\"'} which could result in potential issues in the case where the string being escaped is being used
    535     * in an HTML tag like {@code <select onmouseover="..." />}. If you wish to have more rigorous string escaping, you
    536     * may consider the
    537     * <a href="https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API_JAVA">ESAPI Libraries</a>.
    538     * Further, you can view the <a href="https://github.com/esapi">ESAPI GitHub Org</a>.
    539     *
    540     * @param input  String to escape values in, may be null
    541     * @return String with escaped values, {@code null} if null string input
    542     */
    543    public static final String escapeEcmaScript(final String input) {
    544        return ESCAPE_ECMASCRIPT.translate(input);
    545    }
    546
    547    /**
    548     * Escapes the characters in a {@code String} using HTML entities.
    549     *
    550     * <p>Supports only the HTML 3.0 entities.</p>
    551     *
    552     * @param input  the {@code String} to escape, may be null
    553     * @return a new escaped {@code String}, {@code null} if null string input
    554     */
    555    public static final String escapeHtml3(final String input) {
    556        return ESCAPE_HTML3.translate(input);
    557    }
    558
    559    // HTML and XML
    560    /**
    561     * Escapes the characters in a {@code String} using HTML entities.
    562     *
    563     * <p>
    564     * For example:
    565     * </p>
    566     * <p>{@code "bread" &amp; "butter"}</p>
    567     * becomes:
    568     * <p>
    569     * {@code &quot;bread&quot; &amp;amp; &quot;butter&quot;}.
    570     * </p>
    571     *
    572     * <p>Supports all known HTML 4.0 entities, including funky accents.
    573     * Note that the commonly used apostrophe escape character (&amp;apos;)
    574     * is not a legal entity and so is not supported).</p>
    575     *
    576     * @param input  the {@code String} to escape, may be null
    577     * @return a new escaped {@code String}, {@code null} if null string input
    578     *
    579     * @see <a href="https://hotwired.lycos.com/webmonkey/reference/special_characters/">ISO Entities</a>
    580     * @see <a href="http://www.w3.org/TR/REC-html32#latin1">HTML 3.2 Character Entities for ISO Latin-1</a>
    581     * @see <a href="http://www.w3.org/TR/REC-html40/sgml/entities.html">HTML 4.0 Character entity references</a>
    582     * @see <a href="http://www.w3.org/TR/html401/charset.html#h-5.3">HTML 4.01 Character References</a>
    583     * @see <a href="http://www.w3.org/TR/html401/charset.html#code-position">HTML 4.01 Code positions</a>
    584     */
    585    public static final String escapeHtml4(final String input) {
    586        return ESCAPE_HTML4.translate(input);
    587    }
    588
    589    // Java and JavaScript
    590    /**
    591     * Escapes the characters in a {@code String} using Java String rules.
    592     *
    593     * <p>Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
    594     *
    595     * <p>So a tab becomes the characters {@code '\\'} and
    596     * {@code 't'}.</p>
    597     *
    598     * <p>The only difference between Java strings and JavaScript strings
    599     * is that in JavaScript, a single quote and forward-slash (/) are escaped.</p>
    600     *
    601     * <p>Example:</p>
    602     * <pre>
    603     * input string: He didn't say, "Stop!"
    604     * output string: He didn't say, \"Stop!\"
    605     * </pre>
    606     *
    607     * @param input  String to escape values in, may be null
    608     * @return String with escaped values, {@code null} if null string input
    609     */
    610    public static final String escapeJava(final String input) {
    611        return ESCAPE_JAVA.translate(input);
    612    }
    613
    614    /**
    615     * Escapes the characters in a {@code String} using Json String rules.
    616     *
    617     * <p>Escapes any values it finds into their Json String form.
    618     * Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
    619     *
    620     * <p>So a tab becomes the characters {@code '\\'} and
    621     * {@code 't'}.</p>
    622     *
    623     * <p>The only difference between Java strings and Json strings
    624     * is that in Json, forward-slash (/) is escaped.</p>
    625     *
    626     * <p>See http://www.ietf.org/rfc/rfc4627.txt for further details.</p>
    627     *
    628     * <p>Example:</p>
    629     * <pre>
    630     * input string: He didn't say, "Stop!"
    631     * output string: He didn't say, \"Stop!\"
    632     * </pre>
    633     *
    634     * @param input  String to escape values in, may be null
    635     * @return String with escaped values, {@code null} if null string input
    636     */
    637    public static final String escapeJson(final String input) {
    638        return ESCAPE_JSON.translate(input);
    639    }
    640
    641    /**
    642     * Escapes the characters in a {@code String} using XML entities.
    643     *
    644     * <p>For example: {@code "bread" & "butter"} =&gt;
    645     * {@code &quot;bread&quot; &amp; &quot;butter&quot;}.
    646     * </p>
    647     *
    648     * <p>Note that XML 1.0 is a text-only format: it cannot represent control
    649     * characters or unpaired Unicode surrogate code points, even after escaping.
    650     * {@code escapeXml10} will remove characters that do not fit in the
    651     * following ranges:</p>
    652     *
    653     * <p>{@code #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]}</p>
    654     *
    655     * <p>Though not strictly necessary, {@code escapeXml10} will escape
    656     * characters in the following ranges:</p>
    657     *
    658     * <p>{@code [#x7F-#x84] | [#x86-#x9F]}</p>
    659     *
    660     * <p>The returned string can be inserted into a valid XML 1.0 or XML 1.1
    661     * document. If you want to allow more non-text characters in an XML 1.1
    662     * document, use {@link #escapeXml11(String)}.</p>
    663     *
    664     * @param input  the {@code String} to escape, may be null
    665     * @return a new escaped {@code String}, {@code null} if null string input
    666     * @see #unescapeXml(String)
    667     */
    668    public static String escapeXml10(final String input) {
    669        return ESCAPE_XML10.translate(input);
    670    }
    671
    672    /**
    673     * Escapes the characters in a {@code String} using XML entities.
    674     *
    675     * <p>For example: {@code "bread" & "butter"} =&gt;
    676     * {@code &quot;bread&quot; &amp; &quot;butter&quot;}.
    677     * </p>
    678     *
    679     * <p>XML 1.1 can represent certain control characters, but it cannot represent
    680     * the null byte or unpaired Unicode surrogate code points, even after escaping.
    681     * {@code escapeXml11} will remove characters that do not fit in the following
    682     * ranges:</p>
    683     *
    684     * <p>{@code [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]}</p>
    685     *
    686     * <p>{@code escapeXml11} will escape characters in the following ranges:</p>
    687     *
    688     * <p>{@code [#x1-#x8] | [#xB-#xC] | [#xE-#x1F] | [#x7F-#x84] | [#x86-#x9F]}</p>
    689     *
    690     * <p>The returned string can be inserted into a valid XML 1.1 document. Do not
    691     * use it for XML 1.0 documents.</p>
    692     *
    693     * @param input  the {@code String} to escape, may be null
    694     * @return a new escaped {@code String}, {@code null} if null string input
    695     * @see #unescapeXml(String)
    696     */
    697    public static String escapeXml11(final String input) {
    698        return ESCAPE_XML11.translate(input);
    699    }
    700
    701    /**
    702     * Escapes the characters in a {@code String} using XSI rules.
    703     *
    704     * <p><b>Beware!</b> In most cases you don't want to escape shell commands but use multi-argument
    705     * methods provided by {@link ProcessBuilder} or {@link Runtime#exec(String[])}
    706     * instead.</p>
    707     *
    708     * <p>Example:</p>
    709     * <pre>
    710     * input string: He didn't say, "Stop!"
    711     * output string: He\ didn\'t\ say,\ \"Stop!\"
    712     * </pre>
    713     *
    714     * @see <a href="https://pubs.opengroup.org/onlinepubs/7908799/xcu/chap2.html">Shell Command Language</a>
    715     * @param input  String to escape values in, may be null
    716     * @return String with escaped values, {@code null} if null string input
    717     */
    718    public static final String escapeXSI(final String input) {
    719        return ESCAPE_XSI.translate(input);
    720    }
    721
    722    /**
    723     * Returns a {@code String} value for an unescaped CSV column.
    724     *
    725     * <p>If the value is enclosed in double quotes, and contains a comma, newline
    726     *    or double quote, then quotes are removed.
    727     * </p>
    728     *
    729     * <p>Any double quote escaped characters (a pair of double quotes) are unescaped
    730     *    to just one double quote.</p>
    731     *
    732     * <p>If the value is not enclosed in double quotes, or is and does not contain a
    733     *    comma, newline or double quote, then the String value is returned unchanged.</p>
    734     *
    735     * see <a href="https://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
    736     * <a href="https://tools.ietf.org/html/rfc4180">RFC 4180</a>.
    737     *
    738     * @param input the input CSV column String, may be null
    739     * @return The input String, with enclosing double quotes removed and embedded double
    740     * quotes unescaped, {@code null} if null string input
    741     */
    742    public static final String unescapeCsv(final String input) {
    743        return UNESCAPE_CSV.translate(input);
    744    }
    745
    746    /**
    747     * Unescapes any EcmaScript literals found in the {@code String}.
    748     *
    749     * <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
    750     * into a newline character, unless the {@code '\'} is preceded by another
    751     * {@code '\'}.</p>
    752     *
    753     * @see #unescapeJava(String)
    754     * @param input  the {@code String} to unescape, may be null
    755     * @return A new unescaped {@code String}, {@code null} if null string input
    756     */
    757    public static final String unescapeEcmaScript(final String input) {
    758        return UNESCAPE_ECMASCRIPT.translate(input);
    759    }
    760
    761    /**
    762     * Unescapes a string containing entity escapes to a string
    763     * containing the actual Unicode characters corresponding to the
    764     * escapes. Supports only HTML 3.0 entities.
    765     *
    766     * @param input  the {@code String} to unescape, may be null
    767     * @return a new unescaped {@code String}, {@code null} if null string input
    768     */
    769    public static final String unescapeHtml3(final String input) {
    770        return UNESCAPE_HTML3.translate(input);
    771    }
    772
    773    /**
    774     * Unescapes a string containing entity escapes to a string
    775     * containing the actual Unicode characters corresponding to the
    776     * escapes. Supports HTML 4.0 entities.
    777     *
    778     * <p>For example, the string {@code "&lt;Fran&ccedil;ais&gt;"}
    779     * will become {@code "<Fran�ais>"}</p>
    780     *
    781     * <p>If an entity is unrecognized, it is left alone, and inserted
    782     * verbatim into the result string. e.g. {@code "&gt;&zzzz;x"} will
    783     * become {@code ">&zzzz;x"}.</p>
    784     *
    785     * @param input  the {@code String} to unescape, may be null
    786     * @return a new unescaped {@code String}, {@code null} if null string input
    787     */
    788    public static final String unescapeHtml4(final String input) {
    789        return UNESCAPE_HTML4.translate(input);
    790    }
    791
    792    /**
    793     * Unescapes any Java literals found in the {@code String}.
    794     * For example, it will turn a sequence of {@code '\'} and
    795     * {@code 'n'} into a newline character, unless the {@code '\'}
    796     * is preceded by another {@code '\'}.
    797     *
    798     * @param input  the {@code String} to unescape, may be null
    799     * @return a new unescaped {@code String}, {@code null} if null string input
    800     */
    801    public static final String unescapeJava(final String input) {
    802        return UNESCAPE_JAVA.translate(input);
    803    }
    804
    805    /**
    806     * Unescapes any Json literals found in the {@code String}.
    807     *
    808     * <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
    809     * into a newline character, unless the {@code '\'} is preceded by another
    810     * {@code '\'}.</p>
    811     *
    812     * @see #unescapeJava(String)
    813     * @param input  the {@code String} to unescape, may be null
    814     * @return A new unescaped {@code String}, {@code null} if null string input
    815     */
    816    public static final String unescapeJson(final String input) {
    817        return UNESCAPE_JSON.translate(input);
    818    }
    819
    820    /**
    821     * Unescapes a string containing XML entity escapes to a string
    822     * containing the actual Unicode characters corresponding to the
    823     * escapes.
    824     *
    825     * <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos).
    826     * Does not support DTDs or external entities.</p>
    827     *
    828     * <p>Note that numerical \\u Unicode codes are unescaped to their respective
    829     *    Unicode characters. This may change in future releases.</p>
    830     *
    831     * @param input  the {@code String} to unescape, may be null
    832     * @return a new unescaped {@code String}, {@code null} if null string input
    833     * @see #escapeXml10(String)
    834     * @see #escapeXml11(String)
    835     */
    836    public static final String unescapeXml(final String input) {
    837        return UNESCAPE_XML.translate(input);
    838    }
    839
    840    /**
    841     * Unescapes the characters in a {@code String} using XSI rules.
    842     *
    843     * @see StringEscapeUtils#escapeXSI(String)
    844     * @param input  the {@code String} to unescape, may be null
    845     * @return a new unescaped {@code String}, {@code null} if null string input
    846     */
    847    public static final String unescapeXSI(final String input) {
    848        return UNESCAPE_XSI.translate(input);
    849    }
    850
    851    /**
    852     * {@code StringEscapeUtils} instances should NOT be constructed in
    853     * standard programming.
    854     *
    855     * <p>Instead, the class should be used as:</p>
    856     * <pre>StringEscapeUtils.escapeJava("foo");</pre>
    857     *
    858     * <p>This constructor is public to permit tools that require a JavaBean
    859     * instance to operate.</p>
    860     */
    861    public StringEscapeUtils() {
    862    }
    863
    864}