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