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