View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.vfs2.impl;
18  
19  import java.util.Collection;
20  import java.util.HashMap;
21  import java.util.Map;
22  
23  import org.apache.commons.vfs2.Capability;
24  import org.apache.commons.vfs2.FileName;
25  import org.apache.commons.vfs2.FileObject;
26  import org.apache.commons.vfs2.FileSystemException;
27  import org.apache.commons.vfs2.FileSystemOptions;
28  import org.apache.commons.vfs2.FileType;
29  import org.apache.commons.vfs2.NameScope;
30  import org.apache.commons.vfs2.provider.AbstractFileName;
31  import org.apache.commons.vfs2.provider.AbstractFileSystem;
32  import org.apache.commons.vfs2.provider.DelegateFileObject;
33  
34  /**
35   * A logical file system, made up of set of junctions, or links, to files from
36   * other file systems.
37   * <p>
38   * TODO - Handle nested junctions.
39   */
40  public class VirtualFileSystem extends AbstractFileSystem
41  {
42      private final Map<FileName, FileObject> junctions = new HashMap<FileName, FileObject>();
43  
44      public VirtualFileSystem(final AbstractFileName rootName, final FileSystemOptions fileSystemOptions)
45      {
46          super(rootName, null, fileSystemOptions);
47      }
48  
49      /**
50       * Adds the capabilities of this file system.
51       */
52      @Override
53      protected void addCapabilities(final Collection<Capability> caps)
54      {
55          // TODO - this isn't really true
56          caps.add(Capability.ATTRIBUTES);
57          caps.add(Capability.CREATE);
58          caps.add(Capability.DELETE);
59          caps.add(Capability.GET_TYPE);
60          caps.add(Capability.JUNCTIONS);
61          caps.add(Capability.GET_LAST_MODIFIED);
62          caps.add(Capability.SET_LAST_MODIFIED_FILE);
63          caps.add(Capability.SET_LAST_MODIFIED_FOLDER);
64          caps.add(Capability.LIST_CHILDREN);
65          caps.add(Capability.READ_CONTENT);
66          caps.add(Capability.SIGNING);
67          caps.add(Capability.WRITE_CONTENT);
68          caps.add(Capability.APPEND_CONTENT);
69      }
70  
71      /**
72       * Creates a file object.  This method is called only if the requested
73       * file is not cached.
74       */
75      @Override
76      protected FileObject createFile(final AbstractFileName name) throws Exception
77      {
78          // Find the file that the name points to
79          final FileName junctionPoint = getJunctionForFile(name);
80          final FileObject file;
81          if (junctionPoint != null)
82          {
83              // Resolve the real file
84              final FileObject junctionFile = junctions.get(junctionPoint);
85              final String relName = junctionPoint.getRelativeName(name);
86              file = junctionFile.resolveFile(relName, NameScope.DESCENDENT_OR_SELF);
87          }
88          else
89          {
90              file = null;
91          }
92  
93          // Return a wrapper around the file
94          return new DelegateFileObject(name, this, file);
95      }
96  
97      /**
98       * Adds a junction to this file system.
99       * @param junctionPoint The location of the junction.
100      * @param targetFile The target file to base the junction on.
101      * @throws FileSystemException if an error occurs.
102      */
103     @Override
104     public void addJunction(final String junctionPoint,
105                             final FileObject targetFile)
106         throws FileSystemException
107     {
108         final FileName junctionName = getFileSystemManager().resolveName(getRootName(), junctionPoint);
109 
110         // Check for nested junction - these are not supported yet
111         if (getJunctionForFile(junctionName) != null)
112         {
113             throw new FileSystemException("vfs.impl/nested-junction.error", junctionName);
114         }
115 
116         try
117         {
118             // Add to junction table
119             junctions.put(junctionName, targetFile);
120 
121             // Attach to file
122             final DelegateFileObject junctionFile = (DelegateFileObject) getFileFromCache(junctionName);
123             if (junctionFile != null)
124             {
125                 junctionFile.setFile(targetFile);
126             }
127 
128             // Create ancestors of junction point
129             FileName childName = junctionName;
130             boolean done = false;
131             for (AbstractFileName parentName = (AbstractFileName) childName.getParent();
132                  !done && parentName != null;
133                  childName = parentName, parentName = (AbstractFileName) parentName.getParent())
134             {
135                 DelegateFileObject file = (DelegateFileObject) getFileFromCache(parentName);
136                 if (file == null)
137                 {
138                     file = new DelegateFileObject(parentName, this, null);
139                     putFileToCache(file);
140                 }
141                 else
142                 {
143                     done = file.exists();
144                 }
145 
146                 // As this is the parent of our junction it has to be a folder
147                 file.attachChild(childName, FileType.FOLDER);
148             }
149 
150             // TODO - attach all cached children of the junction point to their real file
151         }
152         catch (final Exception e)
153         {
154             throw new FileSystemException("vfs.impl/create-junction.error", junctionName, e);
155         }
156     }
157 
158     /**
159      * Removes a junction from this file system.
160      * @param junctionPoint The junction to remove.
161      * @throws FileSystemException if an error occurs.
162      */
163     @Override
164     public void removeJunction(final String junctionPoint)
165         throws FileSystemException
166     {
167         final FileName junctionName = getFileSystemManager().resolveName(getRootName(), junctionPoint);
168         junctions.remove(junctionName);
169 
170         // TODO - remove from parents of junction point
171         // TODO - detach all cached children of the junction point from their real file
172     }
173 
174     /**
175      * Locates the junction point for the junction containing the given file.
176      * @param name The FileName.
177      * @return the FileName where the junction occurs.
178      */
179     private FileName getJunctionForFile(final FileName name)
180     {
181         if (junctions.containsKey(name))
182         {
183             // The name points to the junction point directly
184             return name;
185         }
186 
187         // Find matching junction
188         for (final FileName junctionPoint : junctions.keySet())
189         {
190             if (junctionPoint.isDescendent(name))
191             {
192                 return junctionPoint;
193             }
194         }
195 
196         // None
197         return null;
198     }
199 
200     @Override
201     public void close()
202     {
203         super.close();
204         junctions.clear();
205     }
206 }