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.lang3;
018
019import java.io.IOException;
020import java.io.Writer;
021
022import org.apache.commons.lang3.text.translate.AggregateTranslator;
023import org.apache.commons.lang3.text.translate.CharSequenceTranslator;
024import org.apache.commons.lang3.text.translate.EntityArrays;
025import org.apache.commons.lang3.text.translate.JavaUnicodeEscaper;
026import org.apache.commons.lang3.text.translate.LookupTranslator;
027import org.apache.commons.lang3.text.translate.NumericEntityUnescaper;
028import org.apache.commons.lang3.text.translate.OctalUnescaper;
029import org.apache.commons.lang3.text.translate.UnicodeUnescaper;
030
031/**
032 * <p>Escapes and unescapes {@code String}s for
033 * Java, Java Script, HTML and XML.</p>
034 *
035 * <p>#ThreadSafe#</p>
036 * @since 2.0
037 * @version $Id: StringEscapeUtils.java 1467206 2013-04-12 08:39:02Z bayard $
038 */
039public class StringEscapeUtils {
040
041    /* ESCAPE TRANSLATORS */
042
043    /**
044     * Translator object for escaping Java. 
045     * 
046     * While {@link #escapeJava(String)} is the expected method of use, this 
047     * object allows the Java escaping functionality to be used 
048     * as the foundation for a custom translator. 
049     *
050     * @since 3.0
051     */
052    public static final CharSequenceTranslator ESCAPE_JAVA = 
053          new LookupTranslator(
054            new String[][] { 
055              {"\"", "\\\""},
056              {"\\", "\\\\"},
057          }).with(
058            new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE())
059          ).with(
060            JavaUnicodeEscaper.outsideOf(32, 0x7f) 
061        );
062
063    /**
064     * Translator object for escaping EcmaScript/JavaScript. 
065     * 
066     * While {@link #escapeEcmaScript(String)} is the expected method of use, this 
067     * object allows the EcmaScript escaping functionality to be used 
068     * as the foundation for a custom translator. 
069     *
070     * @since 3.0
071     */
072    public static final CharSequenceTranslator ESCAPE_ECMASCRIPT = 
073        new AggregateTranslator(
074            new LookupTranslator(
075                      new String[][] { 
076                            {"'", "\\'"},
077                            {"\"", "\\\""},
078                            {"\\", "\\\\"},
079                            {"/", "\\/"}
080                      }),
081            new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()),
082            JavaUnicodeEscaper.outsideOf(32, 0x7f) 
083        );
084
085    /**
086     * Translator object for escaping Json.
087     *
088     * While {@link #escapeJson(String)} is the expected method of use, this
089     * object allows the Json escaping functionality to be used
090     * as the foundation for a custom translator.
091     *
092     * @since 3.2
093     */
094    public static final CharSequenceTranslator ESCAPE_JSON =
095        new AggregateTranslator(
096            new LookupTranslator(
097                      new String[][] {
098                            {"\"", "\\\""},
099                            {"\\", "\\\\"},
100                            {"/", "\\/"}
101                      }),
102            new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()),
103            JavaUnicodeEscaper.outsideOf(32, 0x7f)
104        );
105
106    /**
107     * Translator object for escaping XML.
108     * 
109     * While {@link #escapeXml(String)} is the expected method of use, this 
110     * object allows the XML escaping functionality to be used 
111     * as the foundation for a custom translator. 
112     *
113     * @since 3.0
114     */
115    public static final CharSequenceTranslator ESCAPE_XML = 
116        new AggregateTranslator(
117            new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
118            new LookupTranslator(EntityArrays.APOS_ESCAPE())
119        );
120
121    /**
122     * Translator object for escaping HTML version 3.0.
123     * 
124     * While {@link #escapeHtml3(String)} is the expected method of use, this 
125     * object allows the HTML escaping functionality to be used 
126     * as the foundation for a custom translator. 
127     *
128     * @since 3.0
129     */
130    public static final CharSequenceTranslator ESCAPE_HTML3 = 
131        new AggregateTranslator(
132            new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
133            new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE())
134        );
135
136    /**
137     * Translator object for escaping HTML version 4.0.
138     * 
139     * While {@link #escapeHtml4(String)} is the expected method of use, this 
140     * object allows the HTML escaping functionality to be used 
141     * as the foundation for a custom translator. 
142     *
143     * @since 3.0
144     */
145    public static final CharSequenceTranslator ESCAPE_HTML4 = 
146        new AggregateTranslator(
147            new LookupTranslator(EntityArrays.BASIC_ESCAPE()),
148            new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE()),
149            new LookupTranslator(EntityArrays.HTML40_EXTENDED_ESCAPE())
150        );
151
152    /**
153     * Translator object for escaping individual Comma Separated Values. 
154     * 
155     * While {@link #escapeCsv(String)} is the expected method of use, this 
156     * object allows the CSV escaping functionality to be used 
157     * as the foundation for a custom translator. 
158     *
159     * @since 3.0
160     */
161    public static final CharSequenceTranslator ESCAPE_CSV = new CsvEscaper();
162
163    // TODO: Create a parent class - 'SinglePassTranslator' ?
164    //       It would handle the index checking + length returning, 
165    //       and could also have an optimization check method.
166    static class CsvEscaper extends CharSequenceTranslator {
167
168        private static final char CSV_DELIMITER = ',';
169        private static final char CSV_QUOTE = '"';
170        private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE);
171        private static final char[] CSV_SEARCH_CHARS = 
172            new char[] {CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF};
173
174        @Override
175        public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
176
177            if(index != 0) {
178                throw new IllegalStateException("CsvEscaper should never reach the [1] index");
179            }
180
181            if (StringUtils.containsNone(input.toString(), CSV_SEARCH_CHARS)) {
182                out.write(input.toString());
183            } else {
184                out.write(CSV_QUOTE);
185                out.write(StringUtils.replace(input.toString(), CSV_QUOTE_STR, CSV_QUOTE_STR + CSV_QUOTE_STR));
186                out.write(CSV_QUOTE);
187            }
188            return input.length();
189        }
190    }
191
192    /* UNESCAPE TRANSLATORS */
193
194    /**
195     * Translator object for unescaping escaped Java. 
196     * 
197     * While {@link #unescapeJava(String)} is the expected method of use, this 
198     * object allows the Java unescaping functionality to be used 
199     * as the foundation for a custom translator. 
200     *
201     * @since 3.0
202     */
203    // TODO: throw "illegal character: \92" as an Exception if a \ on the end of the Java (as per the compiler)?
204    public static final CharSequenceTranslator UNESCAPE_JAVA = 
205        new AggregateTranslator(
206            new OctalUnescaper(),     // .between('\1', '\377'),
207            new UnicodeUnescaper(),
208            new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_UNESCAPE()),
209            new LookupTranslator(
210                      new String[][] { 
211                            {"\\\\", "\\"},
212                            {"\\\"", "\""},
213                            {"\\'", "'"},
214                            {"\\", ""}
215                      })
216        );
217
218    /**
219     * Translator object for unescaping escaped EcmaScript. 
220     * 
221     * While {@link #unescapeEcmaScript(String)} is the expected method of use, this 
222     * object allows the EcmaScript unescaping functionality to be used 
223     * as the foundation for a custom translator. 
224     *
225     * @since 3.0
226     */
227    public static final CharSequenceTranslator UNESCAPE_ECMASCRIPT = UNESCAPE_JAVA;
228
229    /**
230     * Translator object for unescaping escaped Json.
231     *
232     * While {@link #unescapeJson(String)} is the expected method of use, this
233     * object allows the Json unescaping functionality to be used
234     * as the foundation for a custom translator.
235     *
236     * @since 3.2
237     */
238    public static final CharSequenceTranslator UNESCAPE_JSON = UNESCAPE_JAVA;
239
240    /**
241     * Translator object for unescaping escaped HTML 3.0. 
242     * 
243     * While {@link #unescapeHtml3(String)} is the expected method of use, this 
244     * object allows the HTML unescaping functionality to be used 
245     * as the foundation for a custom translator. 
246     *
247     * @since 3.0
248     */
249    public static final CharSequenceTranslator UNESCAPE_HTML3 = 
250        new AggregateTranslator(
251            new LookupTranslator(EntityArrays.BASIC_UNESCAPE()),
252            new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()),
253            new NumericEntityUnescaper()
254        );
255
256    /**
257     * Translator object for unescaping escaped HTML 4.0. 
258     * 
259     * While {@link #unescapeHtml4(String)} is the expected method of use, this 
260     * object allows the HTML unescaping functionality to be used 
261     * as the foundation for a custom translator. 
262     *
263     * @since 3.0
264     */
265    public static final CharSequenceTranslator UNESCAPE_HTML4 = 
266        new AggregateTranslator(
267            new LookupTranslator(EntityArrays.BASIC_UNESCAPE()),
268            new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()),
269            new LookupTranslator(EntityArrays.HTML40_EXTENDED_UNESCAPE()),
270            new NumericEntityUnescaper()
271        );
272
273    /**
274     * Translator object for unescaping escaped XML.
275     * 
276     * While {@link #unescapeXml(String)} is the expected method of use, this 
277     * object allows the XML unescaping functionality to be used 
278     * as the foundation for a custom translator. 
279     *
280     * @since 3.0
281     */
282    public static final CharSequenceTranslator UNESCAPE_XML = 
283        new AggregateTranslator(
284            new LookupTranslator(EntityArrays.BASIC_UNESCAPE()),
285            new LookupTranslator(EntityArrays.APOS_UNESCAPE()),
286            new NumericEntityUnescaper()
287        );
288
289    /**
290     * Translator object for unescaping escaped Comma Separated Value entries.
291     * 
292     * While {@link #unescapeCsv(String)} is the expected method of use, this 
293     * object allows the CSV unescaping functionality to be used 
294     * as the foundation for a custom translator. 
295     *
296     * @since 3.0
297     */
298    public static final CharSequenceTranslator UNESCAPE_CSV = new CsvUnescaper();
299
300    static class CsvUnescaper extends CharSequenceTranslator {
301
302        private static final char CSV_DELIMITER = ',';
303        private static final char CSV_QUOTE = '"';
304        private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE);
305        private static final char[] CSV_SEARCH_CHARS = 
306            new char[] {CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF};
307
308        @Override
309        public int translate(final CharSequence input, final int index, final Writer out) throws IOException {
310
311            if(index != 0) {
312                throw new IllegalStateException("CsvUnescaper should never reach the [1] index");
313            }
314
315            if ( input.charAt(0) != CSV_QUOTE || input.charAt(input.length() - 1) != CSV_QUOTE ) {
316                out.write(input.toString());
317                return input.length();
318            }
319
320            // strip quotes
321            final String quoteless = input.subSequence(1, input.length() - 1).toString();
322
323            if ( StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS) ) {
324                // deal with escaped quotes; ie) ""
325                out.write(StringUtils.replace(quoteless, CSV_QUOTE_STR + CSV_QUOTE_STR, CSV_QUOTE_STR));
326            } else {
327                out.write(input.toString());
328            }
329            return input.length();
330        }
331    }
332
333    /* Helper functions */
334
335    /**
336     * <p>{@code StringEscapeUtils} instances should NOT be constructed in
337     * standard programming.</p>
338     *
339     * <p>Instead, the class should be used as:
340     * <pre>StringEscapeUtils.escapeJava("foo");</pre></p>
341     *
342     * <p>This constructor is public to permit tools that require a JavaBean
343     * instance to operate.</p>
344     */
345    public StringEscapeUtils() {
346      super();
347    }
348
349    // Java and JavaScript
350    //--------------------------------------------------------------------------
351    /**
352     * <p>Escapes the characters in a {@code String} using Java String rules.</p>
353     *
354     * <p>Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
355     *
356     * <p>So a tab becomes the characters {@code '\\'} and
357     * {@code 't'}.</p>
358     *
359     * <p>The only difference between Java strings and JavaScript strings
360     * is that in JavaScript, a single quote and forward-slash (/) are escaped.</p>
361     *
362     * <p>Example:
363     * <pre>
364     * input string: He didn't say, "Stop!"
365     * output string: He didn't say, \"Stop!\"
366     * </pre>
367     * </p>
368     *
369     * @param input  String to escape values in, may be null
370     * @return String with escaped values, {@code null} if null string input
371     */
372    public static final String escapeJava(final String input) {
373        return ESCAPE_JAVA.translate(input);
374    }
375
376    /**
377     * <p>Escapes the characters in a {@code String} using EcmaScript String rules.</p>
378     * <p>Escapes any values it finds into their EcmaScript String form.
379     * Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
380     *
381     * <p>So a tab becomes the characters {@code '\\'} and
382     * {@code 't'}.</p>
383     *
384     * <p>The only difference between Java strings and EcmaScript strings
385     * is that in EcmaScript, a single quote and forward-slash (/) are escaped.</p>
386     *
387     * <p>Note that EcmaScript is best known by the JavaScript and ActionScript dialects. </p>
388     *
389     * <p>Example:
390     * <pre>
391     * input string: He didn't say, "Stop!"
392     * output string: He didn\'t say, \"Stop!\"
393     * </pre>
394     * </p>
395     *
396     * @param input  String to escape values in, may be null
397     * @return String with escaped values, {@code null} if null string input
398     *
399     * @since 3.0
400     */
401    public static final String escapeEcmaScript(final String input) {
402        return ESCAPE_ECMASCRIPT.translate(input);
403    }
404
405    /**
406     * <p>Escapes the characters in a {@code String} using Json String rules.</p>
407     * <p>Escapes any values it finds into their Json String form.
408     * Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.) </p>
409     *
410     * <p>So a tab becomes the characters {@code '\\'} and
411     * {@code 't'}.</p>
412     *
413     * <p>The only difference between Java strings and Json strings
414     * is that in Json, forward-slash (/) is escaped.</p>
415     *
416     * <p>See http://www.ietf.org/rfc/rfc4627.txt for further details. </p>
417     *
418     * <p>Example:
419     * <pre>
420     * input string: He didn't say, "Stop!"
421     * output string: He didn't say, \"Stop!\"
422     * </pre>
423     * </p>
424     *
425     * @param input  String to escape values in, may be null
426     * @return String with escaped values, {@code null} if null string input
427     *
428     * @since 3.2
429     */
430    public static final String escapeJson(final String input) {
431        return ESCAPE_JSON.translate(input);
432    }
433
434    /**
435     * <p>Unescapes any Java literals found in the {@code String}.
436     * For example, it will turn a sequence of {@code '\'} and
437     * {@code 'n'} into a newline character, unless the {@code '\'}
438     * is preceded by another {@code '\'}.</p>
439     * 
440     * @param input  the {@code String} to unescape, may be null
441     * @return a new unescaped {@code String}, {@code null} if null string input
442     */
443    public static final String unescapeJava(final String input) {
444        return UNESCAPE_JAVA.translate(input);
445    }
446
447    /**
448     * <p>Unescapes any EcmaScript literals found in the {@code String}.</p>
449     *
450     * <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
451     * into a newline character, unless the {@code '\'} is preceded by another
452     * {@code '\'}.</p>
453     *
454     * @see #unescapeJava(String)
455     * @param input  the {@code String} to unescape, may be null
456     * @return A new unescaped {@code String}, {@code null} if null string input
457     *
458     * @since 3.0
459     */
460    public static final String unescapeEcmaScript(final String input) {
461        return UNESCAPE_ECMASCRIPT.translate(input);
462    }
463
464    /**
465     * <p>Unescapes any Json literals found in the {@code String}.</p>
466     *
467     * <p>For example, it will turn a sequence of {@code '\'} and {@code 'n'}
468     * into a newline character, unless the {@code '\'} is preceded by another
469     * {@code '\'}.</p>
470     *
471     * @see #unescapeJava(String)
472     * @param input  the {@code String} to unescape, may be null
473     * @return A new unescaped {@code String}, {@code null} if null string input
474     *
475     * @since 3.2
476     */
477    public static final String unescapeJson(final String input) {
478        return UNESCAPE_JSON.translate(input);
479    }
480
481    // HTML and XML
482    //--------------------------------------------------------------------------
483    /**
484     * <p>Escapes the characters in a {@code String} using HTML entities.</p>
485     *
486     * <p>
487     * For example:
488     * </p> 
489     * <p><code>"bread" & "butter"</code></p>
490     * becomes:
491     * <p>
492     * <code>&amp;quot;bread&amp;quot; &amp;amp; &amp;quot;butter&amp;quot;</code>.
493     * </p>
494     *
495     * <p>Supports all known HTML 4.0 entities, including funky accents.
496     * Note that the commonly used apostrophe escape character (&amp;apos;)
497     * is not a legal entity and so is not supported). </p>
498     *
499     * @param input  the {@code String} to escape, may be null
500     * @return a new escaped {@code String}, {@code null} if null string input
501     * 
502     * @see <a href="http://hotwired.lycos.com/webmonkey/reference/special_characters/">ISO Entities</a>
503     * @see <a href="http://www.w3.org/TR/REC-html32#latin1">HTML 3.2 Character Entities for ISO Latin-1</a>
504     * @see <a href="http://www.w3.org/TR/REC-html40/sgml/entities.html">HTML 4.0 Character entity references</a>
505     * @see <a href="http://www.w3.org/TR/html401/charset.html#h-5.3">HTML 4.01 Character References</a>
506     * @see <a href="http://www.w3.org/TR/html401/charset.html#code-position">HTML 4.01 Code positions</a>
507     * 
508     * @since 3.0
509     */
510    public static final String escapeHtml4(final String input) {
511        return ESCAPE_HTML4.translate(input);
512    }
513
514    /**
515     * <p>Escapes the characters in a {@code String} using HTML entities.</p>
516     * <p>Supports only the HTML 3.0 entities. </p>
517     *
518     * @param input  the {@code String} to escape, may be null
519     * @return a new escaped {@code String}, {@code null} if null string input
520     * 
521     * @since 3.0
522     */
523    public static final String escapeHtml3(final String input) {
524        return ESCAPE_HTML3.translate(input);
525    }
526                
527    //-----------------------------------------------------------------------
528    /**
529     * <p>Unescapes a string containing entity escapes to a string
530     * containing the actual Unicode characters corresponding to the
531     * escapes. Supports HTML 4.0 entities.</p>
532     *
533     * <p>For example, the string "&amp;lt;Fran&amp;ccedil;ais&amp;gt;"
534     * will become "&lt;Fran&ccedil;ais&gt;"</p>
535     *
536     * <p>If an entity is unrecognized, it is left alone, and inserted
537     * verbatim into the result string. e.g. "&amp;gt;&amp;zzzz;x" will
538     * become "&gt;&amp;zzzz;x".</p>
539     *
540     * @param input  the {@code String} to unescape, may be null
541     * @return a new unescaped {@code String}, {@code null} if null string input
542     * 
543     * @since 3.0
544     */
545    public static final String unescapeHtml4(final String input) {
546        return UNESCAPE_HTML4.translate(input);
547    }
548
549    /**
550     * <p>Unescapes a string containing entity escapes to a string
551     * containing the actual Unicode characters corresponding to the
552     * escapes. Supports only HTML 3.0 entities.</p>
553     *
554     * @param input  the {@code String} to unescape, may be null
555     * @return a new unescaped {@code String}, {@code null} if null string input
556     * 
557     * @since 3.0
558     */
559    public static final String unescapeHtml3(final String input) {
560        return UNESCAPE_HTML3.translate(input);
561    }
562
563    //-----------------------------------------------------------------------
564    /**
565     * <p>Escapes the characters in a {@code String} using XML entities.</p>
566     *
567     * <p>For example: <tt>"bread" & "butter"</tt> =>
568     * <tt>&amp;quot;bread&amp;quot; &amp;amp; &amp;quot;butter&amp;quot;</tt>.
569     * </p>
570     *
571     * <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos).
572     * Does not support DTDs or external entities.</p>
573     *
574     * <p>Note that Unicode characters greater than 0x7f are as of 3.0, no longer 
575     *    escaped. If you still wish this functionality, you can achieve it 
576     *    via the following: 
577     * {@code StringEscapeUtils.ESCAPE_XML.with( NumericEntityEscaper.between(0x7f, Integer.MAX_VALUE) );}</p>
578     *
579     * @param input  the {@code String} to escape, may be null
580     * @return a new escaped {@code String}, {@code null} if null string input
581     * @see #unescapeXml(java.lang.String)
582     */
583    public static final String escapeXml(final String input) {
584        return ESCAPE_XML.translate(input);
585    }
586                
587
588    //-----------------------------------------------------------------------
589    /**
590     * <p>Unescapes a string containing XML entity escapes to a string
591     * containing the actual Unicode characters corresponding to the
592     * escapes.</p>
593     *
594     * <p>Supports only the five basic XML entities (gt, lt, quot, amp, apos).
595     * Does not support DTDs or external entities.</p>
596     *
597     * <p>Note that numerical \\u Unicode codes are unescaped to their respective 
598     *    Unicode characters. This may change in future releases. </p>
599     *
600     * @param input  the {@code String} to unescape, may be null
601     * @return a new unescaped {@code String}, {@code null} if null string input
602     * @see #escapeXml(String)
603     */
604    public static final String unescapeXml(final String input) {
605        return UNESCAPE_XML.translate(input);
606    }
607                
608
609    //-----------------------------------------------------------------------
610
611    /**
612     * <p>Returns a {@code String} value for a CSV column enclosed in double quotes,
613     * if required.</p>
614     *
615     * <p>If the value contains a comma, newline or double quote, then the
616     *    String value is returned enclosed in double quotes.</p>
617     * </p>
618     *
619     * <p>Any double quote characters in the value are escaped with another double quote.</p>
620     *
621     * <p>If the value does not contain a comma, newline or double quote, then the
622     *    String value is returned unchanged.</p>
623     * </p>
624     *
625     * see <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
626     * <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
627     *
628     * @param input the input CSV column String, may be null
629     * @return the input String, enclosed in double quotes if the value contains a comma,
630     * newline or double quote, {@code null} if null string input
631     * @since 2.4
632     */
633    public static final String escapeCsv(final String input) {
634        return ESCAPE_CSV.translate(input);
635    }
636
637    /**
638     * <p>Returns a {@code String} value for an unescaped CSV column. </p>
639     *
640     * <p>If the value is enclosed in double quotes, and contains a comma, newline 
641     *    or double quote, then quotes are removed. 
642     * </p>
643     *
644     * <p>Any double quote escaped characters (a pair of double quotes) are unescaped 
645     *    to just one double quote. </p>
646     *
647     * <p>If the value is not enclosed in double quotes, or is and does not contain a 
648     *    comma, newline or double quote, then the String value is returned unchanged.</p>
649     * </p>
650     *
651     * see <a href="http://en.wikipedia.org/wiki/Comma-separated_values">Wikipedia</a> and
652     * <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
653     *
654     * @param input the input CSV column String, may be null
655     * @return the input String, with enclosing double quotes removed and embedded double 
656     * quotes unescaped, {@code null} if null string input
657     * @since 2.4
658     */
659    public static final String unescapeCsv(final String input) {
660        return UNESCAPE_CSV.translate(input);
661    }
662
663}