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.provider;
18  
19  import java.io.InputStream;
20  import java.io.OutputStream;
21  import java.security.cert.Certificate;
22  import java.util.HashSet;
23  import java.util.Map;
24  import java.util.Set;
25  
26  import org.apache.commons.vfs2.FileChangeEvent;
27  import org.apache.commons.vfs2.FileContentInfo;
28  import org.apache.commons.vfs2.FileListener;
29  import org.apache.commons.vfs2.FileName;
30  import org.apache.commons.vfs2.FileNotFolderException;
31  import org.apache.commons.vfs2.FileObject;
32  import org.apache.commons.vfs2.FileSystemException;
33  import org.apache.commons.vfs2.FileType;
34  import org.apache.commons.vfs2.RandomAccessContent;
35  import org.apache.commons.vfs2.util.RandomAccessMode;
36  import org.apache.commons.vfs2.util.WeakRefFileListener;
37  
38  /**
39   * A file backed by another file.
40   * <p>
41   * TODO - Extract subclass that overlays the children.
42   * </p>
43   *
44   * @param <AFS> A subclass of AbstractFileSystem.
45   */
46  public class DelegateFileObject<AFS extends AbstractFileSystem> extends AbstractFileObject<AFS>
47          implements FileListener {
48      private FileObject file;
49      private final Set<String> children = new HashSet<>();
50      private boolean ignoreEvent;
51  
52      public DelegateFileObject(final AbstractFileName name, final AFS fileSystem, final FileObject file)
53              throws FileSystemException {
54          super(name, fileSystem);
55          this.file = file;
56          if (file != null) {
57              WeakRefFileListener.installListener(file, this);
58          }
59      }
60  
61      /**
62       * Get access to the delegated file.
63       *
64       * @return The FileObject.
65       * @since 2.0
66       */
67      public FileObject getDelegateFile() {
68          return file;
69      }
70  
71      /**
72       * Adds a child to this file.
73       *
74       * @param baseName The base FileName.
75       * @param type The FileType.
76       * @throws Exception if an error occurs.
77       */
78      public void attachChild(final FileName baseName, final FileType type) throws Exception {
79          final FileType oldType = doGetType();
80          if (children.add(baseName.getBaseName())) {
81              childrenChanged(baseName, type);
82          }
83          maybeTypeChanged(oldType);
84      }
85  
86      /**
87       * Attaches or detaches the target file.
88       *
89       * @param file The FileObject.
90       * @throws Exception if an error occurs.
91       */
92      public void setFile(final FileObject file) throws Exception {
93          final FileType oldType = doGetType();
94  
95          if (file != null) {
96              WeakRefFileListener.installListener(file, this);
97          }
98          this.file = file;
99          maybeTypeChanged(oldType);
100     }
101 
102     /**
103      * Checks whether the file's type has changed, and fires the appropriate events.
104      *
105      * @param oldType The old FileType.
106      * @throws Exception if an error occurs.
107      */
108     private void maybeTypeChanged(final FileType oldType) throws Exception {
109         final FileType newType = doGetType();
110         if (oldType == FileType.IMAGINARY && newType != FileType.IMAGINARY) {
111             handleCreate(newType);
112         } else if (oldType != FileType.IMAGINARY && newType == FileType.IMAGINARY) {
113             handleDelete();
114         }
115     }
116 
117     /**
118      * Determines the type of the file, returns null if the file does not exist.
119      */
120     @Override
121     protected FileType doGetType() throws FileSystemException {
122         if (file != null) {
123             return file.getType();
124         } else if (children.size() > 0) {
125             return FileType.FOLDER;
126         } else {
127             return FileType.IMAGINARY;
128         }
129     }
130 
131     /**
132      * Determines if this file can be read.
133      */
134     @Override
135     protected boolean doIsReadable() throws FileSystemException {
136         if (file != null) {
137             return file.isReadable();
138         }
139         return true;
140     }
141 
142     /**
143      * Determines if this file can be written to.
144      */
145     @Override
146     protected boolean doIsWriteable() throws FileSystemException {
147         if (file != null) {
148             return file.isWriteable();
149         }
150         return false;
151     }
152 
153     /**
154      * Determines if this file is executable.
155      */
156     @Override
157     protected boolean doIsExecutable() throws FileSystemException {
158         if (file != null) {
159             return file.isExecutable();
160         }
161         return false;
162     }
163 
164     /**
165      * Determines if this file is hidden.
166      */
167     @Override
168     protected boolean doIsHidden() throws FileSystemException {
169         if (file != null) {
170             return file.isHidden();
171         }
172         return false;
173     }
174 
175     /**
176      * Lists the children of the file.
177      */
178     @Override
179     protected String[] doListChildren() throws Exception {
180         if (file != null) {
181             final FileObject[] children;
182 
183             try {
184                 children = file.getChildren();
185             }
186             // VFS-210
187             catch (final FileNotFolderException e) {
188                 throw new FileNotFolderException(getName(), e);
189             }
190 
191             final String[] childNames = new String[children.length];
192             for (int i = 0; i < children.length; i++) {
193                 childNames[i] = children[i].getName().getBaseName();
194             }
195             return childNames;
196         }
197         return children.toArray(new String[children.size()]);
198     }
199 
200     /**
201      * Creates this file as a folder.
202      */
203     @Override
204     protected void doCreateFolder() throws Exception {
205         ignoreEvent = true;
206         try {
207             file.createFolder();
208         } finally {
209             ignoreEvent = false;
210         }
211     }
212 
213     /**
214      * Deletes the file.
215      */
216     @Override
217     protected void doDelete() throws Exception {
218         ignoreEvent = true;
219         try {
220             file.delete();
221         } finally {
222             ignoreEvent = false;
223         }
224     }
225 
226     /**
227      * Returns the size of the file content (in bytes). Is only called if {@link #doGetType} returns
228      * {@link FileType#FILE}.
229      */
230     @Override
231     protected long doGetContentSize() throws Exception {
232         return file.getContent().getSize();
233     }
234 
235     /**
236      * Returns the attributes of this file.
237      */
238     @Override
239     protected Map<String, Object> doGetAttributes() throws Exception {
240         return file.getContent().getAttributes();
241     }
242 
243     /**
244      * Sets an attribute of this file.
245      */
246     @Override
247     protected void doSetAttribute(final String atttrName, final Object value) throws Exception {
248         file.getContent().setAttribute(atttrName, value);
249     }
250 
251     /**
252      * Returns the certificates of this file.
253      */
254     @Override
255     protected Certificate[] doGetCertificates() throws Exception {
256         return file.getContent().getCertificates();
257     }
258 
259     /**
260      * Returns the last-modified time of this file.
261      */
262     @Override
263     protected long doGetLastModifiedTime() throws Exception {
264         return file.getContent().getLastModifiedTime();
265     }
266 
267     /**
268      * Sets the last-modified time of this file.
269      *
270      * @since 2.0
271      */
272     @Override
273     protected boolean doSetLastModifiedTime(final long modtime) throws Exception {
274         file.getContent().setLastModifiedTime(modtime);
275         return true;
276     }
277 
278     /**
279      * Creates an input stream to read the file content from.
280      */
281     @Override
282     protected InputStream doGetInputStream() throws Exception {
283         return file.getContent().getInputStream();
284     }
285 
286     /**
287      * Creates an output stream to write the file content to.
288      */
289     @Override
290     protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception {
291         return file.getContent().getOutputStream(bAppend);
292     }
293 
294     /**
295      * Called when a file is created.
296      *
297      * @param event The FileChangeEvent.
298      * @throws Exception if an error occurs.
299      */
300     @Override
301     public void fileCreated(final FileChangeEvent event) throws Exception {
302         if (event.getFile() != file) {
303             return;
304         }
305         if (!ignoreEvent) {
306             handleCreate(file.getType());
307         }
308     }
309 
310     /**
311      * Called when a file is deleted.
312      *
313      * @param event The FileChangeEvent.
314      * @throws Exception if an error occurs.
315      */
316     @Override
317     public void fileDeleted(final FileChangeEvent event) throws Exception {
318         if (event.getFile() != file) {
319             return;
320         }
321         if (!ignoreEvent) {
322             handleDelete();
323         }
324     }
325 
326     /**
327      * Called when a file is changed.
328      * <p>
329      * This will only happen if you monitor the file using {@link org.apache.commons.vfs2.FileMonitor}.
330      * </p>
331      *
332      * @param event The FileChangeEvent.
333      * @throws Exception if an error occurs.
334      */
335     @Override
336     public void fileChanged(final FileChangeEvent event) throws Exception {
337         if (event.getFile() != file) {
338             return;
339         }
340         if (!ignoreEvent) {
341             handleChanged();
342         }
343     }
344 
345     /**
346      * Close the delegated file.
347      *
348      * @throws FileSystemException if an error occurs.
349      */
350     @Override
351     public void close() throws FileSystemException {
352         super.close();
353 
354         if (file != null) {
355             file.close();
356         }
357     }
358 
359     /**
360      * Refresh file information.
361      *
362      * @throws FileSystemException if an error occurs.
363      * @since 2.0
364      */
365     @Override
366     public void refresh() throws FileSystemException {
367         super.refresh();
368         if (file != null) {
369             file.refresh();
370         }
371     }
372 
373     /**
374      * Return file content info.
375      *
376      * @return the file content info of the delegee.
377      * @throws Exception Any thrown Exception is wrapped in FileSystemException.
378      * @since 2.0
379      */
380     protected FileContentInfo doGetContentInfo() throws Exception {
381         return file.getContent().getContentInfo();
382     }
383 
384     /**
385      * Renames the file.
386      *
387      * @param newFile the new location/name.
388      * @throws Exception Any thrown Exception is wrapped in FileSystemException.
389      * @since 2.0
390      */
391     @Override
392     protected void doRename(final FileObject newFile) throws Exception {
393         file.moveTo(((DelegateFileObject) newFile).file);
394     }
395 
396     /**
397      * Removes an attribute of this file.
398      *
399      * @since 2.0
400      */
401     @Override
402     protected void doRemoveAttribute(final String atttrName) throws Exception {
403         file.getContent().removeAttribute(atttrName);
404     }
405 
406     /**
407      * Creates access to the file for random i/o.
408      *
409      * @since 2.0
410      */
411     @Override
412     protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception {
413         return file.getContent().getRandomAccessContent(mode);
414     }
415 }