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