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 } |
