1 /*
2 * Copyright 2012-2025 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.dict;
17
18 import java.io.BufferedInputStream;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Date;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.ListIterator;
27
28 import org.codelibs.curl.CurlResponse;
29 import org.dbflute.optional.OptionalEntity;
30 import org.lastaflute.web.servlet.request.stream.WrittenStreamOut;
31
32 /**
33 * Abstract base class for dictionary files that manage dictionary items.
34 * A dictionary file represents a collection of dictionary items with
35 * CRUD operations and pagination support.
36 *
37 * @param <T> the type of dictionary items managed by this file
38 */
39 public abstract class DictionaryFile<T extends DictionaryItem> {
40 /** The dictionary manager responsible for this file. */
41 protected DictionaryManager dictionaryManager;
42
43 /** The unique identifier for this dictionary file. */
44 protected String id;
45
46 /** The file path of this dictionary. */
47 protected String path;
48
49 /** The timestamp when this dictionary file was created or last modified. */
50 protected Date timestamp;
51
52 /**
53 * Creates a new DictionaryFile with the specified parameters.
54 *
55 * @param id the unique identifier for this dictionary file
56 * @param path the file path of this dictionary
57 * @param timestamp the timestamp of the dictionary file
58 */
59 protected DictionaryFile(final String id, final String path, final Date timestamp) {
60 this.id = id;
61 this.path = path;
62 this.timestamp = timestamp;
63 }
64
65 /**
66 * Returns the unique identifier of this dictionary file.
67 *
68 * @return the dictionary file ID
69 */
70 public String getId() {
71 return id;
72 }
73
74 /**
75 * Gets the file path of this dictionary.
76 *
77 * @return the file path
78 */
79 public String getPath() {
80 return path;
81 }
82
83 /**
84 * Returns the timestamp of this dictionary file.
85 *
86 * @return the timestamp when this dictionary was created or last modified
87 */
88 public Date getTimestamp() {
89 return timestamp;
90 }
91
92 /**
93 * Sets the dictionary manager for this file and returns this instance.
94 *
95 * @param dictionaryManager the dictionary manager to set
96 * @return this dictionary file instance for method chaining
97 */
98 public DictionaryFile<T> manager(final DictionaryManager dictionaryManager) {
99 this.dictionaryManager = dictionaryManager;
100 return this;
101 }
102
103 /**
104 * Writes the content of this dictionary file to the provided output stream.
105 *
106 * @param out the output stream to write to
107 * @throws IOException if an I/O error occurs during writing
108 */
109 public void writeOut(final WrittenStreamOut out) throws IOException {
110 try (final CurlResponse curlResponse = dictionaryManager.getContentResponse(this);
111 final InputStream inputStream = new BufferedInputStream(curlResponse.getContentAsStream())) {
112 out.write(inputStream);
113 }
114 }
115
116 /**
117 * Gets the type identifier for this dictionary file.
118 *
119 * @return the dictionary type
120 */
121 public abstract String getType();
122
123 /**
124 * Retrieves a paginated list of dictionary items.
125 *
126 * @param offset the starting offset for pagination
127 * @param size the number of items to retrieve
128 * @return a paginated list of dictionary items
129 */
130 public abstract PagingList<T> selectList(int offset, int size);
131
132 /**
133 * Retrieves a dictionary item by its ID.
134 *
135 * @param id the item ID
136 * @return an optional containing the item if found
137 */
138 public abstract OptionalEntity<T> get(long id);
139
140 /**
141 * Inserts a new dictionary item.
142 *
143 * @param item the item to insert
144 */
145 public abstract void insert(T item);
146
147 /**
148 * Updates an existing dictionary item.
149 *
150 * @param item the item to update
151 */
152 public abstract void update(T item);
153
154 /**
155 * Deletes a dictionary item.
156 *
157 * @param item the item to delete
158 */
159 public abstract void delete(T item);
160
161 /**
162 * A paginated list implementation that wraps another list and provides
163 * pagination metadata and functionality.
164 *
165 * @param <E> the type of elements in this list
166 */
167 public static class PagingList<E> implements List<E> {
168 /** The underlying list containing the actual data. */
169 private final List<E> parent;
170
171 /** The total number of pages available. */
172 protected int allPageCount;
173
174 /** The total number of records across all pages. */
175 protected int allRecordCount;
176
177 /** The number of records per page. */
178 protected int pageSize;
179
180 /** The current page number (1-based). */
181 protected int currentPageNumber;
182
183 /** The size of the page range for navigation. */
184 protected int pageRangeSize;
185
186 /**
187 * Creates a new PagingList with the specified parameters.
188 *
189 * @param list the underlying list of items for this page
190 * @param offset the starting offset for this page
191 * @param size the page size
192 * @param allRecordCount the total number of records across all pages
193 */
194 public PagingList(final List<E> list, final int offset, final int size, final int allRecordCount) {
195 parent = list;
196 this.allRecordCount = allRecordCount;
197 pageSize = size;
198 currentPageNumber = offset / size + 1;
199 allPageCount = allRecordCount == 0 ? 0 : (allRecordCount - 1) / size + 1;
200 }
201
202 @Override
203 public int size() {
204 return parent.size();
205 }
206
207 @Override
208 public boolean isEmpty() {
209 return parent.isEmpty();
210 }
211
212 @Override
213 public boolean contains(final Object o) {
214 return parent.contains(o);
215 }
216
217 @Override
218 public Iterator<E> iterator() {
219 return parent.iterator();
220 }
221
222 @Override
223 public Object[] toArray() {
224 return parent.toArray();
225 }
226
227 @Override
228 public <T> T[] toArray(final T[] a) {
229 return parent.toArray(a);
230 }
231
232 @Override
233 public boolean add(final E e) {
234 return parent.add(e);
235 }
236
237 @Override
238 public boolean remove(final Object o) {
239 return parent.remove(o);
240 }
241
242 @Override
243 public boolean containsAll(final Collection<?> c) {
244 return parent.containsAll(c);
245 }
246
247 @Override
248 public boolean addAll(final Collection<? extends E> c) {
249 return parent.addAll(c);
250 }
251
252 @Override
253 public boolean addAll(final int index, final Collection<? extends E> c) {
254 return parent.addAll(index, c);
255 }
256
257 @Override
258 public boolean removeAll(final Collection<?> c) {
259 return parent.removeAll(c);
260 }
261
262 @Override
263 public boolean retainAll(final Collection<?> c) {
264 return parent.retainAll(c);
265 }
266
267 @Override
268 public void clear() {
269 parent.clear();
270 }
271
272 @Override
273 public E get(final int index) {
274 return parent.get(index);
275 }
276
277 @Override
278 public E set(final int index, final E element) {
279 return parent.set(index, element);
280 }
281
282 @Override
283 public void add(final int index, final E element) {
284 parent.add(index, element);
285 }
286
287 @Override
288 public E remove(final int index) {
289 return parent.remove(index);
290 }
291
292 @Override
293 public int indexOf(final Object o) {
294 return parent.indexOf(o);
295 }
296
297 @Override
298 public int lastIndexOf(final Object o) {
299 return parent.lastIndexOf(o);
300 }
301
302 @Override
303 public ListIterator<E> listIterator() {
304 return parent.listIterator();
305 }
306
307 @Override
308 public ListIterator<E> listIterator(final int index) {
309 return parent.listIterator(index);
310 }
311
312 @Override
313 public List<E> subList(final int fromIndex, final int toIndex) {
314 return parent.subList(fromIndex, toIndex);
315 }
316
317 /**
318 * Returns the total number of records across all pages.
319 *
320 * @return the total record count
321 */
322 public int getAllRecordCount() {
323 return allRecordCount;
324 }
325
326 /**
327 * Returns the page size (number of records per page).
328 *
329 * @return the page size
330 */
331 public int getPageSize() {
332 return pageSize;
333 }
334
335 /**
336 * Returns the current page number (1-based).
337 *
338 * @return the current page number
339 */
340 public int getCurrentPageNumber() {
341 return currentPageNumber;
342 }
343
344 /**
345 * Returns the total number of pages.
346 *
347 * @return the total page count
348 */
349 public int getAllPageCount() {
350 return allPageCount;
351 }
352
353 /**
354 * Checks if a previous page exists.
355 *
356 * @return true if there is a previous page, false otherwise
357 */
358 public boolean isExistPrePage() {
359 return currentPageNumber != 1;
360 }
361
362 /**
363 * Checks if a next page exists.
364 *
365 * @return true if there is a next page, false otherwise
366 */
367 public boolean isExistNextPage() {
368 return currentPageNumber != allPageCount;
369 }
370
371 /**
372 * Sets the page range size for navigation.
373 *
374 * @param pageRangeSize the page range size to set
375 */
376 public void setPageRangeSize(final int pageRangeSize) {
377 this.pageRangeSize = pageRangeSize;
378 }
379
380 /**
381 * Creates a list of page numbers for navigation.
382 *
383 * @return a list of page numbers within the page range
384 */
385 public List<Integer> createPageNumberList() {
386 int startPage = currentPageNumber - pageRangeSize;
387 if (startPage < 1) {
388 startPage = 1;
389 }
390 int endPage = currentPageNumber + pageRangeSize;
391 if (endPage > allPageCount) {
392 endPage = allPageCount;
393 }
394 final List<Integer> pageNumberList = new ArrayList<>();
395 for (int i = startPage; i <= endPage; i++) {
396 pageNumberList.add(i);
397 }
398 return pageNumberList;
399 }
400
401 }
402
403 }