KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > filesystems > FileUtil


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.openide.filesystems;
21
22 import java.io.File JavaDoc;
23 import java.io.FileFilter JavaDoc;
24 import java.io.FilenameFilter JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.OutputStream JavaDoc;
28 import java.io.SyncFailedException JavaDoc;
29 import java.lang.reflect.InvocationTargetException JavaDoc;
30 import java.net.MalformedURLException JavaDoc;
31 import java.net.URI JavaDoc;
32 import java.net.URL JavaDoc;
33 import java.net.URLStreamHandler JavaDoc;
34 import java.util.Arrays JavaDoc;
35 import java.util.Dictionary JavaDoc;
36 import java.util.Enumeration JavaDoc;
37 import java.util.HashMap JavaDoc;
38 import java.util.HashSet JavaDoc;
39 import java.util.Hashtable JavaDoc;
40 import java.util.Iterator JavaDoc;
41 import java.util.Map JavaDoc;
42 import java.util.Set JavaDoc;
43 import java.util.Stack JavaDoc;
44 import java.util.StringTokenizer JavaDoc;
45 import java.util.WeakHashMap JavaDoc;
46 import java.util.jar.JarEntry JavaDoc;
47 import java.util.jar.JarInputStream JavaDoc;
48 import java.util.logging.Level JavaDoc;
49 import java.util.logging.Logger JavaDoc;
50 import javax.swing.Icon JavaDoc;
51 import javax.swing.JFileChooser JavaDoc;
52 import javax.swing.SwingUtilities JavaDoc;
53 import javax.swing.filechooser.FileSystemView JavaDoc;
54 import org.openide.util.Exceptions;
55 import org.openide.util.NbBundle;
56 import org.openide.util.Utilities;
57 import org.openide.util.WeakListeners;
58
59 /** Common utilities for handling files.
60  * This is a dummy class; all methods are static.
61  */

