Mercurial > hg > digilib-old
comparison servlet/src/digilib/io/DocuDirectory.java @ 531:9cedd170b581 digilibPDF
* PDF generation works now even with subdirectories
* genericsification and clean up
author | robcast |
---|---|
date | Fri, 05 Feb 2010 20:58:38 +0100 |
parents | |
children | e758a49258e8 |
comparison
equal
deleted
inserted
replaced
530:bd6569a95a3c | 531:9cedd170b581 |
---|---|
1 /* DocuDirectory -- Directory of DocuFilesets. | |
2 | |
3 Digital Image Library servlet components | |
4 | |
5 Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de) | |
6 | |
7 This program is free software; you can redistribute it and/or modify it | |
8 under the terms of the GNU General Public License as published by the | |
9 Free Software Foundation; either version 2 of the License, or (at your | |
10 option) any later version. | |
11 | |
12 Please read license.txt for the full details. A copy of the GPL | |
13 may be found at http://www.gnu.org/copyleft/lgpl.html | |
14 | |
15 You should have received a copy of the GNU General Public License | |
16 along with this program; if not, write to the Free Software | |
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | |
19 * Created on 25.02.2003 | |
20 */ | |
21 | |
22 package digilib.io; | |
23 | |
24 import java.io.File; | |
25 import java.io.IOException; | |
26 import java.util.ArrayList; | |
27 import java.util.Collections; | |
28 import java.util.List; | |
29 import java.util.Map; | |
30 | |
31 import org.xml.sax.SAXException; | |
32 | |
33 /** | |
34 * @author casties | |
35 */ | |
36 public class DocuDirectory extends Directory { | |
37 | |
38 /** list of files (DocuDirent) */ | |
39 private List<List<DocuDirent>> list = null; | |
40 | |
41 /** directory object is valid (exists on disk) */ | |
42 private boolean isValid = false; | |
43 | |
44 /** reference of the parent DocuDirCache */ | |
45 private DocuDirCache cache = null; | |
46 | |
47 /** directory name (digilib canonical form) */ | |
48 private String dirName = null; | |
49 | |
50 /** directory metadata */ | |
51 private MetadataMap dirMeta = null; | |
52 | |
53 /** state of metadata is valid */ | |
54 private boolean metaChecked = false; | |
55 | |
56 /** unresolved file metadata */ | |
57 private Map<String, MetadataMap> unresolvedFileMeta = null; | |
58 | |
59 /** time of last access of this object (not the filesystem) */ | |
60 private long objectATime = 0; | |
61 | |
62 /** time directory was last modified on the file system */ | |
63 private long dirMTime = 0; | |
64 | |
65 /** | |
66 * Constructor with digilib directory path and a parent DocuDirCache. | |
67 * | |
68 * Directory names at the given path are appended to the base directories | |
69 * from the cache. The directory is checked on disk and isValid is set. | |
70 * | |
71 * @see readDir | |
72 * | |
73 * @param path | |
74 * digilib directory path name | |
75 * @param cache | |
76 * parent DocuDirCache | |
77 */ | |
78 public DocuDirectory(String path, DocuDirCache cache) { | |
79 this.dirName = path; | |
80 this.cache = cache; | |
81 initDir(); | |
82 checkDir(); | |
83 } | |
84 | |
85 /** | |
86 * Sets and checks the dir object. | |
87 * | |
88 */ | |
89 protected void initDir() { | |
90 String baseDirName = cache.getBaseDirNames()[0]; | |
91 // clear directory first | |
92 //list = new ArrayList[FileOps.NUM_CLASSES]; | |
93 | |
94 list = new ArrayList<List<DocuDirent>>(FileOps.NUM_CLASSES); | |
95 // create empty list | |
96 for (int i=0; i < FileOps.NUM_CLASSES; ++i) { | |
97 list.add(null); | |
98 } | |
99 isValid = false; | |
100 dirMTime = 0; | |
101 // the first directory has to exist | |
102 dir = new File(baseDirName, dirName); | |
103 } | |
104 | |
105 /** | |
106 * number of DocuFiles in this directory. | |
107 * | |
108 */ | |
109 public int size() { | |
110 return ((list != null) && (list.get(0) != null)) ? list.get(0).size() : 0; | |
111 } | |
112 | |
113 /** | |
114 * number of files of this class in this directory. | |
115 * | |
116 * @param fc | |
117 * fileClass | |
118 */ | |
119 public int size(int fc) { | |
120 return ((list != null) && (list.get(fc) != null)) ? list.get(fc).size() : 0; | |
121 } | |
122 | |
123 /** | |
124 * Returns the ImageFile at the index. | |
125 * | |
126 * @param index | |
127 * @return | |
128 */ | |
129 public ImageFileset get(int index) { | |
130 if ((list == null) || (list.get(0) == null) || (index >= list.get(0).size())) { | |
131 return null; | |
132 } | |
133 return (ImageFileset) list.get(0).get(index); | |
134 } | |
135 | |
136 /** | |
137 * Returns the file of the class at the index. | |
138 * | |
139 * @param index | |
140 * @param fc | |
141 * fileClass | |
142 * @return | |
143 */ | |
144 public DocuDirent get(int index, int fc) { | |
145 if ((list == null) || (list.get(fc) == null) || (index >= list.get(fc).size())) { | |
146 return null; | |
147 } | |
148 return (DocuDirent) list.get(fc).get(index); | |
149 } | |
150 | |
151 /** | |
152 * Checks if the directory exists on the filesystem. | |
153 * | |
154 * Sets isValid. | |
155 * | |
156 * @return | |
157 */ | |
158 public boolean checkDir() { | |
159 if (dir == null) { | |
160 initDir(); | |
161 } | |
162 isValid = dir.isDirectory(); | |
163 return isValid; | |
164 } | |
165 | |
166 /** | |
167 * Read the filesystem directory and fill this object. | |
168 * | |
169 * Clears the List and (re)reads all files. | |
170 * | |
171 * @return boolean the directory exists | |
172 */ | |
173 public synchronized boolean readDir() { | |
174 // check directory first | |
175 checkDir(); | |
176 if (!isValid) { | |
177 return false; | |
178 } | |
179 // first file extension to try for scaled directories | |
180 String scalext = null; | |
181 // read all filenames | |
182 logger.debug("reading directory " + dir.getPath()); | |
183 /* | |
184 * using ReadableFileFilter is safer (we won't get directories with file | |
185 * extensions) but slower. | |
186 */ | |
187 File[] allFiles = null; | |
188 if (cache.safeDirIndex) { | |
189 allFiles = dir.listFiles(new FileOps.ReadableFileFilter()); | |
190 } else { | |
191 allFiles = dir.listFiles(); | |
192 } | |
193 //logger.debug(" done"); | |
194 if (allFiles == null) { | |
195 // not a directory | |
196 return false; | |
197 } | |
198 // list of base dirs from the parent cache | |
199 String[] baseDirNames = cache.getBaseDirNames(); | |
200 // number of base dirs | |
201 int nb = baseDirNames.length; | |
202 // array of base dirs | |
203 Directory[] dirs = new Directory[nb]; | |
204 // first entry is this directory | |
205 dirs[0] = this; | |
206 // fill array with the remaining directories | |
207 for (int j = 1; j < nb; j++) { | |
208 File d = new File(baseDirNames[j], dirName); | |
209 if (d.isDirectory()) { | |
210 dirs[j] = new Directory(d); | |
211 logger.debug(" reading scaled directory " + d.getPath()); | |
212 dirs[j].readDir(); | |
213 //logger.debug(" done"); | |
214 } | |
215 } | |
216 | |
217 // go through all file classes | |
218 for (int classIdx = 0; classIdx < FileOps.NUM_CLASSES; classIdx++) { | |
219 int fileClass = cache.getFileClasses()[classIdx]; | |
220 //logger.debug("filtering directory "+dir.getPath()+" for class | |
221 // "+fc); | |
222 File[] fileList = FileOps.listFiles(allFiles, FileOps | |
223 .filterForClass(fileClass)); | |
224 //logger.debug(" done"); | |
225 // number of files in the directory | |
226 int numFiles = fileList.length; | |
227 if (numFiles > 0) { | |
228 // create new list | |
229 list.set(fileClass, new ArrayList<DocuDirent>(numFiles)); | |
230 // sort the file names alphabetically and iterate the list | |
231 // Arrays.sort(fileList); // not needed <hertzhaft> | |
232 Map<Integer, Object> hints = FileOps.newHints(FileOps.HINT_BASEDIRS, dirs); | |
233 hints.put(FileOps.HINT_FILEEXT, scalext); | |
234 for (int i = 0; i < numFiles; i++) { | |
235 DocuDirent f = FileOps.fileForClass(fileClass, fileList[i], | |
236 hints); | |
237 // add the file to our list | |
238 // logger.debug(f.getName()); | |
239 | |
240 list.get(fileClass).add(f); | |
241 f.setParent(this); | |
242 } | |
243 // we sort the inner ArrayList (the list of files not the list of file types) | |
244 // for binarySearch to work (DocuDirent's natural sort order is by filename) | |
245 Collections.sort(list.get(fileClass)); | |
246 } | |
247 } | |
248 // clear the scaled directories | |
249 for (int j = 1; j < nb; j++) { | |
250 if (dirs[j] != null) { | |
251 dirs[j].clearFilenames(); | |
252 } | |
253 } | |
254 // update number of cached files if this was the first time | |
255 if (dirMTime == 0) { | |
256 cache.numFiles += size(); | |
257 } | |
258 dirMTime = dir.lastModified(); | |
259 // read metadata as well | |
260 readMeta(); | |
261 return isValid; | |
262 } | |
263 | |
264 /** | |
265 * Check to see if the directory has been modified and reread if necessary. | |
266 * | |
267 * @return boolean the directory is valid | |
268 */ | |
269 public boolean refresh() { | |
270 if (isValid) { | |
271 if (dir.lastModified() > dirMTime) { | |
272 // on-disk modification time is more recent | |
273 readDir(); | |
274 } | |
275 touch(); | |
276 } | |
277 return isValid; | |
278 } | |
279 | |
280 /** | |
281 * Read directory metadata. | |
282 * | |
283 */ | |
284 public void readMeta() { | |
285 // check for directory metadata... | |
286 File mf = new File(dir, "index.meta"); | |
287 if (mf.canRead()) { | |
288 XMLMetaLoader ml = new XMLMetaLoader(); | |
289 try { | |
290 // read directory meta file | |
291 Map<String, MetadataMap> fileMeta = ml.loadURL(mf.getAbsolutePath()); | |
292 if (fileMeta == null) { | |
293 return; | |
294 } | |
295 // meta for the directory itself is in the "" bin | |
296 dirMeta = fileMeta.remove(""); | |
297 // read meta for files in this directory | |
298 readFileMeta(fileMeta, null); | |
299 // is there meta for other files left? | |
300 if (fileMeta.size() > 0) { | |
301 unresolvedFileMeta = fileMeta; | |
302 } | |
303 } catch (SAXException e) { | |
304 logger.warn("error parsing index.meta", e); | |
305 } catch (IOException e) { | |
306 logger.warn("error reading index.meta", e); | |
307 } | |
308 } | |
309 readParentMeta(); | |
310 metaChecked = true; | |
311 } | |
312 | |
313 /** | |
314 * Read metadata from all known parent directories. | |
315 * | |
316 */ | |
317 public void readParentMeta() { | |
318 // check the parent directories for additional file meta | |
319 Directory dd = parent; | |
320 String path = dir.getName(); | |
321 while (dd != null) { | |
322 if (((DocuDirectory) dd).hasUnresolvedFileMeta()) { | |
323 readFileMeta(((DocuDirectory) dd).unresolvedFileMeta, path); | |
324 } | |
325 // prepend parent dir path | |
326 path = dd.dir.getName() + "/" + path; | |
327 // become next parent | |
328 dd = dd.parent; | |
329 } | |
330 } | |
331 | |
332 /** | |
333 * Read metadata for the files in this directory. | |
334 * | |
335 * Takes a Map with meta-information, adding the relative path before the | |
336 * lookup. | |
337 * | |
338 * @param fileMeta | |
339 * @param relPath | |
340 * @param fc | |
341 * fileClass | |
342 */ | |
343 protected void readFileMeta(Map<String,MetadataMap> fileMeta, String relPath) { | |
344 if (list == null) { | |
345 // there are no files | |
346 return; | |
347 } | |
348 String path = (relPath != null) ? (relPath + "/") : ""; | |
349 // go through all file classes | |
350 for (int nc = 0; nc < list.size(); nc++) { | |
351 int fc = cache.getFileClasses()[nc]; | |
352 if (list.get(fc) == null) { | |
353 continue; | |
354 } | |
355 // iterate through the list of files in this directory | |
356 for (DocuDirent f: list.get(fc)) { | |
357 // prepend path to the filename | |
358 String fn = path + f.getName(); | |
359 // look up meta for this file and remove from dir | |
360 MetadataMap meta = fileMeta.remove(fn); | |
361 if (meta != null) { | |
362 // store meta in file | |
363 f.setFileMeta(meta); | |
364 } | |
365 } | |
366 } | |
367 } | |
368 | |
369 protected void notifyChildMeta(MetadataMap childmeta) { | |
370 List<DocuDirectory> children = cache.getChildren(this.dirName, true); | |
371 if (children.size() > 0) { | |
372 /*for (DocuDirectory d: children) { | |
373 // TODO: finish this! | |
374 //((DocuDirectory) i.next()).readFileMeta() | |
375 }*/ | |
376 } | |
377 } | |
378 | |
379 /** | |
380 * Update access time. | |
381 * | |
382 * @return long time of last access. | |
383 */ | |
384 public long touch() { | |
385 long t = objectATime; | |
386 objectATime = System.currentTimeMillis(); | |
387 return t; | |
388 } | |
389 | |
390 /** | |
391 * Searches for the file with the name <code>fn</code>. | |
392 * | |
393 * Searches the directory for the file with the name <code>fn</code> and | |
394 * returns its index. Returns -1 if the file cannot be found. | |
395 * | |
396 * @param fn | |
397 * filename | |
398 * @param fc | |
399 * file class | |
400 * @return int index of file <code>fn</code> | |
401 */ | |
402 public int indexOf(String fn) { | |
403 int fc = FileOps.classForFilename(fn); | |
404 return indexOf(fn, fc); | |
405 } | |
406 | |
407 /** | |
408 * Searches for the file with the name <code>fn</code> and class fc. | |
409 * | |
410 * Searches the directory for the file with the name <code>fn</code> and | |
411 * returns its index. Returns -1 if the file cannot be found. | |
412 * | |
413 * @param fn | |
414 * filename | |
415 * @return int index of file <code>fn</code> | |
416 */ | |
417 public int indexOf(String fn, int fc) { | |
418 if (!isRead()) { | |
419 // read directory now | |
420 if (!readDir()) { | |
421 return -1; | |
422 } | |
423 } | |
424 List<DocuDirent> fileList = list.get(fc); | |
425 // empty directory? | |
426 if (fileList == null) { | |
427 return -1; | |
428 } | |
429 | |
430 // search for exact match (DocuDirent does compareTo<String>) | |
431 // OBS: fileList needs to be sorted first (see )! <hertzhaft> | |
432 int idx = Collections.binarySearch(fileList, fn); | |
433 if (idx >= 0) { | |
434 return idx; | |
435 } else { | |
436 logger.debug(fn + " not found by binarysearch"); | |
437 // try closest matches without extension | |
438 idx = -idx - 1; | |
439 if ((idx < fileList.size()) | |
440 && isBaseInList(fileList, idx, fn)) { | |
441 // idx matches | |
442 return idx; | |
443 } else if ((idx > 0) | |
444 && isBaseInList(fileList, idx-1, fn)) { | |
445 // idx-1 matches | |
446 return idx - 1; | |
447 } else if ((idx + 1 < fileList.size()) | |
448 && isBaseInList(fileList, idx+1, fn)) { | |
449 // idx+1 matches | |
450 return idx + 1; | |
451 } | |
452 | |
453 } | |
454 return -1; | |
455 } | |
456 | |
457 private boolean isBaseInList(List<DocuDirent> fl, int idx, String fn) { | |
458 String dfn = FileOps.basename((fl.get(idx)) | |
459 .getName()); | |
460 return (dfn.equals(fn)||dfn.equals(FileOps.basename(fn))); | |
461 } | |
462 | |
463 | |
464 /** | |
465 * Finds the DocuDirent with the name <code>fn</code>. | |
466 * | |
467 * Searches the directory for the DocuDirent with the name <code>fn</code> | |
468 * and returns it. Returns null if the file cannot be found. | |
469 * | |
470 * @param fn | |
471 * filename | |
472 * @return DocuDirent | |
473 */ | |
474 public DocuDirent find(String fn) { | |
475 int fc = FileOps.classForFilename(fn); | |
476 int i = indexOf(fn, fc); | |
477 if (i >= 0) { | |
478 return (DocuDirent) list.get(0).get(i); | |
479 } | |
480 return null; | |
481 } | |
482 | |
483 /** | |
484 * Finds the DocuDirent with the name <code>fn</code> and class | |
485 * <code>fc</code>. | |
486 * | |
487 * Searches the directory for the DocuDirent with the name <code>fn</code> | |
488 * and returns it. Returns null if the file cannot be found. | |
489 * | |
490 * @param fn | |
491 * filename | |
492 * @return DocuDirent | |
493 */ | |
494 public DocuDirent find(String fn, int fc) { | |
495 int i = indexOf(fn, fc); | |
496 if (i >= 0) { | |
497 return (DocuDirent) list.get(fc).get(i); | |
498 } | |
499 return null; | |
500 } | |
501 | |
502 /** | |
503 * Returns the digilib canonical name. | |
504 * | |
505 * @return | |
506 */ | |
507 public String getDirName() { | |
508 return dirName; | |
509 } | |
510 | |
511 /** | |
512 * The directory is valid (exists on disk). | |
513 * | |
514 * @return boolean | |
515 */ | |
516 public boolean isValid() { | |
517 return isValid; | |
518 } | |
519 | |
520 /** | |
521 * The directory has been read from disk. | |
522 * | |
523 * @return | |
524 */ | |
525 public boolean isRead() { | |
526 return (dirMTime != 0); | |
527 } | |
528 | |
529 /** | |
530 * @return long | |
531 */ | |
532 public long getAccessTime() { | |
533 return objectATime; | |
534 } | |
535 | |
536 /** | |
537 * @return Hashtable | |
538 */ | |
539 public MetadataMap getDirMeta() { | |
540 return dirMeta; | |
541 } | |
542 | |
543 /** | |
544 * Checks metadata | |
545 * | |
546 */ | |
547 public void checkMeta() { | |
548 if (metaChecked) { | |
549 return; | |
550 } else { | |
551 readMeta(); | |
552 } | |
553 } | |
554 | |
555 /** | |
556 * @return long | |
557 */ | |
558 public long getDirMTime() { | |
559 return dirMTime; | |
560 } | |
561 | |
562 /** | |
563 * Sets the dirMeta. | |
564 * | |
565 * @param dirMeta | |
566 * The dirMeta to set | |
567 */ | |
568 public void setDirMeta(MetadataMap dirMeta) { | |
569 this.dirMeta = dirMeta; | |
570 } | |
571 | |
572 public boolean hasUnresolvedFileMeta() { | |
573 return (this.unresolvedFileMeta != null); | |
574 } | |
575 | |
576 } |