comparison common/src/main/java/digilib/io/DocuDirectory.java @ 903:7779b37d1d05

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