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