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 }