    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 *      https://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 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     * <strong>Security Note.</strong> 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 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 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     * @see <a href="https://hotwired.lycos.com/webmonkey/reference/special_characters/">ISO Entities</a>
    579     * @see <a href="http://www.w3.org/TR/REC-html32#latin1">HTML 3.2 Character Entities for ISO Latin-1</a>
    580     * @see <a href="http://www.w3.org/TR/REC-html40/sgml/entities.html">HTML 4.0 Character entity references</a>
    581     * @see <a href="http://www.w3.org/TR/html401/charset.html#h-5.3">HTML 4.01 Character References</a>
    582     * @see <a href="http://www.w3.org/TR/html401/charset.html#code-position">HTML 4.01 Code positions</a>
    583     */
    584    public static String escapeHtml4(final String input) {
    585        return ESCAPE_HTML4.translate(input);
    586    }
    587
    588    // Java and JavaScript
    589    /**
    590     * Escapes the characters in a {@code String} using Java String rules.
    591     *
    592     * <p>Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
    593     *
    594     * <p>So a tab becomes the characters {@code '\\'} and
    595     * {@code 't'}.</p>
    596     *
    597     * <p>The only difference between Java strings and JavaScript strings
    598     * is that in JavaScript, a single quote and forward-slash (/) are escaped.</p>
    599     *
    600     * <p>Example:</p>
    601     * <pre>
    602     * input string: He didn't say, "Stop!"
    603     * output string: He didn't say, \"Stop!\"
    604     * </pre>
    605     *
    606     * @param input  String to escape values in, may be null
    607     * @return String with escaped values, {@code null} if null string input
    608     */
    609    public static String escapeJava(final String input) {
    610        return ESCAPE_JAVA.translate(input);
    611    }
    612
    613    /**
    614     * Escapes the characters in a {@code String} using Json String rules.
    615     *
    616     * <p>Escapes any values it finds into their Json String form.
    617     * Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
    618     *
    619     * <p>So a tab becomes the characters {@code '\\'} and
    620     * {@code 't'}.</p>
    621     *
    622     * <p>The only difference between Java strings and Json strings
    623     * is that in Json, forward-slash (/) is escaped.</p>
    624     *
    625     * <p>See http://www.ietf.org/rfc/rfc4627.txt for further details.</p>
    626     *
    627     * <p>Example:</p>
    628     * <pre>
    629     * input string: He didn't say, "Stop!"
    630     * output string: He didn't say, \"Stop!\"
    631     * </pre>
    632     *
    633     * @param input  String to escape values in, may be null
    634     * @return String with escaped values, {@code null} if null string input
    635     */
    636    public static String escapeJson(final String input) {
    637        return ESCAPE_JSON.translate(input);
    638    }
    639
    640    /**
    641     * Escapes the characters in a {@code String} using XML entities.
    642     *
    643     * <p>For example: {@code "bread" & "butter"} =&gt;
    644     * {@code &quot;bread&quot; &amp; &quot;butter&quot;}.
    645     * </p>
    646     *
    647     * <p>Note that XML 1.0 is a text-only format: it cannot represent control
    648     * characters or unpaired Unicode surrogate code points, even after escaping.
    649     * {@code escapeXml10} will remove characters that do not fit in the
    650     * following ranges:</p>
    651     *
    652     * <p>{@code #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]}</p>
    653     *
    654     * <p>Though not strictly necessary, {@code escapeXml10} will escape
    655     * characters in the following ranges:</p>
    656     *
    657     * <p>{@code [#x7F-#x84] | [#x86-#x9F]}</p>
    658     *
    659     * <p>The returned string can be inserted into a valid XML 1.0 or XML 1.1
    660     * document. If you want to allow more non-text characters in an XML 1.1
    661     * document, use {@link #escapeXml11(String)}.</p>
    662     *
    663     * @param input  the {@code String} to escape, may be null
    664     * @return a new escaped {@code String}, {@code null} if null string input
    665     * @see #unescapeXml(String)
    666     */
    667    public static String escapeXml10(final String input) {
    668        return ESCAPE_XML10.translate(input);
    669    }
    670
    671    /**
    672     * Escapes the characters in a {@code String} using XML entities.
    673     *
    674     * <p>For example: {@code "bread" & "butter"} =&gt;
    675     * {@code &quot;bread&quot; &amp; &quot;butter&quot;}.
    676     * </p>
    677     *
    678     * <p>XML 1.1 can represent certain control characters, but it cannot represent
    679     * the null byte or unpaired Unicode surrogate code points, even after escaping.
    680     * {@code escapeXml11} will remove characters that do not fit in the following
    681     * ranges:</p>
    682     *
    683     * <p>{@code [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]}</p>
    684     *
    685     * <p>{@code escapeXml11} will escape characters in the following ranges:</p>
    686     *
    687     * <p>{@code [#x1-#x8] | [#xB-#xC] | [#xE-#x1F] | [#x7F-#x84] | [#x86-#x9F]}</p>
    688     *
    689     * <p>The returned string can be inserted into a valid XML 1.1 document. Do not
    690     * use it for XML 1.0 documents.</p>
    691     *
    692     * @param input  the {@code String} to escape, may be null
    693     * @return a new escaped {@code String}, {@code null} if null string input
    694     * @see #unescapeXml(String)
    695     */
    696    public static String escapeXml11(final String input) {
    697        return ESCAPE_XML11.translate(input);
    698    }
    699
    700    /**
    701     * Escapes the characters in a {@code String} using XSI rules.
    702     *
    703     * <p><strong>Beware!</strong> In most cases you don't want to escape shell commands but use multi-argument
    704     * methods provided by {@link ProcessBuilder} or {@link Runtime#exec(String[])}
    705     * instead.</p>
    706     *
    707     * <p>Example:</p>
    708     * <pre>
    709     * input string: He didn't say, "Stop!"
    710     * output string: He\ didn\'t\ say,\ \"Stop!\"
    711     * </pre>
    712     *
    713     * @see <a href="https://pubs.opengroup.org/onlinepubs/7908799/xcu/chap2.html">Shell Command Language</a>
    714     * @param input  String to escape values in, may be null
    715     * @return String with escaped values, {@code null} if null string input
    716     */
    717    public static String escapeXSI(final String input) {
    718        return ESCAPE_XSI.translate(input);
    719    }
    720
    721    /**
    722     * Returns a {@code String} value for an unescaped CSV column.
    723     *
    724     * <p>If the value is enclosed in double quotes, and contains a comma, newline
    725     *    or double quote, then quotes are removed.
    726     * </p>
    727     *
    728     * <p>Any double quote escaped characters (a pair of double quotes) are unescaped
    729     *    to just one double quote.</p>
    730     *
    731     * <p>If the value is not enclosed in double quotes, or is and does not contain a
    732     *    comma, newline or double quote, then the String value is returned unchanged.</p>
    733     *
    734     * see <a href="https://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
    735     * <a href="https://tools.ietf.org/html/rfc4180">RFC 4180</a>.
    736     *
    737     * @param input the input CSV column String, may be null
    738     * @return The input String, with enclosing double quotes removed and embedded double
    739     * quotes unescaped, {@code null} if null string input
    740     */
    741    public static String unescapeCsv(final String input) {
    742        return UNESCAPE_CSV.translate(input);
    743    }
    744
    745    /**
    746     * Unescapes any EcmaScript literals found in the {@code String}.
    747     *
    748     * <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
    749     * into a newline character, unless the {@code '\'} is preceded by another
    750     * {@code '\'}.</p>
    751     *
    752     * @see #unescapeJava(String)
    753     * @param input  the {@code String} to unescape, may be null
    754     * @return A new unescaped {@code String}, {@code null} if null string input
    755     */
    756    public static String unescapeEcmaScript(final String input) {
    757        return UNESCAPE_ECMASCRIPT.translate(input);
    758    }
    759
    760    /**
    761     * Unescapes a string containing entity escapes to a string
    762     * containing the actual Unicode characters corresponding to the
    763     * escapes. Supports only HTML 3.0 entities.
    764     *
    765     * @param input  the {@code String} to unescape, may be null
    766     * @return a new unescaped {@code String}, {@code null} if null string input
    767     */
    768    public static String unescapeHtml3(final String input) {
    769        return UNESCAPE_HTML3.translate(input);
    770    }
    771
    772    /**
    773     * Unescapes a string containing entity escapes to a string
    774     * containing the actual Unicode characters corresponding to the
    775     * escapes. Supports HTML 4.0 entities.
    776     *
    777     * <p>For example, the string {@code "&lt;Fran&ccedil;ais&gt;"}
    778     * will become {@code "<Fran�ais>"}</p>
    779     *
    780     * <p>If an entity is unrecognized, it is left alone, and inserted
    781     * verbatim into the result string. e.g. {@code "&gt;&zzzz;x"} will
    782     * become {@code ">&zzzz;x"}.</p>
    783     *
    784     * @param input  the {@code String} to unescape, may be null
    785     * @return a new unescaped {@code String}, {@code null} if null string input
    786     */
    787    public static String unescapeHtml4(final String input) {
    788        return UNESCAPE_HTML4.translate(input);
    789    }
    790
    791    /**
    792     * Unescapes any Java literals found in the {@code String}.
    793     * For example, it will turn a sequence of {@code '\'} and
    794     * {@code 'n'} into a newline character, unless the {@code '\'}
    795     * is preceded by another {@code '\'}.
    796     *
    797     * @param input  the {@code String} to unescape, may be null
    798     * @return a new unescaped {@code String}, {@code null} if null string input
    799     */
    800    public static String unescapeJava(final String input) {
    801        return UNESCAPE_JAVA.translate(input);
    802    }
    803
    804    /**
    805     * Unescapes any Json literals found in the {@code String}.
    806     *
    807     * <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
    808     * into a newline character, unless the {@code '\'} is preceded by another
    809     * {@code '\'}.</p>
    810     *
    811     * @see #unescapeJava(String)
    812     * @param input  the {@code String} to unescape, may be null
    813     * @return A new unescaped {@code String}, {@code null} if null string input
    814     */
    815    public static String unescapeJson(final String input) {
    816        return UNESCAPE_JSON.translate(input);
    817    }
    818
    819    /**
    820     * Unescapes a string containing XML entity escapes to a string
    821     * containing the actual Unicode characters corresponding to the
    822     * escapes.
    823     *
    824     * <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos).
    825     * Does not support DTDs or external entities.</p>
    826     *
    827     * <p>Note that numerical \\u Unicode codes are unescaped to their respective
    828     *    Unicode characters. This may change in future releases.</p>
    829     *
    830     * @param input  the {@code String} to unescape, may be null
    831     * @return a new unescaped {@code String}, {@code null} if null string input
    832     * @see #escapeXml10(String)
    833     * @see #escapeXml11(String)
    834     */
    835    public static String unescapeXml(final String input) {
    836        return UNESCAPE_XML.translate(input);
    837    }
    838
    839    /**
    840     * Unescapes the characters in a {@code String} using XSI rules.
    841     *
    842     * @see StringEscapeUtils#escapeXSI(String)
    843     * @param input  the {@code String} to unescape, may be null
    844     * @return a new unescaped {@code String}, {@code null} if null string input
    845     */
    846    public static String unescapeXSI(final String input) {
    847        return UNESCAPE_XSI.translate(input);
    848    }
    849
    850    /**
    851     * {@code StringEscapeUtils} instances should NOT be constructed in
    852     * standard programming.
    853     *
    854     * <p>Instead, the class should be used as:</p>
    855     * <pre>StringEscapeUtils.escapeJava("foo");</pre>
    856     *
    857     * <p>This constructor is public to permit tools that require a JavaBean
    858     * instance to operate.</p>
    859     */
    860    public StringEscapeUtils() {
    861    }
    862
    863}