KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > vfs > MergePath


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.vfs;
31
32 import com.caucho.loader.DynamicClassLoader;
33 import com.caucho.server.util.CauchoSystem;
34
35 import java.io.IOException JavaDoc;
36 import java.util.ArrayList JavaDoc;
37 import java.util.Map JavaDoc;
38
39 /**
40  * A merging of several Paths used like a CLASSPATH. When the MergePath
41  * is opened for read, the first path in the list which contains the file will
42  * be the opened file. When the MergePath is opened for write, the first path
43  * in the list is used for the write.
44  *
45  * <p>In the following example, "first" has priority over "second".
46  * If test.xml exists in both "first" and "second", the open will
47  * return "first/test.xml".
48  *
49  * <code><pre>
50  * MergePage merge = new MergePath();
51  * merge.addMergePath(Vfs.lookup("first");
52  * merge.addMergePath(Vfs.lookup("second");
53  *
54  * Path path = merge.lookup("test.xml");
55  * ReadStream is = path.openRead();
56  * </pre></code>
57  *
58  * <p>MergePath corresponds to the "merge:" Vfs schema
59  * <code><pre>
60    Path path = Vfs.lookup("merge:(../custom-foo;foo)");
61  * </pre></code>
62  *
63  * @since Resin 1.2
64  * @since Resin 3.0.10 merge: schema
65  */

