1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.dict.protwords;
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
33 import org.codelibs.core.io.CloseableUtil;
34 import org.codelibs.core.lang.StringUtil;
35 import org.codelibs.curl.CurlResponse;
36 import org.codelibs.fess.Constants;
37 import org.codelibs.fess.dict.DictionaryException;
38 import org.codelibs.fess.dict.DictionaryFile;
39 import org.codelibs.fess.util.ComponentUtil;
40 import org.dbflute.optional.OptionalEntity;
41
42
43
44
45
46 public class ProtwordsFile extends DictionaryFile<ProtwordsItem> {
47 private static final String PROTWORDS = "protwords";
48
49 List<ProtwordsItem> protwordsItemList;
50
51
52
53
54
55
56
57 public ProtwordsFile(final String id, final String path, final Date timestamp) {
58 super(id, path, timestamp);
59 }
60
61 @Override
62 public String getType() {
63 return PROTWORDS;
64 }
65
66 @Override
67 public String getPath() {
68 return path;
69 }
70
71 @Override
72 public synchronized OptionalEntity<ProtwordsItem> get(final long id) {
73 if (protwordsItemList == null) {
74 reload(null);
75 }
76
77 for (final ProtwordsItem ProtwordsItem : protwordsItemList) {
78 if (id == ProtwordsItem.getId()) {
79 return OptionalEntity.of(ProtwordsItem);
80 }
81 }
82 return OptionalEntity.empty();
83 }
84
85 @Override
86 public synchronized PagingList<ProtwordsItem> selectList(final int offset, final int size) {
87 if (protwordsItemList == null) {
88 reload(null);
89 }
90
91 if (offset >= protwordsItemList.size() || offset < 0) {
92 return new PagingList<>(Collections.<ProtwordsItem> emptyList(), offset, size, protwordsItemList.size());
93 }
94
95 int toIndex = offset + size;
96 if (toIndex > protwordsItemList.size()) {
97 toIndex = protwordsItemList.size();
98 }
99
100 return new PagingList<>(protwordsItemList.subList(offset, toIndex), offset, size, protwordsItemList.size());
101 }
102
103 @Override
104 public synchronized void insert(final ProtwordsItem item) {
105 try (ProtwordsUpdater updater = new ProtwordsUpdater(item)) {
106 reload(updater);
107 }
108 }
109
110 @Override
111 public synchronized void update(final ProtwordsItem item) {
112 try (ProtwordsUpdater updater = new ProtwordsUpdater(item)) {
113 reload(updater);
114 }
115 }
116
117 @Override
118 public synchronized void delete(final ProtwordsItem item) {
119 final ProtwordsItem ProtwordsItem = item;
120 ProtwordsItem.setNewInput(StringUtil.EMPTY);
121 try (ProtwordsUpdater updater = new ProtwordsUpdater(item)) {
122 reload(updater);
123 }
124 }
125
126
127
128
129
130 protected void reload(final ProtwordsUpdater updater) {
131 try (CurlResponse curlResponse = dictionaryManager.getContentResponse(this)) {
132 reload(updater, curlResponse.getContentAsStream());
133 } catch (final IOException e) {
134 throw new DictionaryException("Failed to parse " + path, e);
135 }
136 }
137
138
139
140
141
142
143 protected void reload(final ProtwordsUpdater updater, final InputStream in) {
144 final List<ProtwordsItem> itemList = new ArrayList<>();
145 try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, Constants.UTF_8))) {
146 long id = 0;
147 String line = null;
148 while ((line = reader.readLine()) != null) {
149 if (line.length() == 0 || line.charAt(0) == '#') {
150 if (updater != null) {
151 updater.write(line);
152 }
153 continue;
154 }
155
156 final String inputStrings = line;
157 final String input = unescape(inputStrings);
158
159 if (input.length() > 0) {
160 id++;
161 final ProtwordsItem item = new ProtwordsItem(id, input);
162 if (updater != null) {
163 final ProtwordsItem newItem = updater.write(item);
164 if (newItem != null) {
165 itemList.add(newItem);
166 } else {
167 id--;
168 }
169 } else {
170 itemList.add(item);
171 }
172 }
173 }
174 if (updater != null) {
175 final ProtwordsItem item = updater.commit();
176 if (item != null) {
177 itemList.add(item);
178 }
179 }
180 protwordsItemList = itemList;
181 } catch (final IOException e) {
182 throw new DictionaryException("Failed to parse " + path, e);
183 }
184 }
185
186 private String unescape(final String s) {
187 if (s.indexOf('\\') >= 0) {
188 final StringBuilder sb = new StringBuilder();
189 for (int i = 0; i < s.length(); i++) {
190 final char ch = s.charAt(i);
191 if (ch == '\\' && i < s.length() - 1) {
192 i++;
193 sb.append(s.charAt(i));
194 } else {
195 sb.append(ch);
196 }
197 }
198 return sb.toString();
199 }
200 return s;
201 }
202
203
204
205
206
207 public String getSimpleName() {
208 return new File(path).getName();
209 }
210
211
212
213
214
215
216 public synchronized void update(final InputStream in) throws IOException {
217 try (ProtwordsUpdater updater = new ProtwordsUpdater(null)) {
218 reload(updater, in);
219 }
220 }
221
222 @Override
223 public String toString() {
224 return "ProtwordsFile [path=" + path + ", protwordsItemList=" + protwordsItemList + ", id=" + id + "]";
225 }
226
227
228
229
230
231 protected class ProtwordsUpdater implements Closeable {
232
233
234 protected boolean isCommit = false;
235
236
237 protected File newFile;
238
239
240 protected Writer writer;
241
242
243 protected ProtwordsItem item;
244
245
246
247
248
249 protected ProtwordsUpdater(final ProtwordsItem newItem) {
250 FileOutputStream fos = null;
251 try {
252 newFile = ComponentUtil.getSystemHelper().createTempFile(PROTWORDS, ".txt");
253 fos = new FileOutputStream(newFile);
254 writer = new BufferedWriter(new OutputStreamWriter(fos, Constants.UTF_8));
255 fos = null;
256 } catch (final Exception e) {
257 if (fos != null) {
258 try {
259 fos.close();
260 } catch (final IOException ioe) {
261
262 }
263 }
264 if (newFile != null) {
265 newFile.delete();
266 }
267 throw new DictionaryException("Failed to write a userDict file.", e);
268 }
269 item = newItem;
270 }
271
272
273
274
275
276
277 public ProtwordsItem write(final ProtwordsItem oldItem) {
278 try {
279 if (item == null || item.getId() != oldItem.getId() || !item.isUpdated()) {
280 writer.write(oldItem.toLineString());
281 writer.write(Constants.LINE_SEPARATOR);
282 return oldItem;
283 }
284 if (!item.equals(oldItem)) {
285 throw new DictionaryException("Protwords file was updated: old=" + oldItem + " : new=" + item);
286 }
287 try {
288 if (!item.isDeleted()) {
289
290 writer.write(item.toLineString());
291 writer.write(Constants.LINE_SEPARATOR);
292 return new ProtwordsItem(item.getId(), item.getNewInput());
293 }
294 return null;
295 } finally {
296 item.setNewInput(null);
297 }
298 } catch (final IOException e) {
299 throw new DictionaryException("Failed to write: " + oldItem + " -> " + item, e);
300 }
301 }
302
303
304
305
306
307 public void write(final String line) {
308 try {
309 writer.write(line);
310 writer.write(Constants.LINE_SEPARATOR);
311 } catch (final IOException e) {
312 throw new DictionaryException("Failed to write: " + line, e);
313 }
314 }
315
316
317
318
319
320 public ProtwordsItem commit() {
321 isCommit = true;
322 if (item != null && item.isUpdated()) {
323 try {
324 writer.write(item.toLineString());
325 writer.write(Constants.LINE_SEPARATOR);
326 return item;
327 } catch (final IOException e) {
328 throw new DictionaryException("Failed to write: " + item, e);
329 }
330 }
331 return null;
332 }
333
334 @Override
335 public void close() {
336 try {
337 writer.flush();
338 } catch (final IOException e) {
339
340 }
341 CloseableUtil.closeQuietly(writer);
342
343 if (isCommit) {
344 try {
345 dictionaryManager.store(ProtwordsFile.this, newFile);
346 } finally {
347 newFile.delete();
348 }
349 } else {
350 newFile.delete();
351 }
352 }
353 }
354 }