1 /*******************************************************************************
2 
3         Copyright:
4             Copyright (c) 2007 Kris Bell.
5             Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
6             All rights reserved.
7 
8         License:
9             Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
10             See LICENSE_TANGO.txt for details.
11 
12         Version: Oct 2007: Initial version
13 
14         Authors: Kris
15 
16 *******************************************************************************/
17 
18 module ocean.io.vfs.LinkedFolder;
19 
20 import ocean.meta.types.Qualifiers;
21 
22 import ocean.io.vfs.model.Vfs;
23 
24 import ocean.core.ExceptionDefinitions;
25 
26 import ocean.io.vfs.VirtualFolder;
27 
28 /*******************************************************************************
29 
30         LinkedFolder is derived from VirtualFolder, and behaves exactly the
31         same in all but one aspect: it treats mounted folders as an ordered
32         list of alternatives to look for a file. This supports the notion of
33         file 'overrides', whereby "customized" files can be inserted into a
34         chain of alternatives.
35 
36         (Overridden folders are not currently supported.)
37 
38 *******************************************************************************/
39 
40 
41 class LinkedFolder : VirtualFolder
42 {
43         private Link* head;
44 
45         /***********************************************************************
46 
47                 Linked-list of folders.
48 
49         ***********************************************************************/
50 
51         private struct Link
52         {
53                 Link*     next;
54                 VfsFolder folder;
55 
56                 static Link* opCall(VfsFolder folder)
57                 {
58                         auto p = new Link;
59                         p.folder = folder;
60                         return p;
61                 }
62         }
63 
64         /***********************************************************************
65 
66                 All folder must have a name. No '.' or '/' chars are
67                 permitted.
68 
69         ***********************************************************************/
70 
71         this (istring name)
72         {
73                 super (name);
74         }
75 
76         /***********************************************************************
77 
78                 Add a child folder. The child cannot 'overlap' with others
79                 in the tree of the same type. Circular references across a
80                 tree of virtual folders are detected and trapped.
81 
82                 We add the new child at the end of an ordered list, which
83                 we subsequently traverse when looking up a file.
84 
85                 The second argument represents an optional name that the
86                 mount should be known as, instead of the name exposed by
87                 the provided folder (it is not an alias).
88 
89         ***********************************************************************/
90 
91         final override VfsHost mount (VfsFolder folder, istring name=null)
92         {
93                 // traverse to the end of the list
94                 auto link = &head;
95                 while (*link)
96                         link = &(*link).next;
97 
98                 // hook up the new folder
99                 *link = Link (folder);
100 
101                 // and let superclass deal with it
102                 return super.mount (folder, name);
103         }
104 
105         /***********************************************************************
106 
107                 TODO: Unhook a child folder.
108 
109         ***********************************************************************/
110 
111         final override VfsHost dismount (VfsFolder folder)
112         {
113                 assert (0, "LinkedFolder.dismount not implemented");
114         }
115 
116         /***********************************************************************
117 
118                 Return a file representation of the given path. If the
119                 path-head does not refer to an immediate child folder,
120                 and does not match a symbolic link, it is considered to
121                 be unknown.
122 
123                 We scan the set of mounted folders, in the order mounted,
124                 looking for a match. Where one is found, we test to see
125                 that it really exists before returning the reference.
126 
127         ***********************************************************************/
128 
129         final override VfsFile file (istring path)
130         {
131                 auto link = head;
132                 while (link)
133                       {
134                       //Stdout.formatln ("looking in {}", link.folder.toString);
135                       try {
136                           auto file = link.folder.file (path);
137                           if (file.exists)
138                               return file;
139                           } catch (VfsException x) {}
140                       link = link.next;
141                       }
142                 super.error ("file '"~path~"' not found");
143                 return null;
144         }
145 }
146 
147 
148 debug (LinkedFolder)
149 {
150 /*******************************************************************************
151 
152 *******************************************************************************/
153 
154 import ocean.io.Stdout;
155 import ocean.io.vfs.FileFolder;
156 
157 void main()
158 {
159         auto root = new LinkedFolder ("root");
160         auto sub  = new VirtualFolder ("sub");
161         sub.mount (new FileFolder (r"d:/d/import/temp"));
162         sub.map (sub.file(r"temp/subtree/test.txt"), "wumpus");
163 
164         root.mount (new FileFolder (r"d:/d/import/tango"))
165             .mount (new FileFolder (r"c:/"), "windows");
166         root.mount (sub);
167 
168         auto file = root.file (r"wumpus");
169         Stdout.formatln ("file = {}", file);
170 }
171 }