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