View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.text.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          @Override
42          void translateWhole(final CharSequence input, final Writer writer) throws IOException {
43              final String inputSting = input.toString();
44              if (StringUtils.containsNone(inputSting, CSV_SEARCH_CHARS)) {
45                  writer.write(inputSting);
46              } else {
47                  // input needs quoting
48                  writer.write(CSV_QUOTE);
49                  writer.write(StringUtils.replace(inputSting, CSV_QUOTE_STR, CSV_ESCAPED_QUOTE_STR));
50                  writer.write(CSV_QUOTE);
51              }
52          }
53      }
54      /**
55       * Translator for unescaping escaped Comma Separated Value entries.
56       */
57      public static class CsvUnescaper extends SinglePassTranslator {
58  
59          @Override
60          void translateWhole(final CharSequence input, final Writer writer) throws IOException {
61              // is input not quoted?
62              if (input.charAt(0) != CSV_QUOTE || input.charAt(input.length() - 1) != CSV_QUOTE) {
63                  writer.write(input.toString());
64                  return;
65              }
66  
67              // strip quotes
68              final String quoteless = input.subSequence(1, input.length() - 1).toString();
69  
70              if (StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS)) {
71                  // deal with escaped quotes; ie) ""
72                  writer.write(StringUtils.replace(quoteless, CSV_ESCAPED_QUOTE_STR, CSV_QUOTE_STR));
73              } else {
74                  writer.write(quoteless);
75              }
76          }
77      }
78      /** Comma character. */
79      private static final char CSV_DELIMITER = ',';
80      /** Quote character. */
81      private static final char CSV_QUOTE = '"';
82      /** Quote character converted to string. */
83      private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE);
84  
85      /** Escaped quote string. */
86      private static final String CSV_ESCAPED_QUOTE_STR = CSV_QUOTE_STR + CSV_QUOTE_STR;
87  
88      /** CSV key characters in an array. */
89      private static final char[] CSV_SEARCH_CHARS = { CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF };
90  
91      /** Hidden constructor. */
92      private CsvTranslators() { }
93  }