View Javadoc
1   /*
2    * Copyright 2012-2021 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.util;
17  
18  /*
19   * Licensed to the Apache Software Foundation (ASF) under one or more
20   * contributor license agreements.  See the NOTICE file distributed with
21   * this work for additional information regarding copyright ownership.
22   * The ASF licenses this file to You under the Apache License, Version 2.0
23   * (the "License"); you may not use this file except in compliance with
24   * the License.  You may obtain a copy of the License at
25   *
26   *     http://www.apache.org/licenses/LICENSE-2.0
27   *
28   * Unless required by applicable law or agreed to in writing, software
29   * distributed under the License is distributed on an "AS IS" BASIS,
30   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31   * See the License for the specific language governing permissions and
32   * limitations under the License.
33   */
34  
35  import java.util.ArrayList;
36  import java.util.regex.Matcher;
37  import java.util.regex.Pattern;
38  
39  /**
40   * Utility class for parsing CSV text
41   */
42  public final class KuromojiCSVUtil {
43      private static final char QUOTE = '"';
44  
45      private static final char COMMA = ',';
46  
47      private static final Pattern QUOTE_REPLACE_PATTERN = Pattern.compile("^\"([^\"]+)\"$");
48  
49      private static final String ESCAPED_QUOTE = "\"\"";
50  
51      private KuromojiCSVUtil() {
52      } // no instance!!!
53  
54      /**
55       * Parse CSV line
56       *
57       * @param line
58       *            line containing csv-encoded data
59       * @return Array of values
60       */
61      public static String[] parse(final String line) {
62          boolean insideQuote = false;
63          final ArrayList<String> result = new ArrayList<>();
64          int quoteCount = 0;
65          final StringBuilder sb = new StringBuilder();
66          for (int i = 0; i < line.length(); i++) {
67              final char c = line.charAt(i);
68  
69              if (c == QUOTE) {
70                  insideQuote = !insideQuote;
71                  quoteCount++;
72              }
73  
74              if (c == COMMA && !insideQuote) {
75                  String value = sb.toString();
76                  value = unQuoteUnEscape(value);
77                  result.add(value);
78                  sb.setLength(0);
79                  continue;
80              }
81  
82              sb.append(c);
83          }
84  
85          result.add(sb.toString());
86  
87          // Validate
88          if (quoteCount % 2 != 0) {
89              return new String[0];
90          }
91  
92          return result.toArray(new String[result.size()]);
93      }
94  
95      private static String unQuoteUnEscape(final String original) {
96          String result = original;
97  
98          // Unquote
99          if (result.indexOf('\"') >= 0) {
100             final Matcher m = QUOTE_REPLACE_PATTERN.matcher(original);
101             if (m.matches()) {
102                 result = m.group(1);
103             }
104 
105             // Unescape
106             if (result.indexOf(ESCAPED_QUOTE) >= 0) {
107                 result = result.replace(ESCAPED_QUOTE, "\"");
108             }
109         }
110 
111         return result;
112 
113     }
114 
115     /**
116      * Quote and escape input value for CSV
117      *
118      * @param original Original text.
119      * @return Escaped text.
120      */
121     public static String quoteEscape(final String original) {
122         String result = original;
123 
124         if (result.indexOf('\"') >= 0) {
125             result = result.replace("\"", ESCAPED_QUOTE);
126         }
127         if (result.indexOf(COMMA) >= 0) {
128             result = "\"" + result + "\"";
129         }
130         return result;
131     }
132 
133 }