1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.dict.stemmeroverride;
17
18 import java.io.BufferedReader;
19 import java.io.BufferedWriter;
20 import java.io.Closeable;
21 import java.io.File;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.io.OutputStreamWriter;
27 import java.io.Writer;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.Date;
31 import java.util.List;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34
35 import org.apache.logging.log4j.LogManager;
36 import org.apache.logging.log4j.Logger;
37 import org.codelibs.core.io.CloseableUtil;
38 import org.codelibs.core.lang.StringUtil;
39 import org.codelibs.curl.CurlResponse;
40 import org.codelibs.fess.Constants;
41 import org.codelibs.fess.dict.DictionaryException;
42 import org.codelibs.fess.dict.DictionaryFile;
43 import org.codelibs.fess.util.ComponentUtil;
44 import org.dbflute.optional.OptionalEntity;
45
46 public class StemmerOverrideFile extends DictionaryFile<StemmerOverrideItem> {
47 private static final Logger logger = LogManager.getLogger(StemmerOverrideFile.class);
48
49 private static final String STEMMER_OVERRIDE = "stemmeroverride";
50
51 List<StemmerOverrideItem> stemmerOverrideItemList;
52
53 public StemmerOverrideFile(final String id, final String path, final Date timestamp) {
54 super(id, path, timestamp);
55 }
56
57 @Override
58 public String getType() {
59 return STEMMER_OVERRIDE;
60 }
61
62 @Override
63 public String getPath() {
64 return path;
65 }
66
67 @Override
68 public synchronized OptionalEntity<StemmerOverrideItem> get(final long id) {
69 if (stemmerOverrideItemList == null) {
70 reload(null);
71 }
72
73 for (final StemmerOverrideItem stemmerOverrideItem : stemmerOverrideItemList) {
74 if (id == stemmerOverrideItem.getId()) {
75 return OptionalEntity.of(stemmerOverrideItem);
76 }
77 }
78 return OptionalEntity.empty();
79 }
80
81 @Override
82 public synchronized PagingList<StemmerOverrideItem> selectList(final int offset, final int size) {
83 if (stemmerOverrideItemList == null) {
84 reload(null);
85 }
86
87 if (offset >= stemmerOverrideItemList.size() || offset < 0) {
88 return new PagingList<>(Collections.<StemmerOverrideItem> emptyList(), offset, size, stemmerOverrideItemList.size());
89 }
90
91 int toIndex = offset + size;
92 if (toIndex > stemmerOverrideItemList.size()) {
93 toIndex = stemmerOverrideItemList.size();
94 }
95
96 return new PagingList<>(stemmerOverrideItemList.subList(offset, toIndex), offset, size, stemmerOverrideItemList.size());
97 }
98
99 @Override
100 public synchronized void insert(final StemmerOverrideItem item) {
101 try (StemmerOverrideUpdater updater = new StemmerOverrideUpdater(item)) {
102 reload(updater);
103 }
104 }
105
106 @Override
107 public synchronized void update(final StemmerOverrideItem item) {
108 try (StemmerOverrideUpdater updater = new StemmerOverrideUpdater(item)) {
109 reload(updater);
110 }
111 }
112
113 @Override
114 public synchronized void delete(final StemmerOverrideItem item) {
115 final StemmerOverrideItem stemmerOverrideItem = item;
116 stemmerOverrideItem.setNewInput(StringUtil.EMPTY);
117 stemmerOverrideItem.setNewOutput(StringUtil.EMPTY);
118 try (StemmerOverrideUpdater updater = new StemmerOverrideUpdater(item)) {
119 reload(updater);
120 }
121 }
122
123 protected void reload(final StemmerOverrideUpdater updater) {
124 try (CurlResponse curlResponse = dictionaryManager.getContentResponse(this)) {
125 reload(updater, curlResponse.getContentAsStream());
126 } catch (final IOException e) {
127 throw new DictionaryException("Failed to parse " + path, e);
128 }
129 }
130
131 protected void reload(final StemmerOverrideUpdater updater, final InputStream in) {
132 final Pattern parsePattern = Pattern.compile("(.*)\\s*=>\\s*(.*)\\s*$");
133 final List<StemmerOverrideItem> itemList = new ArrayList<>();
134 try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, Constants.UTF_8))) {
135 long id = 0;
136 String line = null;
137 while ((line = reader.readLine()) != null) {
138
139 final String replacedLine = line.replaceAll("#.*$", StringUtil.EMPTY).trim();
140
141
142 if (replacedLine.length() == 0) {
143 if (updater != null) {
144 updater.write(line);
145 }
146 continue;
147 }
148
149 final Matcher m = parsePattern.matcher(replacedLine);
150
151 if (!m.find()) {
152 logger.warn("Failed to parse {} in {}", line, path);
153 if (updater != null) {
154 updater.write("# " + line);
155 }
156 continue;
157 }
158
159 final String input = m.group(1).trim();
160 final String output = m.group(2).trim();
161
162 if (input == null || output == null) {
163 logger.warn("Failed to parse {} to {}", line, path);
164 if (updater != null) {
165 updater.write("# " + line);
166 }
167 continue;
168 }
169
170 id++;
171 final StemmerOverrideItem item = new StemmerOverrideItem(id, input, output);
172
173 if (updater != null) {
174 final StemmerOverrideItem newItem = updater.write(item);
175 if (newItem != null) {
176 itemList.add(newItem);
177 } else {
178 id--;
179 }
180 } else {
181 itemList.add(item);
182 }
183 }
184 if (updater != null) {
185 final StemmerOverrideItem item = updater.commit();
186 if (item != null) {
187 itemList.add(item);
188 }
189 }
190 stemmerOverrideItemList = itemList;
191 } catch (final IOException e) {
192 throw new DictionaryException("Failed to parse " + path, e);
193 }
194 }
195
196 public String getSimpleName() {
197 return new File(path).getName();
198 }
199
200 public synchronized void update(final InputStream in) throws IOException {
201 try (StemmerOverrideUpdater updater = new StemmerOverrideUpdater(null)) {
202 reload(updater, in);
203 }
204 }
205
206 @Override
207 public String toString() {
208 return "StemmerOverrideFile [path=" + path + ", stemmerOverrideItemList=" + stemmerOverrideItemList + ", id=" + id + "]";
209 }
210
211 protected class StemmerOverrideUpdater implements Closeable {
212
213 protected boolean isCommit = false;
214
215 protected File newFile;
216
217 protected Writer writer;
218
219 protected StemmerOverrideItem item;
220
221 protected StemmerOverrideUpdater(final StemmerOverrideItem newItem) {
222 try {
223 newFile = ComponentUtil.getSystemHelper().createTempFile(STEMMER_OVERRIDE, ".txt");
224 writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile), Constants.UTF_8));
225 } catch (final Exception e) {
226 if (newFile != null) {
227 newFile.delete();
228 }
229 throw new DictionaryException("Failed to write a userDict file.", e);
230 }
231 item = newItem;
232 }
233
234 public StemmerOverrideItem write(final StemmerOverrideItem oldItem) {
235 try {
236 if ((item == null) || (item.getId() != oldItem.getId()) || !item.isUpdated()) {
237 writer.write(oldItem.toLineString());
238 writer.write(Constants.LINE_SEPARATOR);
239 return oldItem;
240 }
241 if (!item.equals(oldItem)) {
242 throw new DictionaryException("StemmerOverride file was updated: old=" + oldItem + " : new=" + item);
243 }
244 try {
245 if (!item.isDeleted()) {
246
247 writer.write(item.toLineString());
248 writer.write(Constants.LINE_SEPARATOR);
249 return new StemmerOverrideItem(item.getId(), item.getNewInput(), item.getNewOutput());
250 } else {
251 return null;
252 }
253 } finally {
254 item.setNewInput(null);
255 item.setNewOutput(null);
256 }
257 } catch (final IOException e) {
258 throw new DictionaryException("Failed to write: " + oldItem + " -> " + item, e);
259 }
260 }
261
262 public void write(final String line) {
263 try {
264 writer.write(line);
265 writer.write(Constants.LINE_SEPARATOR);
266 } catch (final IOException e) {
267 throw new DictionaryException("Failed to write: " + line, e);
268 }
269 }
270
271 public StemmerOverrideItem commit() {
272 isCommit = true;
273 if (item != null && item.isUpdated()) {
274 try {
275 writer.write(item.toLineString());
276 writer.write(Constants.LINE_SEPARATOR);
277 return item;
278 } catch (final IOException e) {
279 throw new DictionaryException("Failed to write: " + item, e);
280 }
281 }
282 return null;
283 }
284
285 @Override
286 public void close() {
287 try {
288 writer.flush();
289 } catch (final IOException e) {
290
291 }
292 CloseableUtil.closeQuietly(writer);
293
294 if (isCommit) {
295 try {
296 dictionaryManager.store(StemmerOverrideFile.this, newFile);
297 } finally {
298 newFile.delete();
299 }
300 } else {
301 newFile.delete();
302 }
303 }
304 }
305
306 }