KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > schlichtherle > io > File


1 /*
2  * File.java
3  *
4  * Created on 23. Oktober 2004, 00:31
5  */

6 /*
7  * Copyright 2004-2006 Schlichtherle IT Services
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */

21
22 package de.schlichtherle.io;
23
24 import de.schlichtherle.io.ArchiveController.ArchiveNotFoundException;
25 import de.schlichtherle.io.util.*;
26
27 import java.io.*;
28 import java.lang.ref.*;
29 import java.lang.reflect.*;
30 import java.lang.reflect.Proxy JavaDoc;
31 import java.net.*;
32 import java.util.*;
33
34 import javax.swing.Icon JavaDoc;
35
36 /**
37  * A <em>drop-in-replacement</em> for <code>java.io.File</code> which
38  * provides transparent access to archive files as if they were directories
39  * in a path.
40  * <p>
41  * <b>Warning:</b>The classes in this package access and manipulate archive
42  * files as external resources and may cache some of their state in memory
43  * and temporary files. Third parties must not concurrently access these
44  * archive files unless some precautions have been taken!
45  * For more information please refer to the
46  * <a HREF="package-summary.html#third_party_access">Third Party Access</a>
47  * section in the package description.
48  *
49  * <h3><a name="copy_methods">Copy methods</a></h3>
50  * This class provides a bunch of convenient copy methods which work much
51  * faster and more reliable than the usual read-write-in-a-loop approach for
52  * individual files and its recursive companion for directory trees.
53  * These copy methods fall into the following categories:
54  * <ol>
55  * <li>The (archiveC|c)opy(All)?(To|From) methods (attention: regular
56  * expression crossing) simply return a boolean value indicating success
57  * or failure.
58  * Though this is suboptimal, this is consistent with most methods in
59  * the super class.
60  * <li>The cp(_p)? methods return void and throw an <code>IOException</code>
61  * on failure.
62  * The exception hierarchy is fine grained enough to let you differentiate
63  * between access restrictions, input exceptions and output exceptions.
64  * Their names have been modelled after the Unix "cp" utility with the
65  * optional "-p" switch.
66  * However, none of these methods does recursive copying.
67  * Their name is modelled after the Unix command line utility "cp".
68  * <li>The cat(To|From) methods return a boolean value. In contrast to the
69  * previous methods, they never close their argument streams, so you
70  * can call them multiple times on the same streams to concatenate data.
71  * Their name is modelled after the Unix command line utility "cat".
72  * <li>Finally, the cat method is the core engine for all these methods.
73  * It performs the asynchronous data transfer from an input stream to an
74  * output stream. When used with properly crafted input and output stream
75  * implementations, it delivers the same performance as the transfer
76  * method in the package <code>java.nio</code>.
77  * </ol>
78  * All copy methods use asynchronous I/O, pooled large buffers and pooled
79  * threads (if run on JSE 1.5) to achieve best performance.
80  * <p>
81  * <h4><a name="DDC">Direct Data Copying (DDC)</a></h4>
82  * If data is copied from an archive file to another archive file of the
83  * same type, some of the copy methods use a feature called <i>Direct Data
84  * Copying</i> (DDC) to achieve even better performance:</a>
85  * DDC copies the raw data from the source archive entry to the destination
86  * archive entry without the need to temporarily reproduce, copy and process
87  * the original data again.
88  * <p>
89  * The benefits of this feature are archive driver specific:
90  * In case of ZIP compatible files with compressed entries, it avoids the
91  * need to decompress the data from the source entry just to compress it
92  * again for the destination entry.
93  * In case of TAR compatible files, it avoids the need to create an
94  * additional temporary file, but shows no impact otherwise - TAR doesn't
95  * support compression.
96  *
97  * <h3><a name="false_positives">Identifying Archive Files and False Positives</a></h3>
98  * <p>
99  * Whenever a configured archive file suffix is recognized in a path, TrueZIP
100  * treats the corresponding file or directory as a <i>prospective archive
101  * file</i>.
102  * The word &quot;prospective&quot; suggests that just because a file is named
103  * <i>archive.zip</i> it isn't necessarily a valid ZIP file.
104  * In fact, it could be anything, even a regular directory!
105  * <p>
106  * Such an invalid archive file is called a <i>false positive</i>, which
107  * means a file, special file (a Unix concept) or directory which's path has
108  * a configured archive file suffix, but is actually something else.
109  * TrueZIP correctly identifies all kinds of false positives and treats them
110  * according to what they really are: Regular files, special files or
111  * directories.
112  * <p>
113  * The following table shows how certain methods in this class behave,
114  * depending upon a file's path and its true state in the file system:
115  * <p>
116  * <table border="2" cellpadding="4">
117  * <tr>
118  * <th>Path</th>
119  * <th>True State</th>
120  * <th><code>isArchive()</code><sup>1</sup></th>
121  * <th><code>isDirectory()</code></th>
122  * <th><code>isFile()</code></th>
123  * <th><code>exists()</code></th>
124  * <th><code>length()</code><sup>2</sup></th>
125  * </tr>
126  * <tr>
127  * <td><i>archive.zip</i><sup>3</sup></td>
128  * <td>Valid ZIP file</td>
129  * <td><code>true</code></td>
130  * <td><code>true</code></td>
131  * <td><code>false</code></td>
132  * <td><code>true</code></td>
133  * <td><code>0</code></td>
134  * </tr>
135  * <tr>
136  * <td><i>archive.zip</i></td>
137  * <td>False positive: Regular directory</td>
138  * <td><code>true</code></td>
139  * <td><code>true</code></td>
140  * <td><code>false</code></td>
141  * <td><code>true</code></td>
142  * <td><code><i>?</i></code></td>
143  * </tr>
144  * <tr>
145  * <td><i>archive.zip</i></td>
146  * <td>False positive: Regular file</td>
147  * <td><code>true</code></td>
148  * <td><code>false</code></td>
149  * <td><code>true</code></td>
150  * <td><code>true</code></td>
151  * <td><code><i>?</i></code></td>
152  * </tr>
153  * <tr>
154  * <td><i>archive.zip</i></td>
155  * <td>False positive: Regular special file</td>
156  * <td><code>true</code></td>
157  * <td><code>false</code></td>
158  * <td><code>false</code></td>
159  * <td><code>true</code></td>
160  * <td><code><i>?</i></code></td>
161  * </tr>
162  * <tr>
163  * <td><i>archive.zip</i></td>
164  * <td>File or directory does not exist</td>
165  * <td><code>true</code></td>
166  * <td><code>false</code></td>
167  * <td><code>false</code></td>
168  * <td><code>false</code></td>
169  * <td><code>0</code></td>
170  * </tr>
171  * <tr>
172  * <td colspan="7">&nbsp;</td>
173  * </tr>
174  * <tr>
175  * <td><i>archive.tzp</i><sup>4</sup></td>
176  * <td>Valid RAES encrypted ZIP file with valid key (e.g. password)</td>
177  * <td><code>true</code></td>
178  * <td><code>true</code></td>
179  * <td><code>false</code></td>
180  * <td><code>true</code></td>
181  * <td><code>0</code></td>
182  * </tr>
183  * <tr>
184  * <td><i>archive.tzp</i></td>
185  * <td>Valid RAES encrypted ZIP file with unknown key<sup>5</sup></td>
186  * <td><code>true</code></td>
187  * <td><code>false</code></td>
188  * <td><code>false</code></td>
189  * <td><code>true</code></td>
190  * <td><code><i>?</i></code></td>
191  * </tr>
192  * <tr>
193  * <td><i>archive.tzp</i></td>
194  * <td>False positive: Regular directory</td>
195  * <td><code>true</code></td>
196  * <td><code>true</code></td>
197  * <td><code>false</code></td>
198  * <td><code>true</code></td>
199  * <td><code><i>?</i></code></td>
200  * </tr>
201  * <tr>
202  * <td><i>archive.tzp</i></td>
203  * <td>False positive: Regular file</td>
204  * <td><code>true</code></td>
205  * <td><code>false</code></td>
206  * <td><code>true</code></td>
207  * <td><code>true</code></td>
208  * <td><code><i>?</i></code></td>
209  * </tr>
210  * <tr>
211  * <td><i>archive.tzp</i></td>
212  * <td>False positive: Regular special file</td>
213  * <td><code>true</code></td>
214  * <td><code>false</code></td>
215  * <td><code>false</code></td>
216  * <td><code>true</code></td>
217  * <td><code><i>?</i></code></td>
218  * </tr>
219  * <tr>
220  * <td><i>archive.tzp</i></td>
221  * <td>File or directory does not exist</td>
222  * <td><code>true</code></td>
223  * <td><code>false</code></td>
224  * <td><code>false</code></td>
225  * <td><code>false</code></td>
226  * <td><code>0</code></td>
227  * </tr>
228  * <tr>
229  * <td colspan="7">&nbsp;</td>
230  * </tr>
231  * <tr>
232  * <td><i>other</i></td>
233  * <td>Regular directory</td>
234  * <td><code>false</code></td>
235  * <td><code>true</code></td>
236  * <td><code>false</code></td>
237  * <td><code>true</code></td>
238  * <td><i><code>?</code></i></td>
239  * </tr>
240  * <tr>
241  * <td><i>other</i></td>
242  * <td>Regular file</td>
243  * <td><code>false</code></td>
244  * <td><code>false</code></td>
245  * <td><code>true</code></td>
246  * <td><code>true</code></td>
247  * <td><i><code>?</code></i></td>
248  * </tr>
249  * <tr>
250  * <td><i>other</i></td>
251  * <td>Regular special file</td>
252  * <td><code>false</code></td>
253  * <td><code>false</code></td>
254  * <td><code>false</code></td>
255  * <td><code>true</code></td>
256  * <td><i><code>?</code></i></td>
257  * </tr>
258  * <tr>
259  * <td><i>other</i></td>
260  * <td>File or directory does not exist</td>
261  * <td><code>false</code></td>
262  * <td><code>false</code></td>
263  * <td><code>false</code></td>
264  * <td><code>false</code></td>
265  * <td><code>0</code></td>
266  * </tr>
267  * </table>
268  * <ol>
269  * <li>{@link #isArchive} doesn't check the true state of the file - it just
270  * looks at its path: If the path ends with a configured archive file
271  * suffix, <code>isArchive()</code> always returns <code>true</code>.
272  * <li>{@link #length} always returns <code>0</code> if the path denotes a
273  * valid archive file.
274  * Otherwise, the return value of <code>length()</code> depends on the
275  * platform and file system, which is indicated by <i><code>?</code></i>.
276  * For regular directories on Windows/NTFS for example, the return value
277  * would be <code>0</code>.
278  * <li><i>archive.zip</i> is just an example: If TrueZIP is configured to
279  * recognize TAR.GZ files, the same behavior applies to
280  * <i>archive.tar.gz</i>.</li>
281  * <li>This assumes that <i>.tzp</i> is configured as an archive file suffix
282  * for RAES encrypted ZIP files.
283  * By default, this is <b>not</b> the case.</li>
284  * <li>The methods behave exactly the same for both <i>archive.zip</i> and
285  * <i>archive.tzp</i> with one exception: If the key for an RAES encrypted
286  * ZIP file remains unknown (e.g. because the user cancelled password
287  * prompting), then these methods behave as if the true state of the path
288  * were a special file: Both {@link #isDirectory} and {@link #isFile}
289  * return <code>false</code>, while {@link #exists} returns
290  * <code>true</code>.</li>
291  * </ol>
292  *
293  * <h3><a name="miscellaneous">Miscellaneous</a></h3>
294  * <ol>
295  * <li>When using characters in file names which have no representation in the
296  * character set encoding of an archive file, then the methods in this
297  * class will fail gracefully with an <code>IOException</code> or a
298  * boolean return value of false (depending on the method's way to
299  * indicate an error condition).
300  * This is to protect applications from creating archive entries which
301  * cannot get encoded and decoded again correctly.
302  * An example is the Euro sign which does not have a representation in the
303  * IBM437 character set used by ordinary ZIP files.
304  * <li>Since TrueZIP 6.4, this class is serializable in order to meet the
305  * requirements of its super class.
306  * However, it's not recommended to serialize File instances:
307  * Together with the instance, its archive detector and all associated
308  * archive drivers are serialized, too, which is pretty inefficient for
309  * a single instance.
310  * Serialization might even fail since it's not a general requirement for
311  * the interface implementations to be serializable - although the default
312  * implementations in TrueZIP 6.4 are all serializable.
313  * Instead of serializing File instances, a client application should
314  * serialize paths (which are simply String instances) and leave it up
315  * to the receiver to create a new File instance from it with archive
316  * files recognized by a suitable local archive detector - usually the
317  * {@link #getDefaultArchiveDetector default detector}.
318  * <li>Please refer to the {@link java.io.File super class documentation}
319  * for any undocumented methods.
320  * </ol>
321  *
322  * @see DefaultArchiveDetector API reference for configuring archive type recognition.
323  * @see java.io.File java.io.File
324  * @see <a HREF="package-summary.html#package_description">Package Description</a>
325  * @author Christian Schlichtherle
326  * @version @version@
327  */