66 public class MergePath extends FilesystemPath {
67   private ArrayList JavaDoc<Path> _pathList;
68
69   private Path _bestPath;
70
71   /**
72    * Creates a new merge path.
73    */

74   public MergePath()
75   {
76     super(null, "/", "/");
77
78     _root = this;
79     _pathList = new ArrayList JavaDoc<Path>();
80   }
81
82   /**
83    * @param path canonical path
84    */

85   private MergePath(MergePath root,
86                     String JavaDoc userPath, Map JavaDoc<String JavaDoc,Object JavaDoc> attributes,
87                     String JavaDoc path)
88   {
89     super(root, userPath, path);
90   }
91
92   /**
93    * schemeWalk is called by Path for a scheme lookup like file:/tmp/foo
94    *
95    * @param userPath the user's lookup() path
96    * @param attributes the user's attributes
97    * @param filePath the actual lookup() path
98    * @param offset offset into filePath
99    */

100   protected Path schemeWalk(String JavaDoc userPath,
101                             Map JavaDoc<String JavaDoc,Object JavaDoc> attributes,
102                 String JavaDoc filePath,
103                             int offset)
104   {
105     int length = filePath.length();
106     
107     if (length <= offset || filePath.charAt(offset) != '(')
108       return super.schemeWalk(userPath, attributes, filePath, offset);
109
110     MergePath mergePath = new MergePath();
111     mergePath.setUserPath(userPath);
112
113     int head = ++offset;
114     int tail = head;
115     while (tail < length) {
116       int ch = filePath.charAt(tail);
117       
118       if (ch == ')') {
119     if (head + 1 != tail) {
120       String JavaDoc subPath = filePath.substring(head, tail);
121
122       if (subPath.startsWith("(") && subPath.endsWith(")"))
123         subPath = subPath.substring(1, subPath.length() - 1);
124
125       mergePath.addMergePath(Vfs.lookup(subPath));
126     }
127
128     if (tail + 1 == length)
129       return mergePath;
130     else
131       return mergePath.fsWalk(userPath, attributes, filePath.substring(tail + 1));
132       }
133       else if (ch == ';') {
134     String JavaDoc subPath = filePath.substring(head, tail);
135
136     if (subPath.startsWith("(") && subPath.endsWith(")"))
137       subPath = subPath.substring(1, subPath.length() - 1);
138
139     mergePath.addMergePath(Vfs.lookup(subPath));
140
141     head = ++tail;
142       }
143       else if (ch == '(') {
144     int depth = 1;
145
146     for (tail++; tail < length; tail++) {
147       if (filePath.charAt(tail) == '(')
148         depth++;
149       else if (filePath.charAt(tail) == ')') {
150         tail++;
151         depth--;
152         if (depth == 0)
153           break;
154       }
155     }
156
157     if (depth != 0)
158       return new NotFoundPath(filePath);
159       }
160       else
161     tail++;
162     }
163
164     return new NotFoundPath(filePath);
165   }
166
167   /**
168    * Adds a new path to the end of the merge path.
169    *
170    * @param path the new path to search
171    */

172   public void addMergePath(Path path)
173   {
174     if (! (path instanceof MergePath)) {
175       // Need to normalize so directory paths ends with a "./"
176
if (path.isDirectory())
177     path = path.lookup("./");
178
179       ArrayList JavaDoc<Path> pathList = ((MergePath) _root)._pathList;
180
181       /*
182       if (! pathList.contains(path))
183     pathList.add(path);
184       */

185       pathList.add(path);
186     }
187     else if (((MergePath) path)._root == _root)
188       return;
189     else {
190       MergePath mergePath = (MergePath) path;
191       ArrayList JavaDoc<Path> subPaths = mergePath.getMergePaths();
192       String JavaDoc pathName = "./" + mergePath._pathname + "/";
193
194       for (int i = 0; i < subPaths.size(); i++) {
195         Path subPath = subPaths.get(i);
196         
197         addMergePath(subPath.lookup(pathName));
198       }
199     }
200   }
201
202   /**
203    * Adds the classpath as paths in the MergePath.
204    */

205   public void addClassPath()
206   {
207     addClassPath(Thread.currentThread().getContextClassLoader());
208   }
209   
210   /**
211    * Adds the classpath for the loader as paths in the MergePath.
212    *
213    * @param loader class loader whose classpath should be used to search.
214    */

215   public void addClassPath(ClassLoader JavaDoc loader)
216   {
217     String JavaDoc classpath = null;
218     
219     if (loader instanceof DynamicClassLoader)
220       classpath = ((DynamicClassLoader) loader).getClassPath();
221     else
222       classpath = CauchoSystem.getClassPath();
223
224     addClassPath(classpath);
225   }
226
227   /**
228    * Adds the classpath as paths in the MergePath.
229    */

230   public void addLocalClassPath()
231   {
232     addLocalClassPath(Thread.currentThread().getContextClassLoader());
233   }
234   
235   /**
236    * Adds the classpath for the loader as paths in the MergePath.
237    *
238    * @param loader class loader whose classpath should be used to search.
239    */

240   public void addLocalClassPath(ClassLoader JavaDoc loader)
241   {
242     String JavaDoc classpath = null;
243     
244     if (loader instanceof DynamicClassLoader)
245       classpath = ((DynamicClassLoader) loader).getLocalClassPath();
246     else
247       classpath = System.getProperty("java.class.path");
248
249     addClassPath(classpath);
250   }
251   
252   /**
253    * Adds the classpath for the loader as paths in the MergePath.
254    *
255    * @param classpath class loader whose classpath should be used to search.
256    */

257   public void addClassPath(String JavaDoc classpath)
258   {
259     char sep = CauchoSystem.getPathSeparatorChar();
260     int head = 0;
261     int tail = 0;
262     while (head < classpath.length()) {
263       tail = classpath.indexOf(sep, head);
264
265       String JavaDoc segment = null;
266       if (tail < 0) {
267         segment = classpath.substring(head);
268         head = classpath.length();
269       }
270       else {
271         segment = classpath.substring(head, tail);
272         head = tail + 1;
273       }
274
275       if (segment.equals(""))
276         continue;
277       else if (segment.endsWith(".jar") || segment.endsWith(".zip"))
278         addMergePath(JarPath.create(Vfs.lookup(segment)));
279       else
280         addMergePath(Vfs.lookup(segment));
281     }
282   }
283
284   /**
285    * Return the list of paths searched in the merge path.
286    */

287   public ArrayList JavaDoc<Path> getMergePaths()
288   {
289     return ((MergePath) _root)._pathList;
290   }
291
292   /**
293    * Walking down the path just extends the path. It won't be evaluated
294    * until opening.
295    */

296   public Path fsWalk(String JavaDoc userPath,
297             Map JavaDoc<String JavaDoc,Object JavaDoc> attributes,
298             String JavaDoc path)
299   {
300     ArrayList JavaDoc<Path> pathList = getMergePaths();
301     
302     if (! userPath.startsWith("/") || pathList.size() == 0)
303       return new MergePath((MergePath) _root, userPath, attributes, path);
304
305     String JavaDoc bestPrefix = null;
306     for (int i = 0; i < pathList.size(); i++) {
307       Path subPath = pathList.get(i);
308       String JavaDoc prefix = subPath.getPath();
309
310       if (path.startsWith(prefix) &&
311           (bestPrefix == null || bestPrefix.length() < prefix.length())) {
312         bestPrefix = prefix;
313       }
314     }
315
316     if (bestPrefix != null) {
317       path = path.substring(bestPrefix.length());
318       if (! path.startsWith("/"))
319         path = "/" + path;
320
321       return new MergePath((MergePath) _root, userPath, attributes, path);
322     }
323     
324     return pathList.get(0).lookup(userPath, attributes);
325   }
326
327   /**
328    * Returns the scheme of the best path.
329    */

330   public String JavaDoc getScheme()
331   {
332     return getBestPath().getScheme();
333   }
334
335   /**
336    * Returns the full path name of the best path.
337    */

338   public String JavaDoc getFullPath()
339   {
340     Path path = getBestPath();
341
342     return path.getFullPath();
343   }
344
345   /**
346    * Returns the full native path name of the best path.
347    */

348   public String JavaDoc getNativePath()
349   {
350     Path path = getBestPath();
351
352     return path.getNativePath();
353   }
354
355   /**
356    * Returns the URL of the best path.
357    */

358   public String JavaDoc getURL()
359   {
360     Path path = getBestPath();
361
362     if (! path.exists())
363       path = getWritePath();
364
365     return path.getURL();
366   }
367
368   /**
369    * Returns the relative path into the merge path.
370    */

371   public String JavaDoc getRelativePath()
372   {
373     if (_pathname.startsWith("/"))
374       return "." + _pathname;
375     else
376       return _pathname;
377   }
378
379   /**
380    * True if any file matching this path exists.
381    */

382   public boolean exists()
383   {
384     return getBestPath().exists();
385   }
386
387   /**
388    * True if the best path is a directory.
389    */

390   public boolean isDirectory()
391   {
392     return getBestPath().isDirectory();
393   }
394
395   /**
396    * True if the best path is a file.
397    */

398   public boolean isFile()
399   {
400     return getBestPath().isFile();
401   }
402
403   /**
404    * Returns the length of the best path.
405    */

406   public long getLength()
407   {
408     return getBestPath().getLength();
409   }
410
411   /**
412    * Returns the last modified time of the best path.
413    */

414   public long getLastModified()
415   {
416     return getBestPath().getLastModified();
417   }
418
419   /**
420    * Returns true if the best path can be read.
421    */

422   public boolean canRead()
423   {
424     return getBestPath().canRead();
425   }
426
427   /**
428    * Returns true if the best path can be written to.
429    */

430   public boolean canWrite()
431   {
432     return getBestPath().canWrite();
433   }
434
435   /**
436    * Returns all the resources matching the path.
437    */

438   public ArrayList JavaDoc<Path> getResources(String JavaDoc pathName)
439   {
440     ArrayList JavaDoc<Path> list = new ArrayList JavaDoc<Path>();
441
442     String JavaDoc pathname = _pathname;
443     // XXX: why was this here?
444
if (pathname.startsWith("/"))
445       pathname = "." + pathname;
446
447     ArrayList JavaDoc<Path> pathList = ((MergePath) _root)._pathList;
448     for (int i = 0; i < pathList.size(); i++) {
449       Path path = pathList.get(i);
450
451       path = path.lookup(pathname);
452
453       ArrayList JavaDoc<Path> subResources = path.getResources(pathName);
454       for (int j = 0; j < subResources.size(); j++) {
455         Path newPath = subResources.get(j);
456         
457         if (! list.contains(newPath))
458           list.add(newPath);
459       }
460     }
461
462     return list;
463   }
464
465   /**
466    * Returns all the resources matching the path.
467    */

468   public ArrayList JavaDoc<Path> getResources()
469   {
470     ArrayList JavaDoc<Path> list = new ArrayList JavaDoc<Path>();
471
472     String JavaDoc pathname = _pathname;
473     // XXX: why?
474
if (pathname.startsWith("/"))
475       pathname = "." + pathname;
476
477     ArrayList JavaDoc<Path> pathList = ((MergePath) _root)._pathList;
478     for (int i = 0; i < pathList.size(); i++) {
479       Path path = pathList.get(i);
480
481       path = path.lookup(pathname);
482
483       ArrayList JavaDoc<Path> subResources = path.getResources();
484       for (int j = 0; j < subResources.size(); j++) {
485         Path newPath = subResources.get(j);
486         
487         if (! list.contains(newPath))
488           list.add(newPath);
489       }
490     }
491
492     return list;
493   }
494
495   /**
496    * List the merged directories.
497    */

498   public String JavaDoc []list() throws IOException JavaDoc
499   {
500     ArrayList JavaDoc<String JavaDoc> list = new ArrayList JavaDoc<String JavaDoc>();
501
502     String JavaDoc pathname = _pathname;
503     // XXX:??
504
if (pathname.startsWith("/"))
505       pathname = "." + pathname;
506
507     ArrayList JavaDoc<Path> pathList = ((MergePath) _root)._pathList;
508     for (int i = 0; i < pathList.size(); i++) {
509       Path path = pathList.get(i);
510
511       path = path.lookup(pathname);
512
513       if (path.isDirectory()) {
514         String JavaDoc[]subList = path.list();
515         for (int j = 0; j < subList.length; j++) {
516           if (! list.contains(subList[j]))
517             list.add(subList[j]);
518         }
519       }
520     }
521
522     return (String JavaDoc []) list.toArray(new String JavaDoc[list.size()]);
523   }
524
525   /**
526    * XXX: Probably should mkdir in the first path
527    */

528   public boolean mkdir()
529     throws IOException JavaDoc
530   {
531     return getWritePath().mkdir();
532   }
533   
534   /**
535    * XXX: Probably should mkdir in the first path
536    */

537   public boolean mkdirs()
538     throws IOException JavaDoc
539   {
540     return getWritePath().mkdirs();
541   }
542   
543   /**
544    * Remove the matching path.
545    */

546   public boolean remove()
547     throws IOException JavaDoc
548   {
549     return getBestPath().remove();
550   }
551
552   /**
553    * Renames the path.
554    */

555   public boolean renameTo(Path path)
556     throws IOException JavaDoc
557   {
558     return getBestPath().renameTo(path);
559   }
560
561   /**
562    * Opens the best path for reading.
563    */

564   public StreamImpl openReadImpl() throws IOException JavaDoc
565   {
566     StreamImpl stream = getBestPath().openReadImpl();
567     stream.setPath(this);
568     return stream;
569   }
570
571   /**
572    * Opens the best path for writing. XXX: If the best path doesn't
573    * exist, this should probably create the file in the first path.
574    */

575   public StreamImpl openWriteImpl() throws IOException JavaDoc
576   {
577     StreamImpl stream = getWritePath().openWriteImpl();
578     stream.setPath(this);
579     return stream;
580   }
581
582   /**
583    * Opens the best path for reading and writing. XXX: If the best path
584    * doesn't exist, this should probably create the file in the first path.
585    */

586   public StreamImpl openReadWriteImpl() throws IOException JavaDoc
587   {
588     StreamImpl stream = getWritePath().openReadWriteImpl();
589     stream.setPath(this);
590     return stream;
591   }
592
593   /**
594    * Opens the best path for appending. XXX: If the best path
595    * doesn't exist, this should probably create the file in the first path.
596    */

597   public StreamImpl openAppendImpl() throws IOException JavaDoc
598   {
599     StreamImpl stream = getWritePath().openAppendImpl();
600     stream.setPath(this);
601     return stream;
602   }
603
604   /**
605    * Returns the first matching path.
606    */

607   public Path getWritePath()
608   {
609     String JavaDoc pathname = _pathname;
610     // XXX:??
611
if (pathname.startsWith("/"))
612       pathname = "." + pathname;
613
614     ArrayList JavaDoc<Path> pathList = ((MergePath) _root)._pathList;
615
616     if (pathList.size() == 0)
617       return new NotFoundPath(pathname);
618     else {
619       return pathList.get(0).lookup(pathname);
620     }
621   }
622
623   /**
624    * Returns the first matching path.
625    */

626   public Path getBestPath()
627   {
628     if (_bestPath != null)
629       return _bestPath;
630
631     String JavaDoc pathname = _pathname;
632     // XXX:??
633
if (pathname.startsWith("/"))
634       pathname = "." + pathname;
635
636     ArrayList JavaDoc<Path> pathList = ((MergePath) _root)._pathList;
637     for (int i = 0; i < pathList.size(); i++) {
638       Path path = pathList.get(i);
639
640       Path realPath = path.lookup(pathname);
641
642       realPath.setUserPath(_userPath);
643
644       if (realPath.exists()) {
645         _bestPath = realPath;
646         return realPath;
647       }
648     }
649
650     pathname = _pathname;
651     for (int i = 0; i < pathList.size(); i++) {
652       Path path = pathList.get(i);
653
654       Path realPath = path.lookup(pathname);
655
656       realPath.setUserPath(_userPath);
657
658       if (realPath.exists()) {
659         _bestPath = realPath;
660         return realPath;
661       }
662     }
663
664     if (pathList.size() > 0) {
665       Path path = pathList.get(0);
666
667       if (pathname.startsWith("/"))
668     pathname = "." + pathname;
669
670       Path realPath = path.lookup(pathname);
671
672       realPath.setUserPath(_userPath);
673       
674       return realPath;
675     }
676
677     return new NotFoundPath(_userPath);
678   }
679   
680   /**
681    * Returns a name for the path
682    */

683   public String JavaDoc toString()
684   {
685     return "MergePath[" + _pathname + "]";
686   }
687 }
688
Popular Tags