View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.text.translate;
18  
19  import java.io.IOException;
20  import java.io.Writer;
21  
22  import org.apache.commons.lang3.CharUtils;
23  import org.apache.commons.lang3.StringUtils;
24  
25  /**
26   * This class holds inner classes for escaping/unescaping Comma Separated Values.
27   * <p>
28   * In general the use a high level API like <a href="https://commons.apache.org/proper/commons-csv/">Apache Commons
29   * CSV</a> should be preferred over these low level classes.
30   * </p>
31   *
32   * @see <a href="https://commons.apache.org/proper/commons-csv/apidocs/index.html">Apache Commons CSV</a>
33   */
34  public final class CsvTranslators {
35  
36      /**
37       * Translator for escaping Comma Separated Values.
38       */
39      public static class CsvEscaper extends SinglePassTranslator {
40  
41          /**
42           * Construct a new instance.
43           */
44          public CsvEscaper() {
45              // empty
46          }
47  
48          @Override
49          void translateWhole(final CharSequence input, final Writer writer) throws IOException {
50              final String inputSting = input.toString();
51              if (StringUtils.containsNone(inputSting, CSV_SEARCH_CHARS)) {
52                  writer.write(inputSting);
53              } else {
54                  // input needs quoting
55                  writer.write(CSV_QUOTE);
56                  writer.write(StringUtils.replace(inputSting, CSV_QUOTE_STR, CSV_ESCAPED_QUOTE_STR));
57                  writer.write(CSV_QUOTE);
58              }
59          }
60      }
61      /**
62       * Translator for unescaping escaped Comma Separated Value entries.
63       */
64      public static class CsvUnescaper extends SinglePassTranslator {
65  
66          /**
67           * Construct a new instance.
68           */
69          public CsvUnescaper() {
70              // empty
71          }
72  
73          @Override
74          void translateWhole(final CharSequence input, final Writer writer) throws IOException {
75              // is input not quoted?
76              if (input.charAt(0) != CSV_QUOTE || input.charAt(input.length() - 1) != CSV_QUOTE) {
77                  writer.write(input.toString());
78                  return;
79              }
80  
81              // strip quotes
82              final String quoteless = input.subSequence(1, input.length() - 1).toString();
83  
84              if (StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS)) {
85                  // deal with escaped quotes; ie) ""
86                  writer.write(StringUtils.replace(quoteless, CSV_ESCAPED_QUOTE_STR, CSV_QUOTE_STR));
87              } else {
88                  writer.write(quoteless);
89              }
90          }
91      }
92      /** Comma character. */
93      private static final char CSV_DELIMITER = ',';
94      /** Quote character. */
95      private static final char CSV_QUOTE = '"';
96      /** Quote character converted to string. */
97      private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE);
98  
99      /** Escaped quote string. */
100     private static final String CSV_ESCAPED_QUOTE_STR = CSV_QUOTE_STR + CSV_QUOTE_STR;
101 
102     /** CSV key characters in an array. */
103     private static final char[] CSV_SEARCH_CHARS = { CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF };
104 
105     /** Hidden constructor. */
106     private CsvTranslators() {
107         // empty
108     }
109 }