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