62 public final class FileUtil extends Object JavaDoc {
63     
64     /** Normal header for ZIP files. */
65     private static byte[] ZIP_HEADER_1 = {0x50, 0x4b, 0x03, 0x04};
66     /** Also seems to be used at least in apisupport/project/test/unit/data/example-external-projects/suite3/nbplatform/random/modules/ext/stuff.jar; not known why */
67     private static byte[] ZIP_HEADER_2 = {0x50, 0x4b, 0x05, 0x06};
68     
69     /** transient attributes which should not be copied
70     * of type Set<String>
71     */

72     static final Set JavaDoc<String JavaDoc> transientAttributes = new HashSet JavaDoc<String JavaDoc>();
73
74     static {
75         transientAttributes.add("templateWizardURL"); // NOI18N
76
transientAttributes.add("templateWizardIterator"); // NOI18N
77
transientAttributes.add("templateWizardDescResource"); // NOI18N
78
transientAttributes.add("templateCategory"); // NOI18N
79
transientAttributes.add("instantiatingIterator"); // NOI18N
80
transientAttributes.add("instantiatingWizardURL"); // NOI18N
81
transientAttributes.add("SystemFileSystem.localizingBundle"); // NOI18N
82
transientAttributes.add("SystemFileSystem.icon"); // NOI18N
83
transientAttributes.add("SystemFileSystem.icon32"); // NOI18N
84
}
85
86     /* mapping of file extensions to content-types */
87     private static Dictionary JavaDoc<String JavaDoc, String JavaDoc> map = new Hashtable JavaDoc<String JavaDoc, String JavaDoc>();
88
89     static {
90         // Set up at least this one statically, because it is so basic;
91
// we do not want to rely on Lookup, MIMEResolver, declarative resolvers,
92
// XML layers, etc. just to find this.
93
setMIMEType("xml", "text/xml"); // NOI18N
94
}
95
96     /** Cache for {@link #isArchiveFile(FileObject)}. */
97     private static final Map JavaDoc<FileObject, Boolean JavaDoc> archiveFileCache = new WeakHashMap JavaDoc<FileObject,Boolean JavaDoc>();
98
99     private FileUtil() {
100     }
101
102     /**
103      * Returns FileObject for a folder.
104      * If such a folder does not exist then it is created, including any necessary but nonexistent parent
105      * folders. Note that if this operation fails it may have succeeded in creating some of the necessary
106      * parent folders.
107      * @param folder folder to be created
108      * @return FileObject for a folder
109      * @throws java.io.IOException if the creation fails
110      * @since 7.0
111      */

112     public static FileObject createFolder (final File JavaDoc folder) throws IOException JavaDoc {
113         FileObject retval = null;
114         File JavaDoc root = getRoot(folder);
115         if (!root.exists()) {
116             throw new IOException JavaDoc(folder.getAbsolutePath());
117         }
118         FileObject rootFo = FileUtil.toFileObject(root);
119         assert rootFo != null : root.getAbsolutePath();
120         final String JavaDoc relativePath = getRelativePath(root, folder);
121         try {
122             retval = FileUtil.createFolder(rootFo,relativePath);
123         } catch (IOException JavaDoc ex) {
124             //thus retval = null;
125
}
126         //if refresh needed because of external changes
127
if (retval == null || !retval.isValid()) {
128             rootFo.getFileSystem().refresh(false);
129             retval = FileUtil.createFolder(rootFo,relativePath);
130         }
131         assert retval != null;
132         return retval;
133     }
134     
135     /**Returns FileObject for a data file.
136      * If such a data file does not exist then it is created, including any necessary but nonexistent parent
137      * folders. Note that if this operation fails it may have succeeded in creating some of the necessary
138      * parent folders.
139      * @param data data file to be created
140      * @return FileObject for a data file
141      * @throws java.io.IOException if the creation fails
142      * @since 7.0
143      */

144     public static FileObject createData (final File JavaDoc data) throws IOException JavaDoc {
145         FileObject retval = null;
146         File JavaDoc root = getRoot(data);
147         if (!root.exists()) {
148             throw new IOException JavaDoc(data.getAbsolutePath());
149         }
150         FileObject rootFo = FileUtil.toFileObject(root);
151         assert rootFo != null : root.getAbsolutePath();
152         final String JavaDoc relativePath = getRelativePath(root, data);
153         try {
154             retval = FileUtil.createData(rootFo,relativePath);
155         } catch (IOException JavaDoc ex) {
156             //thus retval = null;
157
}
158         //if refresh needed because of external changes
159
if (retval == null || !retval.isValid()) {
160             rootFo.getFileSystem().refresh(false);
161             retval = FileUtil.createData(rootFo,relativePath);
162         }
163         assert retval != null;
164         return retval;
165     }
166         
167     private static File JavaDoc getRoot(final File JavaDoc dir) {
168         File JavaDoc retval = dir;
169         for (; retval.getParentFile() != null; retval = retval.getParentFile());
170         assert retval != null;
171         return retval;
172     }
173     
174     private static String JavaDoc getRelativePath(final File JavaDoc dir, final File JavaDoc file) {
175         Stack JavaDoc<String JavaDoc> stack = new Stack JavaDoc<String JavaDoc>();
176         File JavaDoc tempFile = file;
177         while(tempFile != null && !tempFile.equals(dir)) {
178             stack.push (tempFile.getName());
179             tempFile = tempFile.getParentFile();
180         }
181         assert tempFile != null : file.getAbsolutePath() + "not found in " + dir.getAbsolutePath();//NOI18N
182
StringBuilder JavaDoc retval = new StringBuilder JavaDoc();
183         while (!stack.isEmpty()) {
184             retval.append(stack.pop());
185             if (!stack.isEmpty()) {
186                 retval.append('/');//NOI18N
187
}
188         }
189         return retval.toString();
190     }
191     
192     /** Copies stream of files.
193     * <P>
194     * Please be aware, that this method doesn't close any of passed streams.
195     * @param is input stream
196     * @param os output stream
197     */

198     public static void copy(InputStream JavaDoc is, OutputStream JavaDoc os)
199     throws IOException JavaDoc {
200         final byte[] BUFFER = new byte[4096];
201         int len;
202
203         for (;;) {
204             len = is.read(BUFFER);
205
206             if (len == -1) {
207                 return;
208             }
209
210             os.write(BUFFER, 0, len);
211         }
212     }
213
214     /** Copies file to the selected folder.
215      * This implementation simply copies the file by stream content.
216     * @param source source file object
217     * @param destFolder destination folder
218     * @param newName file name (without extension) of destination file
219     * @param newExt extension of destination file
220     * @return the created file object in the destination folder
221     * @exception IOException if <code>destFolder</code> is not a folder or does not exist; the destination file already exists; or
222     * another critical error occurs during copying
223     */

224     static FileObject copyFileImpl(FileObject source, FileObject destFolder, String JavaDoc newName, String JavaDoc newExt)
225     throws IOException JavaDoc {
226         FileObject dest = destFolder.createData(newName, newExt);
227
228         FileLock lock = null;
229         InputStream JavaDoc bufIn = null;
230         OutputStream JavaDoc bufOut = null;
231
232         try {
233             lock = dest.lock();
234             bufIn = source.getInputStream();
235
236             if (dest instanceof AbstractFileObject) {
237                 /** prevents from firing fileChange*/
238                 bufOut = ((AbstractFileObject) dest).getOutputStream(lock, false);
239             } else {
240                 bufOut = dest.getOutputStream(lock);
241             }
242
243             copy(bufIn, bufOut);
244             copyAttributes(source, dest);
245         } finally {
246             if (bufIn != null) {
247                 bufIn.close();
248             }
249
250             if (bufOut != null) {
251                 bufOut.close();
252             }
253
254             if (lock != null) {
255                 lock.releaseLock();
256             }
257         }
258
259         return dest;
260     }
261
262     //
263
// public methods
264
//
265

266     /** Factory method that creates an empty implementation of a filesystem that
267      * completely resides in a memory.
268      * @return a blank writable filesystem
269      * @since 4.43
270      */

271     public static FileSystem createMemoryFileSystem() {
272         return new MemoryFileSystem();
273     }
274
275     /** Copies file to the selected folder.
276     * This implementation simply copies the file by stream content.
277     * @param source source file object
278     * @param destFolder destination folder
279     * @param newName file name (without extension) of destination file
280     * @param newExt extension of destination file
281     * @return the created file object in the destination folder
282     * @exception IOException if <code>destFolder</code> is not a folder or does not exist; the destination file already exists; or
283     * another critical error occurs during copying
284     */

285     public static FileObject copyFile(FileObject source, FileObject destFolder, String JavaDoc newName, String JavaDoc newExt)
286     throws IOException JavaDoc {
287         return source.copy(destFolder, newName, newExt);
288     }
289
290     /** Copies file to the selected folder.
291     * This implementation simply copies the file by stream content.
292     * Uses the extension of the source file.
293     * @param source source file object
294     * @param destFolder destination folder
295     * @param newName file name (without extension) of destination file
296     * @return the created file object in the destination folder
297     * @exception IOException if <code>destFolder</code> is not a folder or does not exist; the destination file already exists; or
298     * another critical error occurs during copying
299     */

300     public static FileObject copyFile(FileObject source, FileObject destFolder, String JavaDoc newName)
301     throws IOException JavaDoc {
302         return copyFile(source, destFolder, newName, source.getExt());
303     }
304
305     /** Moves file to the selected folder.
306      * This implementation uses a copy-and-delete mechanism, and automatically uses the necessary lock.
307     * @param source source file object
308     * @param destFolder destination folder
309     * @param newName file name (without extension) of destination file
310     * @return new file object
311     * @exception IOException if either the {@link #copyFile copy} or {@link FileObject#delete delete} failed
312     */

313     public static FileObject moveFile(FileObject source, FileObject destFolder, String JavaDoc newName)
314     throws IOException JavaDoc {
315         FileLock lock = null;
316
317         try {
318             lock = source.lock();
319
320             return source.move(lock, destFolder, newName, source.getExt());
321         } finally {
322             if (lock != null) {
323                 lock.releaseLock();
324             }
325         }
326     }
327
328     /** Returns a folder on given filesystem if such a folder exists.
329      * If not then a folder is created, including any necessary but nonexistent parent
330      * folders. Note that if this operation fails it may have succeeded in creating some of the necessary
331      * parent folders.
332      * The name of the new folder can be
333      * specified as a multi-component pathname whose components are separated
334      * by File.separatorChar or &quot;/&quot; (forward slash).
335      *
336      * @param folder where the new folder will be placed in
337      * @param name name of the new folder
338      * @return the new folder
339      * @exception IOException if the creation fails
340      */

341     public static FileObject createFolder(FileObject folder, String JavaDoc name)
342     throws IOException JavaDoc {
343         String JavaDoc separators;
344
345         if (File.separatorChar != '/') {
346             separators = "/" + File.separatorChar; // NOI18N
347
} else {
348             separators = "/"; // NOI18N
349
}
350
351         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(name, separators);
352
353         while (st.hasMoreElements()) {
354             name = st.nextToken();
355
356             if (name.length() > 0) {
357                 FileObject f = folder.getFileObject(name);
358
359                 if (f == null) {
360                     try {
361                         f = folder.createFolder(name);
362                     } catch (SyncFailedException JavaDoc ex) {
363                         // there might be unconsistency between the cache
364
// and the disk, that is why
365
folder.refresh();
366
367                         // and try again
368
f = folder.getFileObject(name);
369
370                         if (f == null) {
371                             // if still not found than we have to report the
372
// exception
373
throw ex;
374                         }
375                     }
376                 }
377
378                 folder = f;
379             }
380         }
381
382         return folder;
383     }
384
385     /** Returns a data file on given filesystem if such a data file exists.
386     * If not then a data file is created, including any necessary but nonexistent parent
387     * folders. Note that if this operation fails it may have succeeded in creating some of the necessary
388     * parent folders. The name of
389     * data file can be composed as resource name (e. g. org/netbeans/myfolder/mydata ).
390     *
391     * @param folder to begin with creation at
392     * @param name name of data file as a resource
393     * @return the data file for given name
394     * @exception IOException if the creation fails
395     */

396     public static FileObject createData(FileObject folder, String JavaDoc name)
397     throws IOException JavaDoc {
398         if (folder == null) {
399             throw new IllegalArgumentException JavaDoc("Null folder"); // NOI18N
400
}
401
402         if (name == null) {
403             throw new IllegalArgumentException JavaDoc("Null name"); // NOI18N
404
}
405
406         String JavaDoc foldername;
407         String JavaDoc dataname;
408         String JavaDoc fname;
409         String JavaDoc ext;
410         int index = name.lastIndexOf('/');
411         FileObject data;
412
413         // names with '/' on the end are not valid
414
if (index >= name.length()) {
415             throw new IOException JavaDoc("Wrong file name."); // NOI18N
416
}
417
418         // if name contains '/', create necessary folder first
419
if (index != -1) {
420             foldername = name.substring(0, index);
421             dataname = name.substring(index + 1);
422             folder = createFolder(folder, foldername);
423             assert folder != null;
424         } else {
425             dataname = name;
426         }
427
428         // create data
429
index = dataname.lastIndexOf('.');
430
431         if (index != -1) {
432             fname = dataname.substring(0, index);
433             ext = dataname.substring(index + 1);
434         } else {
435             fname = dataname;
436             ext = ""; // NOI18N
437
}
438
439         data = folder.getFileObject(fname, ext);
440
441         if (data == null) {
442             try {
443                 data = folder.createData(fname, ext);
444                 assert data != null : "FileObject.createData cannot return null; called on " + folder + " + " + fname +
445                 " + " + ext; // #50802
446
} catch (SyncFailedException JavaDoc ex) {
447                 // there might be unconsistency between the cache
448
// and the disk, that is why
449
folder.refresh();
450
451                 // and try again
452
data = folder.getFileObject(fname, ext);
453
454                 if (data == null) {
455                     // if still not found than we have to report the
456
// exception
457
throw ex;
458                 }
459             }
460         }
461
462         return data;
463     }
464
465     /** Finds appropriate java.io.File to FileObject if possible.
466      * If not possible then null is returned.
467      * This is the inverse operation of {@link #toFileObject}.
468      * @param fo FileObject whose corresponding File will be looked for
469      * @return java.io.File or null if no corresponding File exists.
470      * @since 1.29
471      */

472     public static File JavaDoc toFile(FileObject fo) {
473         File JavaDoc retVal = (File JavaDoc) fo.getAttribute("java.io.File"); // NOI18N;
474

475         if (retVal == null) {
476             URL JavaDoc fileURL = null;
477             int[] types = new int[] { URLMapper.INTERNAL, URLMapper.EXTERNAL };
478
479             for (int i = 0; ((fileURL == null) || "file".equals(fileURL.getProtocol())) && (i < types.length); i++) { // NOI18N
480
fileURL = URLMapper.findURL(fo, types[i]);
481             }
482
483             if ((fileURL != null) && "file".equals(fileURL.getProtocol())) {
484                 retVal = new File JavaDoc(URI.create(fileURL.toExternalForm()));
485             }
486         }
487
488         return (retVal != null) ? normalizeFile(retVal) : null;
489     }
490
491     /**
492      * Converts a disk file to a matching file object.
493      * This is the inverse operation of {@link #toFile}.
494      * <p class="nonnormative">
495      * If you are running with the MasterFS module enabled, that will guarantee
496      * that this method never returns null for a file which exists on disk.
497      * </p>
498      * @param file a disk file (may or may not exist). This file
499      * must be {@link #normalizeFile normalized}.
500      * @return a corresponding file object, or null if the file does not exist
501      * or there is no {@link URLMapper} available to convert it
502      * @since 4.29
503      */

504     public static FileObject toFileObject(File JavaDoc file) {
505         boolean asserts = false;
506         assert asserts = true;
507         if (asserts && !file.equals(normalizeFile(file))) {
508             throw new IllegalArgumentException JavaDoc(
509                 "Parameter file was not " + // NOI18N
510
"normalized. Was " + file + " instead of " + normalizeFile(file)
511             ); // NOI18N
512
}
513
514         FileObject retVal = null;
515
516         try {
517             URL JavaDoc url = fileToURL(file);
518
519             if (
520                 (url.getAuthority() != null) &&
521                     (Utilities.isWindows() || (Utilities.getOperatingSystem() == Utilities.OS_OS2))
522             ) {
523                 return null;
524             }
525
526             retVal = URLMapper.findFileObject(url);
527
528             /*probably temporary piece of code to catch the cause of #46630*/
529         } catch (MalformedURLException JavaDoc e) {
530             retVal = null;
531         }
532
533         return retVal;
534     }
535         
536     static URL JavaDoc fileToURL(File JavaDoc file) throws MalformedURLException JavaDoc {
537         URL JavaDoc retVal = null;
538
539         if (canBeCanonicalizedOnWindows(file)) {
540             retVal = file.toURI().toURL();
541         } else {
542             retVal = new URL JavaDoc("file:/" + file.getAbsolutePath()); //NOI18N
543
}
544
545         return retVal;
546     }
547
548     /** Finds appropriate FileObjects to java.io.File if possible.
549      * If not possible then empty array is returned. More FileObjects may
550      * correspond to one java.io.File that`s why array is returned.
551      * @param file File whose corresponding FileObjects will be looked for.
552      * The file has to be "normalized" otherwise IllegalArgumentException is thrown.
553      * See {@link #normalizeFile} for how to do that.
554      * @return corresponding FileObjects or empty array if no
555      * corresponding FileObject exists.
556      * @since 1.29
557      * @deprecated Use {@link #toFileObject} instead.
558      */

559     @Deprecated JavaDoc
560     public static FileObject[] fromFile(File JavaDoc file) {
561         FileObject[] retVal;
562
563         if (!file.equals(normalizeFile(file))) {
564             throw new IllegalArgumentException JavaDoc(
565                 "Parameter file was not " + // NOI18N
566
"normalized. Was " + file + " instead of " + normalizeFile(file)
567             ); // NOI18N
568
}
569
570         try {
571             URL JavaDoc url = (file.toURI().toURL());
572
573             if (
574                 (url.getAuthority() != null) &&
575                     (Utilities.isWindows() || (Utilities.getOperatingSystem() == Utilities.OS_OS2))
576             ) {
577                 return null;
578             }
579
580             retVal = URLMapper.findFileObjects(url);
581         } catch (MalformedURLException JavaDoc e) {
582             retVal = null;
583         }
584
585         return retVal;
586     }
587
588     /** Copies attributes from one file to another.
589     * Note: several special attributes will not be copied, as they should
590     * semantically be transient. These include attributes used by the
591     * template wizard (but not the template attribute itself).
592     * @param source source file object
593     * @param dest destination file object
594     * @exception IOException if the copying failed
595     */

596     public static void copyAttributes(FileObject source, FileObject dest)
597     throws IOException JavaDoc {
598         Enumeration JavaDoc<String JavaDoc> attrKeys = source.getAttributes();
599
600         while (attrKeys.hasMoreElements()) {
601             String JavaDoc key = attrKeys.nextElement();
602
603             if (transientAttributes.contains(key)) {
604                 continue;
605             }
606
607             if (isTransient(source, key)) {
608                 continue;
609             }
610
611             Object JavaDoc value = source.getAttribute(key);
612
613             if (value != null) {
614                 dest.setAttribute(key, value);
615             }
616         }
617     }
618
619     static boolean isTransient(FileObject fo, String JavaDoc attrName) {
620         return XMLMapAttr.ModifiedAttribute.isTransient(fo, attrName);
621     }
622
623     /** Extract jar file into folder represented by file object. If the JAR contains
624     * files with name filesystem.attributes, it is assumed that these files
625     * has been created by DefaultAttributes implementation and the content
626     * of these files is treated as attributes and added to extracted files.
627     * <p><code>META-INF/</code> directories are skipped over.
628     *
629     * @param fo file object of destination folder
630     * @param is input stream of jar file
631     * @exception IOException if the extraction fails
632     * @deprecated Use of XML filesystem layers generally obsoletes this method.
633     */

634     @Deprecated JavaDoc
635     public static void extractJar(final FileObject fo, final InputStream JavaDoc is)
636     throws IOException JavaDoc {
637         FileSystem fs = fo.getFileSystem();
638
639         fs.runAtomicAction(
640             new FileSystem.AtomicAction() {
641                 public void run() throws IOException JavaDoc {
642                     extractJarImpl(fo, is);
643                 }
644             }
645         );
646     }
647
648     /** Does the actual extraction of the Jar file.
649      */

650     private static void extractJarImpl(FileObject fo, InputStream JavaDoc is)
651     throws IOException JavaDoc {
652         JarInputStream JavaDoc jis;
653         JarEntry JavaDoc je;
654
655         // files with extended attributes (name, DefaultAttributes.Table)
656
HashMap JavaDoc<String JavaDoc, DefaultAttributes.Table> attributes =
657                 new HashMap JavaDoc<String JavaDoc, DefaultAttributes.Table>(7);
658
659         jis = new JarInputStream JavaDoc(is);
660
661         while ((je = jis.getNextJarEntry()) != null) {
662             String JavaDoc name = je.getName();
663
664             if (name.toLowerCase().startsWith("meta-inf/")) {
665                 continue; // NOI18N
666
}
667
668             if (je.isDirectory()) {
669                 createFolder(fo, name);
670
671                 continue;
672             }
673
674             if (DefaultAttributes.acceptName(name)) {
675                 // file with extended attributes
676
DefaultAttributes.Table table = DefaultAttributes.loadTable(jis, name);
677                 attributes.put(name, table);
678             } else {
679                 // copy the file
680
FileObject fd = createData(fo, name);
681                 FileLock lock = fd.lock();
682
683                 try {
684                     OutputStream JavaDoc os = fd.getOutputStream(lock);
685
686                     try {
687                         copy(jis, os);
688                     } finally {
689                         os.close();
690                     }
691                 } finally {
692                     lock.releaseLock();
693                 }
694             }
695         }
696
697         //
698
// apply all extended attributes
699
//
700
Iterator JavaDoc it = attributes.entrySet().iterator();
701
702         while (it.hasNext()) {
703             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
704
705             String JavaDoc fileName = (String JavaDoc) entry.getKey();
706             int last = fileName.lastIndexOf('/');
707             String JavaDoc dirName;
708
709             if (last != -1) {
710                 dirName = fileName.substring(0, last + 1);
711             } else {
712                 dirName = ""; // NOI18N
713
}
714
715             String JavaDoc prefix = fo.isRoot() ? dirName : (fo.getPath() + '/' + dirName);
716
717             DefaultAttributes.Table t = (DefaultAttributes.Table) entry.getValue();
718             Iterator JavaDoc files = t.keySet().iterator();
719
720             while (files.hasNext()) {
721                 String JavaDoc orig = (String JavaDoc) files.next();
722                 String JavaDoc fn = prefix + orig;
723                 FileObject obj = fo.getFileSystem().findResource(fn);
724
725                 if (obj == null) {
726                     continue;
727                 }
728
729                 Enumeration JavaDoc<String JavaDoc> attrEnum = t.attrs(orig);
730
731                 while (attrEnum.hasMoreElements()) {
732                     // iterate thru all arguments
733
String JavaDoc attrName = attrEnum.nextElement();
734
735                     // Note: even transient attributes set here!
736
Object JavaDoc value = t.getAttr(orig, attrName);
737
738                     if (value != null) {
739                         obj.setAttribute(attrName, value);
740                     }
741                 }
742             }
743         }
744     }
745      // extractJar
746

747     /** Gets the extension of a specified file name. The extension is
748     * everything after the last dot.
749     *
750     * @param fileName name of the file
751     * @return extension of the file (or <code>""</code> if it had none)
752     */

753     public static String JavaDoc getExtension(String JavaDoc fileName) {
754         int index = fileName.lastIndexOf("."); // NOI18N
755

756         if (index == -1) {
757             return ""; // NOI18N
758
} else {
759             return fileName.substring(index + 1);
760         }
761     }
762
763     /** Finds an unused file name similar to that requested in the same folder.
764      * The specified file name is used if that does not yet exist or is
765      * {@link FileObject#isVirtual isVirtual}.
766      * Otherwise, the first available name of the form <code>basename_nnn.ext</code> (counting from one) is used.
767      *
768      * <p><em>Caution:</em> this method does not lock the parent folder
769      * to prevent race conditions: i.e. it is possible (though unlikely)
770      * that the resulting name will have been created by another thread
771      * just as you were about to create the file yourself (if you are,
772      * in fact, intending to create it just after this call). Since you
773      * cannot currently lock a folder against child creation actions,
774      * the safe approach is to use a loop in which a free name is
775      * retrieved; an attempt is made to {@link FileObject#createData create}
776      * that file; and upon an <code>IOException</code> during
777      * creation, retry the loop up to a few times before giving up.
778      *
779     * @param folder parent folder
780     * @param name preferred base name of file
781     * @param ext extension to use
782     * @return a free file name <strong>(without the extension)</strong>
783      */

784     public static String JavaDoc findFreeFileName(FileObject folder, String JavaDoc name, String JavaDoc ext) {
785         if (checkFreeName(folder, name, ext)) {
786             return name;
787         }
788
789         for (int i = 1;; i++) {
790             String JavaDoc destName = name + "_" + i; // NOI18N
791

792             if (checkFreeName(folder, destName, ext)) {
793                 return destName;
794             }
795         }
796     }
797
798     /** Finds an unused folder name similar to that requested in the same parent folder.
799      * <p>See caveat for <code>findFreeFileName</code>.
800      * @see #findFreeFileName findFreeFileName
801     * @param folder parent folder
802     * @param name preferred folder name
803     * @return a free folder name
804     */

805     public static String JavaDoc findFreeFolderName(FileObject folder, String JavaDoc name) {
806         if (checkFreeName(folder, name, null)) {
807             return name;
808         }
809
810         for (int i = 1;; i++) {
811             String JavaDoc destName = name + "_" + i; // NOI18N
812

813             if (checkFreeName(folder, destName, null)) {
814                 return destName;
815             }
816         }
817     }
818
819     /**
820      * Gets a relative resource path between folder and fo.
821      * @param folder root of filesystem or any other folder in folders hierarchy
822      * @param fo arbitrary FileObject in folder's tree (including folder itself)
823      * @return relative path between folder and fo. The returned path never
824      * starts with a '/'. It never ends with a '/'. Specifically, if
825      * folder==fo, returns "". Returns <code>null</code> if fo is not in
826      * folder's tree.
827      * @see #isParentOf
828      * @since 4.16
829      */

830     public static String JavaDoc getRelativePath(FileObject folder, FileObject fo) {
831         if (!isParentOf(folder, fo) && (folder != fo)) {
832             return null;
833         }
834
835         String JavaDoc result = fo.getPath().substring(folder.getPath().length());
836
837         if (result.startsWith("/")) {
838             result = result.substring(1);
839         }
840
841         return result;
842     }
843
844     /** Test if given name is free in given folder.
845      * @param fo folder to check in
846      * @param name name of the file or folder to check
847      * @param ext extension of the file (null for folders)
848      * @return true, if such name does not exists
849      */

850     private static boolean checkFreeName(FileObject fo, String JavaDoc name, String JavaDoc ext) {
851         if ((Utilities.isWindows() || (Utilities.getOperatingSystem() == Utilities.OS_OS2)) || Utilities.isMac()) {
852             // case-insensitive, do some special check
853
Enumeration JavaDoc<? extends FileObject> en = fo.getChildren(false);
854
855             while (en.hasMoreElements()) {
856                 fo = en.nextElement();
857
858                 String JavaDoc n = fo.getName();
859                 String JavaDoc e = fo.getExt();
860
861                 // different names => check others
862
if (!n.equalsIgnoreCase(name)) {
863                     continue;
864                 }
865
866                 // same name + without extension => no
867
if (((ext == null) || (ext.trim().length() == 0)) && ((e == null) || (e.trim().length() == 0))) {
868                     return fo.isVirtual();
869                 }
870
871                 // one of there is witout extension => check next
872
if ((ext == null) || (e == null)) {
873                     continue;
874                 }
875
876                 if (ext.equalsIgnoreCase(e)) {
877                     // same name + same extension => no
878
return fo.isVirtual();
879                 }
880             }
881
882             // no of the files has similar name and extension
883
return true;
884         } else {
885             if (ext == null) {
886                 fo = fo.getFileObject(name);
887
888                 if (fo == null) {
889                     return true;
890                 }
891
892                 return fo.isVirtual();
893             } else {
894                 fo = fo.getFileObject(name, ext);
895
896                 if (fo == null) {
897                     return true;
898                 }
899
900                 return fo.isVirtual();
901             }
902         }
903     }
904
905     // note: "sister" is preferred in English, please don't ask me why --jglick // NOI18N
906

907     /** Finds brother file with same base name but different extension.
908     * @param fo the file to find the brother for or <CODE>null</CODE>
909     * @param ext extension for the brother file
910     * @return a brother file (with the requested extension and the same parent folder as the original) or
911     * <CODE>null</CODE> if the brother file does not exist or the original file was <CODE>null</CODE>
912     */

913     public static FileObject findBrother(FileObject fo, String JavaDoc ext) {
914         if (fo == null) {
915             return null;
916         }
917
918         FileObject parent = fo.getParent();
919
920         if (parent == null) {
921             return null;
922         }
923
924         return parent.getFileObject(fo.getName(), ext);
925     }
926
927     /** Obtain MIME type for a well-known extension.
928     * If there is a case-sensitive match, that is used, else will fall back
929     * to a case-insensitive match.
930     * @param ext the extension: <code>"jar"</code>, <code>"zip"</code>, etc.
931     * @return the MIME type for the extension, or <code>null</code> if the extension is unrecognized
932     * @deprecated use {@link #getMIMEType(FileObject) getMIMEType(FileObject)} as MIME cannot
933     * be generally detected by file object extension.
934     */

935     @Deprecated JavaDoc
936     public static String JavaDoc getMIMEType(String JavaDoc ext) {
937         String JavaDoc s = map.get(ext);
938
939         if (s != null) {
940             return s;
941         } else {
942             return map.get(ext.toLowerCase());
943         }
944     }
945
946     /** Resolves MIME type. Registered resolvers are invoked and used to achieve this goal.
947     * Resolvers must subclass MIMEResolver. If resolvers don`t recognize MIME type then
948     * MIME type is obtained for a well-known extension.
949     * @param fo whose MIME type should be recognized
950     * @return the MIME type for the FileObject, or <code>null</code> if the FileObject is unrecognized
951     */

952     public static String JavaDoc getMIMEType(FileObject fo) {
953         String JavaDoc retVal = MIMESupport.findMIMEType(fo, null);
954
955         if (retVal == null) {
956             retVal = getMIMEType(fo.getExt());
957         }
958
959         return retVal;
960     }
961
962     /** Finds mime type by calling getMIMEType, but
963      * instead of returning null it fallbacks to default type
964      * either text/plain or content/unknown (even for folders)
965      */

966     static String JavaDoc getMIMETypeOrDefault(FileObject fo) {
967         String JavaDoc def = getMIMEType(fo.getExt());
968         String JavaDoc t = MIMESupport.findMIMEType(fo, def);
969
970         if (t == null) {
971             // #42965: never allowed
972
t = "content/unknown"; // NOI18N
973
}
974
975         return t;
976     }
977
978     /**
979      * Register MIME type for a new extension.
980      * Note that you may register a case-sensitive extension if that is
981      * relevant (for example <samp>*.C</samp> for C++) but if you register
982      * a lowercase extension it will by default apply to uppercase extensions
983      * too (for use on Windows or generally for situations where filenames
984      * become accidentally uppercase).
985      * @param ext the file extension (should be lowercase unless you specifically care about case)
986      * @param mimeType the new MIME type
987      * @throws IllegalArgumentException if this extension was already registered with a <em>different</em> MIME type
988      * @see #getMIMEType
989      * @deprecated You should instead use the more general {@link MIMEResolver} system.
990      */

991     @Deprecated JavaDoc
992     public static void setMIMEType(String JavaDoc ext, String JavaDoc mimeType) {
993         synchronized (map) {
994             String JavaDoc old = map.get(ext);
995
996             if (old == null) {
997                 map.put(ext, mimeType);
998             } else {
999                 if (!old.equals(mimeType)) {
1000                    throw new IllegalArgumentException JavaDoc(
1001                        "Cannot overwrite existing MIME type mapping for extension `" + // NOI18N
1002
ext + "' with " + mimeType + " (was " + old + ")"
1003                    ); // NOI18N
1004
}
1005
1006                // else do nothing
1007
}
1008        }
1009    }
1010
1011    /**
1012     * Construct a stream handler that handles the <code>nbfs</code> URL protocol
1013     * used for accessing file objects directly.
1014     * This method is not intended for module use; only the core
1015     * should need to call it.
1016     * Modules probably need only use {@link URLMapper} to create and decode such
1017     * URLs.
1018     * @since 3.17
1019     */

1020    public static URLStreamHandler JavaDoc nbfsURLStreamHandler() {
1021        return FileURL.HANDLER;
1022    }
1023
1024    /** Recursively checks whether the file is underneath the folder. It checks whether
1025     * the file and folder are located on the same filesystem, in such case it checks the
1026     * parent <code>FileObject</code> of the file recursively until the folder is found
1027     * or the root of the filesystem is reached.
1028     * <p><strong>Warning:</strong> this method will return false in the case that
1029     * <code>folder == fo</code>.
1030     * @param folder the root of folders hierarchy to search in
1031     * @param fo the file to search for
1032     * @return <code>true</code>, if <code>fo</code> lies somewhere underneath the <code>folder</code>,
1033     * <code>false</code> otherwise
1034     * @since 3.16
1035     */

1036    public static boolean isParentOf(FileObject folder, FileObject fo) {
1037        if (folder == null) {
1038            throw new IllegalArgumentException JavaDoc("Tried to pass null folder arg"); // NOI18N
1039
}
1040
1041        if (fo == null) {
1042            throw new IllegalArgumentException JavaDoc("Tried to pass null fo arg"); // NOI18N
1043
}
1044
1045        if (folder.isData()) {
1046            return false;
1047        }
1048
1049        try {
1050            if (folder.getFileSystem() != fo.getFileSystem()) {
1051                return false;
1052            }
1053        } catch (FileStateInvalidException e) {
1054            return false;
1055        }
1056
1057        FileObject parent = fo.getParent();
1058
1059        while (parent != null) {
1060            if (parent == folder) {
1061                return true;
1062            }
1063
1064            parent = parent.getParent();
1065        }
1066
1067        return false;
1068    }
1069
1070    /** Creates a weak implementation of FileChangeListener.
1071     *
1072     * @param l the listener to delegate to
1073     * @param source the source that the listener should detach from when
1074     * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE>
1075     * @return a FileChangeListener delegating to <CODE>l</CODE>.
1076     * @since 4.10
1077     */

1078    public static FileChangeListener weakFileChangeListener(FileChangeListener l, Object JavaDoc source) {
1079        return WeakListeners.create(FileChangeListener.class, l, source);
1080    }
1081
1082    /** Creates a weak implementation of FileStatusListener.
1083     *
1084     * @param l the listener to delegate to
1085     * @param source the source that the listener should detach from when
1086     * listener <CODE>l</CODE> is freed, can be <CODE>null</CODE>
1087     * @return a FileChangeListener delegating to <CODE>l</CODE>.
1088     * @since 4.10
1089     */

1090    public static FileStatusListener weakFileStatusListener(FileStatusListener l, Object JavaDoc source) {
1091        return WeakListeners.create(FileStatusListener.class, l, source);
1092    }
1093
1094    /**
1095     * Get an appropriate display name for a file object.
1096     * If the file corresponds to a path on disk, this will be the disk path.
1097     * Otherwise the name will mention the filesystem name or archive name in case
1098     * the file comes from archive and relative path. Relative path will be mentioned
1099     * just in case that passed <code>FileObject</code> isn't root {@link FileObject#isRoot}.
1100     *
1101     * @param fo a file object
1102     * @return a display name indicating where the file is
1103     * @since 4.39
1104     */

1105    public static String JavaDoc getFileDisplayName(FileObject fo) {
1106        String JavaDoc displayName = null;
1107        File JavaDoc f = FileUtil.toFile(fo);
1108
1109        if (f != null) {
1110            displayName = f.getAbsolutePath();
1111        } else {
1112            FileObject archiveFile = FileUtil.getArchiveFile(fo);
1113
1114            if (archiveFile != null) {
1115                displayName = getArchiveDisplayName(fo, archiveFile);
1116            }
1117        }
1118
1119        if (displayName == null) {
1120            try {
1121                if (fo.isRoot()) {
1122                    displayName = fo.getFileSystem().getDisplayName();
1123                } else {
1124                    displayName = NbBundle.getMessage(
1125                            FileUtil.class, "LBL_file_in_filesystem", fo.getPath(), fo.getFileSystem().getDisplayName()
1126                        );
1127                }
1128            } catch (FileStateInvalidException e) {
1129                // Not relevant now, just use the simple path.
1130
displayName = fo.getPath();
1131            }
1132        }
1133
1134        return displayName;
1135    }
1136
1137    private static String JavaDoc getArchiveDisplayName(FileObject fo, FileObject archiveFile) {
1138        String JavaDoc displayName = null;
1139
1140        File JavaDoc f = FileUtil.toFile(archiveFile);
1141
1142        if (f != null) {
1143            String JavaDoc archivDisplayName = f.getAbsolutePath();
1144
1145            if (fo.isRoot()) {
1146                displayName = archivDisplayName;
1147            } else {
1148                String JavaDoc entryPath = fo.getPath();
1149                displayName = NbBundle.getMessage(
1150                        FileUtil.class, "LBL_file_in_filesystem", entryPath, archivDisplayName
1151                    );
1152            }
1153        }
1154
1155        return displayName;
1156    }
1157
1158    /**
1159     * Normalize a file path to a clean form.
1160     * This method may for example make sure that the returned file uses
1161     * the natural case on Windows; that old Windows 8.3 filenames are changed to the long form;
1162     * that relative paths are changed to be
1163     * absolute; that <code>.</code> and <code>..</code> sequences are removed; etc.
1164     * Unlike {@link File#getCanonicalFile} this method will not traverse symbolic links on Unix.
1165     * <p>This method involves some overhead and should not be called frivolously.
1166     * Generally it should be called on <em>incoming</em> pathnames that are gotten from user input
1167     * (including filechoosers), configuration files, Ant properties, etc. <em>Internal</em>
1168     * calculations should not need to renormalize paths since {@link File#listFiles},
1169     * {@link File#getParentFile}, etc. will not produce abnormal variants.
1170     * @param file file to normalize
1171     * @return normalized file
1172     * @since 4.48
1173     */

1174    public static File JavaDoc normalizeFile(final File JavaDoc file) {
1175        File JavaDoc retFile;
1176
1177        if ((Utilities.isWindows() || (Utilities.getOperatingSystem() == Utilities.OS_OS2))) {
1178            retFile = normalizeFileOnWindows(file);
1179        } else if (Utilities.isMac()) {
1180            retFile = normalizeFileOnMac(file);
1181        } else {
1182            retFile = normalizeFileOnUnixAlike(file);
1183        }
1184
1185        return (file.getPath().equals(retFile.getPath())) ? file : retFile;
1186    }
1187
1188    private static File JavaDoc normalizeFileOnUnixAlike(File JavaDoc file) {
1189        // On Unix, do not want to traverse symlinks.
1190
if (file.getAbsolutePath().equals("/..")) { // NOI18N
1191

1192            // Special treatment.
1193
file = new File JavaDoc("/"); // NOI18N
1194
} else {
1195            // URI.normalize removes ../ and ./ sequences nicely.
1196
file = new File JavaDoc(file.toURI().normalize()).getAbsoluteFile();
1197        }
1198
1199        return file;
1200    }
1201
1202    private static File JavaDoc normalizeFileOnMac(final File JavaDoc file) {
1203        File JavaDoc retVal = file;
1204
1205        try {
1206            // URI.normalize removes ../ and ./ sequences nicely.
1207
File JavaDoc absoluteFile = new File JavaDoc(file.toURI().normalize());
1208            File JavaDoc canonicalFile = file.getCanonicalFile();
1209            boolean isSymLink = !canonicalFile.getAbsolutePath().equalsIgnoreCase(absoluteFile.getAbsolutePath());
1210
1211            if (isSymLink) {
1212                retVal = normalizeSymLinkOnMac(absoluteFile);
1213            } else {
1214                retVal = canonicalFile;
1215            }
1216        } catch (IOException JavaDoc ioe) {
1217            Logger.getAnonymousLogger().severe("Normalization failed on file " + file + ": " + ioe);
1218
1219            // OK, so at least try to absolutize the path
1220
retVal = file.getAbsoluteFile();
1221        }
1222
1223        return retVal;
1224    }
1225
1226    /**
1227     * @param file is expected to be already absolute with removed ../ and ./
1228     */

1229    private static File JavaDoc normalizeSymLinkOnMac(final File JavaDoc file)
1230    throws IOException JavaDoc {
1231        File JavaDoc retVal = File.listRoots()[0];
1232        File JavaDoc pureCanonicalFile = retVal;
1233
1234        final String JavaDoc pattern = File.separator + ".." + File.separator; //NOI18N
1235
final String JavaDoc fileName;
1236
1237        { // strips insufficient non-<tt>".."</tt> segments preceding them
1238

1239            String JavaDoc tmpFileName = file.getAbsolutePath();
1240            int index = tmpFileName.lastIndexOf(pattern);
1241
1242            if (index > -1) {
1243                tmpFileName = tmpFileName.substring(index + pattern.length()); //Remove starting {/../}*
1244
}
1245
1246            fileName = tmpFileName;
1247        }
1248
1249        /*normalized step after step*/
1250        StringTokenizer JavaDoc fileSegments = new StringTokenizer JavaDoc(fileName, File.separator);
1251
1252        while (fileSegments.hasMoreTokens()) {
1253            File JavaDoc absolutelyEndingFile = new File JavaDoc(pureCanonicalFile, fileSegments.nextToken());
1254            pureCanonicalFile = absolutelyEndingFile.getCanonicalFile();
1255
1256            boolean isSymLink = !pureCanonicalFile.getAbsolutePath().equalsIgnoreCase(
1257                    absolutelyEndingFile.getAbsolutePath()
1258                );
1259
1260            if (isSymLink) {
1261                retVal = new File JavaDoc(retVal, absolutelyEndingFile.getName());
1262            } else {
1263                retVal = new File JavaDoc(retVal, pureCanonicalFile.getName());
1264            }
1265        }
1266
1267        return retVal;
1268    }
1269
1270    private static File JavaDoc normalizeFileOnWindows(final File JavaDoc file) {
1271        File JavaDoc retVal = null;
1272
1273        if (canBeCanonicalizedOnWindows(file)) {
1274            try {
1275                retVal = file.getCanonicalFile();
1276            } catch (IOException JavaDoc e) {
1277                Logger.getAnonymousLogger().severe(
1278                    "getCanonicalFile() on file " + file + " failed. " + e.toString()
1279                ); // NOI18N
1280
}
1281        }
1282
1283        return (retVal != null) ? retVal : file.getAbsoluteFile();
1284    }
1285
1286    private static FileSystemView JavaDoc fileSystemView;
1287    private static float javaSpecVersion;
1288    private static boolean canBeCanonicalizedOnWindows(final File JavaDoc file) {
1289        /*#4089199, #95031 - Flopy and empty CD-drives can't be canonicalized*/
1290        boolean canBeCanonizalized = true;
1291        if (file.getParent() == null && is4089199()) {//NOI18N
1292
FileSystemView JavaDoc fsv = getFileSystemView();
1293            canBeCanonizalized = (fsv != null) ? !fsv.isFloppyDrive(file) && file.exists() : false;
1294        }
1295        
1296        return canBeCanonizalized;
1297    }
1298    
1299    private static boolean is4089199() {
1300        return getJavaSpecVersion() < 1.6;
1301    }
1302    
1303    private static float getJavaSpecVersion() {
1304        synchronized(FileUtil.class) {
1305            if (javaSpecVersion == 0) {
1306                javaSpecVersion = Float.valueOf(System.getProperty("java.specification.version"));//NOI18N
1307
}
1308        }
1309        return javaSpecVersion;
1310    }
1311    
1312    private static FileSystemView JavaDoc getFileSystemView() {
1313        boolean init = false;
1314        final FileSystemView JavaDoc[] fsv = {fileSystemView};
1315        
1316        synchronized(FileUtil.class) {
1317            init = is4089199() && fsv[0] == null;
1318        }
1319        
1320        if (init) {
1321            try {
1322                if (SwingUtilities.isEventDispatchThread()) {
1323                    fsv[0] = javax.swing.filechooser.FileSystemView.getFileSystemView();
1324                } else {
1325                    SwingUtilities.invokeAndWait(new java.lang.Runnable JavaDoc() {
1326                        public void run() {
1327                            fsv[0] = javax.swing.filechooser.FileSystemView.getFileSystemView();
1328                        }
1329                    });
1330                }
1331            } catch (InterruptedException JavaDoc ex) {
1332                Thread.currentThread().interrupt();
1333            } catch (InvocationTargetException JavaDoc ex) {}
1334            
1335            synchronized(FileUtil.class) {
1336                fileSystemView = fsv[0];
1337            }
1338        }
1339        return fileSystemView;
1340    }
1341
1342    /**
1343     * Returns a FileObject representing the root folder of an archive.
1344     * Clients may need to first call {@link #isArchiveFile(FileObject)} to determine
1345     * if the file object refers to an archive file.
1346     * @param fo a ZIP- (or JAR-) format archive file
1347     * @return a virtual archive root folder, or null if the file is not actually an archive
1348     * @since 4.48
1349     */

1350    public static FileObject getArchiveRoot(FileObject fo) {
1351        URL JavaDoc archiveURL = URLMapper.findURL(fo, URLMapper.EXTERNAL);
1352
1353        if (archiveURL == null) {
1354            return null;
1355        }
1356
1357        return URLMapper.findFileObject(getArchiveRoot(archiveURL));
1358    }
1359
1360    /**
1361     * Returns a URL representing the root of an archive.
1362     * Clients may need to first call {@link #isArchiveFile(URL)} to determine if the URL
1363     * refers to an archive file.
1364     * @param url of a ZIP- (or JAR-) format archive file
1365     * @return the <code>jar</code>-protocol URL of the root of the archive
1366     * @since 4.48
1367     */

1368    public static URL JavaDoc getArchiveRoot(URL JavaDoc url) {
1369        try {
1370            // XXX TBD whether the url should ever be escaped...
1371
return new URL JavaDoc("jar:" + url + "!/"); // NOI18N
1372
} catch (MalformedURLException JavaDoc e) {
1373            throw new AssertionError JavaDoc(e);
1374        }
1375    }
1376
1377    /**
1378     * Returns a FileObject representing an archive file containing the
1379     * FileObject given by the parameter.
1380     * <strong>Remember</strong> that any path within the archive is discarded
1381     * so you may need to check for non-root entries.
1382     * @param fo a file in a JAR filesystem
1383     * @return the file corresponding to the archive itself,
1384     * or null if <code>fo</code> is not an archive entry
1385     * @since 4.48
1386     */

1387    public static FileObject getArchiveFile(FileObject fo) {
1388        try {
1389            FileSystem fs = fo.getFileSystem();
1390
1391            if (fs instanceof JarFileSystem) {
1392                File JavaDoc jarFile = ((JarFileSystem) fs).getJarFile();
1393
1394                return toFileObject(jarFile);
1395            }
1396        } catch (FileStateInvalidException e) {
1397            Exceptions.printStackTrace(e);
1398        }
1399
1400        return null;
1401    }
1402
1403    /**
1404     * Returns the URL of the archive file containing the file
1405     * referred to by a <code>jar</code>-protocol URL.
1406     * <strong>Remember</strong> that any path within the archive is discarded
1407     * so you may need to check for non-root entries.
1408     * @param url a URL
1409     * @return the embedded archive URL, or null if the URL is not a
1410     * <code>jar</code>-protocol URL containing <code>!/</code>
1411     * @since 4.48
1412     */

1413    public static URL JavaDoc getArchiveFile(URL JavaDoc url) {
1414        String JavaDoc protocol = url.getProtocol();
1415
1416        if ("jar".equals(protocol)) { //NOI18N
1417

1418            String JavaDoc path = url.getPath();
1419            int index = path.indexOf("!/"); //NOI18N
1420

1421            if (index >= 0) {
1422                try {
1423                    return new URL JavaDoc(path.substring(0, index));
1424                } catch (MalformedURLException JavaDoc mue) {
1425                    Exceptions.printStackTrace(mue);
1426                }
1427            }
1428        }
1429
1430        return null;
1431    }
1432
1433    /**
1434     * Tests if a file represents a JAR or ZIP archive.
1435     * @param fo the file to be tested
1436     * @return true if the file looks like a ZIP-format archive
1437     * @since 4.48
1438     */

1439    public static boolean isArchiveFile(FileObject fo) {
1440        if (fo == null) {
1441            throw new IllegalArgumentException JavaDoc("Cannot pass null to FileUtil.isArchiveFile"); // NOI18N
1442
}
1443
1444        if (!fo.isValid()) {
1445            return false;
1446        }
1447        // XXX Special handling of virtual file objects: try to determine it using its name, but don't cache the
1448
// result; when the file is checked out the more correct method can be used
1449
if (fo.isVirtual()) {
1450            String JavaDoc path = fo.getPath();
1451            int index = path.lastIndexOf('.');
1452
1453            return (index != -1) && (index > path.lastIndexOf('/') + 1);
1454        }
1455
1456        if (fo.isFolder()) {
1457            return false;
1458        }
1459
1460        // First check the cache.
1461
Boolean JavaDoc b = archiveFileCache.get(fo);
1462
1463        if (b == null) {
1464            // Need to check it.
1465
try {
1466                InputStream JavaDoc in = fo.getInputStream();
1467
1468                try {
1469                    byte[] buffer = new byte[4];
1470                    int len = in.read(buffer, 0, 4);
1471
1472                    if (len == 4) {
1473                        // Got a header, see if it is a ZIP file.
1474
b = Boolean.valueOf(Arrays.equals(ZIP_HEADER_1, buffer) || Arrays.equals(ZIP_HEADER_2, buffer));
1475                    } else {
1476                        //If the length is less than 4, it can be either
1477
//broken (empty) archive file or other empty file.
1478
//Return false and don't cache it, when the archive
1479
//file will be written and closed its length will change
1480
return false;
1481                    }
1482                } finally {
1483                    in.close();
1484                }
1485            } catch (IOException JavaDoc ioe) {
1486                Logger.getLogger(FileUtil.class.getName()).log(Level.INFO, null, ioe);
1487            }
1488
1489            if (b == null) {
1490                String JavaDoc path = fo.getPath();
1491                int index = path.lastIndexOf('.');
1492                b = ((index != -1) && (index > path.lastIndexOf('/') + 1)) ? Boolean.TRUE : Boolean.FALSE;
1493            }
1494
1495            archiveFileCache.put(fo, b);
1496        }
1497
1498        return b.booleanValue();
1499    }
1500
1501    /**
1502     * Tests if a URL represents a JAR or ZIP archive.
1503     * If there is no such file object, the test is done by heuristic: any URL with an extension is
1504     * treated as an archive.
1505     * @param url a URL to a file
1506     * @return true if the URL seems to represent a ZIP-format archive
1507     * @since 4.48
1508     */

1509    public static boolean isArchiveFile(URL JavaDoc url) {
1510        if (url == null) {
1511            throw new NullPointerException JavaDoc("Cannot pass null URL to FileUtil.isArchiveFile"); // NOI18N
1512
}
1513
1514        if ("jar".equals(url.getProtocol())) { //NOI18N
1515

1516            //Already inside archive, return false
1517
return false;
1518        }
1519
1520        FileObject fo = URLMapper.findFileObject(url);
1521
1522        if ((fo != null) && !fo.isVirtual()) {
1523            return isArchiveFile(fo);
1524        } else {
1525            String JavaDoc urlPath = url.getPath();
1526            int index = urlPath.lastIndexOf('.');
1527
1528            return (index != -1) && (index > urlPath.lastIndexOf('/') + 1);
1529        }
1530    }
1531
1532    /**
1533     * Make sure that a JFileChooser does not traverse symlinks on Unix.
1534     * @param chooser a file chooser
1535     * @param currentDirectory if not null, a file to set as the current directory
1536     * using {@link JFileChooser#setCurrentDirectory} without canonicalizing
1537     * @see <a HREF="http://www.netbeans.org/issues/show_bug.cgi?id=46459">Issue #46459</a>
1538     * @see <a HREF="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4906607">JRE bug #4906607</a>
1539     * @since org.openide/1 4.42
1540     */

1541    public static void preventFileChooserSymlinkTraversal(JFileChooser JavaDoc chooser, File JavaDoc currentDirectory) {
1542        if (!(Utilities.isWindows() || (Utilities.getOperatingSystem() == Utilities.OS_OS2))) {
1543            chooser.setCurrentDirectory(wrapFileNoCanonicalize(currentDirectory));
1544            chooser.setFileSystemView(new NonCanonicalizingFileSystemView());
1545        } else {
1546            chooser.setCurrentDirectory(currentDirectory);
1547        }
1548    }
1549
1550    static boolean assertDeprecatedMethod() {
1551        Thread.dumpStack();
1552
1553        return true;
1554    }
1555
1556    private static File JavaDoc wrapFileNoCanonicalize(File JavaDoc f) {
1557        if (f instanceof NonCanonicalizingFile) {
1558            return f;
1559        } else if (f != null) {
1560            return new NonCanonicalizingFile(f);
1561        } else {
1562            return null;
1563        }
1564    }
1565
1566    private static File JavaDoc[] wrapFilesNoCanonicalize(File JavaDoc[] fs) {
1567        if (fs != null) {
1568            for (int i = 0; i < fs.length; i++) {
1569                fs[i] = wrapFileNoCanonicalize(fs[i]);
1570            }
1571        }
1572
1573        return fs;
1574    }
1575
1576    private static final class NonCanonicalizingFile extends File JavaDoc {
1577        public NonCanonicalizingFile(File JavaDoc orig) {
1578            this(orig.getPath());
1579        }
1580
1581        private NonCanonicalizingFile(String JavaDoc path) {
1582            super(path);
1583        }
1584
1585        private NonCanonicalizingFile(URI JavaDoc uri) {
1586            super(uri);
1587        }
1588
1589        public File JavaDoc getCanonicalFile() throws IOException JavaDoc {
1590            return wrapFileNoCanonicalize(normalizeFile(super.getAbsoluteFile()));
1591        }
1592
1593        public String JavaDoc getCanonicalPath() throws IOException JavaDoc {
1594            return normalizeFile(super.getAbsoluteFile()).getAbsolutePath();
1595        }
1596
1597        public File JavaDoc getParentFile() {
1598            return wrapFileNoCanonicalize(super.getParentFile());
1599        }
1600
1601        public File JavaDoc getAbsoluteFile() {
1602            return wrapFileNoCanonicalize(super.getAbsoluteFile());
1603        }
1604
1605        public File JavaDoc[] listFiles() {
1606            return wrapFilesNoCanonicalize(super.listFiles());
1607        }
1608
1609        public File JavaDoc[] listFiles(FileFilter JavaDoc filter) {
1610            return wrapFilesNoCanonicalize(super.listFiles(filter));
1611        }
1612
1613        public File JavaDoc[] listFiles(FilenameFilter JavaDoc filter) {
1614            return wrapFilesNoCanonicalize(super.listFiles(filter));
1615        }
1616    }
1617
1618    private static final class NonCanonicalizingFileSystemView extends FileSystemView JavaDoc {
1619        private final FileSystemView JavaDoc delegate = FileSystemView.getFileSystemView();
1620
1621        public NonCanonicalizingFileSystemView() {
1622        }
1623
1624        public boolean isFloppyDrive(File JavaDoc dir) {
1625            return delegate.isFloppyDrive(dir);
1626        }
1627
1628        public boolean isComputerNode(File JavaDoc dir) {
1629            return delegate.isComputerNode(dir);
1630        }
1631
1632        public File JavaDoc createNewFolder(File JavaDoc containingDir)
1633        throws IOException JavaDoc {
1634            return wrapFileNoCanonicalize(delegate.createNewFolder(containingDir));
1635        }
1636
1637        public boolean isDrive(File JavaDoc dir) {
1638            return delegate.isDrive(dir);
1639        }
1640
1641        public boolean isFileSystemRoot(File JavaDoc dir) {
1642            return delegate.isFileSystemRoot(dir);
1643        }
1644
1645        public File JavaDoc getHomeDirectory() {
1646            return wrapFileNoCanonicalize(delegate.getHomeDirectory());
1647        }
1648
1649        public File JavaDoc createFileObject(File JavaDoc dir, String JavaDoc filename) {
1650            return wrapFileNoCanonicalize(delegate.createFileObject(dir, filename));
1651        }
1652
1653        public Boolean JavaDoc isTraversable(File JavaDoc f) {
1654            return delegate.isTraversable(f);
1655        }
1656
1657        public boolean isFileSystem(File JavaDoc f) {
1658            return delegate.isFileSystem(f);
1659        }
1660
1661        /*
1662        protected File createFileSystemRoot(File f) {
1663            return translate(delegate.createFileSystemRoot(f));
1664        }
1665         */

1666        public File JavaDoc getChild(File JavaDoc parent, String JavaDoc fileName) {
1667            return wrapFileNoCanonicalize(delegate.getChild(parent, fileName));
1668        }
1669
1670        public File JavaDoc getParentDirectory(File JavaDoc dir) {
1671            return wrapFileNoCanonicalize(delegate.getParentDirectory(dir));
1672        }
1673
1674        public Icon JavaDoc getSystemIcon(File JavaDoc f) {
1675            return delegate.getSystemIcon(f);
1676        }
1677
1678        public boolean isParent(File JavaDoc folder, File JavaDoc file) {
1679            return delegate.isParent(folder, file);
1680        }
1681
1682        public String JavaDoc getSystemTypeDescription(File JavaDoc f) {
1683            return delegate.getSystemTypeDescription(f);
1684        }
1685
1686        public File JavaDoc getDefaultDirectory() {
1687            return wrapFileNoCanonicalize(delegate.getDefaultDirectory());
1688        }
1689
1690        public String JavaDoc getSystemDisplayName(File JavaDoc f) {
1691            return delegate.getSystemDisplayName(f);
1692        }
1693
1694        public File JavaDoc[] getRoots() {
1695            return wrapFilesNoCanonicalize(delegate.getRoots());
1696        }
1697
1698        public boolean isHiddenFile(File JavaDoc f) {
1699            return delegate.isHiddenFile(f);
1700        }
1701
1702        public File JavaDoc[] getFiles(File JavaDoc dir, boolean useFileHiding) {
1703            return wrapFilesNoCanonicalize(delegate.getFiles(dir, useFileHiding));
1704        }
1705
1706        public boolean isRoot(File JavaDoc f) {
1707            return delegate.isRoot(f);
1708        }
1709
1710        public File JavaDoc createFileObject(String JavaDoc path) {
1711            return wrapFileNoCanonicalize(delegate.createFileObject(path));
1712        }
1713    }
1714}
1715
Popular Tags