comparison servlet/src/digilib/io/FileOps.java @ 339:6d2032b6121d gen2_1

new directory and cache work
author robcast
date Wed, 17 Nov 2004 18:17:34 +0100
parents 0ff3ede32060
children
comparison
equal deleted inserted replaced
3:794a9f25f15c 339:6d2032b6121d
1 /* FileOps -- Utility class for file operations 1 /*
2 2 * FileOps -- Utility class for file operations
3 Digital Image Library servlet components 3 *
4 4 * Digital Image Library servlet components
5 Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de) 5 *
6 6 * Copyright (C) 2001, 2002 Robert Casties (robcast@mail.berlios.de)
7 This program is free software; you can redistribute it and/or modify it 7 *
8 under the terms of the GNU General Public License as published by the 8 * This program is free software; you can redistribute it and/or modify it
9 Free Software Foundation; either version 2 of the License, or (at your 9 * under the terms of the GNU General Public License as published by the Free
10 option) any later version. 10 * Software Foundation; either version 2 of the License, or (at your option)
11 11 * any later version.
12 Please read license.txt for the full details. A copy of the GPL 12 *
13 may be found at http://www.gnu.org/copyleft/lgpl.html 13 * Please read license.txt for the full details. A copy of the GPL may be found
14 14 * at http://www.gnu.org/copyleft/lgpl.html
15 You should have received a copy of the GNU General Public License 15 *
16 along with this program; if not, write to the Free Software 16 * You should have received a copy of the GNU General Public License along with
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 18 * Place, Suite 330, Boston, MA 02111-1307 USA
19 */ 19 *
20 */
20 21
21 package digilib.io; 22 package digilib.io;
22 23
23 import java.io.*; 24 import java.io.File;
24 import java.util.*; 25 import java.io.FileFilter;
25 26 import java.util.ArrayList;
26 import digilib.*; 27 import java.util.Arrays;
27 28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.StringTokenizer;
33
34 import org.apache.log4j.Logger;
28 35
29 public class FileOps { 36 public class FileOps {
30 37
31 private Utils util = null; 38 private static Logger logger = Logger.getLogger(FileOps.class);
32 public static String[] fileTypes = { 39
33 "jpg", "image/jpeg", 40 /**
34 "jpeg", "image/jpeg", 41 * Array of file extensions and corresponding mime-types.
35 "png", "image/png", 42 */
36 "gif", "image/gif", 43 private static String[][] ft = { { "jpg", "image/jpeg" },
37 "tif", "image/tiff", 44 { "jpeg", "image/jpeg" }, { "jp2", "image/jp2" },
38 "tiff", "image/tiff"}; 45 { "png", "image/png" }, { "gif", "image/gif" },
39 46 { "tif", "image/tiff" }, { "tiff", "image/tiff" },
40 public FileOps() { 47 { "txt", "text/plain" }, { "html", "text/html" },
41 util = new Utils(); 48 { "htm", "text/html" }, { "xml", "text/xml" },
42 } 49 { "svg", "image/svg+xml" } };
43 50
44 public FileOps(Utils u) { 51 public static Map fileTypes;
45 util = u; 52
46 } 53 public static List imageExtensions;
47 54
48 public void setUtils(Utils u) { 55 public static List textExtensions;
49 util = u; 56
50 } 57 public static List svgExtensions;
51 58
52 59 public static final int CLASS_NONE = -1;
53 /** 60
54 * get the mime type for a file format (by extension) 61 public static final int CLASS_IMAGE = 0;
55 */ 62
56 public static String mimeForFile(File f) { 63 public static final int CLASS_TEXT = 1;
57 String fn = f.getName(); 64
58 for (int i = 0; i < fileTypes.length; i += 2) { 65 public static final int CLASS_SVG = 2;
59 if (fn.toLowerCase().endsWith(fileTypes[i])) { 66
60 return fileTypes[i+1]; 67 public static final int NUM_CLASSES = 3;
61 } 68
62 } 69 public static int[] fileClasses = {};
63 return null; 70
64 } 71 public static int[] fcIndexes = {};
65 72
66 /** 73 public static final Integer HINT_BASEDIRS = new Integer(1);
67 * get a filehandle for a file or directory name 74
68 * returns File number n if fn is directory (starts with 1) 75 public static final Integer HINT_FILEEXT = new Integer(2);
69 */ 76
70 public File getFile(String fn, int n) throws FileOpException { 77 public static final Integer HINT_DIRS = new Integer(3);
71 util.dprintln(4, "getFile ("+fn+", "+n+")"); 78
72 79 public static File[] baseDirs = {};
73 File f = new File(fn); 80
74 // if fn is a file name then return file 81 public static DocuDirCache cache;
75 if (f.isFile()) { 82
76 return f; 83 /**
77 } 84 * static initializer for FileOps
78 // if fn is a directory name then open directory 85 */
79 if (f.isDirectory()) { 86 static {
80 File[] fl = f.listFiles(new ImgFileFilter()); 87 fileTypes = new HashMap();
81 Arrays.sort(fl); 88 imageExtensions = new ArrayList();
82 if ((n > 0) && (n <= fl.length)) { 89 textExtensions = new ArrayList();
83 return fl[n - 1]; 90 svgExtensions = new ArrayList();
84 } 91 // iterate through file types in ft and fill the Map and Lists
85 } 92 for (int i = 0; i < ft.length; i++) {
86 throw new FileOpException("Unable to find file: "+fn); 93 String ext = ft[i][0];
87 } 94 String mt = ft[i][1];
88 95 fileTypes.put(ext, mt);
89 /** 96 if (classForMimetype(mt) == CLASS_IMAGE) {
90 * get the number of files in a directory 97 imageExtensions.add(ext);
91 * (almost the same as getFile) 98 } else if (classForMimetype(mt) == CLASS_TEXT) {
92 * returns 0 in case of problems 99 textExtensions.add(ext);
93 */ 100 } else if (classForMimetype(mt) == CLASS_SVG) {
94 public int getNumFiles(String fn) throws FileOpException { 101 svgExtensions.add(ext);
95 util.dprintln(4, "getNumFiles ("+fn+")"); 102 }
96 103 }
97 File f = new File(fn); 104 }
98 // if fn is a file name then return 1 105
99 if (f.isFile()) { 106 /** sets the array of actually used file classes */
100 return 1; 107 public static void setFileClasses(int[] fc) {
101 } 108 fileClasses = fc;
102 // if fn is a directory name then return the number of files 109 fcIndexes = new int[NUM_CLASSES];
103 if (f.isDirectory()) { 110 for (int i = 0; i < fc.length; i++) {
104 return f.listFiles(new ImgFileFilter()).length; 111 fcIndexes[fc[i]] = i;
105 } 112 }
106 // then fn must be something strange... 113 }
107 return 0; 114
108 } 115 /** returns the array of actually used file classes */
109 116 public static int[] getFileClasses() {
110 117 return fileClasses;
111 /** 118 }
112 * get a filehandle for a file or directory name out of a list 119
113 * dirs is a list of base directories, fn is the appended file/dirname 120 /** returns an element from the array of actally used file classes */
114 * searches dirs until fn exists (backwards if fwd is false) 121 public static int getFileClass(int idx) {
115 * returns File number n if fn is directory (starts with 1) 122 try {
116 */ 123 return fileClasses[idx];
117 public File getFileVariant(String[] dirs, String fn, int n, boolean fwd) throws FileOpException { 124 } catch (Exception e) {
118 util.dprintln(4, "getVariantFile ("+dirs+", "+fn+", "+n+")"); 125 }
119 126 return CLASS_NONE;
120 File f = null; 127 }
121 int start = 0; 128
122 int inc = 1; 129 /** Returns the index number for the given file class.
123 int end = dirs.length; 130 * @param fc
124 if (fwd == false) { 131 * @return
125 start = dirs.length - 1; 132 */
126 inc = -1; 133 public static int getFCindex(int fc) {
127 end = 0; 134 try {
128 } 135 return fcIndexes[fc];
129 136 } catch (Exception e) {
130 for (int i = start; i != end; i += inc) { 137 }
131 try { 138 return -1;
132 f = getFile(dirs[i]+fn, n); 139 }
133 } catch (FileOpException e) { 140
134 f = null; 141 /**
135 } 142 * returns the file class for a mime-type
136 if (f != null) { 143 *
137 return f; 144 * @param mt
138 } 145 * @return
139 } 146 */
140 throw new FileOpException("Unable to find file: "+fn); 147 public static int classForMimetype(String mt) {
141 } 148 if (mt == null) {
142 149 return CLASS_NONE;
143 /** 150 }
144 * get the number of files in a directory 151 if (mt.startsWith("image/svg")) {
145 * (almost the same as getFileVariant) 152 return CLASS_SVG;
146 * returns 0 in case of problems 153 } else if (mt.startsWith("image")) {
147 */ 154 return CLASS_IMAGE;
148 public int getNumFilesVariant(String[] dirs, String fn, boolean fwd) throws FileOpException { 155 } else if (mt.startsWith("text")) {
149 util.dprintln(4, "getNumFilesVariant ("+dirs+", "+fn+")"); 156 return CLASS_TEXT;
150 157 }
151 int nf = 0; 158 return CLASS_NONE;
152 int start = 0; 159 }
153 int inc = 1; 160
154 int end = dirs.length; 161 /**
155 if (fwd == false) { 162 * get the mime type for a file format (by extension)
156 start = dirs.length - 1; 163 */
157 inc = -1; 164 public static String mimeForFile(File f) {
158 end = 0; 165 return (String) fileTypes.get(extname(f.getName().toLowerCase()));
159 } 166 }
160 167
161 for (int i = start; i != end; i += inc) { 168 /**
162 try { 169 * get the file class for the filename (by extension)
163 nf = getNumFiles(dirs[i]+fn); 170 *
164 } catch (FileOpException e) { 171 * @param fn
165 nf = 0; 172 * @return
166 } 173 */
167 if (nf > 0) { 174 public static int classForFilename(String fn) {
168 return nf; 175 String mt = (String) fileTypes.get(extname(fn).toLowerCase());
169 } 176 return classForMimetype(mt);
170 } 177 }
171 return 0; 178
172 } 179 /**
173 180 * get the file class for the file (by extension)
174 /** 181 *
175 * FileFilter for image types (helper class for getFile) 182 * @param fn
176 */ 183 * @return
177 private class ImgFileFilter implements FileFilter { 184 */
178 185 public static int classForFile(File f) {
179 public boolean accept(File f) { 186 return classForFilename(f.getName());
180 if (f.isFile()) { 187 }
181 return (mimeForFile(f) != null); 188
182 } else { 189 public static Iterator getImageExtensionIterator() {
183 return false; 190 return imageExtensions.iterator();
184 } 191 }
185 } 192
186 } 193 public static Iterator getTextExtensionIterator() {
187 194 return textExtensions.iterator();
195 }
196
197 public static Iterator getSVGExtensionIterator() {
198 return svgExtensions.iterator();
199 }
200
201 /**
202 * convert a string with a list of pathnames into an array of strings using
203 * the system's path separator string
204 */
205 public static String[] pathToArray(String paths) {
206 // split list into directories
207 StringTokenizer dirs = new StringTokenizer(paths, File.pathSeparator);
208 int n = dirs.countTokens();
209 if (n < 1) {
210 return null;
211 }
212 // add directories into array
213 String[] pathArray = new String[n];
214 for (int i = 0; i < n; i++) {
215 String s = dirs.nextToken();
216 // make shure the dir name ends with a directory separator
217 if (s.endsWith(File.separator)) {
218 pathArray[i] = s;
219 } else {
220 pathArray[i] = s + File.separator;
221 }
222 }
223 return pathArray;
224 }
225
226 /**
227 * Extract the base of a file name (sans extension).
228 *
229 * Returns the filename without the extension. The extension is the part
230 * behind the last dot in the filename. If the filename has no dot the full
231 * file name is returned.
232 *
233 * @param fn
234 * @return
235 */
236 public static String basename(String fn) {
237 if (fn == null) {
238 return null;
239 }
240 int i = fn.lastIndexOf('.');
241 if (i > 0) {
242 return fn.substring(0, i);
243 }
244 return fn;
245 }
246
247 /**
248 * Extract the base of a file name (sans extension).
249 *
250 * Returns the filename without the extension. The extension is the part
251 * behind the last dot in the filename. If the filename has no dot the full
252 * file name is returned.
253 *
254 * @param f
255 * @return
256 */
257 public static String basename(File f) {
258 if (f == null) {
259 return null;
260 }
261 return basename(f.getName());
262 }
263
264 /**
265 * Extract the extension of a file name.
266 *
267 * Returns the extension of a file name. The extension is the part behind
268 * the last dot in the filename. If the filename has no dot the empty string
269 * is returned.
270 *
271 * @param fn
272 * @return
273 */
274 public static String extname(String fn) {
275 if (fn == null) {
276 return null;
277 }
278 int i = fn.lastIndexOf('.');
279 if (i > 0) {
280 return fn.substring(i + 1);
281 }
282 return "";
283 }
284
285 /**
286 * Extract the parent directory of a (digilib) path name.
287 *
288 * Returns the parent directory of a path name. The parent is the part
289 * before the last slash in the path name. If the path name has no slash the
290 * empty string is returned.
291 *
292 * @param fn
293 * @return
294 */
295 public static String dlParent(String fn) {
296 if (fn == null) {
297 return null;
298 }
299 int i = fn.lastIndexOf('/');
300 if (i > 0) {
301 return fn.substring(0, i);
302 }
303 return "";
304 }
305
306 /**
307 * Extract the dir/file name of a (digilib) path name.
308 *
309 * The file/dir name is the part after the last slash in the path name. If
310 * the path name has no slash the same string is returned.
311 *
312 * @param path
313 * @return
314 */
315 public static String dlName(String path) {
316 if (path == null) {
317 return null;
318 }
319 int i = path.lastIndexOf('/');
320 if (i > 0) {
321 return path.substring(i+1);
322 }
323 return path;
324 }
325
326 /**
327 * Normalize a path name.
328 *
329 * Removes leading and trailing slashes. Returns null if there is other
330 * unwanted stuff in the path name.
331 *
332 * @param pathname
333 * @return
334 */
335 public static String normalName(String pathname) {
336 if (pathname == null) {
337 return null;
338 }
339 // upper-dir references are unwanted
340 if (pathname.indexOf("../") >= 0) {
341 return null;
342 }
343 int a = 0;
344 int e = pathname.length() - 1;
345 if (e < 0) {
346 return pathname;
347 }
348 // leading and trailing "/" are removed
349 while ((a <= e) && (pathname.charAt(a) == '/')) {
350 a++;
351 }
352 while ((a < e) && (pathname.charAt(e) == '/')) {
353 e--;
354 }
355 return pathname.substring(a, e + 1);
356 }
357
358 public static StringTokenizer dlPathIterator(String path) {
359 return new StringTokenizer(path, "/");
360 }
361
362
363 /**
364 * FileFilter for general files
365 */
366 static class ReadableFileFilter implements FileFilter {
367
368 public boolean accept(File f) {
369 return f.canRead();
370 }
371 }
372
373 /**
374 * FileFilter for image types (helper class for getFile)
375 */
376 static class ImageFileFilter implements FileFilter {
377
378 public boolean accept(File f) {
379 return (classForFilename(f.getName()) == CLASS_IMAGE);
380 }
381 }
382
383 /**
384 * FileFilter for text types (helper class for getFile)
385 */
386 static class TextFileFilter implements FileFilter {
387
388 public boolean accept(File f) {
389 return (classForFilename(f.getName()) == CLASS_TEXT);
390 }
391 }
392
393 /**
394 * FileFilter for svg types (helper class for getFile).
395 *
396 */
397 static class SVGFileFilter implements FileFilter {
398
399 public boolean accept(File f) {
400 return (classForFilename(f.getName()) == CLASS_SVG);
401 }
402 }
403
404 /**
405 * Factory for FileFilters (image or text).
406 *
407 * @param fileClass
408 * @return
409 */
410 public static FileFilter filterForClass(int fileClass) {
411 if (fileClass == CLASS_IMAGE) {
412 return new ImageFileFilter();
413 }
414 if (fileClass == CLASS_TEXT) {
415 return new TextFileFilter();
416 }
417 if (fileClass == CLASS_SVG) {
418 return new SVGFileFilter();
419 }
420 return null;
421 }
422
423 /**
424 * Factory for DocuDirents based on file class.
425 *
426 * Returns an ImageFileset, TextFile or SVGFile.
427 *
428 * @param fileClass
429 * @param file
430 * @param parent
431 * @param hints
432 * optional additional parameters
433 * @return
434 */
435 public static DigiDirent fileForClass(int fileClass, File file,
436 DigiDirectory parent, Map hints) {
437 // what class of file do we have?
438 if (fileClass == CLASS_IMAGE) {
439 // image file
440 return new ImageFileset(file, parent, hints);
441 } else if (fileClass == CLASS_TEXT) {
442 // text file
443 return new TextFile(file, parent);
444 } else if (fileClass == CLASS_SVG) {
445 // text file
446 return new SVGFile(file, parent);
447 }
448 // anything else is a generic dir or file
449 if (file.isDirectory()) {
450 return getCachedDirectory(file, null, parent);
451 }
452 return new DigiDirent(file.getName(), parent);
453 }
454
455 /**
456 * Filters a list of Files through a FileFilter.
457 *
458 * @param files
459 * @param filter
460 * @return
461 */
462 public static File[] listFiles(File[] files, FileFilter filter) {
463 if (files == null) {
464 return null;
465 }
466 File[] ff = new File[files.length];
467 int ffi = 0;
468 for (int i = 0; i < files.length; i++) {
469 if (filter.accept(files[i])) {
470 ff[ffi] = files[i];
471 ffi++;
472 }
473 }
474 File[] fff = new File[ffi];
475 System.arraycopy(ff, 0, fff, 0, ffi);
476 return fff;
477 }
478
479 /**
480 * Returns the closest matching file out of an array.
481 *
482 * Compares the files sans extensions if no direct match is found. Returns
483 * null if no match is found.
484 *
485 * @param fn
486 * @param files
487 * @return
488 */
489 public static File findFile(File fn, File[] files) {
490 // try the same filename as the original
491 int fileIdx = Arrays.binarySearch(files, fn);
492 if (fileIdx >= 0) {
493 return files[fileIdx];
494 } else {
495 // try closest matches without extension
496 String fb = FileOps.basename(fn);
497 fileIdx = -fileIdx - 1;
498 if ((fileIdx < files.length)
499 && (FileOps.basename(files[fileIdx]).equals(fb))) {
500 // idx ok
501 return files[fileIdx];
502 } else if ((fileIdx > 0)
503 && (FileOps.basename(files[fileIdx - 1]).equals(fb))) {
504 // idx-1 ok
505 return files[fileIdx - 1];
506 } else if ((fileIdx + 1 < files.length)
507 && (FileOps.basename(files[fileIdx + 1]).equals(fb))) {
508 // idx+1 ok
509 return files[fileIdx + 1];
510 }
511 }
512 // unknown
513 return null;
514 }
515
516 /**
517 * Returns the closest matching file out of an array.
518 *
519 * Compares the files sans extensions if no direct match is found. Returns
520 * null if no match is found.
521 *
522 * @param fn
523 * @param files
524 * @return
525 */
526 public static String findFilename(String fn, String[] files) {
527 // try the same filename as the original
528 int fileIdx = Arrays.binarySearch(files, fn);
529 if (fileIdx >= 0) {
530 return files[fileIdx];
531 } else {
532 // try closest matches without extension
533 String fb = FileOps.basename(fn);
534 fileIdx = -fileIdx - 1;
535 if ((fileIdx < files.length)
536 && (FileOps.basename(files[fileIdx]).equals(fb))) {
537 // idx ok
538 return files[fileIdx];
539 } else if ((fileIdx > 0)
540 && (FileOps.basename(files[fileIdx - 1]).equals(fb))) {
541 // idx-1 ok
542 return files[fileIdx - 1];
543 } else if ((fileIdx + 1 < files.length)
544 && (FileOps.basename(files[fileIdx + 1]).equals(fb))) {
545 // idx+1 ok
546 return files[fileIdx + 1];
547 }
548 }
549 // unknown
550 return null;
551 }
552 /**
553 * Returns a File for a base directory and a digilib-path.
554 *
555 * @param basedir
556 * @param dlpath
557 * @return
558 */
559 public static File getRealFile(File basedir, String dlpath) {
560 // does this work on all platforms??
561 return new File(basedir, dlpath);
562 }
563
564 /** Returns a File for a digilib-path.
565 *
566 * The file is assumed to be in the first base directory.
567 *
568 * @param dlpath
569 * @return
570 */
571 public static File getRealFile(String dlpath) {
572 // does this work on all platforms??
573 return new File(baseDirs[0], dlpath);
574 }
575
576 /**
577 * Creates a new empty hints Map.
578 *
579 * @return
580 */
581 public static Map newHints() {
582 Map m = new HashMap();
583 return m;
584 }
585
586 /**
587 * Creates a new hints Map with the given first element.
588 *
589 * @param type
590 * @param value
591 * @return
592 */
593 public static Map newHints(Integer type, Object value) {
594 Map m = new HashMap();
595 if (type != null) {
596 m.put(type, value);
597 }
598 return m;
599 }
600
601 /**
602 * @return Returns the baseDirs.
603 */
604 public static File[] getBaseDirs() {
605 return baseDirs;
606 }
607
608 /**
609 * @param baseDirs
610 * The baseDirs to set.
611 */
612 public static void setBaseDirs(File[] baseDirs) {
613 FileOps.baseDirs = baseDirs;
614 }
615
616
617 /**
618 * Returns a DigiDirectory instance that is guaranteed to be unique in the
619 * cache.
620 *
621 * @param dir
622 * @param parent
623 * @return
624 */
625 public static DigiDirectory getCachedDirectory(File dir, String dlpath, DigiDirectory parent) {
626 if (dir == null) {
627 dir = FileOps.getRealFile(dlpath);
628 }
629 DigiDirectory dd = null;
630 if (parent == null) {
631 // create a new parent by starting at the root
632 StringBuffer ps = new StringBuffer();
633 DigiDirectory p = cache.getRootDir();
634 // walk the path
635 for (StringTokenizer i = dlPathIterator(dlpath); i.hasMoreTokens();) {
636 p.check();
637 String dn = i.nextToken();
638 ps.append("/");
639 ps.append(dn);
640 DigiDirectory d = cache.get(dn);
641 if (d == null) {
642 dd = new DigiDirectory(FileOps.getRealFile(dn), ps.toString(), p);
643 }
644 if (d.getParent() != p) {
645 logger.warn("digidirectory "+d.getDLPath()+" has wrong parent: "+p.getDLPath());
646 }
647 p = d;
648 }
649 } else {
650 if (dlpath == null) {
651 dlpath = parent.getDLPath() + "/" + dir.getName();
652 }
653 dd = cache.get(dlpath);
654 if (dd == null) {
655 dd = new DigiDirectory(dir, dlpath, parent);
656 } else {
657 logger.debug("reusing directory:" + dlpath);
658 }
659 }
660 return dd;
661 }
662
663 /**
664 * @return Returns the cache.
665 */
666 public static DocuDirCache getCache() {
667 return cache;
668 }
669 /**
670 * @param cache The cache to set.
671 */
672 public static void setCache(DocuDirCache cache) {
673 FileOps.cache = cache;
674 }
188 } 675 }