328 public class File extends java.io.File JavaDoc implements FileConstants, Cloneable JavaDoc {
329
330     /** The filesystem roots. */
331     private static final HashSet roots = new HashSet(Arrays.asList(listRoots()));
332
333     /** The prefix of a UNC (a Windows concept). */
334     private static final String JavaDoc uncPrefix = separator + separator;
335
336     private static final Executor readerExecutor
337             = getExecutor("TrueZIP InputStream Reader");
338
339     /**
340      * @see #setLenient(boolean)
341      * @see #isLenient()
342      */

343     private static boolean lenient = true;
344
345     private static ArchiveDetector defaultDetector = ArchiveDetector.DEFAULT;
346
347     /**
348      * The entry is used to implement the behaviour of most methods in case
349      * this file object represents neither a archive file or an entry
350      * in a archive file.
351      * If this file object is constructed from another file object, then this
352      * field is initialized with that file object. This is provided to support
353      * subclasses of <code>File</code> which are provided by third parties and
354      * their behaviour is required as a default.
355      * This is an essential feature to support archive files as
356      * directories for <code>javax.swing.JFileChooser</code>, because the
357      * <code>FileSystemView</code> class creates objects which are actually
358      * instances of {@link sun.awt.shell.ShellFolder}, which is a subclass of
359      * {@link java.io.File}.
360      * <p>
361      * Finally, to support <code>JFileChooser</code>, we are providing an
362      * customized {@link javax.swing.filechooser.FileSystemView} which
363      * simply wraps most methods and creates an instance of this class from
364      * the object returned by the method of the base <code>FileSystemView</code>
365      * class as a file.
366      */

367     private final java.io.File JavaDoc delegate;
368
369     /**
370      * @see #getArchiveDetector
371      */

372     private final ArchiveDetector detector;
373
374     /**
375      * This field should be considered final!
376      *
377      * @see #getInnerArchive
378      */

379     private File innerArchive;
380
381     /**
382      * This field should be considered final!
383      *
384      * @see #getInnerEntryName
385      */

386     private String JavaDoc innerEntryName;
387
388     /**
389      * This field should be considered final!
390      *
391      * @see #getEnclArchive
392      */

393     private File enclArchive;
394
395     /**
396      * This field should be considered final!
397      *
398      * @see #getEnclEntryName
399      */

400     private String JavaDoc enclEntryName;
401
402     /**
403      * This refers to the archive controller if and only if this file refers
404      * to an archive file, otherwise it's <code>null</code>.
405      * This field should be considered final!
406      */

407     private transient ArchiveController controller; // never transmit this over the wire!
408

409     /**
410      * Equivalent to {@link #File(java.io.File, ArchiveDetector)
411      * File(file, getDefaultArchiveDetector())}.
412      */

413     public File(java.io.File JavaDoc blueprint) {
414         this(blueprint, defaultDetector);
415     }
416
417     /**
418      * Constructs a new <code>File</code> instance which may use the given
419      * {@link ArchiveDetector} to detect any archive files in its pathname.
420      *
421      * @param blueprint The file to use as a blueprint. If this is an instance
422      * of this class, its fields are copied and the
423      * <code>detector</code> parameter is ignored.
424      * @param detector The object used to detect any archive files
425      * in the pathname and configure their parameters if and only if
426      * <code>blueprint</code> is not an instance of this this class.
427      */

428     public File(
429             final java.io.File JavaDoc blueprint,
430             final ArchiveDetector detector) {
431         super(blueprint.getPath());
432
433         if (blueprint instanceof File) {
434             final File file = (File) blueprint;
435             this.delegate = file.delegate;
436             this.detector = file.detector;
437             this.enclArchive = file.enclArchive;
438             this.enclEntryName = file.enclEntryName;
439             this.innerArchive = file.isArchive() ? this : file.innerArchive;
440             this.innerEntryName = file.innerEntryName;
441             this.controller = file.controller;
442         } else {
443             this.delegate = blueprint;
444             this.detector = detector;
445             init((File) null);
446         }
447
448         assert invariants(true);
449     }
450
451     /**
452      * Equivalent to {@link #File(String, ArchiveDetector)
453      * File(pathname, getDefaultArchiveDetector())}.
454      */

455     public File(String JavaDoc pathName) {
456         this(pathName, defaultDetector);
457     }
458
459     /**
460      * Constructs a new <code>File</code> instance which uses the given
461      * {@link ArchiveDetector} to detect any archive files in its pathname
462      * and configure their parameters.
463      *
464      * @param detector The object used to detect any archive
465      * files in the pathname and configure their parameters.
466      * @param pathName The pathname of the file.
467      */

468     public File(
469             final String JavaDoc pathName,
470             final ArchiveDetector detector) {
471         super(pathName);
472
473         delegate = new java.io.File JavaDoc(pathName);
474         this.detector = detector;
475         init((File) null);
476
477         assert invariants(true);
478     }
479
480     /**
481      * Equivalent to {@link #File(String, String, ArchiveDetector)
482      * File(parent, child, getDefaultArchiveDetector())}.
483      */

484     public File(String JavaDoc parent, String JavaDoc child) {
485         this(parent, child, defaultDetector);
486     }
487
488     /**
489      * Constructs a new <code>File</code> instance which uses the given
490      * {@link ArchiveDetector} to detect any archive files in its pathname
491      * and configure their parameters.
492      *
493      * @param detector The object used to detect any archive
494      * files in the pathname and configure their parameters.
495      * @param parent The parent pathname as a {@link String}.
496      * @param child The child pathname as a {@link String}.
497      */

498     public File(
499             final String JavaDoc parent,
500             final String JavaDoc child,
501             final ArchiveDetector detector) {
502         super(parent, child);
503
504         delegate = new java.io.File JavaDoc(parent, child);
505         this.detector = detector;
506         init((File) null);
507
508         assert invariants(true);
509     }
510
511     /**
512      * Equivalent to {@link #File(java.io.File, String, ArchiveDetector)
513      * File(parent, child, null)}.
514      *
515      * @param parent The parent directory as a <code>File</code> instance.
516      * If this parameter is an instance of this class, its
517      * <code>ArchiveDetector</code> is used to detect any archive files
518      * in the pathname of this <code>File</code> instance.
519      * Otherwise, the {@link #getDefaultArchiveDetector()} is used.
520      * This is used in order to make this <code>File</code> instance
521      * behave as if it had been created by one of the {@link #listFiles}
522      * methods called on <code>parent</code> instead.
523      * @param child The child pathname as a {@link String}.
524      */

525     public File(java.io.File JavaDoc parent, String JavaDoc child) {
526         this(parent, child, null);
527     }
528
529     /**
530      * Constructs a new <code>File</code> instance which uses the given
531      * {@link ArchiveDetector} to detect any archive files in its pathname
532      * and configure their parameters.
533      *
534      * @param parent The parent directory as a <code>File</code> instance.
535      * @param child The child pathname as a {@link String}.
536      * @param detector The object used to detect any archive
537      * files in the pathname of this <code>File</code> instance.
538      * If this is <code>null</code>, the <code>ArchiveDetector</code>
539      * is inherited from <code>parent</code> if it's an instance of
540      * this class or the {@link #getDefaultArchiveDetector()} otherwise.
541      * This may be used in order to make this <code>File</code> instance
542      * behave as if it had been created by one of the {@link #listFiles}
543      * methods called on <code>parent</code> instead.
544      * @throws NullPointerException If <code>child</code> is <code>null</code>.
545      */

546     public File(
547             final java.io.File JavaDoc parent,
548             final String JavaDoc child,
549             final ArchiveDetector detector) {
550         super(parent, child);
551
552         delegate = new java.io.File JavaDoc(parent, child);
553         if (parent instanceof File) {
554             final File smartParent = (File) parent;
555             this.detector = detector != null ? detector : smartParent.detector;
556             init(smartParent);
557         } else {
558             this.detector = detector != null ? detector : defaultDetector;
559             init((File) null);
560         }
561
562         assert invariants(true);
563     }
564
565     /**
566      * Constructs a new <code>File</code> instance from the given
567      * <code>uri</code>. This method behaves similar to
568      * {@link java.io.File#File(URI) new java.io.File(uri)} with the following
569      * amendment:
570      * If the URI matches the pattern
571      * <code>(jar:)*file:(<i>path</i>!/)*<i>entry</i></code>, then the
572      * constructed file object treats the URI like a (possibly ZIPped) file.
573      * <p>
574      * For example, in a Java application which is running from a JAR in the
575      * local file system you could use this constructor to arbitrarily access
576      * (and modify) all entries in the JAR file from which the application is
577      * currently running by using the following simple method:
578      * <pre>
579      * public File getResourceAsFile(String resource) {
580      * URL url = getClass().getResource(resource);
581      * try {
582      * return new File(new URI(url.toExternalForm()));
583      * } catch (Exception notAJaredFileURI) {
584      * return null;
585      * }
586      * }
587      * </pre>
588      * The newly created <code>File</code> instance uses
589      * {@link ArchiveDetector#ALL} as its <code>ArchiveDetector</code>.
590      *
591      * @param uri an absolute, hierarchical URI with a scheme equal to
592      * <code>file</code> or <code>jar</code>, a non-empty path component,
593      * and undefined authority, query, and fragment components.
594      * @throws NullPointerException if <code>uri</code> is <code>null</code>.
595      * @throws IllegalArgumentException if the preconditions on the
596      * parameter <code>uri</code> do not hold.
597      */

598     public File(URI uri) {
599         this(uri, ArchiveDetector.ALL);
600     }
601
602     // Unfortunately, this constructor has a significant overhead as the jar:
603
// schemes need to be processed twice, first before initializing the super
604
// class and second when initializing this sub class.
605
File(
606             final URI uri,
607             final ArchiveDetector detector) {
608         super(unjarFileURI(uri));
609
610         delegate = new java.io.File JavaDoc(super.getPath());
611         this.detector = detector;
612         init(uri);
613
614         assert invariants(true);
615     }
616
617     /**
618      * Converts a (jar:)*file: URI to a plain file: URI or returns the
619      * provided URI again if it doesn't match this pattern.
620      */

621     private static final URI unjarFileURI(final URI uri) {
622         try {
623             final String JavaDoc scheme = uri.getScheme();
624             final String JavaDoc ssp = Paths.normalize(uri.getSchemeSpecificPart(), '/');
625             return unjarFileURIImpl(new URI(scheme, ssp, null));
626         } catch (URISyntaxException ignored) {
627             // Ignore any exception with possibly only a subpart of the
628
// original URI.
629
}
630         throw new IllegalArgumentException JavaDoc(uri + ": Not a valid (possibly jared) file URI!");
631     }
632
633     private static final URI unjarFileURIImpl(final URI uri)
634     throws URISyntaxException {
635         final String JavaDoc scheme = uri.getScheme();
636         if ("jar".equalsIgnoreCase(scheme)) {
637             final String JavaDoc rssp = uri.getRawSchemeSpecificPart();
638             final int i;
639             if (rssp.endsWith("!"))
640                 i = rssp.length() - 1;
641             else
642                 i = rssp.lastIndexOf("!/");
643
644             if (i <= 0)
645                 return unjarFileURI(new URI(rssp)); // ignore redundant jar: scheme
646

647             final URI subURI = new URI(
648                     rssp.substring(0, i) + rssp.substring(i + 1)); // cut out '!'
649
final String JavaDoc subScheme = subURI.getScheme();
650             if ("jar".equalsIgnoreCase(subScheme)) {
651                 final URI processedSubURI = unjarFileURIImpl(subURI);
652                 if (processedSubURI != subURI)
653                     return processedSubURI;
654                 // No match, e.g. "jar:jar:http://host/dir!/dir!/file".
655
} else if ("file".equalsIgnoreCase(subScheme)) {
656                 return subURI; // e.g. "file:///usr/bin"
657
}
658         } else if ("file".equalsIgnoreCase(scheme)) {
659             return uri;
660         }
661         throw new URISyntaxException(uri.toString(), "Not a valid (possibly jared) file URI!");
662     }
663
664     /**
665      * This constructor is <em>not</em> for public use - do not use it!
666      *
667      * @see FileFactory
668      */

669     public File(
670             final java.io.File JavaDoc delegate,
671             final File innerArchive,
672             final ArchiveDetector detector) {
673         super(delegate.getPath());
674
675         assert assertParams(delegate, innerArchive, detector);
676
677         this.delegate = delegate;
678
679         final String JavaDoc path = delegate.getPath();
680         if (innerArchive != null) {
681             final int innerArchivePathLength
682                     = innerArchive.getPath().length();
683             if (path.length() == innerArchivePathLength) {
684                 this.detector = innerArchive.detector;
685                 this.innerArchive = this;
686                 this.innerEntryName = EMPTY;
687                 this.enclArchive = innerArchive.enclArchive;
688                 this.enclEntryName = innerArchive.enclEntryName;
689                 this.controller = ArchiveController.getInstance(this);
690             } else {
691                 this.detector = detector;
692                 this.innerArchive = this.enclArchive = innerArchive;
693                 this.innerEntryName = this.enclEntryName
694                         = path.substring(innerArchivePathLength + 1) // cut off leading separatorChar
695
.replace(separatorChar, ENTRY_SEPARATOR_CHAR);
696             }
697         } else {
698             this.detector = detector;
699         }
700
701         assert invariants(true);
702     }
703
704     /**
705      * If assertions are disabled, the call to this method is thrown away by
706      * the HotSpot compiler, so there is no performance penalty.
707      */

708     private static final boolean assertParams(
709             final java.io.File JavaDoc delegate,
710             final File innerArchive,
711             final ArchiveDetector detector)
712     throws AssertionError JavaDoc {
713         assert delegate != null : "delegate is null!";
714         assert !(delegate instanceof File) : "delegate must not be a de.schlichtherle.io.File!";
715         if (innerArchive != null) {
716             assert innerArchive.isArchive() : "innerArchive must be an archive!";
717             assert containsImpl(innerArchive, delegate)
718                     : "innerArchive must contain delegate!";
719         }
720         assert detector != null : "detector is null!";
721
722         return true;
723     }
724
725     /**
726      * This constructor is <em>not</em> for public use - do not use it!
727      *
728      * @see FileFactory
729      */

730     public File(
731             final File blueprint,
732             final java.io.File JavaDoc delegate,
733             final File enclArchive) {
734         super(delegate.getPath());
735
736         assert parameters(blueprint, delegate, enclArchive);
737
738         this.delegate = delegate;
739         this.detector = blueprint.detector;
740         this.enclArchive = enclArchive;
741         this.enclEntryName = blueprint.enclEntryName;
742         this.innerArchive = blueprint.isArchive() ? this : enclArchive;
743         this.innerEntryName = blueprint.innerEntryName;
744         this.controller = blueprint.controller;
745
746         assert invariants(blueprint.controller != null);
747     }
748
749     /**
750      * Initialize this file object by scanning its pathname for archive
751      * files, using the given <code>ancestor</code> file (i.e. a direct or
752      * indirect parent file) if any.
753      * <code>entry</code> and <code>detector</code> must already be
754      * initialized!
755      * Must not be called to re-initialize this object!
756      */

757     private void init(final File ancestor) {
758         final String JavaDoc path = super.getPath();
759         assert ancestor == null || path.startsWith(ancestor.getPath());
760         assert delegate.getPath().equals(path);
761         assert detector != null;
762
763         final StringBuffer JavaDoc enclEntryNameBuf = new StringBuffer JavaDoc(path.length());
764         init(ancestor, detector, 0, path, enclEntryNameBuf, new String JavaDoc[2]);
765         enclEntryName = enclEntryNameBuf.length() > 0 ? enclEntryNameBuf.toString() : null;
766
767         if (innerArchive == this) {
768             // controller init has been deferred until now in
769
// order to provide the ArchiveController with a fully
770
// initialized object.
771
innerEntryName = EMPTY;
772             controller = ArchiveController.getInstance(this);
773         } else if (innerArchive == enclArchive) {
774             innerEntryName = enclEntryName;
775         }
776     }
777
778     private void init(
779             File ancestor,
780             ArchiveDetector detector,
781             int skip,
782             final String JavaDoc path,
783             final StringBuffer JavaDoc enclEntryNameBuf,
784             final String JavaDoc[] split) {
785         if (path == null) {
786             assert enclArchive == null;
787             enclEntryNameBuf.setLength(0);
788             return;
789         }
790
791         Paths.split(path, separatorChar, split);
792         final String JavaDoc parent = split[0];
793         final String JavaDoc base = split[1];
794
795         if (base.length() == 0 || ".".equals(base)) {
796             // Fall through.
797
} else if ("..".equals(base)) {
798             skip++;
799         } else if (skip > 0) {
800             skip--;
801         } else {
802             if (ancestor != null) {
803                 final int pathLen = path.length();
804                 final int ancestorPathLen = ancestor.getPath().length();
805                 if (pathLen == ancestorPathLen) {
806                     // Found ancestor: Process it and stop.
807
// The following assertion is wrong: enclEntryNameBuf may
808
// indeed be null if the full path ends with just
809
// a single dot after the last separator, i.e. the base
810
// name is ".", indicating the current directory.
811
// assert enclEntryNameBuf.length() > 0;
812
enclArchive = ancestor.innerArchive;
813                     if (!ancestor.isArchive()) {
814                         if (ancestor.isEntry()) {
815                             if (enclEntryNameBuf.length() > 0) {
816                                 enclEntryNameBuf.insert(0, '/');
817                                 enclEntryNameBuf.insert(0, ancestor.enclEntryName);
818                             } else { // TODO: Simplify this!
819
// Example: new File(new File(new File("archive.zip"), "entry"), ".")
820
// with ArchiveDetector.DEFAULT.
821
assert enclArchive == ancestor.enclArchive;
822                                 enclEntryNameBuf.append(ancestor.enclEntryName);
823                             }
824                         } else {
825                             assert enclArchive == null;
826                             enclEntryNameBuf.setLength(0);
827                         }
828                     } else if (enclEntryNameBuf.length() <= 0) { // TODO: Simplify this!
829
// Example: new File(new File("archive.zip"), ".")
830
// with ArchiveDetector.DEFAULT.
831
assert enclArchive == ancestor;
832                         innerArchive = this;
833                         enclArchive = ancestor.enclArchive;
834                         if (ancestor.enclEntryName != null)
835                             enclEntryNameBuf.append(ancestor.enclEntryName);
836                     }
837                     if (innerArchive != this)
838                         innerArchive = enclArchive;
839                     return;
840                 } else if (pathLen < ancestorPathLen) {
841                     detector = ancestor.detector;
842                     ancestor = ancestor.enclArchive;
843                 }
844             }
845
846             final boolean isArchive = detector.getArchiveDriver(path) != null;
847             if (enclEntryNameBuf.length() > 0) {
848                 if (isArchive) {
849                     enclArchive = detector.createFile(path); // use the same detector for the parent directory
850
if (innerArchive != this)
851                         innerArchive = enclArchive;
852                     return;
853                 }
854                 enclEntryNameBuf.insert(0, '/');
855                 enclEntryNameBuf.insert(0, base);
856             } else {
857                 if (isArchive)
858                     innerArchive = this;
859                 enclEntryNameBuf.append(base);
860             }
861         }
862
863         init(ancestor, detector, skip, parent, enclEntryNameBuf, split);
864     }
865
866     /**
867      * Uses the given (jar:)*file: URI to initialize this file object.
868      * Note that we already know that the provided URI matches this pattern!
869      * <code>entry</code> and <code>detector</code> must already be
870      * initialized!
871      * Must not be called to re-initialize this object!
872      */

873     private void init(final URI uri) {
874         assert uri != null;
875         assert delegate.getPath().equals(super.getPath());
876         assert detector != null;
877
878         init(uri, 0, Paths.cutTrailingSeparators(
879                 uri.getSchemeSpecificPart(), '/'));
880
881         if (innerArchive == this) {
882             // controller init has been deferred until now in
883
// order to provide the ArchiveController with a fully
884
// initialized object.
885
controller = ArchiveController.getInstance(this);
886         }
887     }
888
889     /**
890      * TODO: Provide a means to detect other archive schemes, not only
891      * <code>"jar:"</code>.
892      */

893     private void init(
894             URI uri,
895             int skip,
896             final String JavaDoc path) {
897         String JavaDoc scheme = uri.getScheme();
898         if (path == null || !"jar".equalsIgnoreCase(scheme)) {
899             assert enclArchive == null;
900             enclEntryName = null;
901             return;
902         }
903
904         final String JavaDoc[] split = Paths.split(path, '/');
905         String JavaDoc parent = split[0];
906         final String JavaDoc base = split[1]; // TODO: Review: intern()?
907

908         if (base.length() == 0 || ".".equals(base)) {
909             // Fall through.
910
} else if ("..".equals(base)) {
911             skip++;
912         } else if (skip > 0) {
913             skip--;
914         } else {
915             final int baseEnd = base.length() - 1;
916             final boolean isArchive = base.charAt(baseEnd) == '!';
917             if (enclEntryName != null) {
918                 if (isArchive) {
919                     enclArchive = detector.createFile(createURI(scheme, path)); // use the same detector for the parent directory
920
if (innerArchive != this) {
921                         innerArchive = enclArchive;
922                         innerEntryName = enclEntryName;
923                     }
924                     return;
925                 }
926                 enclEntryName = base + "/" + enclEntryName;
927             } else {
928                 if (isArchive) {
929                     innerArchive = this;
930                     innerEntryName = EMPTY;
931                     int i = parent.indexOf(':');
932                     assert i >= 0;
933                     scheme = parent.substring(0, i);
934                     assert scheme.matches("[a-zA-Z]+");
935                     if (i == parent.length() - 1) // scheme only?
936
return;
937                     uri = createURI(parent.substring(0, i), parent.substring(i + 1));
938                     enclEntryName = base.substring(0, baseEnd); // cut off trailing '!'!
939
parent = uri.getSchemeSpecificPart();
940                 } else {
941                     enclEntryName = base;
942                 }
943             }
944         }
945
946         init(uri, skip, parent);
947     }
948
949     /**
950      * Creates a URI from a scheme and a scheme specific part.
951      * Note that the scheme specific part may contain whitespace.
952      * {@link "https://truezip.dev.java.net/issues/show_bug.cgi?id=1"}
953      */

954     private URI createURI(String JavaDoc scheme, String JavaDoc ssp)
955     throws IllegalArgumentException JavaDoc {
956         try {
957             return new URI(scheme, ssp, null);
958         } catch (URISyntaxException syntaxError) {
959         IllegalArgumentException JavaDoc iae = new IllegalArgumentException JavaDoc(syntaxError.toString());
960         iae.initCause(syntaxError);
961         throw iae;
962         }
963     }
964
965     /**
966      * This is called by package private constructors if and only if
967      * assertions are enabled to assert that their parameters are valid.
968      * If assertions are disabled, the call to this method is thrown away by
969      * the HotSpot compiler, so there is no performance penalty.
970      */

971     private static boolean parameters(
972             final File blueprint,
973             final java.io.File JavaDoc delegate,
974             final File enclArchive)
975     throws AssertionError JavaDoc {
976         assert delegate != null : "delegate is null!";
977         assert !(delegate instanceof File)
978                 : "delegate must not be a de.schlichtherle.io.File!";
979         assert blueprint != null : "blueprint is null!";
980
981         String JavaDoc delegatePath = delegate.getPath();
982         final java.io.File JavaDoc normalizedBlueprint = normalize(blueprint);
983         String JavaDoc normalizedBlueprintPath = normalizedBlueprint.getPath();
984         String JavaDoc normalizedBlueprintBase = normalizedBlueprint.getName();
985         // Windows and MacOS are case preserving, however UNIX is case
986
// sensitive. If we meet an unknown platform, we assume that it is
987
// case preserving, which means that two pathnames are considered
988
// equal if they differ by case only.
989
// In the context of this constructor, this implements a liberal
990
// in-dubio-pro-reo parameter check.
991
if (separatorChar != '/') {
992             delegatePath = delegatePath.toLowerCase();
993             normalizedBlueprintPath = normalizedBlueprintPath.toLowerCase();
994             normalizedBlueprintBase = normalizedBlueprintBase.toLowerCase();
995         }
996         if (!".".equals(normalizedBlueprintBase)
997             && !"..".equals(normalizedBlueprintBase)
998             && !normalizedBlueprintPath.startsWith("." + separator)
999             && !normalizedBlueprintPath.startsWith(".." + separator)) {
1000            assert delegatePath.endsWith(normalizedBlueprintPath)
1001                    : "delegate and blueprint must identify the same directory!";
1002            if (enclArchive != null) {
1003                assert enclArchive.isArchive()
1004                        : "enclArchive must be an archive file!";
1005                assert containsImpl(enclArchive, delegate.getParentFile())
1006                        : "enclArchive must be an ancestor of delegate!";
1007            }
1008        }
1009
1010        return true;
1011    }
1012
1013    private void readObject(final ObjectInputStream in)
1014    throws IOException, ClassNotFoundException JavaDoc {
1015        in.defaultReadObject();
1016        if (innerArchive == this) {
1017            assert EMPTY.equals(innerEntryName); // equal, but...
1018
assert EMPTY != innerEntryName; // not identical!
1019
assert controller == null; // transient!
1020
innerEntryName = EMPTY; // postfix!
1021
controller = ArchiveController.getInstance(this); // postfix!
1022
}
1023        assert invariants(true);
1024    }
1025
1026    /**
1027     * This is called by all constructors if and only if assertions
1028     * are enabled to assert that the instance invariants are properly obeyed.
1029     * If assertions are disabled, the call to this method is thrown away by
1030     * the HotSpot compiler, so there is no performance penalty.
1031     */

1032    private boolean invariants(final boolean controllerInit)
1033    throws AssertionError JavaDoc {
1034        assert delegate != null;
1035        assert !(delegate instanceof File);
1036        assert delegate.getPath().equals(super.getPath());
1037        assert detector != null;
1038        assert (innerArchive != null) == (innerEntryName != null);
1039        assert (enclArchive != null) == (enclEntryName != null);
1040        assert (innerArchive == this
1041                    && innerArchive != enclArchive
1042                    && innerEntryName == EMPTY
1043                    && innerEntryName != enclEntryName
1044                    && (controllerInit
1045                        ? controller != null
1046                        : controller == null))
1047                ^ (innerArchive == enclArchive
1048                    && innerEntryName == enclEntryName
1049                    && controller == null);
1050        assert enclArchive == null
1051                || containsImpl(enclArchive, delegate.getParentFile())
1052                    && enclEntryName.length() > 0
1053                    && (separatorChar == '/'
1054                        || enclEntryName.indexOf(separatorChar) == -1);
1055
1056        return true;
1057    }
1058
1059    /**
1060     * Removes any <code>"."</code> and <code>".."</code> directories from the
1061     * path wherever possible.
1062     *
1063     * @param file The file instance which's path is to be normalized.
1064     * @return <code>file</code> if it was already in normalized form.
1065     * Otherwise, an instance of the class {@link java.io.File
1066     * java.io.File} is returned.
1067     * Note that the returned object is never an instance of this
1068     * class, so it safe to use it as an blueprint for constructing
1069     * a new object of this class.
1070     */

1071    static java.io.File JavaDoc normalize(java.io.File JavaDoc file) {
1072        final String JavaDoc possiblyDotifiedPath = file.getPath();
1073        final String JavaDoc path = Paths.normalize(possiblyDotifiedPath, separatorChar);
1074        if (path != possiblyDotifiedPath) // mind contract of Paths.normalize!
1075
return new java.io.File JavaDoc(path);
1076        else
1077            return file;
1078    }
1079
1080    /**
1081     * Equivalent to {@link #umount(boolean, boolean, boolean, boolean)
1082     * umount(false, true, false, true)}.
1083     */

1084    public static final void umount()
1085    throws ArchiveException {
1086        ArchiveController.updateAll("", false, true, false, true, true, true);
1087    }
1088
1089    /**
1090     * Equivalent to {@link #umount(boolean, boolean, boolean, boolean)
1091     * umount(false, closeStreams, false, closeStreams)}.
1092     */

1093    public static final void umount(boolean closeStreams)
1094    throws ArchiveException {
1095        ArchiveController.updateAll("",
1096                false, closeStreams,
1097                false, closeStreams,
1098                true, true);
1099    }
1100
1101    /**
1102     * Updates <em>all</em> archive files in the native file system
1103     * with the contents of their virtual file system, resets all cached
1104     * state and deletes all temporary files.
1105     * This method is thread safe.
1106     * <p>
1107     * <b>When to call this method:</b>
1108     * <p>
1109     * Unlike a real file system TrueZIP caches some of the archive file
1110     * state in memory and temporary files, which is why the following rules
1111     * exist.
1112     * <p>
1113     * You only ever need to call this method (or its simplified sibling
1114     * {@link #umount()}) if:
1115     * <ol>
1116     * <li>Your application is terminating.
1117     * <li>The client application needs to use an instance of this class
1118     * which has been constructed with an archive detector as its
1119     * constructor parameter which does not recognize the file as an
1120     * archive file ({@link ArchiveDetector#NULL} for example).
1121     * <li>The client application needs to use a
1122     * {@link java.io.File java.io.File} instance to access an archive
1123     * file like a regular file.
1124     * <li>Another class loader has defined another instance of this class
1125     * and the other (package private) classes in this package.
1126     * <li>Another process needs to access an archive file which is in use
1127     * by TrueZIP while this instance of the JVM is still running.
1128     * </ol>
1129     * <p>
1130     * Strictly speaking, (1) is even redundant because a JVM shutdown hook
1131     * will call this method automatically, but doing lengthy operations
1132     * in a JVM shutdown hook is generally discouraged and you couldn't
1133     * deal with any {@link ArchiveException} thrown in the shutdown hook
1134     * anyway (the shutdown hook will just print a stack trace on stderr).
1135     * So please consider the shutdown hook to be a fallback solution and
1136     * call this method explicitly once before your application terminates
1137     * in a finally-block like this:
1138     * <code>
1139     * <pre>
1140     * public static void main(String[] args) throws ArchiveException {
1141     * try {
1142     * // App code goes here.
1143     * } finally {
1144     * File.umount(); // always call in finally-block!
1145     * }
1146     * }
1147     * </pre>
1148     * </code>
1149     * You may want to catch an {@link ArchiveException} or an
1150     * {@link ArchiveWarningException} thrown by this method and deal with
1151     * it appropriately, but you should always keep the call in a
1152     * finally-block.
1153     * <p>
1154     * For all other reasons, after calling this method, the other party must
1155     * complete any changes to the archive files before this <code>File</code>
1156     * instance accesses the archive files again because the file system is
1157     * then mounted automatically again for future use.
1158     * <p>
1159     * <b>Exception handling:</b>
1160     * <p>
1161     * This method is guaranteed to process <em>all</em> archive files
1162     * which are in use or have been touched by this package.
1163     * However, processing some of these archive files may fail for a number
1164     * of reasons.
1165     * Hence, during processing, a <em>sequential archive exception chain</em>
1166     * is constructed and thrown upon termination unless its empty.
1167     * Note that sequential archive exception chaining is a concept which
1168     * is completely orthogonal to Java's general exception cause chaining:
1169     * In a sequential archive exception chain, each archive exception may
1170     * still have a chain of other exceptions as its cause (most likely
1171     * {@link IOException}s).
1172     * <p>
1173     * Archive exceptions fall into two categories:
1174     * <ol>
1175     * <li>The class {@link ArchiveWarningException} is the root of all
1176     * warning exception types.
1177     * These exceptions are thrown if an archive has been completely
1178     * updated, but some warning conditions apply.
1179     * No data has been lost.
1180     * <li>Its super class {@link ArchiveException} is the root of all
1181     * other exceptions types (unless it's an
1182     * <code>ArchiveWarningException</code> again).
1183     * These exceptions are thrown if an archive could not get updated
1184     * successfully.
1185     * This implies loss of some or all archive data.
1186     * </ol>
1187     * <p>
1188     * Note that the effect which is indicated by an archive exception is
1189     * local: An exception thrown when processing an archive file does not
1190     * imply an archive exception or loss of data when processing another
1191     * archive file.
1192     * <p>
1193     * When the archive exception chain is thrown by this method, it is first
1194     * sorted according to (1) descending order of priority and (2) ascending
1195     * order of appearance, and the resulting head exception is then thrown.
1196     * Since <code>ArchiveWarningException</code>s have a lower priority
1197     * than <code>ArchiveException</code>s, they are always pushed back to
1198     * the end of the chain, so that an application can use the following
1199     * simple idiom to detect if only some warnings or at least one severe
1200     * error has occured:
1201     * <code>
1202     * <pre>
1203     * try {
1204     * File.update(); // or File.umount() - with or without parameters
1205     * } catch (ArchiveWarningException ignore) {
1206     * // Only instances of the class ArchiveWarningException exist in
1207     * // the chain of exceptions. We decide to ignore this.
1208     * } catch (ArchiveException failure) {
1209     * // At least one exception occured which is not just an
1210     * // ArchiveWarningException. This is a severe situation that
1211     * // needs to be handled.
1212     *
1213     * // Print the chain of exceptions in order of descending
1214     * // priority and ascending appearance.
1215     * //failure.printStackTrace();
1216     *
1217     * // Print the chain of exceptions in order of appearance instead.
1218     * failure.sortAppearance().printStackTrace();
1219     * }
1220     * </pre>
1221     * </code>
1222     * Please note that the {@link Exception#getMessage()} method (and hence
1223     * {@link Exception#printStackTrace()} will concatenate the detail
1224     * messages of the exceptions in the chain in the given order.
1225     * <p>
1226     * <b>Optimizing calls to this method:</b>
1227     * <p>
1228     * Depending on the size and amount of the archive files the client
1229     * application is using, this method can be a lengthy operation,
1230     * especially since TrueZIP currently supports only full archive
1231     * updates.
1232     * Therefore, you may opt to:
1233     * <ol>
1234     * <li>Call this method whenever the client application has finished
1235     * processing an archive file.
1236     * This is the best strategy in terms of performance, robustness and
1237     * whatever, but may actually be very difficult to implement.
1238     * <li>Call this method from a background thread with <code>false</code>
1239     * as its parameter in order not to force the closing of any open
1240     * streams which may be used by other streams concurrently.
1241     * This may provide some ease of mind, but is usually not the best
1242     * solution in terms of performance because the same archive file
1243     * might be updated several times, whenever there is a change.
1244     * The disadvantage is that the background thread does not know when
1245     * the client application has finished operating on an archive,
1246     * which would be best moment to update it.
1247     * <li>Implement a progress monitor using
1248     * {@link ArchiveStatistics#getUpdateTotalByteCountRead()} and
1249     * {@link ArchiveStatistics#getUpdateTotalByteCountRead()} on the
1250     * instance returned by {@link #getLiveArchiveStatistics()}.
1251     * Please refer to the source code of <code>nzip.ProgressMonitor</code>
1252     * in the base package for a simple example.
1253     * </ol>
1254     *
1255     * @param waitInputStreams Suppose any other thread has still one or more
1256     * archive entry input streams open.
1257     * Then if and only if this parameter is <code>true</code>, this
1258     * method will wait until all other threads have closed their
1259     * archive entry input streams.
1260     * Archive entry input streams opened (and not yet closed) by the
1261     * current thread are always ignored.
1262     * If the current thread gets interrupted while waiting, it will
1263     * stop waiting and proceed normally as if this parameter were
1264     * <code>false</code>.
1265     * Be careful with this parameter value: If a stream has not been
1266     * closed because the client application does not always properly
1267     * close its streams, even on an {@link IOException} (which is a
1268     * typical bug in many Java applications), then this method may
1269     * not return until the current thread gets interrupted!
1270     * @param closeInputStreams Suppose there are any open input streams
1271     * for any archive entries because the application has forgot to
1272     * close all {@link FileInputStream} objects or another thread is
1273     * still busy doing I/O on an archive.
1274     * Then if this parameter is <code>true</code>, an update is forced
1275     * and an {@link ArchiveBusyWarningException} is finally thrown to
1276     * indicate that any subsequent operations on these streams
1277     * will fail with an {@link ArchiveEntryStreamClosedException}
1278     * because they have been forced to close.
1279     * This may also be used to recover an application from a
1280     * {@link FileBusyException} thrown by a constructor of
1281     * {@link FileInputStream} or {@link FileOutputStream}.
1282     * If this parameter is <code>false</code>, the respective archive
1283     * file is <em>not</em> updated and an {@link ArchiveBusyException}
1284     * is thrown to indicate that the application must close all entry
1285     * input streams first.
1286     * @param waitOutputStreams Similar to <code>waitInputStreams</code>,
1287     * but applies to archive entry output streams instead.
1288     * @param closeOutputStreams Similar to <code>closeInputStreams</code>,
1289     * but applies to archive entry output streams instead.
1290     * If this parameter is <code>true</code>, then
1291     * <code>closeInputStreams</code> must be <code>true</code>, too.
1292     * Otherwise, an <code>IllegalArgumentException</code> is thrown.
1293     * @throws ArchiveBusyWarningExcepion If an archive file has been updated
1294     * while the application is using any open streams to access it
1295     * concurrently.
1296     * These streams have been forced to close and the entries of
1297     * output streams may contain only partial data.
1298     * @throws ArchiveWarningException If only warning conditions occur
1299     * throughout the course of this method which imply that the
1300     * respective archive file has been updated with constraints,
1301     * such as a failure to set the last modification time of the
1302     * archive file to the last modification time of its implicit
1303     * root directory.
1304     * @throws ArchiveBusyException If an archive file could not get updated
1305     * because the application is using an open stream.
1306     * No data is lost and the archive file can still get updated by
1307     * calling this method again.
1308     * @throws ArchiveException If any error conditions occur throughout the
1309     * course of this method which imply loss of data.
1310     * This usually means that at least one of the archive files
1311     * has been created externally and was corrupted or it cannot
1312     * get updated because the file system of the temp file or target
1313     * file folder is full.
1314     * @throws IllegalArgumentException If <code>closeInputStreams</code> is
1315     * <code>false</code> and <code>closeOutputStreams</code> is
1316     * <code>true</code>.
1317     * @see #update(File)
1318     * @see #umount()
1319     * @see #umount(File)
1320     */

1321    public static final void umount(
1322            boolean waitInputStreams, boolean closeInputStreams,
1323            boolean waitOutputStreams, boolean closeOutputStreams)
1324    throws ArchiveException {
1325        ArchiveController.updateAll("",
1326                waitInputStreams, closeInputStreams,
1327                waitOutputStreams, closeOutputStreams,
1328                true, true);
1329    }
1330
1331    /**
1332     * Equivalent to {@link #umount(File, boolean, boolean, boolean, boolean)
1333     * umount(archive, false, true, false, true)}.
1334     */

1335    public static final void umount(File archive)
1336    throws ArchiveException {
1337        umount(archive, false, true, false, true);
1338    }
1339
1340    /**
1341     * Equivalent to {@link #umount(File, boolean, boolean, boolean, boolean)
1342     * umount(archive, false, closeStreams, false, closeStreams)}.
1343     */

1344    public static final void umount(File archive, boolean closeStreams)
1345    throws ArchiveException {
1346        umount(archive, false, closeStreams, false, closeStreams);
1347    }
1348
1349    /**
1350     * Similar to
1351     * {@link #umount(boolean, boolean, boolean, boolean)
1352     * umount(waitInputStreams, closeInputStreams, waitOutputStreams, closeOutputStreams)},
1353     * but will only update the given <code>archive</code> and all its enclosed
1354     * (nested) archives.
1355     *
1356     * @param archive A top level archive file.
1357     * @throws NullPointerException If <code>archive</code> is <code>null</code>.
1358     * @throws IllegalArgumentException If <code>archive</code> is not an
1359     * archive or is enclosed in another archive (is not top level).
1360     * @see #update()
1361     * @see #update(File)
1362     * @see #umount()
1363     */

1364    public static final void umount(
1365            File archive,
1366            boolean waitInputStreams, boolean closeInputStreams,
1367            boolean waitOutputStreams, boolean closeOutputStreams)
1368    throws ArchiveException {
1369        if (!archive.isArchive())
1370            throw new IllegalArgumentException JavaDoc(archive.getPath() + " (not an archive)");
1371        if (archive.getEnclArchive() != null)
1372            throw new IllegalArgumentException JavaDoc(archive.getPath() + " (not a top level archive)");
1373        ArchiveController.updateAll(archive.getCanOrAbsPath(),
1374                waitInputStreams, closeInputStreams,
1375                waitOutputStreams, closeOutputStreams,
1376                true, true);
1377    }
1378
1379    /**
1380     * Equivalent to {@link #update(boolean, boolean, boolean, boolean)
1381     * update(false, true, false, true)}.
1382     */

1383    public static final void update()
1384    throws ArchiveException {
1385        ArchiveController.updateAll("",
1386                false, true,
1387                false, true,
1388                false, true);
1389    }
1390
1391    /**
1392     * Equivalent to {@link #update(boolean, boolean, boolean, boolean)
1393     * update(false, closeStreams, false, closeStreams)}.
1394     */

1395    public static final void update(boolean closeStreams)
1396    throws ArchiveException {
1397        ArchiveController.updateAll("",
1398                false, closeStreams,
1399                false, closeStreams,
1400                false, true);
1401    }
1402
1403    /**
1404     * Like {@link #umount(boolean, boolean, boolean, boolean)
1405     * umount(waitInputStreams, closeInputStreams, waitOutputStreams, closeOutputStreams)},
1406     * but may retain some temporary files in order to speed up subsequent
1407     * access to the archive files again.
1408     * <p>
1409     * <b>Warning:</b> Do not use this method unless you fully understand
1410     * its implications.
1411     * In particular, if your client application does not seem to recognize
1412     * changes made to archive files by
1413     * <a HREF="package_summary.html#third_party_access">third parties</code>,
1414     * replace the calls to this method with <code>umount(*)</code>.
1415     *
1416     * @see #update()
1417     * @see #umount()
1418     * @see #umount(boolean, boolean, boolean, boolean)
1419     */

1420    public static final void update(
1421            boolean waitInputStreams, boolean closeInputStreams,
1422            boolean waitOutputStreams, boolean closeOutputStreams)
1423    throws ArchiveException {
1424        ArchiveController.updateAll("",
1425                waitInputStreams, closeInputStreams,
1426                waitOutputStreams, closeOutputStreams,
1427                false, true);
1428    }
1429
1430    /**
1431     * Equivalent to {@link #update(File, boolean, boolean, boolean, boolean)
1432     * update(archive, false, true, false, true)}.
1433     */

1434    public static final void update(File archive)
1435    throws ArchiveException {
1436        update(archive, false, true, false, true);
1437    }
1438
1439    /**
1440     * Equivalent to {@link #update(File, boolean, boolean, boolean, boolean)
1441     * update(archive, false, closeStreams, false, closeStreams)}.
1442     */

1443    public static final void update(File archive, boolean closeStreams)
1444    throws ArchiveException {
1445        update(archive, false, closeStreams, false, closeStreams);
1446    }
1447
1448    /**
1449     * Similar to
1450     * {@link #update(boolean, boolean, boolean, boolean)
1451     * update(waitInputStreams, closeInputStreams, waitOutputStreams, closeOutputStreams)},
1452     * but will only update the given <code>archive</code> and all its enclosed
1453     * (nested) archives.
1454     *
1455     * @param archive A top level archive file.
1456     * @throws NullPointerException If <code>archive</code> is <code>null</code>.
1457     * @throws IllegalArgumentException If <code>archive</code> is not an
1458     * archive or is enclosed in another archive (is not top level).
1459     * @see #update()
1460     * @see #umount()
1461     * @see #umount(File)
1462     */

1463    public static final void update(
1464            File archive,
1465            boolean waitInputStreams, boolean closeInputStreams,
1466            boolean waitOutputStreams, boolean closeOutputStreams)
1467    throws ArchiveException {
1468        if (!archive.isArchive())
1469            throw new IllegalArgumentException JavaDoc(archive.getPath() + " (not an archive)");
1470        if (archive.getEnclArchive() != null)
1471            throw new IllegalArgumentException JavaDoc(archive.getPath() + " (not a top level archive)");
1472        ArchiveController.updateAll(archive.getCanOrAbsPath(),
1473                waitInputStreams, closeInputStreams,
1474                waitOutputStreams, closeOutputStreams,
1475                false, true);
1476    }
1477
1478    /**
1479     * Returns a proxy instance which encapsulates <i>live</i> statistics
1480     * about the total set of archives operated by this package.
1481     * Any call to a method of the returned instance returns an element of
1482     * the statistics which is lively updated, so there is no need to
1483     * repeatedly call this method in order to get updated statistics.
1484     * <p>
1485     * Note that this method returns <em>live</em> statistics rather than
1486     * <em>real time</em> statistics.
1487     * So there may be a slight delay until the values returned reflect
1488     * the actual state of this package.
1489     * This delay increases if the system is under heavy load.
1490     *
1491     * @see ArchiveStatistics
1492     */

1493    public static final ArchiveStatistics getLiveArchiveStatistics() {
1494        return ArchiveController.getLiveStatistics();
1495    }
1496
1497    /**
1498     * This class property controls whether (1) archive files and enclosed
1499     * directories shall be created on the fly if they don't exist and (2)
1500     * open archive entry streams should automatically be closed if they are
1501     * only weakly reachable.
1502     * By default, this class property is <code>true</code>.
1503     * <ol>
1504     * <li>
1505     * Consider the following path: "a/outer.zip/b/inner.zip/c".
1506     * Now let's assume that "a" exists as a directory in the native file
1507     * system, while all other parts of this path don't, and that TrueZIP's
1508     * default configuration is used which would recognize "outer.zip" and
1509     * "inner.zip" as ZIP files.
1510     * <p>
1511     * If this class property is set to <code>false</code>, then
1512     * the client application would have to call
1513     * <code>new File("a/outer.zip/b/inner.zip").mkdirs()</code>
1514     * before it could actually create the innermost "c" entry as a file
1515     * or directory.
1516     * <p>
1517     * More formally, before you can access a node in the virtual file
1518     * system, all its parent directories must exist, including archive
1519     * files. This emulates the behaviour of native file systems.
1520     * <p>
1521     * If this class property is set to <code>true</code> however, then
1522     * any missing parent directories (including archive files) up to the
1523     * outermost archive file ("outer.zip") are created on the fly when using
1524     * operations to create the innermost element of the path ("c").
1525     * <p>
1526     * This allows applications to succeed when doing this:
1527     * <code>new File("a/outer.zip/b/inner.zip/c").createNewFile()</code>,
1528     * or that:
1529     * <code>new FileOutputStream("a/outer.zip/b/inner.zip/c")</code>.
1530     * <p>
1531     * Note that in any case the parent directory of the outermost archive
1532     * file ("a"), must exist - TrueZIP does not create regular directories
1533     * in the native file system on the fly.
1534     * </li>
1535     * <li>
1536     * Many Java applications unfortunately fail to close their streams in all
1537     * cases, in particular if an <code>IOException</code> occured while
1538     * accessing it.
1539     * However, open streams are a limited resource in any operating system
1540     * and may interfere with other services of the OS (on Windows, you can't
1541     * delete an open file).
1542     * This is called the "unclosed streams issue".
1543     * <p>
1544     * Likewise, in TrueZIP an unclosed archive entry stream may result in an
1545     * <code>ArchiveBusy(Warning)?Exception</code> to be thrown when
1546     * {@link #update} or {@link #umount} is called.
1547     * In order to prevent this, TrueZIP's archive entry streams have a
1548     * {@link Object#finalize()} method which closes an archive entry stream
1549     * if its garbage collected.
1550     * <p>
1551     * Now if this class property is set to <code>false</code>, then
1552     * TrueZIP maintains a hard reference to all archive entry streams
1553     * until {@link #update} or {@link #umount} is called, which will deal
1554     * with them: If they are not closed, an
1555     * <code>ArchiveBusy(Warning)?Exception</code> is thrown, depending on
1556     * the boolean parameters to these methods.
1557     * <p>
1558     * This setting is useful if you do not want to tolerate the
1559     * "unclosed streams issue" in a client application.
1560     * <p>
1561     * If this class property is set to <code>true</code> however, then
1562     * TrueZIP maintains only a weak reference to all archive entry streams.
1563     * This allows the garbage collector to finalize them before
1564     * {@link #update} or {@link #umount} is called.
1565     * The finalize() method will then close these archive entry streams,
1566     * which exempts them, from triggering an
1567     * <code>ArchiveBusy(Warning)?Exception</code> on the next call to
1568     * {@link #update} or {@link #umount}.
1569     * However, closing an archive entry output stream this way may result
1570     * in loss of buffered data, so it's only a workaround for this issue.
1571     * <p>
1572     * Note that for the setting of this class property to take effect, any
1573     * change must be made before an archive is first accessed.
1574     * The setting will then persist until the archive is reset by the next
1575     * call to {@link #update} or {@link #umount}.
1576     * <p>
1577     * Historical note: Since TrueZIP 6.0 and before TrueZIP 6.4, archive
1578     * entry streams were always only referenced by a weak reference by
1579     * TrueZIP.
1580     * This class property has been overloaded with this semantic in order
1581     * to allow client applications to test for the "unclosed streams issue".
1582     * </li>
1583     * </ol>
1584     * @see #createNewFile
1585     * @see FileInputStream
1586     * @see FileOutputStream
1587     */

1588    public static final void setLenient(final boolean lenient) {
1589        File.lenient = lenient;
1590    }
1591
1592    /** @see #setLenient(boolean) */
1593    public static final boolean isLenient() {
1594        return lenient;
1595    }
1596
1597    /**
1598     * This class property controls how archive files are recognized.
1599     * When a new <code>File</code> instance is created and no
1600     * {@link ArchiveDetector} is provided to the constructor,
1601     * or when some method of this class are called which accept an
1602     * <code>ArchiveDetector</code> parameter,
1603     * then this class property is used.
1604     * Changing this value affects all newly created <code>File</code>
1605     * instances, but not any existing ones.
1606     *
1607     * @param detector The default {@link ArchiveDetector} to use
1608     * for newly created <code>File</code> instances which have not
1609     * been created with an explicit <code>ArchiveDetector</code>.
1610     * @see ArchiveDetector
1611     * @see #getDefaultArchiveDetector()
1612     * @throws NullPointerException If <code>detector</code> is
1613     * <code>null</code>.
1614     */

1615    public static final void setDefaultArchiveDetector(final ArchiveDetector detector) {
1616        if (detector == null)
1617            throw new NullPointerException JavaDoc();
1618        File.defaultDetector = detector;
1619    }
1620
1621    /**
1622     * Returns the default {@link ArchiveDetector} to be used if no
1623     * <code>ArchiveDetector</code> is passed explicitly to the constructor
1624     * of a <code>File</code> instance or methods which accept this parameter
1625     * type.
1626     * <p>
1627     * Note that this class property defaults to
1628     * <code>ArchiveDetector.DEFAULT</code>
1629     *
1630     * @see ArchiveDetector
1631     * @see #setDefaultArchiveDetector(ArchiveDetector)
1632     */

1633    public static final ArchiveDetector getDefaultArchiveDetector() {
1634        return defaultDetector;
1635    }
1636
1637    /**
1638     * Equivalent to <code>detector.createFile(this)</code>.
1639     * Subclasses should not to overwrite this method, but provide a custom
1640     * implementation of the {@link FileFactory} interface instead.
1641     */

1642    public Object JavaDoc clone() {
1643        return detector.createFile(this);
1644    }
1645
1646    /**
1647     * Behaves like the superclass implementation, but actually either
1648     * returns <code>null</code> or a new instance of this class, so you can
1649     * safely cast it.
1650     */

1651    public java.io.File JavaDoc getParentFile() {
1652        final java.io.File JavaDoc parent = delegate.getParentFile();
1653        if (parent == null)
1654            return null;
1655
1656        assert super.getName().equals(delegate.getName());
1657        if (enclArchive != null
1658                && enclArchive.getPath().length() == parent.getPath().length()) {
1659            assert enclArchive.getPath().equals(parent.getPath());
1660            return enclArchive;
1661        }
1662
1663        // This must not only be called for performance reasons, but also in
1664
// order to prevent the parent pathname from being rescanned for
1665
// archive files with a different detector, which could
1666
// trigger an update and reconfiguration of the respective
1667
// archive controller!
1668
return detector.createFile(parent, enclArchive);
1669    }
1670
1671    /**
1672     * Returns the first parent directory (starting from this file) which is
1673     * <em>not</em> an archive file or a file located in an archive file.
1674     */

1675    public File getNonArchivedParentFile() {
1676        final File enclArchive = this.enclArchive;
1677        return enclArchive != null
1678                ? enclArchive.getNonArchivedParentFile()
1679                : (File) getParentFile();
1680    }
1681
1682    /**
1683     * Behaves like the superclass implementation, but actually either
1684     * returns <code>null</code> or a new instance of this class, so you can
1685     * safely cast it.
1686     *
1687     * @see java.io.File#getAbsoluteFile() java.io.File.getAbsoluteFile()
1688     */

1689    public java.io.File JavaDoc getAbsoluteFile() {
1690        File enclArchive = this.enclArchive;
1691        if (enclArchive != null)
1692            enclArchive = (File) enclArchive.getAbsoluteFile();
1693        return detector.createFile(this, delegate.getAbsoluteFile(), enclArchive);
1694    }
1695
1696    /**
1697     * Similar to {@link #getAbsoluteFile()}, but removes any <code>"."</code>
1698     * and <code>".."</code> directories from the pathname wherever possible.
1699     * The result is similar to {@link #getCanonicalFile()}, but symbolic
1700     * links are not resolved.
1701     * This could be used if <code>getCanonicalFile()</code> throws an
1702     * IOException.
1703     *
1704     * @see #getNormalizedFile()
1705     */

1706    public File getNormalizedAbsoluteFile() {
1707        File enclArchive = this.enclArchive;
1708        if (enclArchive != null)
1709            enclArchive = enclArchive.getNormalizedAbsoluteFile();
1710        return detector.createFile(this, normalize(delegate.getAbsoluteFile()), enclArchive);
1711    }
1712
1713    /**
1714     * Removes any <code>"."</code> and <code>".."</code> directories from the
1715     * absolute pathname wherever possible.
1716     *
1717     * @return The normalized absolute pathname of this file as a {@link String}.
1718     *
1719     * @since TrueZIP 6.0
1720     */

1721    public String JavaDoc getNormalizedAbsolutePath() {
1722        return Paths.normalize(getAbsolutePath(), separatorChar);
1723    }
1724
1725    /**
1726     * Removes any <code>"."</code> and <code>".."</code> directories from the
1727     * pathname wherever possible.
1728     *
1729     * @return If this file is already normalized, it is returned.
1730     * Otherwise a new instance of this class is returned.
1731     */

1732    public File getNormalizedFile() {
1733        final java.io.File JavaDoc normalizedFile = normalize(this);
1734        if (normalizedFile == this)
1735            return this;
1736        assert normalizedFile != null;
1737        assert !(normalizedFile instanceof File);
1738        assert normalize(enclArchive) == enclArchive;
1739        return detector.createFile(this, normalizedFile, enclArchive);
1740    }
1741
1742    /**
1743     * Removes any <code>"."</code>, <code>".."</code> and empty directories
1744     * from the pathname wherever possible.
1745     *
1746     * @return The normalized pathname of this file as a {@link String}.
1747     *
1748     * @since TrueZIP 6.0
1749     */

1750    public String JavaDoc getNormalizedPath() {
1751        return Paths.normalize(getPath(), separatorChar);
1752    }
1753
1754    /**
1755     * Behaves like the superclass implementation, but actually either
1756     * returns <code>null</code> or a new instance of this class, so you can
1757     * safely cast it.
1758     *
1759     * @see java.io.File#getCanonicalFile() java.io.File.getCanonicalFile()
1760     */

1761    public java.io.File JavaDoc getCanonicalFile() throws IOException {
1762        File enclArchive = this.enclArchive;
1763        if (enclArchive != null)
1764            enclArchive = (File) enclArchive.getCanonicalFile();
1765        // Note: entry.getCanonicalFile() may change case!
1766
return detector.createFile(this, delegate.getCanonicalFile(), enclArchive);
1767    }
1768
1769    /**
1770     * This convenience method simply returns the canonical form of this
1771     * abstract pathname or the normalized absolute form if resolving the
1772     * prior fails.
1773     *
1774     * @return The canonical or absolute pathname of this file as a
1775     * <code>File</code> instance.
1776     */

1777    public final File getCanOrAbsFile() {
1778        File enclArchive = this.enclArchive;
1779        if (enclArchive != null)
1780            enclArchive = enclArchive.getCanOrAbsFile();
1781        return detector.createFile(this, getCanOrAbsFile(delegate), enclArchive);
1782    }
1783
1784    private static java.io.File JavaDoc getCanOrAbsFile(java.io.File JavaDoc file) {
1785        try {
1786            return file.getCanonicalFile();
1787        } catch (IOException failure) {
1788            final java.io.File JavaDoc parent = file.getParentFile();
1789            return normalize(parent != null
1790                    ? new java.io.File JavaDoc(getCanOrAbsFile(parent), file.getName())
1791                    : file.getAbsoluteFile());
1792        }
1793    }
1794
1795    /**
1796     * This convenience method simply returns the canonical form of this
1797     * abstract pathname or the normalized absolute form if resolving the
1798     * prior fails.
1799     *
1800     * @return The canonical or absolute pathname of this file as a
1801     * <code>String</code> instance.
1802     *
1803     * @since TrueZIP 6.0
1804     */

1805    public String JavaDoc getCanOrAbsPath() {
1806        return getCanOrAbsFile().getPath();
1807    }
1808
1809    /**
1810     * Returns <code>true</code> if and only if the path represented by this
1811     * instance ends with a suffix for an archive file.
1812     * <p>
1813     * Whether or not this is true depends solely on the {@link ArchiveDetector}
1814     * used to construct this instance.
1815     * If no <code>ArchiveDetector</code> was explicitly passed to the
1816     * constructor, {@link #getDefaultArchiveDetector()} is used.
1817     * <p>
1818     * Please note that no tests on the file's true state are performed!
1819     * If you need to know whether this file is really an archive file
1820     * (and the correct password has been entered in case it's an RAES
1821     * encrypted ZIP file), you should call {@link #isDirectory}, too.
1822     * This will automount the virtual file system from the archive file and
1823     * return <code>true</code> if and only if it's a valid archive file.
1824     *
1825     * @see <a HREF="#false_positives">Identifying Archive Files and False Positives</a>
1826     * @see #isDirectory
1827     * @see #isEntry
1828     */

1829    public final boolean isArchive() {
1830        return innerArchive == this;
1831    }
1832
1833    /**
1834     * Returns <code>true</code> if and only if the path represented by this
1835     * instance names an archive file as an ancestor.
1836     * <p>
1837     * Whether or not this is true depends solely on the {@link ArchiveDetector}
1838     * used to construct this instance.
1839     * If no <code>ArchiveDetector</code> was explicitly passed to the
1840     * constructor, {@link #getDefaultArchiveDetector()} is used.
1841     * <p>
1842     * Please note that no tests on the file's true state are performed!
1843     * If you need to know whether this file is really an entry in an archive
1844     * file (and the correct password has been entered in case it's an RAES
1845     * encrypted ZIP file), you should call
1846     * {@link #getParentFile}.{@link #isDirectory}, too.
1847     * This will automount the virtual file system from the archive file and
1848     * return <code>true</code> if and only if it's a valid archive file.
1849     *
1850     * @see #isArchive
1851     */

1852    public final boolean isEntry() {
1853        return enclEntryName != null;
1854    }
1855
1856    /**
1857     * Returns the innermost archive file in this pathname.
1858     * I.e. if this object is a archive file, then this method returns
1859     * this object.
1860     * If this object is a file or directory located within a
1861     * archive file, then this methods returns the file representing the
1862     * enclosing archive file, or <code>null</code> otherwise.
1863     * <p>
1864     * This method always returns an undotified pathname, i.e. all
1865     * occurences of <code>"."</code> and <code>".."</code> in the pathname are
1866     * removed according to their meaning wherever possible.
1867     * <p>
1868     * In order to support nesting levels greater than one, this method returns
1869     * a <code>File</code>, i.e. it could be an entry within another archive
1870     * file again.
1871     */

1872    public final File getInnerArchive() {
1873        return innerArchive;
1874    }
1875
1876    /**
1877     * Returns the entry name in the innermost archive file.
1878     * I.e. if this object is a archive file, then this method returns
1879     * the empty string <code>""</code>.
1880     * If this object is a file or directory located within an
1881     * archive file, then this method returns the relative pathname of
1882     * the entry in the enclosing archive file separated by the entry
1883     * separator character <code>'/'</code>, or <code>null</code>
1884     * otherwise.
1885     * <p>
1886     * This method always returns an undotified pathname, i.e. all
1887     * occurences of <code>"."</code> and <code>".."</code> in the pathname are
1888     * removed according to their meaning wherever possible.
1889     */

1890    public final String JavaDoc getInnerEntryName() {
1891        return innerEntryName;
1892    }
1893
1894    /**
1895     * Returns the enclosing archive file in this pathname.
1896     * I.e. if this object is an entry located within an archive file,
1897     * then this method returns the file representing the enclosing archive
1898     * file, or <code>null</code> otherwise.
1899     * <p>
1900     * This method always returns an undotified pathname, i.e. all
1901     * occurences of <code>"."</code> and <code>".."</code> in the pathname are
1902     * removed according to their meaning wherever possible.
1903     * <p>
1904     * In order to support nesting levels greater than one, this method returns
1905     * a <code>File</code>, i.e. it could be an entry within another archive
1906     * file again.
1907     */

1908    public final File getEnclArchive() {
1909        return enclArchive;
1910    }
1911
1912    /**
1913     * Returns the entry pathname in the enclosing archive file.
1914     * I.e. if this object is an entry located within a archive file,
1915     * then this method returns the relative pathname of the entry in the
1916     * enclosing archive file separated by the entry separator character
1917     * <code>'/'</code>, or <code>null</code> otherwise.
1918     * <p>
1919     * This method always returns an undotified pathname, i.e. all
1920     * occurences of <code>"."</code> and <code>".."</code> in the pathname are
1921     * removed according to their meaning wherever possible.
1922     */

1923    public final String JavaDoc getEnclEntryName() {
1924        return enclEntryName;
1925    }
1926
1927    /**
1928     * Returns the {@link ArchiveDetector} that was used to construct this
1929     * object - never <code>null</code>.
1930     */

1931    public final ArchiveDetector getArchiveDetector() {
1932        return detector;
1933    }
1934
1935    /**
1936     * Returns the legacy {@link java.io.File java.io.File} object to which
1937     * some methods of this class delegate if this object does not represent
1938     * an archive file or an entry in an archive file.
1939     * This is required for cooperation with some other packages which
1940     * inherit from the super class, too, like
1941     * <code>sun.awt.shell.ShellFolder</code>.
1942     * <p>
1943     * In case you want to convert an instance of this class which recognized
1944     * the leaf of its path as an archive file to a file instance which
1945     * doesn't recognize this archive file, use the following code instead:
1946     * <code>ArchiveDetector.NULL.createFile((File) file.getParentFile(), file.getName())</code>
1947     *
1948     * @return An instance of the {@link java.io.File java.io.File} class or
1949     * one of its subclasses, but never an instance of this class or
1950     * its subclasses and never <code>null</code>.
1951     * @deprecated This method exists for technical reasons only and is
1952     * not part of the public API. Do <em>not</em> use it!
1953     */

1954    public final java.io.File JavaDoc getDelegate() {
1955        return delegate;
1956    }
1957
1958    /**
1959     * Returns the archive controller for this file if this is an archive file,
1960     * or <code>null</code> otherwise.
1961     */

1962    final ArchiveController getArchiveController() {
1963        return controller;
1964    }
1965
1966    /**
1967     * Returns <code>true</code> if and only if the path represented
1968     * by this instance is a direct or indirect parent of the path
1969     * represented by the specified <code>file</code>.
1970     * <p>
1971     * <b>Note:</b>
1972     * <ul>
1973     * <li>This method uses the canonical pathnames or, if failing to
1974     * canonicalize the paths, at least the normalized absolute
1975     * pathnames in order to compute reliable results.
1976     * <li>This method does <em>not</em> test the actual status
1977     * of any file or directory in the file system.
1978     * It just tests the paths.
1979     * </ul>
1980     *
1981     * @param file The path to test for being a child of this path.
1982     *
1983     * @throws NullPointerException If the parameter is <code>null</code>.
1984     */

1985    public boolean isParentOf(java.io.File JavaDoc file) {
1986        // Canonicalise both files and call the actual implementation
1987
File canOrAbsFile = getCanOrAbsFile();
1988        try {
1989            return containsImpl(canOrAbsFile, file.getCanonicalFile().getParentFile());
1990        } catch (IOException exc) {
1991            return containsImpl(canOrAbsFile, normalize(file.getAbsoluteFile()).getParentFile());
1992        }
1993    }
1994
1995    /**
1996     * Returns <code>true</code> if and only if the path represented
1997     * by this instance contains the path represented by the specified
1998     * <code>file</code>,
1999     * where a path is said to contain another path if and only
2000     * if it is equal or a parent of the other path.
2001     * <p>
2002     * <b>Note:</b>
2003     * <ul>
2004     * <li>This method uses the canonical pathnames or, if failing to
2005     * canonicalize the paths, at the least normalized absolute
2006     * pathnames in order to compute reliable results.
2007     * <li>This method does <em>not</em> test the actual status
2008     * of any file or directory in the file system.
2009     * It just tests the paths.
2010     * </ul>
2011     *
2012     * @param file The path to test for being contained by this path.
2013     *
2014     * @throws NullPointerException If the parameter is <code>null</code>.
2015     *
2016     * @since TrueZIP 5.1
2017     */

2018    public boolean contains(java.io.File JavaDoc file) {
2019        return contains(this, file);
2020    }
2021
2022    /**
2023     * Returns <code>true</code> if and only if the path represented
2024     * by <code>a</code> contains the path represented by <code>b</code>,
2025     * where a path is said to contain another path if and only
2026     * if it is equal or a parent of the other path.
2027     * <p>
2028     * <b>Note:</b>
2029     * <ul>
2030     * <li>This method uses the canonical pathnames or, if failing to
2031     * canonicalize the paths, at least the normalized absolute
2032     * pathnames in order to compute reliable results.
2033     * <li>This method does <em>not</em> test the actual status
2034     * of any file or directory in the file system.
2035     * It just tests the paths.
2036     * </ul>
2037     *
2038     * @param a The path to test for containing <code>b</code>.
2039     * @param b The path to test for being contained by <code>a</code>.
2040     * @throws NullPointerException If any parameter is <code>null</code>.
2041     * @since TrueZIP 5.1
2042     */

2043    public static boolean contains(java.io.File JavaDoc a, java.io.File JavaDoc b) {
2044        a = getCanOrAbsFile(a);
2045        b = getCanOrAbsFile(b);
2046        return containsImpl(a, b);
2047    }
2048
2049    private static boolean containsImpl(
2050            final java.io.File JavaDoc a,
2051            final java.io.File JavaDoc b) {
2052        if (b == null)
2053            return false;
2054
2055        String JavaDoc aPath = a.getPath();
2056        String JavaDoc bPath = b.getPath();
2057        // Windows and MacOS are case preserving, however UNIX is case
2058
// sensitive. If we meet an unknown platform, we assume that it is
2059
// case preserving, which means that two pathnames are considered
2060
// equal if they differ by case only.
2061
// In the context of this method, this implements a conservative
2062
// (in-dubio-contra-reo) parameter check.
2063
if (separatorChar != '/') {
2064            aPath = aPath.toLowerCase();
2065            bPath = bPath.toLowerCase();
2066        }
2067
2068        if (!bPath.startsWith(aPath))
2069            return false;
2070        final int aLength = aPath.length();
2071        final int bLength = bPath.length();
2072        if (aLength == bLength)
2073            return true;
2074        else if (aLength < bLength)
2075            return bPath.charAt(aLength) == separatorChar;
2076        else
2077            return false;
2078
2079        // Old, somewhat slower implementation
2080
/*if (a.equals(b))
2081            return true;
2082        return containsImpl(a, b.getParentFile());*/

2083    }
2084
2085    /**
2086     * Returns <code>true</code> if and only if this file denotes a file system
2087     * root or a UNC (if running on the Windows platform).
2088     */

2089    public boolean isFileSystemRoot() {
2090        File canOrAbsFile = getCanOrAbsFile();
2091        return roots.contains(canOrAbsFile) || isUNC(canOrAbsFile.getPath());
2092    }
2093
2094    /**
2095     * Returns <code>true</code> if and only if this file denotes a UNC.
2096     * Note that this may be only relevant on the Windows platform.
2097     */

2098    public boolean isUNC() {
2099        return isUNC(getCanOrAbsFile().getPath());
2100    }
2101
2102    // TODO: Make this private!
2103
/**
2104     * Returns <code>true</code> if and only if the given path is a UNC.
2105     * Note that this may be only relevant on the Windows platform.
2106     *
2107     * @deprecated This method will be made private in the next major version.
2108     */

2109    protected static final boolean isUNC(final String JavaDoc path) {
2110        return path.startsWith(uncPrefix) && path.indexOf(separatorChar, 2) > 2;
2111    }
2112
2113    public int hashCode() {
2114        // Note that we cannot just return the pathnames' hash code:
2115
// Some platforms consider the case of files when comparing file
2116
// paths and some don't.
2117
// However, the entries INSIDE a archive file ALWAYS consider
2118
// case.
2119
// In addition, on Mac OS the Java implementation is not consistent
2120
// with the filesystem, i.e. the fs ignores case whereas
2121
// java.io.File.equals(...) and java.io.File.hashcode() consider case.
2122
// The following code distinguishes these cases.
2123
final File enclArchive = this.enclArchive;
2124        if (enclArchive != null) {
2125            // This file IS enclosed in a archive file.
2126
return enclArchive.hashCode() + enclEntryName.hashCode();
2127        } else {
2128            // This file is NOT enclosed in a archive file.
2129
return delegate.hashCode();
2130        }
2131    }
2132
2133    /**
2134     * Tests this abstract pathname for equality with the given object.
2135     * Returns <code>true</code> if and only if the argument is not
2136     * <code>null</code> and is an abstract pathname that denotes the same
2137     * abstract pathname for a file or directory as this abstract pathname.
2138     * <p>
2139     * If the given file is not an instance of this class, the call is
2140     * forwarded to the superclass in order to ensure the required symmetry
2141     * of {@link Object#equals(Object)}.
2142     * <p>
2143     * Otherwise, whether or not two abstract pathnames are equal depends upon the
2144     * underlying operating and file system:
2145     * On UNIX systems, alphabetic case is significant in comparing pathnames.
2146     * On Microsoft Windows systems it is not unless the pathname denotes
2147     * an entry in an archive file. In the latter case, the left part of the
2148     * pathname up to the (leftmost) archive file is compared ignoring case
2149     * while the remainder (the entry name) is compared considering case.
2150     * This case distinction allows an application on Windows to deal with
2151     * archive files generated on other platforms which may contain different
2152     * entry with names that just differ in case (like e.g. hello.txt and
2153     * HELLO.txt).
2154     * <p>
2155     * Thus, on Windows the following assertions all succeed:
2156     * <pre>
2157     * File a, b;
2158     * a = new File("c:\\any.txt");
2159     * b = new File("C:\\ANY.TXT");
2160     * assert a.equals(b);
2161     * assert b.equals(a);
2162     * a = new File("c:\\any.zip\\test.txt"),
2163     * b = new File("C:\\ANY.ZIP\\test.txt");
2164     * assert a.equals(b);
2165     * assert b.equals(a);
2166     * a = new File("c:/any.zip/test.txt");
2167     * b = new File("C:\\ANY.ZIP\\test.txt");
2168     * assert a.equals(b);
2169     * assert b.equals(a);
2170     * a = new File("c:\\any.zip\\test.txt");
2171     * b = new File("C:/ANY.ZIP/test.txt");
2172     * assert a.equals(b);
2173     * assert b.equals(a);
2174     * a = new File("c:/any.zip/test.txt");
2175     * b = new File("C:/ANY.ZIP/test.txt");
2176     * assert a.equals(b);
2177     * assert b.equals(a);
2178     * a = new File("\\\\localhost\\any.zip\\test.txt");
2179     * b = new File("\\\\LOCALHOST\\ANY.ZIP\\test.txt");
2180     * assert a.equals(b);
2181     * assert b.equals(a);
2182     * a = new File("//localhost/any.zip/test.txt");
2183     * b = new File("\\\\LOCALHOST\\ANY.ZIP\\test.txt");
2184     * assert a.equals(b);
2185     * assert b.equals(a);
2186     * a = new File("\\\\localhost\\any.zip\\test.txt");
2187     * b = new File("//LOCALHOST/ANY.ZIP/test.txt");
2188     * assert a.equals(b);
2189     * assert b.equals(a);
2190     * a = new File("//localhost/any.zip/test.txt");
2191     * b = new File("//LOCALHOST/ANY.ZIP/test.txt");
2192     * assert a.equals(b);
2193     * assert b.equals(a);
2194     * a = new File("c:\\any.zip\\test.txt");
2195     * b = new File("c:\\any.zip\\TEST.TXT");
2196     * assert !a.equals(b); // two different entries in same ZIP file!
2197     * assert !b.equals(a);
2198     * </pre>
2199     *
2200     * @param other The object to be compared with this abstract pathname.
2201     *
2202     * @return <code>true</code> if and only if the objects are equal,
2203     * <code>false</code> otherwise
2204     *
2205     * @see #compareTo(Object)
2206     * @see Object#equals(Object)
2207     */

2208    public boolean equals(final Object JavaDoc other) {
2209        if (other instanceof File)
2210            return compareTo((File) other) == 0;
2211        return super.equals(other); // don't use entry - would break symmetry requirement!
2212
}
2213
2214    /**
2215     * Compares this file's pathname to the given file's pathname.
2216     * <p>
2217     * If the given file is not an instance of this class, the call is
2218     * forwarded to the superclass in order to ensure the required symmetry
2219     * of {@link Comparable#compareTo(Object)}.
2220     * <p>
2221     * Otherwise, whether or not two abstract pathnames compare equal depends
2222     * upon the underlying operating and file system:
2223     * On UNIX platforms, alphabetic case is significant in comparing pathnames.
2224     * On the Windows platform it is not unless the pathname denotes
2225     * an entry in an archive file. In the latter case, the left part of the
2226     * pathname up to the (leftmost) archive file is compared in platform
2227     * dependent manner (hence ignoring case) while the remainder (the entry
2228     * name) is compared considering case.
2229     * This case distinction allows an application on the Windows platform to
2230     * deal with archive files generated on other platforms which may contain
2231     * different entries with names that just differ in case
2232     * (like e.g. <code>"hello.txt"</code> and <code>"HELLO.txt"</code>).
2233     *
2234     * @param other The file to be compared with this abstract pathname.
2235     *
2236     * @return A negative integer, zero, or a positive integer as this object
2237     * is less than, equal to, or greater than the given file.
2238     *
2239     * @see #equals(Object)
2240     * @see Comparable#compareTo(Object)
2241     */

2242    public int compareTo(java.io.File JavaDoc other) {
2243        if (this == other)
2244            return 0;
2245
2246        if (!(other instanceof File)) {
2247            // Degrade this file to a plain file in order to ensure
2248
// sgn(this.compareTo(other)) == -sgn(other.compareTo(this)).
2249
return super.compareTo(other); // don't use entry - would break antisymmetry requirement!
2250
}
2251
2252        final File file = (File) other;
2253
2254        // Note that we cannot just compare the pathnames:
2255
// Some platforms consider the case of files when comparing file
2256
// paths and some don't.
2257
// However, the entries INSIDE a archive file ALWAYS consider
2258
// case.
2259
// The following code distinguishes these cases.
2260
final File enclArchive = this.enclArchive;
2261        if (enclArchive != null) {
2262            // This file IS enclosed in a archive file.
2263
final File fileEnclArchive = file.enclArchive;
2264            if (fileEnclArchive != null) {
2265                // The given file IS enclosed in a archive file, too.
2266
int ret = enclArchive.compareTo(fileEnclArchive);
2267                if (ret == 0) {
2268                    // Now that the paths of the enclosing archive
2269
// files compare equal, let's compare the entry names.
2270
ret = enclEntryName.compareTo(file.enclEntryName);
2271                }
2272
2273                return ret;
2274            }
2275        }
2276
2277        // Degrade this file to a plain file in order to ensure
2278
// sgn(this.compareTo(other)) == -sgn(other.compareTo(this)).
2279
return super.compareTo(other); // don't use entry - would break antisymmetry requirement!
2280
}
2281
2282    /**
2283     * Returns The top level archive file in the pathname or <code>null</code>
2284     * if this pathname does not denote an archive.
2285     * A top level archive is not enclosed in another archive.
2286     * If this does not return <code>null</code>, this denotes the longest
2287     * part of the pathname which actually may (but does not need to) exist
2288     * as a regular file in the native file system.
2289     */

2290    public File getTopLevelArchive() {
2291        final File enclArchive = this.enclArchive;
2292        return enclArchive != null
2293                ? enclArchive.getTopLevelArchive()
2294                : innerArchive;
2295    }
2296
2297    public String JavaDoc getAbsolutePath() {
2298        return delegate.getAbsolutePath();
2299    }
2300
2301    public String JavaDoc getCanonicalPath() throws IOException {
2302        return delegate.getCanonicalPath();
2303    }
2304
2305    public String JavaDoc getName() {
2306        return delegate.getName();
2307    }
2308
2309    public String JavaDoc getParent() {
2310        return delegate.getParent();
2311    }
2312
2313    public String JavaDoc getPath() {
2314        return delegate.getPath();
2315    }
2316
2317    public boolean isAbsolute() {
2318        return delegate.isAbsolute();
2319    }
2320
2321    public boolean isHidden() {
2322        return delegate.isHidden();
2323    }
2324
2325    public String JavaDoc toString() {
2326        return delegate.toString();
2327    }
2328
2329    public java.net.URI JavaDoc toURI() {
2330        return delegate.toURI();
2331    }
2332
2333    public java.net.URL JavaDoc toURL() throws java.net.MalformedURLException JavaDoc {
2334        return delegate.toURL();
2335    }
2336
2337    /**
2338     * Throws an <code>ArchiveNotFoundException</code> if and only if this
2339     * file is a true archive file, not just a false positive, including
2340     * RAES encrypted ZIP files for which key prompting has been cancelled
2341     * or disabled.
2342     */

2343    final void ensureNotVirtualDirectory(final String JavaDoc prefix)
2344    throws ArchiveNotFoundException {
2345        if (isArchive() && (isDirectory() || (exists() && !isFile()))) {
2346            String JavaDoc msg = "virtual directory";
2347            if (prefix != null)
2348                msg = prefix + " " + msg;
2349            throw getArchiveController().new ArchiveNotFoundException(msg);
2350        }
2351    }
2352
2353    //
2354
// File system operations:
2355
//
2356

2357    /**
2358     * This file system operation <em>is</em> atomic.
2359     *
2360     * @see <a HREF="#false_positives">Identifying Archive Files and False Positives</a>
2361     * @see java.io.File#exists java.io.File.exists()
2362     */

2363    public boolean exists() {
2364        if (enclArchive == null)
2365            return delegate.exists();
2366        try {
2367            return enclArchive.getArchiveController().exists(enclEntryName);
2368        } catch (ArchiveController.FalsePositiveNativeException failure) {
2369            return delegate.exists();
2370        }
2371    }
2372
2373    /**
2374     * Similar to its super class implementation, but returns
2375     * <code>false</code> for a valid archive file.
2376     * <p>
2377     * This file system operation <em>is</em> atomic.
2378     *
2379     * @see <a HREF="#false_positives">Identifying Archive Files and False Positives</a>
2380     * @see java.io.File#isFile java.io.File.isFile()
2381     */

2382    public boolean isFile() {
2383        if (innerArchive == null)
2384            return delegate.isFile();
2385        try {
2386            return innerArchive.getArchiveController().isFile(innerEntryName);
2387        } catch (ArchiveController.FalsePositiveNativeException failure) {
2388            if (isArchive()
2389                    && failure.getCause() instanceof FileNotFoundException) {
2390                // This appears to be an archive file, but we could not
2391
// access it.
2392
// One of the many reasons may be that the target file is an
2393
// RAES encrypted ZIP file for which password prompting has
2394
// been disabled or cancelled by the user.
2395
// In any of these cases we do not want this package to treat
2396
// this file like a plain file.
2397
// For the forementioned case, this implies that the RAES
2398
// encrypted file is identified as a special file, i.e.
2399
// exists() returns true, while both isFile() and isDirectory()
2400
// return false.
2401
return false;
2402            } else {
2403                return delegate.isFile();
2404            }
2405        }
2406    }
2407
2408    /**
2409     * Similar to its super class implementation, but returns
2410     * <code>true</code> for a valid archive file.
2411     * <p>
2412     * In case an RAES encrypted ZIP file is tested which is accessed for the
2413     * first time, the user is prompted for the password (if password based
2414     * encryption is used).
2415     * Note that this is not the only method which would prompt the user for
2416     * a password: For example, {@link #length} would prompt the user and
2417     * return <code>0</code> unless the user cancels the prompting or the
2418     * file is a false positive archive file.
2419     * <p>
2420     * This file system operation <em>is</em> atomic.
2421     *
2422     * @see <a HREF="#false_positives">Identifying Archive Files and False Positives</a>
2423     * @see java.io.File#isDirectory java.io.File.isDirectory()
2424     */

2425    public boolean isDirectory() {
2426        if (innerArchive == null)
2427            return delegate.isDirectory();
2428        try {
2429            return innerArchive.getArchiveController().isDirectory(innerEntryName);
2430        } catch (ArchiveController.FalsePositiveNativeException failure) {
2431            return delegate.isDirectory();
2432        }
2433    }
2434
2435    /**
2436     * Returns an icon for this file or directory if it is in <i>open</i>
2437     * state for {@link de.schlichtherle.io.swing.JFileTree}
2438     * or <code>null</code> if the default should be used.
2439     */

2440    public Icon JavaDoc getOpenIcon() {
2441        if (innerArchive == null)
2442            return null;
2443        try {
2444            return innerArchive.getArchiveController().getOpenIcon(innerEntryName);
2445        } catch (ArchiveController.FalsePositiveNativeException failure) {
2446            return null;
2447        }
2448    }
2449
2450    /**
2451     * Returns an icon for this file or directory if it is in <i>closed</i>
2452     * state for {@link de.schlichtherle.io.swing.JFileTree}
2453     * or <code>null</code> if the default should be used.
2454     */

2455    public Icon JavaDoc getClosedIcon() {
2456        if (innerArchive == null)
2457            return null;
2458        try {
2459            return innerArchive.getArchiveController().getClosedIcon(innerEntryName);
2460        } catch (ArchiveController.FalsePositiveNativeException failure) {
2461            return null;
2462        }
2463    }
2464
2465    public boolean canRead() {
2466        // More thorough test than exists
2467
if (innerArchive == null)
2468            return delegate.canRead();
2469        try {
2470            return innerArchive.getArchiveController().canRead(innerEntryName);
2471        } catch (ArchiveController.FalsePositiveNativeException failure) {
2472            return delegate.canRead();
2473        }
2474    }
2475
2476    public boolean canWrite() {
2477        if (innerArchive == null)
2478            return delegate.canWrite();
2479        try {
2480            return innerArchive.getArchiveController().canWrite(innerEntryName);
2481        } catch (ArchiveController.FalsePositiveNativeException failure) {
2482            return delegate.canWrite();
2483        }
2484    }
2485
2486    /**
2487     * Returns <code>true</code> if the given file exists or can be created
2488     * and at least one byte can be successfully written to it - the file is
2489     * restored to its previous state afterwards.
2490     * This is a much stronger test than {@link #canWrite()}.
2491     * <p>
2492     * Please note that if the file is actually open for reading or other
2493     * activities this method may not be able to reset the last modification
2494     * time of the file after testing, in which case <code>false</code> is
2495     * returned.
2496     * This is known to apply to the Windows platform, but not on Unix
2497     * platforms.
2498     */

2499    static boolean isWritableOrCreatable(final java.io.File JavaDoc file) {
2500        try {
2501            if (!file.exists()) {
2502                final boolean created = file.createNewFile();
2503                boolean ok = isWritableOrCreatable(file);
2504                if (created && !file.delete())
2505                    ok = false; // be conservative!
2506
return ok;
2507            } else if (file.canWrite()) {
2508                // Some operating and file system combinations make File.canWrite()
2509
// believe that the file is writable although it's not.
2510
// We are not that gullible, so let's test this...
2511
final long time = file.lastModified();
2512                if (!file.setLastModified(time + 1)) {
2513                    // This may happen on Windows and normally means that
2514
// somebody else has opened this file
2515
// (regardless of read or write mode).
2516
// Be conservative: We don't allow writing to this file!
2517
return false;
2518                }
2519
2520                boolean ok;
2521                try {
2522                    // Open the file for reading and writing, requiring any
2523
// update to its contents to be written to the filesystem
2524
// synchronously.
2525
// As Dr. Simon White from Catalysoft, Cambridge, UK reported,
2526
// "rws" does NOT work on Mac OS X with Apple's Java 1.5
2527
// Release 1 (equivalent to Sun's Java 1.5.0_02), however
2528
// it DOES work with Apple's Java 1.5 Release 3.
2529
// He also confirmed that "rwd" works on Apple's
2530
// Java 1.5 Release 1, so we use this instead.
2531
// Thank you very much for spending the time to fix this
2532
// issue, Dr. White!
2533
final RandomAccessFile raf = new RandomAccessFile(file, "rwd");
2534                    try {
2535                        final boolean empty;
2536                        int octet = raf.read();
2537                        if (octet == -1) {
2538                            octet = 0; // assume first byte is 0
2539
empty = true;
2540                        } else {
2541                            empty = false;
2542                        }
2543
2544                        // Let's test if we can (over)write the first byte.
2545
raf.seek(0);
2546                        raf.write((octet ^ -1) & 0xFF); // write complement
2547
try {
2548                            // Rewrite original content and check success.
2549
raf.seek(0);
2550                            raf.write(octet);
2551                            raf.seek(0);
2552                            final int check = raf.read();
2553                            // This should always return true unless the storage
2554
// device is faulty.
2555
ok = octet == check;
2556                        } finally {
2557                            if (empty)
2558                                raf.setLength(0);
2559                        }
2560                    } finally {
2561                        raf.close();
2562                    }
2563                } finally {
2564                    if (!file.setLastModified(time)) {
2565                        // This may happen on Windows and normally means that
2566
// somebody else has opened this file meanwhile
2567
// (regardless of read or write mode).
2568
// Be conservative: We don't allow (further) writing to
2569
// this file!
2570
ok = false;
2571                    }
2572                }
2573                return ok;
2574            } else { // if (file.exists() && !file.canWrite()) {
2575
return false;
2576            }
2577        } catch (IOException failure) {
2578            return false; // don't allow writing if anything goes wrong!
2579
}
2580    }
2581
2582    /**
2583     * Like the super class implementation, but is aware of archive
2584     * files in its path.
2585     * For entries in a archive file, this is effectively a no-op:
2586     * The method will only return <code>true</code> if the entry exists and the
2587     * archive file was mounted read only.
2588     * <p>
2589     * This file system operation <em>is</em> atomic.
2590     */

2591    public boolean setReadOnly() {
2592        if (innerArchive == null)
2593            return delegate.setReadOnly();
2594        try {
2595            return innerArchive.getArchiveController().setReadOnly(innerEntryName);
2596        } catch (ArchiveController.FalsePositiveNativeException failure) {
2597            return delegate.setReadOnly();
2598        }
2599    }
2600
2601    /**
2602     * Returns the (uncompressed) length of the file.
2603     * The length returned of a valid archive file is <code>0</code> in order
2604     * to correctly emulate virtual directories across all platforms.
2605     * <p>
2606     * In case an RAES encrypted ZIP file is tested which is accessed for the
2607     * first time, the user is prompted for the password (if password based
2608     * encryption is used).
2609     * Note that this is not the only method which would prompt the user for
2610     * a password: For example, {@link #isDirectory} would prompt the user and
2611     * return <code>true</code> unless the user cancels the prompting or the
2612     * file is a false positive archive file.
2613     * <p>
2614     * This file system operation <em>is</em> atomic.
2615     *
2616     * @see <a HREF="#false_positives">Identifying Archive Files and False Positives</a>
2617     * @see java.io.File#length java.io.File.length()
2618     */

2619    public long length() {
2620        if (innerArchive == null)
2621            return delegate.length();
2622        try {
2623            return innerArchive.getArchiveController().length(innerEntryName);
2624        } catch (ArchiveController.FalsePositiveNativeException failure) {
2625            return delegate.length();
2626        }
2627    }
2628
2629    /**
2630     * Returns a <code>long</code> value representing the time this file was last
2631     * modified, measured in milliseconds since the epoch (00:00:00 GMT,
2632     * January 1, 1970), or <code>0L</code> if the file does not exist or if an
2633     * I/O error occurs or if this is a ghost directory in a archive
2634     * file.
2635     * <p>
2636     * This file system operation <em>is</em> atomic.
2637     *
2638     * @see <a HREF="package.html">Package description for more information
2639     * about ghost directories.</a>
2640     */

2641    public long lastModified() {
2642        if (innerArchive == null)
2643            return delegate.lastModified();
2644        try {
2645            return innerArchive.getArchiveController().lastModified(innerEntryName);
2646        } catch (ArchiveController.FalsePositiveNativeException failure) {
2647            return delegate.lastModified();
2648        }
2649    }
2650
2651    /**
2652     * Behaves similar to the super class.
2653     * <p>
2654     * Please note that calling this method causes a severe performance
2655     * penalty if the file is an entry in a archive file which has
2656     * just been written (such as after a normal copy operation).
2657     * If you want to copy a file's contents as well as its last modification
2658     * time, use {@link #archiveCopyFrom(java.io.File)} or
2659     * {@link #archiveCopyTo(java.io.File)} instead.
2660     * <p>
2661     * This file system operation <em>is</em> atomic.
2662     *
2663     * @see #archiveCopyFrom(java.io.File)
2664     * @see #archiveCopyTo(java.io.File)
2665     * @see java.io.File#setLastModified(long)
2666     */

2667    public boolean setLastModified(final long time) {
2668        if (innerArchive == null)
2669            return delegate.setLastModified(time);
2670        try {
2671            return innerArchive.getArchiveController().setLastModified(
2672                    innerEntryName, time);
2673        } catch (ArchiveController.FalsePositiveNativeException failure) {
2674            return delegate.setLastModified(time);
2675        }
2676    }
2677
2678    /**
2679     * Returns the names of the members in this directory in a newly
2680     * created array.
2681     * The returned array is <em>not</em> sorted.
2682     * This is the most efficient list method.
2683     * <p>
2684     * <b>Note:</b> Archive entries with absolute pathnames are ignored by
2685     * this method and are never returned.
2686     * <p>
2687     * This file system operation <em>is</em> atomic.
2688     */

2689    public String JavaDoc[] list() {
2690        if (innerArchive == null)
2691            return delegate.list();
2692        try {
2693            return innerArchive.getArchiveController().list(innerEntryName);
2694        } catch (ArchiveController.FalsePositiveNativeException failure) {
2695            return delegate.list();
2696        }
2697    }
2698
2699    /**
2700     * Returns the names of the members in this directory which are
2701     * accepted by <code>filenameFilter</code> in a newly created array.
2702     * The returned array is <em>not</em> sorted.
2703     * <p>
2704     * <b>Note:</b> Archive entries with absolute pathnames are ignored by
2705     * this method and are never returned.
2706     * <p>
2707     * This file system operation <em>is</em> atomic.
2708     *
2709     * @return <code>null</code> if this is not a directory or an archive file,
2710     * a valid (but maybe empty) array otherwise.
2711     */

2712    public String JavaDoc[] list(final FilenameFilter filenameFilter) {
2713        if (innerArchive == null)
2714            return delegate.list(filenameFilter);
2715        try {
2716            return innerArchive.getArchiveController().list(
2717                    innerEntryName,
2718                    filenameFilter,
2719                    this);
2720        } catch (ArchiveController.FalsePositiveNativeException failure) {
2721            return delegate.list(filenameFilter);
2722        }
2723    }
2724
2725    /**
2726     * Equivalent to {@link #listFiles(FilenameFilter, FileFactory)
2727     * listFiles((FilenameFilter) null, getArchiveDetector())}.
2728     */

2729    public java.io.File JavaDoc[] listFiles() {
2730        return listFiles((FilenameFilter) null, detector);
2731    }
2732
2733    /**
2734     * Returns <code>File</code> objects for the members in this directory
2735     * in a newly created array.
2736     * The returned array is <em>not</em> sorted.
2737     * <p>
2738     * Since TrueZIP 6.4, the returned array is an array of this class.
2739     * Previously, the returned array was an array of <code>java.io.File</code>
2740     * which solely contained instances of this class.
2741     * <p>
2742     * Note that archive entries with absolute pathnames are ignored by this
2743     * method and are never returned.
2744     * <p>
2745     * This file system operation <em>is</em> atomic.
2746     *
2747     * @param factory The factory used to create the member file of this
2748     * directory.
2749     * This could be an {@link ArchiveDetector} in order to detect any
2750     * archives by the member file names.
2751     * @return <code>null</code> if this is not a directory or an archive file,
2752     * a valid (but maybe empty) array otherwise.
2753     */

2754    public File[] listFiles(final FileFactory factory) {
2755        return listFiles((FilenameFilter) null, factory);
2756    }
2757
2758    /**
2759     * Equivalent to {@link #listFiles(FilenameFilter, FileFactory)
2760     * listFiles(filenameFilter, getArchiveDetector())}.
2761     */

2762    public java.io.File JavaDoc[] listFiles(final FilenameFilter filenameFilter) {
2763        return listFiles(filenameFilter, detector);
2764    }
2765
2766    /**
2767     * Returns <code>File</code> objects for the members in this directory
2768     * which are accepted by <code>filenameFilter</code> in a newly created
2769     * array.
2770     * The returned array is <em>not</em> sorted.
2771     * <p>
2772     * Since TrueZIP 6.4, the returned array is an array of this class.
2773     * Previously, the returned array was an array of <code>java.io.File</code>
2774     * which solely contained instances of this class.
2775     * <p>
2776     * Note that archive entries with absolute pathnames are ignored by this
2777     * method and are never returned.
2778     * <p>
2779     * This file system operation <em>is</em> atomic.
2780     *
2781     * @param factory The factory used to create the member file of this
2782     * directory.
2783     * This could be an {@link ArchiveDetector} in order to detect any
2784     * archives by the member file names.
2785     * @return <code>null</code> if this is not a directory or an archive file,
2786     * a valid (but maybe empty) array otherwise.
2787     */

2788    public File[] listFiles(
2789            final FilenameFilter filenameFilter,
2790            final FileFactory factory) {
2791        if (innerArchive == null)
2792            return convert(delegate.listFiles(filenameFilter), factory);
2793        try {
2794            return innerArchive.getArchiveController().listFiles(
2795                    innerEntryName, filenameFilter, this, factory);
2796        } catch (ArchiveController.FalsePositiveNativeException failure) {
2797            return convert(delegate.listFiles(filenameFilter), factory);
2798        }
2799    }
2800
2801    private static File[] convert(
2802            final java.io.File JavaDoc[] files,
2803            final FileFactory factory) {
2804        if (files == null)
2805            return null;
2806
2807        File[] results = new File[files.length];
2808        for (int i = files.length; 0 <= --i; )
2809            results[i] = factory.createFile(files[i]);
2810
2811        return results;
2812    }
2813
2814    /**
2815     * Equivalent to {@link #listFiles(FileFilter, FileFactory)
2816     * listFiles(fileFilter, getArchiveDetector())}.
2817     */

2818    public final java.io.File JavaDoc[] listFiles(final FileFilter fileFilter) {
2819        return listFiles(fileFilter, detector);
2820    }
2821
2822    /**
2823     * Returns <code>File</code> objects for the members in this directory
2824     * which are accepted by <code>fileFilter</code> in a newly created array.
2825     * The returned array is <em>not</em> sorted.
2826     * <p>
2827     * Since TrueZIP 6.4, the returned array is an array of this class.
2828     * Previously, the returned array was an array of <code>java.io.File</code>
2829     * which solely contained instances of this class.
2830     * <p>
2831     * Note that archive entries with absolute pathnames are ignored by this
2832     * method and are never returned.
2833     * <p>
2834     * This file system operation <em>is</em> atomic.
2835     *
2836     * @param factory The factory used to create the member file of this
2837     * directory.
2838     * This could be an {@link ArchiveDetector} in order to detect any
2839     * archives by the member file names.
2840     * @return <code>null</code> if this is not a directory or an archive file,
2841     * a valid (but maybe empty) array otherwise.
2842     */

2843    public File[] listFiles(
2844            final FileFilter fileFilter,
2845            final FileFactory factory) {
2846        if (innerArchive == null)
2847            return delegateListFiles(fileFilter, factory);
2848        try {
2849            return innerArchive.getArchiveController().listFiles(
2850                    innerEntryName, fileFilter, this, factory);
2851        } catch (ArchiveController.FalsePositiveNativeException failure) {
2852            return delegateListFiles(fileFilter, factory);
2853        }
2854    }
2855
2856    private File[] delegateListFiles(
2857            final FileFilter fileFilter,
2858            final FileFactory factory) {
2859        // When filtering, we want to pass in <code>de.schlichtherle.io.File</code>
2860
// objects rather than <code>java.io.File</code> objects, so we cannot
2861
// just call <code>entry.listFiles(FileFilter)</code>.
2862
// Instead, we will query the entry for the children names (i.e.
2863
// Strings) only, construct <code>de.schlichtherle.io.File</code>
2864
// instances from this and then apply the filter to construct the
2865
// result list.
2866

2867        final List filteredList = new ArrayList();
2868        final String JavaDoc[] children = delegate.list();
2869        if (children == null)
2870            return null;
2871
2872        for (int i = 0, l = children.length; i < l; i++) {
2873            final String JavaDoc child = children[i];
2874            final File file = factory.createFile(this, child);
2875            if (fileFilter == null || fileFilter.accept(file))
2876                filteredList.add(file);
2877        }
2878        final File[] list = new File[filteredList.size()];
2879        filteredList.toArray(list);
2880
2881        return list;
2882    }
2883
2884    /**
2885     * Creates a new, empty file similar to its superclass implementation.
2886     * <p>
2887     * Please note that you cannot link a archive file with this
2888     * method, even if this file's pathname looks like a archive file
2889     * (like e.g. <code>"archive.zip"</code>) and hence {@link #isArchive()}
2890     * returns <code>true</code>.
2891     * <p>
2892     * To link a archive file, use {@link #mkdir()} on a file object
2893     * which's pathname looks like a archive file instead or,
2894     * if {@link #isLenient()} returns <code>true</code>, simply link any of
2895     * its entries (like e.g. <code>"archive.zip/file.txt"</code>).
2896     * <p>
2897     * Please note that this is <em>not</em> an atomic operation if this file
2898     * is actually an entry located in a archive file.
2899     * The call will succeed, but other processes will <em>not</em> be able
2900     * to see this archive entry unless {@link #update()} or {@link #umount()}
2901     * get called.
2902     * However, the created file can be seen by this JVM instance just as
2903     * normal if they are using the classes in this package.
2904     * <p>
2905     * This file system operation <em>is</em> atomic.
2906     *
2907     * @see java.io.File#createNewFile()
2908     * @see DefaultArchiveDetector
2909     */

2910    public boolean createNewFile() throws IOException {
2911        if (enclArchive == null)
2912            return delegate.createNewFile();
2913        try {
2914            return enclArchive.getArchiveController().createNewFile(
2915                    enclEntryName, isLenient());
2916        } catch (ArchiveController.FalsePositiveNativeException failure) {
2917            return delegate.createNewFile();
2918        } catch (IOException failure) {
2919            throw failure;
2920        }
2921    }
2922
2923    public boolean mkdirs() {
2924        if (innerArchive == null)
2925            return delegate.mkdirs();
2926
2927        final File parent = (File) getParentFile();
2928        if (parent != null && !parent.exists())
2929            parent.mkdirs();
2930
2931        // TODO: Profile: return parent.isDirectory() && mkdir();
2932
// May perform better in certain situations where (probably false
2933
// positive) archive files are involved.
2934
return mkdir();
2935    }
2936
2937    /**
2938     * Like the superclass implementation, but also creates empty archive
2939     * files.
2940     * <p>
2941     * This file system operation <em>is</em> atomic.
2942     */

2943    public boolean mkdir() {
2944        if (innerArchive == null)
2945            return delegate.mkdir();
2946        try {
2947            return innerArchive.getArchiveController().mkdir(
2948                    innerEntryName, isLenient());
2949        } catch (ArchiveController.FalsePositiveNativeException failure) {
2950            // We are trying to link a directory which is enclosed in a false
2951
// positive archive file which is actually a regular
2952
// directory in the native file system.
2953
// Now the directory we are trying to link must not be an archive
2954
// file, because otherwise its controller would have identified
2955
// the enclosing archive file as a false positive native directory
2956
// and created its file system accordingly, to the effect that
2957
// we would never get here.
2958
assert !isArchive();
2959            return delegate.mkdir();
2960        }
2961    }
2962
2963    /**
2964     * Deletes an archive entry, archive or regular node in the native file
2965     * system.
2966     *
2967     * In a nutshell, the rules for a file to be deletable are like this:
2968     * <ol>
2969     * <li>
2970     * If an entry in an archive is to be deleted, the operation will
2971     * succeed unless the archive is read only, the entry refers to a
2972     * non-empty directory or there is an open output stream for this
2973     * entry.
2974     * An open input stream will not prevent an archive entry to be deleted.
2975     * <li>
2976     * If an archive is to be deleted, the operation will succeed unless
2977     * the archive is not empty or there are any open input or output streams
2978     * for its entries.
2979     * <li>
2980     * If a node in the native file system is to be deleted, the rules of
2981     * the respective file system apply.
2982     * </ol>
2983     * <p>
2984     * This file system operation <em>is</em> atomic.
2985     *
2986     * @see java.io.File#delete
2987     */

2988    public boolean delete() {
2989        if (innerArchive == null)
2990            return delegate.delete();
2991        try {
2992            return innerArchive.getArchiveController().delete(innerEntryName);
2993        } catch (ArchiveController.FalsePositiveNativeException failure) {
2994            if (isArchive()
2995                    && !delegate.isDirectory()
2996                    && failure.getCause() instanceof FileNotFoundException) {
2997                // dito
2998
return false;
2999            } else {
3000                return delegate.delete();
3001            }
3002        }
3003    }
3004
3005    /**
3006     * Deletes the entire directory tree represented by this object,
3007     * regardless whether this is a file or directory, whether the directory
3008     * is empty or not and whether the file or directory is actually an
3009     * archive file, an entry in an archive file or not enclosed in an
3010     * archive file at all.
3011     * <p>
3012     * This file system operation is <em>not</em> atomic.
3013     *
3014     * @return <code>true</code> if and only if the entire directory tree was
3015     * successfully deleted.
3016     */

3017    public boolean deleteAll() {
3018        boolean ok = true;
3019        if (isDirectory()) {
3020            java.io.File JavaDoc[] members = listFiles(ArchiveDetector.NULL);
3021            for (int i = members.length; --i >= 0; )
3022                ok &= ((File) members[i]).deleteAll();
3023        }
3024        return ok && delete();
3025    }
3026
3027    public void deleteOnExit() {
3028        if (innerArchive == null) {
3029            delegate.deleteOnExit();
3030            return;
3031        }
3032
3033        if (isArchive()) {
3034            // We cannot prompt the user for a password in the shutdown hook
3035
// in case this is an RAES encrypted ZIP file.
3036
// So we do this now instead.
3037
isDirectory();
3038        }
3039
3040        ArchiveController.ShutdownHook.deleteOnExit.add(this);
3041    }
3042
3043    /**
3044     * Equivalent to {@link #renameTo(java.io.File, ArchiveDetector)
3045     * renameTo(dst, getArchiveDetector())}.
3046     */

3047    public final boolean renameTo(final java.io.File JavaDoc dst) {
3048        return renameTo(dst, detector);
3049    }
3050
3051    /**
3052     * Behaves similar to the super class, but renames this file or directory
3053     * by recursively copying its data if this object or the <code>dst</code>
3054     * object is either an archive file or an entry located in an archive file.
3055     * Hence, in these cases only this file system operation is <em>not</em>
3056     * atomic.
3057     *
3058     * @param detector The object used to detect archive files in the path.
3059     */

3060    public boolean renameTo(
3061            final java.io.File JavaDoc dst,
3062            final ArchiveDetector detector) {
3063        // Nice trick, but wouldn't be thread safe!
3064
/*if (enclArchive == null) {
3065            if (!(dst instanceof File) || ((File) dst).enclArchive == null) {
3066                try {
3067                    umount(this);
3068                    umount((File) dst);
3069                } catch (ArchiveException failure) {
3070                    return false;
3071                }
3072                return delegate.renameTo(dst);
3073            }
3074        }*/

3075
3076        if (innerArchive == null) {
3077            if (!(dst instanceof File) || ((File) dst).innerArchive == null)
3078                return delegate.renameTo(dst);
3079        }
3080
3081        return !dst.exists()
3082            && !contains(this, dst)
3083            && mv(this, dst, detector);
3084    }
3085
3086    private static boolean mv(
3087            final java.io.File JavaDoc src,
3088            final java.io.File JavaDoc dst,
3089            final ArchiveDetector detector) {
3090        boolean ok = true;
3091        if (src.isDirectory()) {
3092            dst.mkdir();
3093            //ok = dst.mkdir();
3094
//if (ok) {
3095
final String JavaDoc[] members = src.list();
3096                if (dst instanceof File && ((File) dst).innerArchive != null) {
3097                    // Create sorted entries if writing a new archive file.
3098
// This is courtesy only, so natural order is sufficient.
3099
Arrays.sort(members);
3100                }
3101                for (int i = 0, l = members.length; i < l; i++) {
3102                    String JavaDoc member = members[i];
3103                    ok &= mv(
3104                            detector.createFile(src, member),
3105                            detector.createFile(dst, member),
3106                            detector);
3107                }
3108                long srcLastModified = src.lastModified();
3109                // Use current time for copies of ghost directories!
3110
if (srcLastModified > 0
3111                        || !(src instanceof File) || !((File) src).isEntry())
3112                    ok &= dst.setLastModified(srcLastModified);
3113            //}
3114
} else if (src.isFile()) { // !isDirectory()
3115
try {
3116                cp_p(src, dst);
3117            } catch (IOException failure) {
3118                ok = false;
3119            }
3120        } else {
3121            ok = false;
3122        }
3123        return ok && src.delete(); // does not unlink if not ok!
3124
}
3125
3126    /**
3127     * Copies the input stream <code>in</code> to this file and closes it.
3128     * <p>
3129     * <table border="2" cellpadding="4">
3130     * <tr>
3131     * <th>Feature</th>
3132     * <th>Supported</th>
3133     * </tr>
3134     * <tr>
3135     * <td>Preserves file attributes</td>
3136     * <td>None</td>
3137     * </tr>
3138     * <tr>
3139     * <td>Copies directories recursively</td>
3140     * <td>No</td>
3141     * </tr>
3142     * <tr>
3143     * <td>Reads and overwrites special files</td>
3144     * <td>Yes</td>
3145     * </tr>
3146     * <tr>
3147     * <td>Closes parameter streams</td>
3148     * <td>Always</td>
3149     * </tr>
3150     * <tr>
3151     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3152     * <td>No</td>
3153     * </tr>
3154     * <tr>
3155     * <td>Deletes partial written files on failure</td>
3156     * <td>Yes</td>
3157     * </tr>
3158     * <tr>
3159     * <td>Deletes partial written directories on failure</td>
3160     * <td>n/a</td>
3161     * </tr>
3162     * <tr>
3163     * <td>Atomic</td>
3164     * <td>No</td>
3165     * </tr>
3166     * </table>
3167     *
3168     * @return <code>true</code> if and only if the operation succeeded.
3169     * @throws NullPointerException If any parameter is <code>null</code>.
3170     * @see <a HREF="#copy_methods">Copy Methods</a>
3171     */

3172    public boolean copyFrom(final InputStream in) {
3173        try {
3174            final OutputStream out = detector.createFileOutputStream(this, false);
3175            try {
3176                cp(in, out); // always closes in and out
3177
return true;
3178            } catch (IOException failure) {
3179                delete();
3180            }
3181        } catch (IOException failure) {
3182        }
3183        return false;
3184    }
3185
3186    /**
3187     * Copies the file <code>src</code> to this file.
3188     * <p>
3189     * <table border="2" cellpadding="4">
3190     * <tr>
3191     * <th>Feature</th>
3192     * <th>Supported</th>
3193     * </tr>
3194     * <tr>
3195     * <td>Preserves file attributes</td>
3196     * <td>None</td>
3197     * </tr>
3198     * <tr>
3199     * <td>Copies directories recursively</td>
3200     * <td>No</td>
3201     * </tr>
3202     * <tr>
3203     * <td>Reads and overwrites special files</td>
3204     * <td>Yes</td>
3205     * </tr>
3206     * <tr>
3207     * <td>Closes parameter streams</td>
3208     * <td>n/a</td>
3209     * </tr>
3210     * <tr>
3211     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3212     * <td>Yes</td>
3213     * </tr>
3214     * <tr>
3215     * <td>Deletes partial written files on failure</td>
3216     * <td>Yes</td>
3217     * </tr>
3218     * <tr>
3219     * <td>Deletes partial written directories on failure</td>
3220     * <td>n/a</td>
3221     * </tr>
3222     * <tr>
3223     * <td>Atomic</td>
3224     * <td>No</td>
3225     * </tr>
3226     * </table>
3227     *
3228     * @param src The source file. Note that although this just needs to
3229     * be a plain <code>java.io.File</code>, archive entries are only
3230     * supported for instances of this class.
3231     * @return <code>true</code> if and only if the operation succeeded.
3232     * @throws NullPointerException If any parameter is <code>null</code>.
3233     * @see <a HREF="#copy_methods">Copy Methods</a>
3234     */

3235    public boolean copyFrom(final java.io.File JavaDoc src) {
3236        try {
3237            cp(src, this);
3238            return true;
3239        } catch (IOException failure) {
3240            return false;
3241        }
3242    }
3243
3244    /**
3245     * Recursively copies the file or directory <code>src</code>
3246     * to this file or directory.
3247     * This version uses the {@link ArchiveDetector} which was used to
3248     * construct this object to detect any archive files in the source
3249     * and destination directory trees.
3250     * <p>
3251     * <table border="2" cellpadding="4">
3252     * <tr>
3253     * <th>Feature</th>
3254     * <th>Supported</th>
3255     * </tr>
3256     * <tr>
3257     * <td>Preserves file attributes</td>
3258     * <td>None</td>
3259     * </tr>
3260     * <tr>
3261     * <td>Copies directories recursively</td>
3262     * <td>Yes</td>
3263     * </tr>
3264     * <tr>
3265     * <td>Reads and overwrites special files</td>
3266     * <td>No</td>
3267     * </tr>
3268     * <tr>
3269     * <td>Closes parameter streams</td>
3270     * <td>n/a</td>
3271     * </tr>
3272     * <tr>
3273     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3274     * <td>Yes</td>
3275     * </tr>
3276     * <tr>
3277     * <td>Deletes partial written files on failure</td>
3278     * <td>Yes</td>
3279     * </tr>
3280     * <tr>
3281     * <td>Deletes partial written directories on failure</td>
3282     * <td>No</td>
3283     * </tr>
3284     * <tr>
3285     * <td>Atomic</td>
3286     * <td>No</td>
3287     * </tr>
3288     * </table>
3289     *
3290     * @param src The source file. Note that although this just needs to
3291     * be a plain <code>java.io.File</code>, archive files and entries
3292     * are only supported for instances of this class.
3293     * @return <code>true</code> if and only if the operation succeeded.
3294     * @throws NullPointerException If any parameter is <code>null</code>.
3295     * @see <a HREF="#copy_methods">Copy Methods</a>
3296     */

3297    public boolean copyAllFrom(final java.io.File JavaDoc src) {
3298        return cp_r(src, this, detector, detector, false);
3299    }
3300
3301    /**
3302     * Recursively copies the file or directory <code>src</code>
3303     * to this file or directory.
3304     * This version uses the given archive detector to detect any archive
3305     * files in the source and destination directory trees.
3306     * <p>
3307     * <table border="2" cellpadding="4">
3308     * <tr>
3309     * <th>Feature</th>
3310     * <th>Supported</th>
3311     * </tr>
3312     * <tr>
3313     * <td>Preserves file attributes</td>
3314     * <td>None</td>
3315     * </tr>
3316     * <tr>
3317     * <td>Copies directories recursively</td>
3318     * <td>Yes</td>
3319     * </tr>
3320     * <tr>
3321     * <td>Reads and overwrites special files</td>
3322     * <td>No</td>
3323     * </tr>
3324     * <tr>
3325     * <td>Closes parameter streams</td>
3326     * <td>n/a</td>
3327     * </tr>
3328     * <tr>
3329     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3330     * <td>Yes</td>
3331     * </tr>
3332     * <tr>
3333     * <td>Deletes partial written files on failure</td>
3334     * <td>Yes</td>
3335     * </tr>
3336     * <tr>
3337     * <td>Deletes partial written directories on failure</td>
3338     * <td>No</td>
3339     * </tr>
3340     * <tr>
3341     * <td>Atomic</td>
3342     * <td>No</td>
3343     * </tr>
3344     * </table>
3345     *
3346     * @param src The source file. Note that although this just needs to
3347     * be a plain <code>java.io.File</code>, archive files and entries
3348     * are only supported for instances of this class.
3349     * @param detector The object used to detect any archive files
3350     * in the source and destination directory trees.
3351     * @throws NullPointerException If any parameter is <code>null</code>.
3352     * @see <a HREF="#copy_methods">Copy Methods</a>
3353     */

3354    public boolean copyAllFrom(
3355            final java.io.File JavaDoc src,
3356            final ArchiveDetector detector) {
3357        return cp_r(src, this, detector, detector, false);
3358    }
3359
3360    /**
3361     * Recursively copies the file or directory <code>src</code>
3362     * to this file or directory.
3363     * By using different {@link ArchiveDetector}s for the source and
3364     * destination, this method can be used to do advanced stuff like
3365     * unzipping any archive file in the source tree to a plain directory
3366     * in the destination tree (where <code>srcDetector</code> could be
3367     * {@link ArchiveDetector#DEFAULT} and <code>dstDetector</code> must be
3368     * {@link ArchiveDetector#NULL}) or changing the encoding by subclassing
3369     * the {@link DefaultArchiveDetector}.
3370     * <p>
3371     * <table border="2" cellpadding="4">
3372     * <tr>
3373     * <th>Feature</th>
3374     * <th>Supported</th>
3375     * </tr>
3376     * <tr>
3377     * <td>Preserves file attributes</td>
3378     * <td>None</td>
3379     * </tr>
3380     * <tr>
3381     * <td>Copies directories recursively</td>
3382     * <td>Yes</td>
3383     * </tr>
3384     * <tr>
3385     * <td>Reads and overwrites special files</td>
3386     * <td>No</td>
3387     * </tr>
3388     * <tr>
3389     * <td>Closes parameter streams</td>
3390     * <td>n/a</td>
3391     * </tr>
3392     * <tr>
3393     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3394     * <td>Yes</td>
3395     * </tr>
3396     * <tr>
3397     * <td>Deletes partial written files on failure</td>
3398     * <td>Yes</td>
3399     * </tr>
3400     * <tr>
3401     * <td>Deletes partial written directories on failure</td>
3402     * <td>No</td>
3403     * </tr>
3404     * <tr>
3405     * <td>Atomic</td>
3406     * <td>No</td>
3407     * </tr>
3408     * </table>
3409     *
3410     * @param src The source file. Note that although this just needs to
3411     * be a plain <code>java.io.File</code>, archive files and entries
3412     * are only supported for instances of this class.
3413     * @param srcDetector The object used to detect any archive files
3414     * in the source directory tree.
3415     * @param dstDetector The object used to detect any archive files
3416     * in the destination directory tree.
3417     * @return <code>true</code> if and only if the operation succeeded.
3418     * @throws NullPointerException If any parameter is <code>null</code>.
3419     * @see <a HREF="#copy_methods">Copy Methods</a>
3420     */

3421    public boolean copyAllFrom(
3422            final java.io.File JavaDoc src,
3423            final ArchiveDetector srcDetector,
3424            final ArchiveDetector dstDetector) {
3425        return cp_r(src, this, srcDetector, dstDetector, false);
3426    }
3427
3428    /**
3429     * Copies this file to the output stream <code>out</code> and closes it.
3430     * <p>
3431     * <table border="2" cellpadding="4">
3432     * <tr>
3433     * <th>Feature</th>
3434     * <th>Supported</th>
3435     * </tr>
3436     * <tr>
3437     * <td>Preserves file attributes</td>
3438     * <td>None</td>
3439     * </tr>
3440     * <tr>
3441     * <td>Copies directories recursively</td>
3442     * <td>No</td>
3443     * </tr>
3444     * <tr>
3445     * <td>Reads and overwrites special files</td>
3446     * <td>Yes</td>
3447     * </tr>
3448     * <tr>
3449     * <td>Closes parameter streams</td>
3450     * <td>Always</td>
3451     * </tr>
3452     * <tr>
3453     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3454     * <td>No</td>
3455     * </tr>
3456     * <tr>
3457     * <td>Deletes partial written files on failure</td>
3458     * <td>n/a</td>
3459     * </tr>
3460     * <tr>
3461     * <td>Deletes partial written directories on failure</td>
3462     * <td>n/a</td>
3463     * </tr>
3464     * <tr>
3465     * <td>Atomic</td>
3466     * <td>No</td>
3467     * </tr>
3468     * </table>
3469     *
3470     * @return <code>true</code> if and only if the operation succeeded.
3471     * @throws NullPointerException If any parameter is <code>null</code>.
3472     * @see <a HREF="#copy_methods">Copy Methods</a>
3473     */

3474    public boolean copyTo(final OutputStream out) {
3475        try {
3476            final InputStream in = detector.createFileInputStream(this);
3477            cp(in, out); // always closes in and out
3478
return true;
3479        } catch (IOException failed) {
3480            return false;
3481        }
3482    }
3483
3484    /**
3485     * Copies this file to the file <code>dst</code>.
3486     * <p>
3487     * <table border="2" cellpadding="4">
3488     * <tr>
3489     * <th>Feature</th>
3490     * <th>Supported</th>
3491     * </tr>
3492     * <tr>
3493     * <td>Preserves file attributes</td>
3494     * <td>None</td>
3495     * </tr>
3496     * <tr>
3497     * <td>Copies directories recursively</td>
3498     * <td>No</td>
3499     * </tr>
3500     * <tr>
3501     * <td>Reads and overwrites special files</td>
3502     * <td>Yes</td>
3503     * </tr>
3504     * <tr>
3505     * <td>Closes parameter streams</td>
3506     * <td>n/a</td>
3507     * </tr>
3508     * <tr>
3509     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3510     * <td>Yes</td>
3511     * </tr>
3512     * <tr>
3513     * <td>Deletes partial written files on failure</td>
3514     * <td>Yes</td>
3515     * </tr>
3516     * <tr>
3517     * <td>Deletes partial written directories on failure</td>
3518     * <td>n/a</td>
3519     * </tr>
3520     * <tr>
3521     * <td>Atomic</td>
3522     * <td>No</td>
3523     * </tr>
3524     * </table>
3525     *
3526     * @param dst The destination file. Note that although this just needs to
3527     * be a plain <code>java.io.File</code>, archive entries are only
3528     * supported for instances of this class.
3529     * @return <code>true</code> if the file has been successfully copied.
3530     * @throws NullPointerException If any parameter is <code>null</code>.
3531     * @see <a HREF="#copy_methods">Copy Methods</a>
3532     */

3533    public boolean copyTo(final java.io.File JavaDoc dst) {
3534        try {
3535            cp(this, dst);
3536            return true;
3537        } catch (IOException failure) {
3538            return false;
3539        }
3540    }
3541
3542    /**
3543     * Recursively copies this file or directory to the file or directory
3544     * <code>dst</code>.
3545     * This version uses the {@link ArchiveDetector} which was used to
3546     * construct this object to detect any archive files in the source
3547     * and destination directory trees.
3548     * <p>
3549     * <table border="2" cellpadding="4">
3550     * <tr>
3551     * <th>Feature</th>
3552     * <th>Supported</th>
3553     * </tr>
3554     * <tr>
3555     * <td>Preserves file attributes</td>
3556     * <td>None</td>
3557     * </tr>
3558     * <tr>
3559     * <td>Copies directories recursively</td>
3560     * <td>Yes</td>
3561     * </tr>
3562     * <tr>
3563     * <td>Reads and overwrites special files</td>
3564     * <td>No</td>
3565     * </tr>
3566     * <tr>
3567     * <td>Closes parameter streams</td>
3568     * <td>n/a</td>
3569     * </tr>
3570     * <tr>
3571     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3572     * <td>Yes</td>
3573     * </tr>
3574     * <tr>
3575     * <td>Deletes partial written files on failure</td>
3576     * <td>Yes</td>
3577     * </tr>
3578     * <tr>
3579     * <td>Deletes partial written directories on failure</td>
3580     * <td>No</td>
3581     * </tr>
3582     * <tr>
3583     * <td>Atomic</td>
3584     * <td>No</td>
3585     * </tr>
3586     * </table>
3587     *
3588     * @param dst The destination file. Note that although this just needs to
3589     * be a plain <code>java.io.File</code>, archive files and entries
3590     * are only supported for instances of this class.
3591     * @return <code>true</code> if and only if the operation succeeded.
3592     * @throws NullPointerException If any parameter is <code>null</code>.
3593     * @see <a HREF="#copy_methods">Copy Methods</a>
3594     */

3595    public boolean copyAllTo(final java.io.File JavaDoc dst) {
3596        return cp_r(this, dst, detector, detector, false);
3597    }
3598
3599    /**
3600     * Recursively copies this file or directory to the file or directory
3601     * <code>dst</code>.
3602     * This version uses the given archive detector to detect any archive
3603     * files in the source and destination directory trees.
3604     * <p>
3605     * <table border="2" cellpadding="4">
3606     * <tr>
3607     * <th>Feature</th>
3608     * <th>Supported</th>
3609     * </tr>
3610     * <tr>
3611     * <td>Preserves file attributes</td>
3612     * <td>None</td>
3613     * </tr>
3614     * <tr>
3615     * <td>Copies directories recursively</td>
3616     * <td>Yes</td>
3617     * </tr>
3618     * <tr>
3619     * <td>Reads and overwrites special files</td>
3620     * <td>No</td>
3621     * </tr>
3622     * <tr>
3623     * <td>Closes parameter streams</td>
3624     * <td>n/a</td>
3625     * </tr>
3626     * <tr>
3627     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3628     * <td>Yes</td>
3629     * </tr>
3630     * <tr>
3631     * <td>Deletes partial written files on failure</td>
3632     * <td>Yes</td>
3633     * </tr>
3634     * <tr>
3635     * <td>Deletes partial written directories on failure</td>
3636     * <td>No</td>
3637     * </tr>
3638     * <tr>
3639     * <td>Atomic</td>
3640     * <td>No</td>
3641     * </tr>
3642     * </table>
3643     *
3644     * @param dst The destination file. Note that although this just needs to
3645     * be a plain <code>java.io.File</code>, archive files and entries
3646     * are only supported for instances of this class.
3647     * @param detector The object used to detect any archive files
3648     * in the source and destination directory trees.
3649     * @return <code>true</code> if and only if the operation succeeded.
3650     * @throws NullPointerException If any parameter is <code>null</code>.
3651     * @see <a HREF="#copy_methods">Copy Methods</a>
3652     */

3653    public boolean copyAllTo(
3654            final java.io.File JavaDoc dst,
3655            final ArchiveDetector detector) {
3656        return cp_r(this, dst, detector, detector, false);
3657    }
3658
3659    /**
3660     * Recursively copies this file or directory to the file or directory
3661     * <code>dst</code>.
3662     * By using different {@link ArchiveDetector}s for the source and
3663     * destination, this method can be used to do advanced stuff like
3664     * unzipping any archive file in the source tree to a plain directory
3665     * in the destination tree (where <code>srcDetector</code> could be
3666     * {@link ArchiveDetector#DEFAULT} and <code>dstDetector</code> must be
3667     * {@link ArchiveDetector#NULL}) or changing the encoding by subclassing
3668     * the {@link DefaultArchiveDetector}.
3669     * <p>
3670     * <table border="2" cellpadding="4">
3671     * <tr>
3672     * <th>Feature</th>
3673     * <th>Supported</th>
3674     * </tr>
3675     * <tr>
3676     * <td>Preserves file attributes</td>
3677     * <td>None</td>
3678     * </tr>
3679     * <tr>
3680     * <td>Copies directories recursively</td>
3681     * <td>Yes</td>
3682     * </tr>
3683     * <tr>
3684     * <td>Reads and overwrites special files</td>
3685     * <td>No</td>
3686     * </tr>
3687     * <tr>
3688     * <td>Closes parameter streams</td>
3689     * <td>n/a</td>
3690     * </tr>
3691     * <tr>
3692     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3693     * <td>Yes</td>
3694     * </tr>
3695     * <tr>
3696     * <td>Deletes partial written files on failure</td>
3697     * <td>Yes</td>
3698     * </tr>
3699     * <tr>
3700     * <td>Deletes partial written directories on failure</td>
3701     * <td>No</td>
3702     * </tr>
3703     * <tr>
3704     * <td>Atomic</td>
3705     * <td>No</td>
3706     * </tr>
3707     * </table>
3708     *
3709     * @param dst The destination file. Note that although this just needs to
3710     * be a plain <code>java.io.File</code>, archive files and entries
3711     * are only supported for instances of this class.
3712     * @param srcDetector The object used to detect any archive files
3713     * in the source directory tree.
3714     * @param dstDetector The object used to detect any archive files
3715     * in the destination directory tree.
3716     * @return <code>true</code> if and only if the operation succeeded.
3717     * @throws NullPointerException If any parameter is <code>null</code>.
3718     * @see <a HREF="#copy_methods">Copy Methods</a>
3719     */

3720    public boolean copyAllTo(
3721            final java.io.File JavaDoc dst,
3722            final ArchiveDetector srcDetector,
3723            final ArchiveDetector dstDetector) {
3724        return cp_r(this, dst, srcDetector, dstDetector, false);
3725    }
3726
3727    /**
3728     * Copies the file <code>src</code> to this file and tries to preserve
3729     * all attributes of the source file to the destination file, too.
3730     * Note that the current implementation only preserves the last
3731     * modification time.
3732     * <p>
3733     * <table border="2" cellpadding="4">
3734     * <tr>
3735     * <th>Feature</th>
3736     * <th>Supported</th>
3737     * </tr>
3738     * <tr>
3739     * <td>Preserves file attributes</td>
3740     * <td>Best effort</td>
3741     * </tr>
3742     * <tr>
3743     * <td>Copies directories recursively</td>
3744     * <td>No</td>
3745     * </tr>
3746     * <tr>
3747     * <td>Reads and overwrites special files</td>
3748     * <td>Yes</td>
3749     * </tr>
3750     * <tr>
3751     * <td>Closes parameter streams</td>
3752     * <td>n/a</td>
3753     * </tr>
3754     * <tr>
3755     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3756     * <td>Yes</td>
3757     * </tr>
3758     * <tr>
3759     * <td>Deletes partial written files on failure</td>
3760     * <td>Yes</td>
3761     * </tr>
3762     * <tr>
3763     * <td>Deletes partial written directories on failure</td>
3764     * <td>n/a</td>
3765     * </tr>
3766     * <tr>
3767     * <td>Atomic</td>
3768     * <td>No</td>
3769     * </tr>
3770     * </table>
3771     *
3772     * @param src The source file. Note that although this just needs to
3773     * be a plain <code>java.io.File</code>, archive entries are only
3774     * supported for instances of this class.
3775     * @return <code>true</code> if and only if the operation succeeded.
3776     * @throws NullPointerException If any parameter is <code>null</code>.
3777     * @see <a HREF="#copy_methods">Copy Methods</a>
3778     */

3779    public boolean archiveCopyFrom(final java.io.File JavaDoc src) {
3780        try {
3781            cp_p(src, this);
3782            return true;
3783        } catch (IOException failure) {
3784            return false;
3785        }
3786    }
3787
3788    /**
3789     * Recursively copies the file or directory <code>src</code> to this file
3790     * or directory and tries to preserve all attributes of the source
3791     * file to the destination file, too.
3792     * Note that the current implementation only preserves the last
3793     * modification time.
3794     * <p>
3795     * This version uses the {@link ArchiveDetector} which was used to
3796     * construct this object to detect any archive files in the source
3797     * and destination directory trees.
3798     * <p>
3799     * <table border="2" cellpadding="4">
3800     * <tr>
3801     * <th>Feature</th>
3802     * <th>Supported</th>
3803     * </tr>
3804     * <tr>
3805     * <td>Preserves file attributes</td>
3806     * <td>Best effort</td>
3807     * </tr>
3808     * <tr>
3809     * <td>Copies directories recursively</td>
3810     * <td>Yes</td>
3811     * </tr>
3812     * <tr>
3813     * <td>Reads and overwrites special files</td>
3814     * <td>No</td>
3815     * </tr>
3816     * <tr>
3817     * <td>Closes parameter streams</td>
3818     * <td>n/a</td>
3819     * </tr>
3820     * <tr>
3821     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3822     * <td>Yes</td>
3823     * </tr>
3824     * <tr>
3825     * <td>Deletes partial written files on failure</td>
3826     * <td>Yes</td>
3827     * </tr>
3828     * <tr>
3829     * <td>Deletes partial written directories on failure</td>
3830     * <td>No</td>
3831     * </tr>
3832     * <tr>
3833     * <td>Atomic</td>
3834     * <td>No</td>
3835     * </tr>
3836     * </table>
3837     *
3838     * @param src The source file. Note that although this just needs to
3839     * be a plain <code>java.io.File</code>, archive files and entries
3840     * are only supported for instances of this class.
3841     * @return <code>true</code> if and only if the operation succeeded.
3842     * @throws NullPointerException If any parameter is <code>null</code>.
3843     * @see <a HREF="#copy_methods">Copy Methods</a>
3844     */

3845    public boolean archiveCopyAllFrom(final java.io.File JavaDoc src) {
3846        return cp_r(src, this, detector, detector, true);
3847    }
3848
3849    /**
3850     * Recursively copies the file or directory <code>src</code> to this file
3851     * or directory and tries to preserve all attributes of the source
3852     * file to the destination file, too.
3853     * Note that the current implementation only preserves the last
3854     * modification time.
3855     * <p>
3856     * This version uses the given archive detector to detect any archive
3857     * files in the source and destination directory trees.
3858     * <p>
3859     * <table border="2" cellpadding="4">
3860     * <tr>
3861     * <th>Feature</th>
3862     * <th>Supported</th>
3863     * </tr>
3864     * <tr>
3865     * <td>Preserves file attributes</td>
3866     * <td>Best effort</td>
3867     * </tr>
3868     * <tr>
3869     * <td>Copies directories recursively</td>
3870     * <td>Yes</td>
3871     * </tr>
3872     * <tr>
3873     * <td>Reads and overwrites special files</td>
3874     * <td>No</td>
3875     * </tr>
3876     * <tr>
3877     * <td>Closes parameter streams</td>
3878     * <td>n/a</td>
3879     * </tr>
3880     * <tr>
3881     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3882     * <td>Yes</td>
3883     * </tr>
3884     * <tr>
3885     * <td>Deletes partial written files on failure</td>
3886     * <td>Yes</td>
3887     * </tr>
3888     * <tr>
3889     * <td>Deletes partial written directories on failure</td>
3890     * <td>No</td>
3891     * </tr>
3892     * <tr>
3893     * <td>Atomic</td>
3894     * <td>No</td>
3895     * </tr>
3896     * </table>
3897     *
3898     * @param src The source file. Note that although this just needs to
3899     * be a plain <code>java.io.File</code>, archive files and entries
3900     * are only supported for instances of this class.
3901     * @param detector The object used to detect any archive files
3902     * in the source and destination directory trees.
3903     * @return <code>true</code> if and only if the operation succeeded.
3904     * @throws NullPointerException If any parameter is <code>null</code>.
3905     * @see <a HREF="#copy_methods">Copy Methods</a>
3906     */

3907    public boolean archiveCopyAllFrom(
3908            final java.io.File JavaDoc src,
3909            final ArchiveDetector detector) {
3910        return cp_r(src, this, detector, detector, true);
3911    }
3912
3913    /**
3914     * Recursively copies the file or directory <code>src</code> to this file
3915     * or directory and tries to preserve all attributes of the source
3916     * file to the destination file, too.
3917     * Note that the current implementation only preserves the last
3918     * modification time.
3919     * <p>
3920     * By using different {@link ArchiveDetector}s for the source and
3921     * destination, this method can be used to do advanced stuff like
3922     * unzipping any archive file in the source tree to a plain directory
3923     * in the destination tree (where <code>srcDetector</code> could be
3924     * {@link ArchiveDetector#DEFAULT} and <code>dstDetector</code> must be
3925     * {@link ArchiveDetector#NULL}) or changing the encoding by subclassing
3926     * the {@link DefaultArchiveDetector}.
3927     * <p>
3928     * <table border="2" cellpadding="4">
3929     * <tr>
3930     * <th>Feature</th>
3931     * <th>Supported</th>
3932     * </tr>
3933     * <tr>
3934     * <td>Preserves file attributes</td>
3935     * <td>Best effort</td>
3936     * </tr>
3937     * <tr>
3938     * <td>Copies directories recursively</td>
3939     * <td>Yes</td>
3940     * </tr>
3941     * <tr>
3942     * <td>Reads and overwrites special files</td>
3943     * <td>No</td>
3944     * </tr>
3945     * <tr>
3946     * <td>Closes parameter streams</td>
3947     * <td>n/a</td>
3948     * </tr>
3949     * <tr>
3950     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
3951     * <td>Yes</td>
3952     * </tr>
3953     * <tr>
3954     * <td>Deletes partial written files on failure</td>
3955     * <td>Yes</td>
3956     * </tr>
3957     * <tr>
3958     * <td>Deletes partial written directories on failure</td>
3959     * <td>No</td>
3960     * </tr>
3961     * <tr>
3962     * <td>Atomic</td>
3963     * <td>No</td>
3964     * </tr>
3965     * </table>
3966     *
3967     * @param src The source file. Note that although this just needs to
3968     * be a plain <code>java.io.File</code>, archive files and entries
3969     * are only supported for instances of this class.
3970     * @param srcDetector The object used to detect any archive files
3971     * in the source directory tree.
3972     * @param dstDetector The object used to detect archive files
3973     * in the destination directory tree.
3974     * @return <code>true</code> if and only if the operation succeeded.
3975     * @throws NullPointerException If any parameter is <code>null</code>.
3976     * @see <a HREF="#copy_methods">Copy Methods</a>
3977     */

3978    public boolean archiveCopyAllFrom(
3979            final java.io.File JavaDoc src,
3980            final ArchiveDetector srcDetector,
3981            final ArchiveDetector dstDetector) {
3982        return cp_r(src, this, srcDetector, dstDetector, true);
3983    }
3984
3985    /**
3986     * Copies this file to the file <code>dst</code> and tries to preserve
3987     * all attributes of the source
3988     * file to the destination file, too.
3989     * Note that the current implementation only preserves the last
3990     * modification time.
3991     * <p>
3992     * <table border="2" cellpadding="4">
3993     * <tr>
3994     * <th>Feature</th>
3995     * <th>Supported</th>
3996     * </tr>
3997     * <tr>
3998     * <td>Preserves file attributes</td>
3999     * <td>Best effort</td>
4000     * </tr>
4001     * <tr>
4002     * <td>Copies directories recursively</td>
4003     * <td>No</td>
4004     * </tr>
4005     * <tr>
4006     * <td>Reads and overwrites special files</td>
4007     * <td>Yes</td>
4008     * </tr>
4009     * <tr>
4010     * <td>Closes parameter streams</td>
4011     * <td>n/a</td>
4012     * </tr>
4013     * <tr>
4014     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
4015     * <td>Yes</td>
4016     * </tr>
4017     * <tr>
4018     * <td>Deletes partial written files on failure</td>
4019     * <td>Yes</td>
4020     * </tr>
4021     * <tr>
4022     * <td>Deletes partial written directories on failure</td>
4023     * <td>n/a</td>
4024     * </tr>
4025     * <tr>
4026     * <td>Atomic</td>
4027     * <td>No</td>
4028     * </tr>
4029     * </table>
4030     *
4031     * @param dst The destination file. Note that although this just needs to
4032     * be a plain <code>java.io.File</code>, archive entries are only
4033     * supported for instances of this class.
4034     * @return <code>true</code> if and only if the operation succeeded.
4035     * @throws NullPointerException If any parameter is <code>null</code>.
4036     * @see <a HREF="#copy_methods">Copy Methods</a>
4037     */

4038    public boolean archiveCopyTo(java.io.File JavaDoc dst) {
4039        try {
4040            cp_p(this, dst);
4041            return true;
4042        } catch (IOException failure) {
4043            return false;
4044        }
4045    }
4046
4047    /**
4048     * Recursively copies this file or directory to the file or directory
4049     * <code>dst</code> and tries to preserve all attributes of the source
4050     * file to the destination file, too.
4051     * Note that the current implementation only preserves the last
4052     * modification time.
4053     * <p>
4054     * This version uses the {@link ArchiveDetector} which was used to
4055     * construct this object to detect any archive files in the source
4056     * and destination directory trees.
4057     * <p>
4058     * <table border="2" cellpadding="4">
4059     * <tr>
4060     * <th>Feature</th>
4061     * <th>Supported</th>
4062     * </tr>
4063     * <tr>
4064     * <td>Preserves file attributes</td>
4065     * <td>Best effort</td>
4066     * </tr>
4067     * <tr>
4068     * <td>Copies directories recursively</td>
4069     * <td>Yes</td>
4070     * </tr>
4071     * <tr>
4072     * <td>Reads and overwrites special files</td>
4073     * <td>No</td>
4074     * </tr>
4075     * <tr>
4076     * <td>Closes parameter streams</td>
4077     * <td>n/a</td>
4078     * </tr>
4079     * <tr>
4080     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
4081     * <td>Yes</td>
4082     * </tr>
4083     * <tr>
4084     * <td>Deletes partial written files on failure</td>
4085     * <td>Yes</td>
4086     * </tr>
4087     * <tr>
4088     * <td>Deletes partial written directories on failure</td>
4089     * <td>No</td>
4090     * </tr>
4091     * <tr>
4092     * <td>Atomic</td>
4093     * <td>No</td>
4094     * </tr>
4095     * </table>
4096     *
4097     * @param dst The destination file. Note that although this just needs to
4098     * be a plain <code>java.io.File</code>, archive files and entries
4099     * are only supported for instances of this class.
4100     * @return <code>true</code> if and only if the operation succeeded.
4101     * @throws NullPointerException If any parameter is <code>null</code>.
4102     * @see <a HREF="#copy_methods">Copy Methods</a>
4103     */

4104    public boolean archiveCopyAllTo(final java.io.File JavaDoc dst) {
4105        return cp_r(this, dst, detector, detector, true);
4106    }
4107
4108    /**
4109     * Recursively copies this file or directory to the file or directory
4110     * <code>dst</code> and tries to preserve all attributes of the source
4111     * file to the destination file, too.
4112     * Note that the current implementation only preserves the last
4113     * modification time.
4114     * <p>
4115     * This version uses the given archive detector to detect any archive
4116     * files in the source and destination directory trees.
4117     * This version uses the given archive detector to detect any archive
4118     * files in the source and destination directory trees.
4119     * <p>
4120     * <table border="2" cellpadding="4">
4121     * <tr>
4122     * <th>Feature</th>
4123     * <th>Supported</th>
4124     * </tr>
4125     * <tr>
4126     * <td>Preserves file attributes</td>
4127     * <td>Best effort</td>
4128     * </tr>
4129     * <tr>
4130     * <td>Copies directories recursively</td>
4131     * <td>Yes</td>
4132     * </tr>
4133     * <tr>
4134     * <td>Reads and overwrites special files</td>
4135     * <td>No</td>
4136     * </tr>
4137     * <tr>
4138     * <td>Closes parameter streams</td>
4139     * <td>n/a</td>
4140     * </tr>
4141     * <tr>
4142     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
4143     * <td>Yes</td>
4144     * </tr>
4145     * <tr>
4146     * <td>Deletes partial written files on failure</td>
4147     * <td>Yes</td>
4148     * </tr>
4149     * <tr>
4150     * <td>Deletes partial written directories on failure</td>
4151     * <td>No</td>
4152     * </tr>
4153     * <tr>
4154     * <td>Atomic</td>
4155     * <td>No</td>
4156     * </tr>
4157     * </table>
4158     *
4159     * @param dst The destination file. Note that although this just needs to
4160     * be a plain <code>java.io.File</code>, archive files and entries
4161     * are only supported for instances of this class.
4162     * @param detector The object used to detect any archive files
4163     * in the source and destination directory trees.
4164     * @return <code>true</code> if and only if the operation succeeded.
4165     * @throws NullPointerException If any parameter is <code>null</code>.
4166     * @see <a HREF="#copy_methods">Copy Methods</a>
4167     */

4168    public boolean archiveCopyAllTo(
4169            final java.io.File JavaDoc dst,
4170            final ArchiveDetector detector) {
4171        return cp_r(this, dst, detector, detector, true);
4172    }
4173
4174    /**
4175     * Recursively copies this file or directory to the file or directory
4176     * <code>dst</code> and tries to preserve all attributes of the source
4177     * file to the destination file, too.
4178     * Note that the current implementation only preserves the last
4179     * modification time.
4180     * <p>
4181     * By using different {@link ArchiveDetector}s for the source and
4182     * destination, this method can be used to do advanced stuff like
4183     * unzipping any archive file in the source tree to a plain directory
4184     * in the destination tree (where <code>srcDetector</code> could be
4185     * {@link ArchiveDetector#DEFAULT} and <code>dstDetector</code> must be
4186     * {@link ArchiveDetector#NULL}) or changing the encoding by subclassing
4187     * the {@link DefaultArchiveDetector}.
4188     * <p>
4189     * <table border="2" cellpadding="4">
4190     * <tr>
4191     * <th>Feature</th>
4192     * <th>Supported</th>
4193     * </tr>
4194     * <tr>
4195     * <td>Preserves file attributes</td>
4196     * <td>Best effort</td>
4197     * </tr>
4198     * <tr>
4199     * <td>Copies directories recursively</td>
4200     * <td>Yes</td>
4201     * </tr>
4202     * <tr>
4203     * <td>Reads and overwrites special files</td>
4204     * <td>No</td>
4205     * </tr>
4206     * <tr>
4207     * <td>Closes parameter streams</td>
4208     * <td>n/a</td>
4209     * </tr>
4210     * <tr>
4211     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
4212     * <td>Yes</td>
4213     * </tr>
4214     * <tr>
4215     * <td>Deletes partial written files on failure</td>
4216     * <td>Yes</td>
4217     * </tr>
4218     * <tr>
4219     * <td>Deletes partial written directories on failure</td>
4220     * <td>No</td>
4221     * </tr>
4222     * <tr>
4223     * <td>Atomic</td>
4224     * <td>No</td>
4225     * </tr>
4226     * </table>
4227     *
4228     * @param dst The destination file. Note that although this just needs to
4229     * be a plain <code>java.io.File</code>, archive files and entries
4230     * are only supported for instances of this class.
4231     * @param srcDetector The object used to detect any archive files
4232     * in the source directory tree.
4233     * @param dstDetector The object used to detect any archive files
4234     * in the destination directory tree.
4235     * @return <code>true</code> if and only if the operation succeeded.
4236     * @throws NullPointerException If any parameter is <code>null</code>.
4237     * @see <a HREF="#copy_methods">Copy Methods</a>
4238     */

4239    public boolean archiveCopyAllTo(
4240            final java.io.File JavaDoc dst,
4241            final ArchiveDetector srcDetector,
4242            final ArchiveDetector dstDetector) {
4243        return cp_r(this, dst, srcDetector, dstDetector, true);
4244    }
4245
4246    /**
4247     * Copies the input stream <code>in</code> to the output stream
4248     * <code>out</code>.
4249     * <p>
4250     * <table border="2" cellpadding="4">
4251     * <tr>
4252     * <th>Feature</th>
4253     * <th>Supported</th>
4254     * </tr>
4255     * <tr>
4256     * <td>Preserves file attributes</td>
4257     * <td>n/a</td>
4258     * </tr>
4259     * <tr>
4260     * <td>Copies directories recursively</td>
4261     * <td>n/a</td>
4262     * </tr>
4263     * <tr>
4264     * <td>Reads and overwrites special files</td>
4265     * <td>n/a</td>
4266     * </tr>
4267     * <tr>
4268     * <td>Closes parameter streams</td>
4269     * <td>Always</td>
4270     * </tr>
4271     * <tr>
4272     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
4273     * <td>n/a</td>
4274     * </tr>
4275     * <tr>
4276     * <td>Deletes partial written files on failure</td>
4277     * <td>n/a</td>
4278     * </tr>
4279     * <tr>
4280     * <td>Deletes partial written directories on failure</td>
4281     * <td>n/a</td>
4282     * </tr>
4283     * <tr>
4284     * <td>Atomic</td>
4285     * <td>No</td>
4286     * </tr>
4287     * </table>
4288     *
4289     * @param in The input stream.
4290     * @param out The output stream.
4291     * @throws InputIOException If copying the data fails because of an
4292     * IOException in the input stream.
4293     * @throws IOException If copying the data fails because of an
4294     * IOException in the output stream.
4295     * @throws NullPointerException If any parameter is <code>null</code>.
4296     * @see <a HREF="#copy_methods">Copy Methods</a>
4297     */

4298    public static void cp(
4299            final InputStream in,
4300            final OutputStream out)
4301    throws IOException {
4302        try {
4303            try {
4304                cat(in, out);
4305            } finally {
4306                out.close();
4307            }
4308        } finally {
4309            try {
4310                in.close();
4311            } catch (IOException failure) {
4312                throw new InputIOException(failure);
4313            }
4314        }
4315    }
4316
4317    /**
4318     * Copies <code>src</code> to <code>dst</code>.
4319     * <p>
4320     * <table border="2" cellpadding="4">
4321     * <tr>
4322     * <th>Feature</th>
4323     * <th>Supported</th>
4324     * </tr>
4325     * <tr>
4326     * <td>Preserves file attributes</td>
4327     * <td>None</td>
4328     * </tr>
4329     * <tr>
4330     * <td>Copies directories recursively</td>
4331     * <td>No</td>
4332     * </tr>
4333     * <tr>
4334     * <td>Reads and overwrites special files</td>
4335     * <td>Yes</td>
4336     * </tr>
4337     * <tr>
4338     * <td>Closes parameter streams</td>
4339     * <td>n/a</td>
4340     * </tr>
4341     * <tr>
4342     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
4343     * <td>Yes</td>
4344     * </tr>
4345     * <tr>
4346     * <td>Deletes partial written files on failure</td>
4347     * <td>Yes</td>
4348     * </tr>
4349     * <tr>
4350     * <td>Deletes partial written directories on failure</td>
4351     * <td>n/a</td>
4352     * </tr>
4353     * <tr>
4354     * <td>Atomic</td>
4355     * <td>No</td>
4356     * </tr>
4357     * </table>
4358     *
4359     * @param src The source file. Note that although this just needs to
4360     * be a plain <code>java.io.File</code>, archive entries are only
4361     * supported for instances of this class.
4362     * @param dst The destination file. Note that although this just needs to
4363     * be a plain <code>java.io.File</code>, archive entries are only
4364     * supported for instances of this class.
4365     * @throws FileBusyException If an archive entry cannot get accessed
4366     * because the client application is trying to input or output
4367     * to the same archive file simultaneously and the respective
4368     * archive driver does not support this or the archive file needs
4369     * an automatic update which cannot get performed because the
4370     * client is still using other open {@link FileInputStream}s or
4371     * {@link FileOutputStream}s for other entries in the same archive
4372     * file.
4373     * @throws FileNotFoundException If either the source or the destination
4374     * cannot get accessed.
4375     * @throws InputIOException If copying the data fails because of an
4376     * IOException in the source.
4377     * @throws IOException If copying the data fails because of an
4378     * IOException in the destination.
4379     * @throws NullPointerException If any parameter is <code>null</code>.
4380     * @see <a HREF="#copy_methods">Copy Methods</a>
4381     */

4382    public static final void cp(java.io.File JavaDoc src, java.io.File JavaDoc dst)
4383    throws IOException {
4384        cp(src, dst, false);
4385    }
4386
4387    /**
4388     * Copies <code>src</code> to <code>dst</code> and tries to preserve
4389     * all attributes of the source file to the destination file, too.
4390     * Currently, only the last modification time is preserved.
4391     * <p>
4392     * <table border="2" cellpadding="4">
4393     * <tr>
4394     * <th>Feature</th>
4395     * <th>Supported</th>
4396     * </tr>
4397     * <tr>
4398     * <td>Preserves file attributes</td>
4399     * <td>Best effort</td>
4400     * </tr>
4401     * <tr>
4402     * <td>Copies directories recursively</td>
4403     * <td>No</td>
4404     * </tr>
4405     * <tr>
4406     * <td>Reads and overwrites special files</td>
4407     * <td>Yes</td>
4408     * </tr>
4409     * <tr>
4410     * <td>Closes parameter streams</td>
4411     * <td>n/a</td>
4412     * </tr>
4413     * <tr>
4414     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
4415     * <td>Yes</td>
4416     * </tr>
4417     * <tr>
4418     * <td>Deletes partial written files on failure</td>
4419     * <td>Yes</td>
4420     * </tr>
4421     * <tr>
4422     * <td>Deletes partial written directories on failure</td>
4423     * <td>n/a</td>
4424     * </tr>
4425     * <tr>
4426     * <td>Atomic</td>
4427     * <td>No</td>
4428     * </tr>
4429     * </table>
4430     *
4431     * @param src The source file. Note that although this just needs to
4432     * be a plain <code>java.io.File</code>, archive entries are only
4433     * supported for instances of this class.
4434     * @param dst The destination file. Note that although this just needs to
4435     * be a plain <code>java.io.File</code>, archive entries are only
4436     * supported for instances of this class.
4437     * @throws FileBusyException If an archive entry cannot get accessed
4438     * because the client application is trying to input or output
4439     * to the same archive file simultaneously and the respective
4440     * archive driver does not support this or the archive file needs
4441     * an automatic update which cannot get performed because the
4442     * client is still using other open {@link FileInputStream}s or
4443     * {@link FileOutputStream}s for other entries in the same archive
4444     * file.
4445     * @throws FileNotFoundException If either the source or the destination
4446     * cannot get accessed.
4447     * @throws InputIOException If copying the data fails because of an
4448     * IOException in the source.
4449     * @throws IOException If copying the data fails because of an
4450     * IOException in the destination.
4451     * @throws NullPointerException If any parameter is <code>null</code>.
4452     * @see <a HREF="#copy_methods">Copy Methods</a>
4453     */

4454    public static final void cp_p(java.io.File JavaDoc src, java.io.File JavaDoc dst)
4455    throws IOException {
4456        cp(src, dst, true);
4457    }
4458
4459    private static final void cp(
4460            final java.io.File JavaDoc src,
4461            final java.io.File JavaDoc dst,
4462            final boolean preserve)
4463    throws IOException {
4464        if (contains(src, dst))
4465            throw new ContainsFileException(src, dst);
4466        cp0(src, dst, preserve);
4467    }
4468
4469    /**
4470     * Unchecked parameters version.
4471     */

4472    private static void cp0(
4473            final java.io.File JavaDoc src,
4474            final java.io.File JavaDoc dst,
4475            final boolean preserve)
4476    throws IOException {
4477        try {
4478            ArchiveController.cp(src, dst, preserve);
4479        } catch (FileNotFoundException failure) {
4480            throw failure;
4481        } catch (IOException failure) {
4482            dst.delete();
4483            throw failure;
4484        }
4485    }
4486
4487    private static final boolean cp_r(
4488            final java.io.File JavaDoc src,
4489            final java.io.File JavaDoc dst,
4490            final ArchiveDetector srcDetector,
4491            final ArchiveDetector dstDetector,
4492            final boolean preserve) {
4493        try {
4494            if (contains(src, dst))
4495                return false;
4496            cp_r0(src, dst, srcDetector, dstDetector, preserve);
4497            return true;
4498        } catch (IOException failure) {
4499            return false;
4500        }
4501    }
4502
4503    /**
4504     * Unchecked parameters version.
4505     */

4506    private static void cp_r0(
4507            final java.io.File JavaDoc src,
4508            final java.io.File JavaDoc dst,
4509            final ArchiveDetector srcDetector,
4510            final ArchiveDetector dstDetector,
4511            final boolean preserve)
4512    throws IOException {
4513        if (src.isDirectory()) {
4514            if (!dst.mkdir() && !dst.isDirectory())
4515                throw new IOException("Destination is not a directory!");
4516            final String JavaDoc[] members = src.list();
4517            if (dst instanceof File && ((File) dst).innerArchive != null) {
4518                // Create sorted entries if writing a new archive.
4519
// This is a courtesy only, so natural order is sufficient.
4520
Arrays.sort(members);
4521            }
4522            for (int i = 0, l = members.length; i < l; i++) {
4523                final String JavaDoc member = members[i];
4524                cp_r0( srcDetector.createFile(src, member),
4525                        dstDetector.createFile(dst, member),
4526                        srcDetector, dstDetector,
4527                        preserve);
4528            }
4529            if (preserve) {
4530                long srcLastModified = src.lastModified();
4531                // Use current time for copies of ghost directories!
4532
if (srcLastModified > 0
4533                        || !(src instanceof File) || !((File) src).isEntry())
4534                    if (!dst.setLastModified(srcLastModified))
4535                        throw new IOException("Cannot set last modification time!");
4536            }
4537        } else if (src.isFile() && (!dst.exists() || dst.isFile())) {
4538            cp0(src, dst, preserve);
4539        } else {
4540            throw new IOException("Cannot copy non-existent or special files!");
4541        }
4542    }
4543
4544    /**
4545     * Copies the input stream <code>in</code> to this file or
4546     * entry in an archive file without closing the input stream.
4547     * <p>
4548     * <table border="2" cellpadding="4">
4549     * <tr>
4550     * <th>Feature</th>
4551     * <th>Supported</th>
4552     * </tr>
4553     * <tr>
4554     * <td>Preserves file attributes</td>
4555     * <td>n/a</td>
4556     * </tr>
4557     * <tr>
4558     * <td>Copies directories recursively</td>
4559     * <td>n/a</td>
4560     * </tr>
4561     * <tr>
4562     * <td>Reads and overwrites special files</td>
4563     * <td>Yes</td>
4564     * </tr>
4565     * <tr>
4566     * <td>Closes parameter streams</td>
4567     * <td>Never</td>
4568     * </tr>
4569     * <tr>
4570     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
4571     * <td>n/a</td>
4572     * </tr>
4573     * <tr>
4574     * <td>Deletes partial written files on failure</td>
4575     * <td>Yes</td>
4576     * </tr>
4577     * <tr>
4578     * <td>Deletes partial written directories on failure</td>
4579     * <td>n/a</td>
4580     * </tr>
4581     * <tr>
4582     * <td>Atomic</td>
4583     * <td>No</td>
4584     * </tr>
4585     * </table>
4586     *
4587     * @param in The input stream.
4588     * @return <code>true</code> if and only if the operation succeeded.
4589     * @see <a HREF="#copy_methods">Copy Methods</a>
4590     */

4591    public boolean catFrom(final InputStream in) {
4592        try {
4593            final OutputStream out = detector.createFileOutputStream(this, false);
4594            try {
4595                try {
4596                    cat(in, out);
4597                } finally {
4598                    out.close();
4599                }
4600                return true;
4601            } catch (IOException failure) {
4602                delete();
4603            }
4604        } catch (IOException failure) {
4605        }
4606        return false;
4607    }
4608
4609    /**
4610     * Copies this file or entry in an archive file to the output stream
4611     * <code>out</code> without closing it.
4612     * <p>
4613     * <table border="2" cellpadding="4">
4614     * <tr>
4615     * <th>Feature</th>
4616     * <th>Supported</th>
4617     * </tr>
4618     * <tr>
4619     * <td>Preserves file attributes</td>
4620     * <td>n/a</td>
4621     * </tr>
4622     * <tr>
4623     * <td>Copies directories recursively</td>
4624     * <td>n/a</td>
4625     * </tr>
4626     * <tr>
4627     * <td>Reads and overwrites special files</td>
4628     * <td>Yes</td>
4629     * </tr>
4630     * <tr>
4631     * <td>Closes parameter streams</td>
4632     * <td>Never</td>
4633     * </tr>
4634     * <tr>
4635     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
4636     * <td>n/a</td>
4637     * </tr>
4638     * <tr>
4639     * <td>Deletes partial written files on failure</td>
4640     * <td>n/a</td>
4641     * </tr>
4642     * <tr>
4643     * <td>Deletes partial written directories on failure</td>
4644     * <td>n/a</td>
4645     * </tr>
4646     * <tr>
4647     * <td>Atomic</td>
4648     * <td>No</td>
4649     * </tr>
4650     * </table>
4651     *
4652     * @param out The output stream.
4653     * @return <code>true</code> if and only if the operation succeeded.
4654     * @see <a HREF="#copy_methods">Copy Methods</a>
4655     */

4656    public boolean catTo(final OutputStream out) {
4657        try {
4658            final InputStream in = detector.createFileInputStream(this);
4659            try {
4660                cat(in, out);
4661            } finally {
4662                in.close();
4663            }
4664            return true;
4665        } catch (IOException failure) {
4666        }
4667        return false;
4668    }
4669
4670    /**
4671     * Copies all data from one stream to another without closing them.
4672     * <p>
4673     * <table border="2" cellpadding="4">
4674     * <tr>
4675     * <th>Feature</th>
4676     * <th>Supported</th>
4677     * </tr>
4678     * <tr>
4679     * <td>Preserves file attributes</td>
4680     * <td>n/a</td>
4681     * </tr>
4682     * <tr>
4683     * <td>Copies directories recursively</td>
4684     * <td>n/a</td>
4685     * </tr>
4686     * <tr>
4687     * <td>Reads and overwrites special files</td>
4688     * <td>n/a</td>
4689     * </tr>
4690     * <tr>
4691     * <td>Closes parameter streams</td>
4692     * <td>Never</td>
4693     * </tr>
4694     * <tr>
4695     * <td><a HREF="#DDC">Direct Data Copying (DDC)</a></td>
4696     * <td>n/a</td>
4697     * </tr>
4698     * <tr>
4699     * <td>Deletes partial written files on failure</td>
4700     * <td>n/a</td>
4701     * </tr>
4702     * <tr>
4703     * <td>Deletes partial written directories on failure</td>
4704     * <td>n/a</td>
4705     * </tr>
4706     * <tr>
4707     * <td>Atomic</td>
4708     * <td>No</td>
4709     * </tr>
4710     * </table>
4711     *
4712     * @param in The input stream.
4713     * @param out The output stream.
4714     * @throws InputIOException If copying the data fails because of an
4715     * IOException in the input stream.
4716     * @throws IOException If copying the data fails because of an
4717     * IOException in the output stream.
4718     * @see <a HREF="#copy_methods">Copy Methods</a>
4719     */

4720    public static void cat(final InputStream in, final OutputStream out)
4721    throws IOException {
4722        if (in == null || out == null)
4723            throw new NullPointerException JavaDoc();
4724
4725        // Note that we do not use PipedInput/OutputStream because these
4726
// classes are slooowww. This is partially because they are using
4727
// Object.wait()/notify() in a suboptimal way and partially because
4728
// they copy data to and from an additional buffer byte array, which
4729
// is redundant if the data to be transferred is already held in
4730
// another byte array.
4731
// As an implication of the latter reason, although the idea of
4732
// adopting the pipe concept to threads looks tempting it is actually
4733
// bad design: Pipes are a good means of interprocess communication,
4734
// where processes cannot access each others data directly without
4735
// using an external data structure like the pipe as a commonly shared
4736
// FIFO buffer.
4737
// However, threads are different: They share the same memory and thus
4738
// we can use much more elaborated algorithms for data transfer.
4739

4740        // Finally, in this case we will simply cycle through an array of
4741
// byte buffers, where an additionally created reader executor will fill
4742
// the buffers with data from the input and the current executor will
4743
// flush the filled buffers to the output.
4744

4745        final Buffer[] buffers = allocateBuffers();
4746
4747        /*
4748         * The task that cycles through the buffers in order to fill them
4749         * with input.
4750         */

4751        class Reader implements Runnable JavaDoc {
4752
4753            /** The index of the next buffer to be written. */
4754            int off;
4755
4756            /** The number of buffers filled with data to be written. */
4757            int len;
4758
4759            /** The IOException that happened in this task, if any. */
4760            volatile InputIOException exception;
4761
4762            public void run() {
4763                // Cache some data for better performance.
4764
final InputStream _in = in;
4765                final Buffer[] _buffers = buffers;
4766                final int _buffersLen = buffers.length;
4767
4768                // The writer executor interrupts this executor to signal
4769
// that it cannot handle more input because there has been
4770
// an IOException during writing.
4771
// We stop processing in this case.
4772
int read;
4773                do {
4774                    // Wait until a buffer is available.
4775
final Buffer buffer;
4776                    synchronized (this) {
4777                        while (len >= _buffersLen) {
4778                            try {
4779                                wait();
4780                            } catch (InterruptedException JavaDoc interrupted) {
4781                                return;
4782                            }
4783                        }
4784                        buffer = _buffers[(off + len) % _buffersLen];
4785                    }
4786
4787                    // Fill buffer until end of file or buffer.
4788
// This should normally complete in one loop cycle, but
4789
// we do not depend on this as it would be a violation
4790
// of InputStream's contract.
4791
final byte[] buf = buffer.buf;
4792                    try {
4793                        read = _in.read(buf, 0, buf.length);
4794                    } catch (IOException failure) {
4795                        read = -1;
4796                        exception = new InputIOException(failure);
4797                    }
4798                    if (Thread.interrupted())
4799                        read = -1; // throws away buf - OK in this context
4800
buffer.read = read;
4801
4802                    // Advance head and notify writer.
4803
synchronized (this) {
4804                        len++;
4805                        notify(); // only the writer could be waiting now!
4806
}
4807                } while (read != -1);
4808            }
4809        } // class Reader
4810

4811        try {
4812            final Reader reader = new Reader();
4813            final Task task = readerExecutor.submit(reader);
4814
4815            // Cache some data for better performance.
4816
final int buffersLen = buffers.length;
4817
4818            int write;
4819            while (true) {
4820                // Wait until a buffer is available.
4821
final int off;
4822                final Buffer buffer;
4823                synchronized (reader) {
4824                    while (reader.len <= 0) {
4825                        try {
4826                            reader.wait();
4827                        } catch (InterruptedException JavaDoc ignored) {
4828                        }
4829                    }
4830                    off = reader.off;
4831                    buffer = buffers[off];
4832                }
4833
4834                // Stop on last buffer.
4835
write = buffer.read;
4836                if (write == -1)
4837                    break; // reader has terminated because of EOF or exception
4838

4839                // Process buffer.
4840
final byte[] buf = buffer.buf;
4841                try {
4842                    out.write(buf, 0, write);
4843                } catch (IOException failure) {
4844                    // Cancel reader thread synchronously.
4845
// Cancellation of the reader thread is required
4846
// so that a re-entry to the cat(...) method by the same
4847
// thread cannot not reuse the same shared buffers that
4848
// an unfinished reader thread of a previous call is
4849
// still using.
4850
task.cancel();
4851                    throw failure;
4852                }
4853
4854                // Advance tail and notify reader.
4855
synchronized (reader) {
4856                    reader.off = (off + 1) % buffersLen;
4857                    reader.len--;
4858                    reader.notify(); // only the reader could be waiting now!
4859
}
4860            }
4861
4862            if (reader.exception != null)
4863                throw reader.exception;
4864        } finally {
4865            releaseBuffers(buffers);
4866        }
4867    }
4868
4869    private static Executor getExecutor(final String JavaDoc threadName) {
4870        try {
4871            // Take advantage of Java 5 cached thread pools.
4872
final Class JavaDoc cl = Class.forName("de.schlichtherle.io.JSE5Executor");
4873            final Constructor co = cl.getConstructor(new Class JavaDoc[] { String JavaDoc.class });
4874            return (Executor) co.newInstance(new Object JavaDoc[] { threadName });
4875        } catch (Throwable JavaDoc failure) {
4876            return new LegacyExecutor(threadName);
4877        }
4878    }
4879
4880    interface Executor {
4881        Task submit(Runnable JavaDoc target);
4882    }
4883
4884    interface Task {
4885        void cancel();
4886    }
4887
4888    private static final Buffer[] allocateBuffers() {
4889        synchronized (Buffer.list) {
4890            Buffer[] buffers;
4891            for (Iterator i = Buffer.list.iterator(); i.hasNext(); ) {
4892                buffers = (Buffer[]) ((Reference) i.next()).get();
4893                i.remove();
4894                if (buffers != null)
4895                    return buffers;
4896            }
4897        }
4898
4899        // A minimum of two buffers is required.
4900
// The actual number is optimized to compensate for oscillating
4901
// I/O bandwidths like e.g. with network shares.
4902
final Buffer[] buffers = new Buffer[4];
4903        for (int i = buffers.length; --i >= 0; )
4904            buffers[i] = new Buffer();
4905        return buffers;
4906    }
4907
4908    private static final void releaseBuffers(Buffer[] buffers) {
4909        synchronized (Buffer.list) {
4910            Buffer.list.add(new SoftReference(buffers));
4911        }
4912    }
4913
4914    private static final class Buffer {
4915        /**
4916         * Each entry in this list holds a soft reference to an array
4917         * initialized with instances of this class.
4918         */

4919        static final List list = new LinkedList();
4920
4921        /** The byte buffer used for asynchronous reading and writing. */
4922        byte[] buf = new byte[64 * 1024]; // TODO: Reuse FLATER_BUF_LENGTH of de.schlichtherle.util.zip.ZipConstants
4923

4924        /** The actual number of bytes read into the buffer. */
4925        int read;
4926    }
4927}
4928
Popular Tags