Mercurial > hg > digilib-old
annotate common/src/main/java/digilib/io/DocuDirectory.java @ 1041:81c2378f12ff
put the child back into the bath :-)
| author | hertzhaft |
|---|---|
| date | Thu, 22 Mar 2012 17:51:52 +0100 |
| parents | 7bcc6765c209 |
| children |
| 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 String baseDirName = cache.getBaseDirNames()[0]; |
| 563 | 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) { | |
| 531 | 94 list.add(null); |
| 95 } | |
| 159 | 96 dirMTime = 0; |
| 97 // the first directory has to exist | |
| 759 | 98 dir = new File(baseDirName, path); |
| 99 isValid = dir.isDirectory(); | |
| 159 | 100 } |
| 156 | 101 |
| 270 | 102 /** |
| 103 * number of DocuFiles in this directory. | |
| 104 * | |
| 151 | 105 */ |
| 270 | 106 public int size() { |
| 531 | 107 return ((list != null) && (list.get(0) != null)) ? list.get(0).size() : 0; |
| 151 | 108 } |
| 91 | 109 |
| 270 | 110 /** |
| 111 * number of files of this class in this directory. | |
| 151 | 112 * |
| 270 | 113 * @param fc |
| 114 * fileClass | |
| 151 | 115 */ |
| 563 | 116 public int size(FileClass fc) { |
| 117 return ((list != null) && (list.get(fc.ordinal()) != null)) ? list.get(fc.ordinal()).size() : 0; | |
| 91 | 118 } |
| 119 | |
| 270 | 120 /** |
| 563 | 121 * Returns the ImageFileSet at the index. |
| 151 | 122 * |
| 123 * @param index | |
| 124 * @return | |
| 125 */ | |
| 583 | 126 public DocuDirent get(int index) { |
| 531 | 127 if ((list == null) || (list.get(0) == null) || (index >= list.get(0).size())) { |
| 122 | 128 return null; |
| 151 | 129 } |
| 583 | 130 return list.get(0).get(index); |
| 159 | 131 } |
| 132 | |
| 270 | 133 /** |
| 134 * Returns the file of the class at the index. | |
| 159 | 135 * |
| 136 * @param index | |
| 270 | 137 * @param fc |
| 138 * fileClass | |
| 159 | 139 * @return |
| 140 */ | |
| 563 | 141 public DocuDirent get(int index, FileClass fc) { |
| 142 if ((list == null) || (list.get(fc.ordinal()) == null) || (index >= list.get(fc.ordinal()).size())) { | |
| 159 | 143 return null; |
| 144 } | |
| 563 | 145 return (DocuDirent) list.get(fc.ordinal()).get(index); |
| 159 | 146 } |
| 147 | |
| 270 | 148 /** |
| 149 * Read the filesystem directory and fill this object. | |
| 86 | 150 * |
| 130 | 151 * Clears the List and (re)reads all files. |
| 86 | 152 * |
| 153 * @return boolean the directory exists | |
| 154 */ | |
| 759 | 155 public synchronized boolean readDir() { |
| 159 | 156 // check directory first |
| 157 if (!isValid) { | |
| 158 return false; | |
| 159 } | |
| 759 | 160 // re-check modification time because the thread may have slept |
| 161 if (dir.lastModified() <= dirMTime) { | |
| 162 return true; | |
| 163 } | |
| 259 | 164 // read all filenames |
| 759 | 165 logger.debug("reading directory "+this+" = "+dir.getPath()); |
| 583 | 166 File[] allFiles = null; |
| 259 | 167 /* |
| 270 | 168 * using ReadableFileFilter is safer (we won't get directories with file |
| 169 * extensions) but slower. | |
| 259 | 170 */ |
| 583 | 171 // allFiles = dir.listFiles(new FileOps.ReadableFileFilter()); |
| 545 | 172 allFiles = dir.listFiles(); |
| 259 | 173 if (allFiles == null) { |
| 174 // not a directory | |
| 175 return false; | |
| 176 } | |
| 583 | 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 } | |
| 270 | 196 } |
| 197 } | |
| 198 | |
| 159 | 199 // go through all file classes |
| 583 | 200 for (FileClass fileClass : cache.getFileClasses()) { |
| 201 File[] fileList = FileOps.listFiles(allFiles, | |
| 202 FileOps.filterForClass(fileClass)); | |
| 159 | 203 // number of files in the directory |
| 270 | 204 int numFiles = fileList.length; |
| 205 if (numFiles > 0) { | |
| 91 | 206 // create new list |
| 583 | 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); | |
| 159 | 212 // add the file to our list |
| 583 | 213 dl.add(df); |
| 86 | 214 } |
| 583 | 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); | |
| 86 | 221 } |
| 222 } | |
| 270 | 223 // clear the scaled directories |
| 583 | 224 for (Directory d: dirs) { |
| 225 if (d != null) { | |
| 226 d.clearFilenames(); | |
| 270 | 227 } |
| 228 } | |
| 229 // update number of cached files if this was the first time | |
| 230 if (dirMTime == 0) { | |
| 937 | 231 cache.numImgFiles.addAndGet(size(FileClass.IMAGE)); |
| 270 | 232 } |
| 159 | 233 dirMTime = dir.lastModified(); |
| 234 // read metadata as well | |
| 235 readMeta(); | |
| 86 | 236 return isValid; |
| 237 } | |
| 238 | |
| 270 | 239 /** |
| 240 * Check to see if the directory has been modified and reread if necessary. | |
| 86 | 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 | |
| 270 | 255 /** |
| 256 * Read directory metadata. | |
| 257 * | |
| 86 | 258 */ |
| 259 public void readMeta() { | |
| 260 // check for directory metadata... | |
| 130 | 261 File mf = new File(dir, "index.meta"); |
| 262 if (mf.canRead()) { | |
| 263 XMLMetaLoader ml = new XMLMetaLoader(); | |
| 264 try { | |
| 265 // read directory meta file | |
| 531 | 266 Map<String, MetadataMap> fileMeta = ml.loadURL(mf.getAbsolutePath()); |
| 130 | 267 if (fileMeta == null) { |
| 268 return; | |
| 269 } | |
| 270 // meta for the directory itself is in the "" bin | |
| 531 | 271 dirMeta = fileMeta.remove(""); |
| 151 | 272 // read meta for files in this directory |
| 273 readFileMeta(fileMeta, null); | |
| 274 // is there meta for other files left? | |
| 130 | 275 if (fileMeta.size() > 0) { |
| 151 | 276 unresolvedFileMeta = fileMeta; |
| 130 | 277 } |
| 278 } catch (SAXException e) { | |
| 282 | 279 logger.warn("error parsing index.meta", e); |
| 130 | 280 } catch (IOException e) { |
| 282 | 281 logger.warn("error reading index.meta", e); |
| 130 | 282 } |
| 151 | 283 } |
| 284 readParentMeta(); | |
| 233 | 285 metaChecked = true; |
| 151 | 286 } |
| 130 | 287 |
| 270 | 288 /** |
| 289 * Read metadata from all known parent directories. | |
| 290 * | |
| 151 | 291 */ |
| 292 public void readParentMeta() { | |
| 293 // check the parent directories for additional file meta | |
| 294 Directory dd = parent; | |
| 156 | 295 String path = dir.getName(); |
| 151 | 296 while (dd != null) { |
| 156 | 297 if (((DocuDirectory) dd).hasUnresolvedFileMeta()) { |
| 298 readFileMeta(((DocuDirectory) dd).unresolvedFileMeta, path); | |
| 151 | 299 } |
| 300 // prepend parent dir path | |
| 301 path = dd.dir.getName() + "/" + path; | |
| 302 // become next parent | |
| 303 dd = dd.parent; | |
| 304 } | |
| 305 } | |
| 306 | |
| 270 | 307 /** |
| 308 * Read metadata for the files in this directory. | |
| 151 | 309 * |
| 270 | 310 * Takes a Map with meta-information, adding the relative path before the |
| 311 * lookup. | |
| 151 | 312 * |
| 313 * @param fileMeta | |
| 314 * @param relPath | |
| 270 | 315 * @param fc |
| 316 * fileClass | |
| 151 | 317 */ |
| 531 | 318 protected void readFileMeta(Map<String,MetadataMap> fileMeta, String relPath) { |
| 151 | 319 if (list == null) { |
| 320 // there are no files | |
| 321 return; | |
| 322 } | |
| 323 String path = (relPath != null) ? (relPath + "/") : ""; | |
| 159 | 324 // go through all file classes |
| 563 | 325 for (FileClass fc: FileClass.values()) { |
| 326 if (list.get(fc.ordinal()) == null) { | |
| 159 | 327 continue; |
| 328 } | |
| 329 // iterate through the list of files in this directory | |
| 563 | 330 for (DocuDirent f: list.get(fc.ordinal())) { |
| 159 | 331 // prepend path to the filename |
| 332 String fn = path + f.getName(); | |
| 333 // look up meta for this file and remove from dir | |
| 531 | 334 MetadataMap meta = fileMeta.remove(fn); |
| 159 | 335 if (meta != null) { |
| 336 // store meta in file | |
| 337 f.setFileMeta(meta); | |
| 338 } | |
| 339 } | |
| 340 } | |
| 341 } | |
| 342 | |
| 531 | 343 protected void notifyChildMeta(MetadataMap childmeta) { |
| 344 List<DocuDirectory> children = cache.getChildren(this.dirName, true); | |
| 159 | 345 if (children.size() > 0) { |
| 531 | 346 /*for (DocuDirectory d: children) { |
| 159 | 347 // TODO: finish this! |
| 348 //((DocuDirectory) i.next()).readFileMeta() | |
| 531 | 349 }*/ |
| 130 | 350 } |
| 86 | 351 } |
| 352 | |
| 270 | 353 /** |
| 354 * Update access time. | |
| 86 | 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 | |
| 270 | 364 /** |
| 365 * Searches for the file with the name <code>fn</code>. | |
| 86 | 366 * |
| 270 | 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 | |
| 86 | 374 * @return int index of file <code>fn</code> |
| 375 */ | |
| 376 public int indexOf(String fn) { | |
| 563 | 377 FileClass fc = FileOps.classForFilename(fn); |
| 159 | 378 return indexOf(fn, fc); |
| 379 } | |
| 270 | 380 |
| 381 /** | |
| 382 * Searches for the file with the name <code>fn</code> and class fc. | |
| 159 | 383 * |
| 270 | 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 | |
| 159 | 389 * @return int index of file <code>fn</code> |
| 390 */ | |
| 563 | 391 public int indexOf(String fn, FileClass fc) { |
| 176 | 392 if (!isRead()) { |
| 393 // read directory now | |
| 394 if (!readDir()) { | |
| 395 return -1; | |
| 396 } | |
| 397 } | |
| 563 | 398 List<DocuDirent> fileList = list.get(fc.ordinal()); |
| 246 | 399 // empty directory? |
| 288 | 400 if (fileList == null) { |
| 246 | 401 return -1; |
| 402 } | |
|
472
f8ca069517a2
Bugfix for images not found in dir: added sorting for ArrayLists of ImageFilesets
hertzhaft
parents:
341
diff
changeset
|
403 |
| 531 | 404 // 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
|
405 // OBS: fileList needs to be sorted first (see )! <hertzhaft> |
| 288 | 406 int idx = Collections.binarySearch(fileList, fn); |
| 270 | 407 if (idx >= 0) { |
| 408 return idx; | |
| 409 } else { | |
|
472
f8ca069517a2
Bugfix for images not found in dir: added sorting for ArrayLists of ImageFilesets
hertzhaft
parents:
341
diff
changeset
|
410 logger.debug(fn + " not found by binarysearch"); |
| 270 | 411 // try closest matches without extension |
| 282 | 412 idx = -idx - 1; |
| 288 | 413 if ((idx < fileList.size()) |
| 563 | 414 && isBasenameInList(fileList, idx, fn)) { |
| 282 | 415 // idx matches |
| 416 return idx; | |
| 417 } else if ((idx > 0) | |
| 563 | 418 && isBasenameInList(fileList, idx-1, fn)) { |
| 282 | 419 // idx-1 matches |
| 270 | 420 return idx - 1; |
| 288 | 421 } else if ((idx + 1 < fileList.size()) |
| 563 | 422 && isBasenameInList(fileList, idx+1, fn)) { |
| 282 | 423 // idx+1 matches |
| 270 | 424 return idx + 1; |
| 86 | 425 } |
| 270 | 426 |
| 86 | 427 } |
| 428 return -1; | |
| 429 } | |
| 430 | |
|
576
dad720e9b12b
try: DocuDirent as interface, ImageFile inherits from ImageInput and implements DocuDirent
robcast
parents:
574
diff
changeset
|
431 private boolean isBasenameInList(List<DocuDirent> fileList, int idx, String fn) { |
| 583 | 432 String dfn = FileOps.basename((fileList.get(idx)).getName()); |
| 341 | 433 return (dfn.equals(fn)||dfn.equals(FileOps.basename(fn))); |
| 434 } | |
| 435 | |
| 436 | |
| 270 | 437 /** |
| 438 * Finds the DocuDirent with the name <code>fn</code>. | |
| 86 | 439 * |
| 270 | 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 | |
| 187 | 445 * @return DocuDirent |
| 86 | 446 */ |
| 187 | 447 public DocuDirent find(String fn) { |
| 563 | 448 FileClass fc = FileOps.classForFilename(fn); |
| 159 | 449 int i = indexOf(fn, fc); |
| 86 | 450 if (i >= 0) { |
| 583 | 451 return list.get(0).get(i); |
| 159 | 452 } |
| 453 return null; | |
| 454 } | |
| 455 | |
| 270 | 456 /** |
| 457 * Finds the DocuDirent with the name <code>fn</code> and class | |
| 458 * <code>fc</code>. | |
| 159 | 459 * |
| 270 | 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 | |
| 187 | 465 * @return DocuDirent |
| 159 | 466 */ |
| 563 | 467 public DocuDirent find(String fn, FileClass fc) { |
| 159 | 468 int i = indexOf(fn, fc); |
| 469 if (i >= 0) { | |
| 583 | 470 return list.get(fc.ordinal()).get(i); |
| 86 | 471 } |
| 472 return null; | |
| 473 } | |
| 474 | |
| 475 /** | |
| 270 | 476 * Returns the digilib canonical name. |
| 477 * | |
| 478 * @return | |
| 86 | 479 */ |
| 480 public String getDirName() { | |
| 481 return dirName; | |
| 482 } | |
| 483 | |
| 270 | 484 /** |
| 485 * The directory is valid (exists on disk). | |
| 159 | 486 * |
| 86 | 487 * @return boolean |
| 488 */ | |
| 489 public boolean isValid() { | |
| 490 return isValid; | |
| 491 } | |
| 492 | |
| 270 | 493 /** |
| 494 * The directory has been read from disk. | |
| 159 | 495 * |
| 496 * @return | |
| 497 */ | |
| 498 public boolean isRead() { | |
| 499 return (dirMTime != 0); | |
| 500 } | |
| 501 | |
| 86 | 502 /** |
| 503 * @return long | |
| 504 */ | |
| 505 public long getAccessTime() { | |
| 506 return objectATime; | |
| 507 } | |
| 508 | |
| 509 /** | |
| 510 * @return Hashtable | |
| 511 */ | |
| 531 | 512 public MetadataMap getDirMeta() { |
| 86 | 513 return dirMeta; |
| 514 } | |
| 270 | 515 |
| 233 | 516 /** |
| 517 * Checks metadata | |
| 518 * | |
| 519 */ | |
| 520 public void checkMeta() { | |
| 521 if (metaChecked) { | |
| 522 return; | |
| 523 } else { | |
| 524 readMeta(); | |
| 525 } | |
| 526 } | |
| 86 | 527 |
| 528 /** | |
| 529 * @return long | |
| 530 */ | |
| 531 public long getDirMTime() { | |
| 532 return dirMTime; | |
| 533 } | |
| 534 | |
| 535 /** | |
| 536 * Sets the dirMeta. | |
| 270 | 537 * |
| 538 * @param dirMeta | |
| 539 * The dirMeta to set | |
| 86 | 540 */ |
| 531 | 541 public void setDirMeta(MetadataMap dirMeta) { |
| 86 | 542 this.dirMeta = dirMeta; |
| 543 } | |
| 544 | |
| 151 | 545 public boolean hasUnresolvedFileMeta() { |
| 546 return (this.unresolvedFileMeta != null); | |
| 547 } | |
| 548 | |
| 86 | 549 } |
