1 /*
   2  * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.filechooser;
  27 
  28 import java.awt.Image;
  29 import java.beans.PropertyChangeListener;
  30 import java.io.File;
  31 import java.io.FileNotFoundException;
  32 import java.io.IOException;
  33 import java.lang.ref.WeakReference;
  34 import java.security.AccessController;
  35 import java.security.PrivilegedAction;
  36 import java.text.MessageFormat;
  37 import java.util.ArrayList;
  38 import java.util.List;
  39 
  40 import javax.swing.Icon;
  41 import javax.swing.ImageIcon;
  42 import javax.swing.JFileChooser;
  43 import javax.swing.UIManager;
  44 
  45 import jdk.internal.ref.CleanerFactory;
  46 import sun.awt.shell.ShellFolder;
  47 
  48 /**
  49  * FileSystemView is JFileChooser's gateway to the
  50  * file system. Since the JDK1.1 File API doesn't allow
  51  * access to such information as root partitions, file type
  52  * information, or hidden file bits, this class is designed
  53  * to intuit as much OS-specific file system information as
  54  * possible.
  55  *
  56  * <p>
  57  *
  58  * Java Licensees may want to provide a different implementation of
  59  * FileSystemView to better handle a given operating system.
  60  *
  61  * @author Jeff Dinkins
  62  */
  63 
  64 // PENDING(jeff) - need to provide a specification for
  65 // how Mac/OS2/BeOS/etc file systems can modify FileSystemView
  66 // to handle their particular type of file system.
  67 
  68 public abstract class FileSystemView {
  69 
  70     static FileSystemView windowsFileSystemView = null;
  71     static FileSystemView unixFileSystemView = null;
  72     //static FileSystemView macFileSystemView = null;
  73     static FileSystemView genericFileSystemView = null;
  74 
  75     private boolean useSystemExtensionHiding =
  76             UIManager.getDefaults().getBoolean("FileChooser.useSystemExtensionHiding");
  77 
  78     /**
  79      * Returns the file system view.
  80      * @return the file system view
  81      */
  82     public static FileSystemView getFileSystemView() {
  83         if(File.separatorChar == '\\') {
  84             if(windowsFileSystemView == null) {
  85                 windowsFileSystemView = new WindowsFileSystemView();
  86             }
  87             return windowsFileSystemView;
  88         }
  89 
  90         if(File.separatorChar == '/') {
  91             if(unixFileSystemView == null) {
  92                 unixFileSystemView = new UnixFileSystemView();
  93             }
  94             return unixFileSystemView;
  95         }
  96 
  97         // if(File.separatorChar == ':') {
  98         //    if(macFileSystemView == null) {
  99         //      macFileSystemView = new MacFileSystemView();
 100         //    }
 101         //    return macFileSystemView;
 102         //}
 103 
 104         if(genericFileSystemView == null) {
 105             genericFileSystemView = new GenericFileSystemView();
 106         }
 107         return genericFileSystemView;
 108     }
 109 
 110     /**
 111      * Constructs a FileSystemView.
 112      */
 113     public FileSystemView() {
 114         final WeakReference<FileSystemView> weakReference = new WeakReference<>(this);
 115         final PropertyChangeListener pcl = evt -> {
 116             final FileSystemView fsv = weakReference.get();
 117             if (fsv != null && evt.getPropertyName().equals("lookAndFeel")) {
 118                 fsv.useSystemExtensionHiding =
 119                         UIManager.getDefaults().getBoolean(
 120                                 "FileChooser.useSystemExtensionHiding");
 121             }
 122         };
 123 
 124         UIManager.addPropertyChangeListener(pcl);
 125         CleanerFactory.cleaner().register(this, () -> {
 126             UIManager.removePropertyChangeListener(pcl);
 127         });
 128     }
 129 
 130     /**
 131      * Determines if the given file is a root in the navigable tree(s).
 132      * Examples: Windows 98 has one root, the Desktop folder. DOS has one root
 133      * per drive letter, <code>C:\</code>, <code>D:\</code>, etc. Unix has one root,
 134      * the <code>"/"</code> directory.
 135      *
 136      * The default implementation gets information from the <code>ShellFolder</code> class.
 137      *
 138      * @param f a <code>File</code> object representing a directory
 139      * @return <code>true</code> if <code>f</code> is a root in the navigable tree.
 140      * @see #isFileSystemRoot
 141      */
 142     public boolean isRoot(File f) {
 143         if (f == null || !f.isAbsolute()) {
 144             return false;
 145         }
 146 
 147         File[] roots = getRoots();
 148         for (File root : roots) {
 149             if (root.equals(f)) {
 150                 return true;
 151             }
 152         }
 153         return false;
 154     }
 155 
 156     /**
 157      * Returns true if the file (directory) can be visited.
 158      * Returns false if the directory cannot be traversed.
 159      *
 160      * @param f the <code>File</code>
 161      * @return <code>true</code> if the file/directory can be traversed, otherwise <code>false</code>
 162      * @see JFileChooser#isTraversable
 163      * @see FileView#isTraversable
 164      * @since 1.4
 165      */
 166     public Boolean isTraversable(File f) {
 167         return Boolean.valueOf(f.isDirectory());
 168     }
 169 
 170     /**
 171      * Name of a file, directory, or folder as it would be displayed in
 172      * a system file browser. Example from Windows: the "M:\" directory
 173      * displays as "CD-ROM (M:)"
 174      *
 175      * The default implementation gets information from the ShellFolder class.
 176      *
 177      * @param f a <code>File</code> object
 178      * @return the file name as it would be displayed by a native file chooser
 179      * @see JFileChooser#getName
 180      * @since 1.4
 181      */
 182     public String getSystemDisplayName(File f) {
 183         if (f == null) {
 184             return null;
 185         }
 186 
 187         String name = f.getName();
 188 
 189         if (!name.equals("..") && !name.equals(".") &&
 190                 (useSystemExtensionHiding || !isFileSystem(f) || isFileSystemRoot(f)) &&
 191                 (f instanceof ShellFolder || f.exists())) {
 192 
 193             try {
 194                 name = getShellFolder(f).getDisplayName();
 195             } catch (FileNotFoundException e) {
 196                 return null;
 197             }
 198 
 199             if (name == null || name.length() == 0) {
 200                 name = f.getPath(); // e.g. "/"
 201             }
 202         }
 203 
 204         return name;
 205     }
 206 
 207     /**
 208      * Type description for a file, directory, or folder as it would be displayed in
 209      * a system file browser. Example from Windows: the "Desktop" folder
 210      * is described as "Desktop".
 211      *
 212      * Override for platforms with native ShellFolder implementations.
 213      *
 214      * @param f a <code>File</code> object
 215      * @return the file type description as it would be displayed by a native file chooser
 216      * or null if no native information is available.
 217      * @see JFileChooser#getTypeDescription
 218      * @since 1.4
 219      */
 220     public String getSystemTypeDescription(File f) {
 221         return null;
 222     }
 223 
 224     /**
 225      * Icon for a file, directory, or folder as it would be displayed in
 226      * a system file browser. Example from Windows: the "M:\" directory
 227      * displays a CD-ROM icon.
 228      *
 229      * The default implementation gets information from the ShellFolder class.
 230      *
 231      * @param f a <code>File</code> object
 232      * @return an icon as it would be displayed by a native file chooser
 233      * @see JFileChooser#getIcon
 234      * @since 1.4
 235      */
 236     public Icon getSystemIcon(File f) {
 237         if (f == null) {
 238             return null;
 239         }
 240 
 241         ShellFolder sf;
 242 
 243         try {
 244             sf = getShellFolder(f);
 245         } catch (FileNotFoundException e) {
 246             return null;
 247         }
 248 
 249         Image img = sf.getIcon(false);
 250 
 251         if (img != null) {
 252             return new ImageIcon(img, sf.getFolderType());
 253         } else {
 254             return UIManager.getIcon(f.isDirectory() ? "FileView.directoryIcon" : "FileView.fileIcon");
 255         }
 256     }
 257 
 258     /**
 259      * On Windows, a file can appear in multiple folders, other than its
 260      * parent directory in the filesystem. Folder could for example be the
 261      * "Desktop" folder which is not the same as file.getParentFile().
 262      *
 263      * @param folder a <code>File</code> object representing a directory or special folder
 264      * @param file a <code>File</code> object
 265      * @return <code>true</code> if <code>folder</code> is a directory or special folder and contains <code>file</code>.
 266      * @since 1.4
 267      */
 268     public boolean isParent(File folder, File file) {
 269         if (folder == null || file == null) {
 270             return false;
 271         } else if (folder instanceof ShellFolder) {
 272                 File parent = file.getParentFile();
 273                 if (parent != null && parent.equals(folder)) {
 274                     return true;
 275                 }
 276             File[] children = getFiles(folder, false);
 277             for (File child : children) {
 278                 if (file.equals(child)) {
 279                     return true;
 280                 }
 281             }
 282             return false;
 283         } else {
 284             return folder.equals(file.getParentFile());
 285         }
 286     }
 287 
 288     /**
 289      *
 290      * @param parent a <code>File</code> object representing a directory or special folder
 291      * @param fileName a name of a file or folder which exists in <code>parent</code>
 292      * @return a File object. This is normally constructed with <code>new
 293      * File(parent, fileName)</code> except when parent and child are both
 294      * special folders, in which case the <code>File</code> is a wrapper containing
 295      * a <code>ShellFolder</code> object.
 296      * @since 1.4
 297      */
 298     public File getChild(File parent, String fileName) {
 299         if (parent instanceof ShellFolder) {
 300             File[] children = getFiles(parent, false);
 301             for (File child : children) {
 302                 if (child.getName().equals(fileName)) {
 303                     return child;
 304                 }
 305             }
 306         }
 307         return createFileObject(parent, fileName);
 308     }
 309 
 310 
 311     /**
 312      * Checks if <code>f</code> represents a real directory or file as opposed to a
 313      * special folder such as <code>"Desktop"</code>. Used by UI classes to decide if
 314      * a folder is selectable when doing directory choosing.
 315      *
 316      * @param f a <code>File</code> object
 317      * @return <code>true</code> if <code>f</code> is a real file or directory.
 318      * @since 1.4
 319      */
 320     public boolean isFileSystem(File f) {
 321         if (f instanceof ShellFolder) {
 322             ShellFolder sf = (ShellFolder)f;
 323             // Shortcuts to directories are treated as not being file system objects,
 324             // so that they are never returned by JFileChooser.
 325             return sf.isFileSystem() && !(sf.isLink() && sf.isDirectory());
 326         } else {
 327             return true;
 328         }
 329     }
 330 
 331     /**
 332      * Creates a new folder with a default folder name.
 333      *
 334      * @param containingDir a {@code File} object denoting directory to contain the new folder
 335      * @return a {@code File} object denoting the newly created folder
 336      * @throws IOException if new folder could not be created
 337      */
 338     public abstract File createNewFolder(File containingDir) throws IOException;
 339 
 340     /**
 341      * Returns whether a file is hidden or not.
 342      *
 343      * @param f a {@code File} object
 344      * @return true if the given {@code File} denotes a hidden file
 345      */
 346     public boolean isHiddenFile(File f) {
 347         return f.isHidden();
 348     }
 349 
 350 
 351     /**
 352      * Is dir the root of a tree in the file system, such as a drive
 353      * or partition. Example: Returns true for "C:\" on Windows 98.
 354      *
 355      * @param dir a <code>File</code> object representing a directory
 356      * @return <code>true</code> if <code>f</code> is a root of a filesystem
 357      * @see #isRoot
 358      * @since 1.4
 359      */
 360     public boolean isFileSystemRoot(File dir) {
 361         return ShellFolder.isFileSystemRoot(dir);
 362     }
 363 
 364     /**
 365      * Used by UI classes to decide whether to display a special icon
 366      * for drives or partitions, e.g. a "hard disk" icon.
 367      *
 368      * The default implementation has no way of knowing, so always returns false.
 369      *
 370      * @param dir a directory
 371      * @return <code>false</code> always
 372      * @since 1.4
 373      */
 374     public boolean isDrive(File dir) {
 375         return false;
 376     }
 377 
 378     /**
 379      * Used by UI classes to decide whether to display a special icon
 380      * for a floppy disk. Implies isDrive(dir).
 381      *
 382      * The default implementation has no way of knowing, so always returns false.
 383      *
 384      * @param dir a directory
 385      * @return <code>false</code> always
 386      * @since 1.4
 387      */
 388     public boolean isFloppyDrive(File dir) {
 389         return false;
 390     }
 391 
 392     /**
 393      * Used by UI classes to decide whether to display a special icon
 394      * for a computer node, e.g. "My Computer" or a network server.
 395      *
 396      * The default implementation has no way of knowing, so always returns false.
 397      *
 398      * @param dir a directory
 399      * @return <code>false</code> always
 400      * @since 1.4
 401      */
 402     public boolean isComputerNode(File dir) {
 403         return ShellFolder.isComputerNode(dir);
 404     }
 405 
 406 
 407     /**
 408      * Returns all root partitions on this system. For example, on
 409      * Windows, this would be the "Desktop" folder, while on DOS this
 410      * would be the A: through Z: drives.
 411      *
 412      * @return an array of {@code File} objects representing all root partitions
 413      *         on this system
 414      */
 415     public File[] getRoots() {
 416         // Don't cache this array, because filesystem might change
 417         File[] roots = (File[])ShellFolder.get("roots");
 418 
 419         for (int i = 0; i < roots.length; i++) {
 420             if (isFileSystemRoot(roots[i])) {
 421                 roots[i] = createFileSystemRoot(roots[i]);
 422             }
 423         }
 424         return roots;
 425     }
 426 
 427 
 428     // Providing default implementations for the remaining methods
 429     // because most OS file systems will likely be able to use this
 430     // code. If a given OS can't, override these methods in its
 431     // implementation.
 432 
 433     /**
 434      * Returns the home directory.
 435      * @return the home directory
 436      */
 437     public File getHomeDirectory() {
 438         return createFileObject(System.getProperty("user.home"));
 439     }
 440 
 441     /**
 442      * Return the user's default starting directory for the file chooser.
 443      *
 444      * @return a <code>File</code> object representing the default
 445      *         starting folder
 446      * @since 1.4
 447      */
 448     public File getDefaultDirectory() {
 449         File f = (File)ShellFolder.get("fileChooserDefaultFolder");
 450         if (isFileSystemRoot(f)) {
 451             f = createFileSystemRoot(f);
 452         }
 453         return f;
 454     }
 455 
 456     /**
 457      * Returns a File object constructed in dir from the given filename.
 458      *
 459      * @param dir an abstract pathname denoting a directory
 460      * @param filename a {@code String} representation of a pathname
 461      * @return a {@code File} object created from {@code dir} and {@code filename}
 462      */
 463     public File createFileObject(File dir, String filename) {
 464         if(dir == null) {
 465             return new File(filename);
 466         } else {
 467             return new File(dir, filename);
 468         }
 469     }
 470 
 471     /**
 472      * Returns a File object constructed from the given path string.
 473      *
 474      * @param path {@code String} representation of path
 475      * @return a {@code File} object created from the given {@code path}
 476      */
 477     public File createFileObject(String path) {
 478         File f = new File(path);
 479         if (isFileSystemRoot(f)) {
 480             f = createFileSystemRoot(f);
 481         }
 482         return f;
 483     }
 484 
 485 
 486     /**
 487      * Gets the list of shown (i.e. not hidden) files.
 488      *
 489      * @param dir the root directory of files to be returned
 490      * @param useFileHiding determine if hidden files are returned
 491      * @return an array of {@code File} objects representing files and
 492      *         directories in the given {@code dir}. It includes hidden
 493      *         files if {@code useFileHiding} is false.
 494      */
 495     public File[] getFiles(File dir, boolean useFileHiding) {
 496         List<File> files = new ArrayList<File>();
 497 
 498         // add all files in dir
 499         if (!(dir instanceof ShellFolder)) {
 500             try {
 501                 dir = getShellFolder(dir);
 502             } catch (FileNotFoundException e) {
 503                 return new File[0];
 504             }
 505         }
 506 
 507         File[] names = ((ShellFolder) dir).listFiles(!useFileHiding);
 508 
 509         if (names == null) {
 510             return new File[0];
 511         }
 512 
 513         for (File f : names) {
 514             if (Thread.currentThread().isInterrupted()) {
 515                 break;
 516             }
 517 
 518             if (!(f instanceof ShellFolder)) {
 519                 if (isFileSystemRoot(f)) {
 520                     f = createFileSystemRoot(f);
 521                 }
 522                 try {
 523                     f = ShellFolder.getShellFolder(f);
 524                 } catch (FileNotFoundException e) {
 525                     // Not a valid file (wouldn't show in native file chooser)
 526                     // Example: C:\pagefile.sys
 527                     continue;
 528                 } catch (InternalError e) {
 529                     // Not a valid file (wouldn't show in native file chooser)
 530                     // Example C:\Winnt\Profiles\joe\history\History.IE5
 531                     continue;
 532                 }
 533             }
 534             if (!useFileHiding || !isHiddenFile(f)) {
 535                 files.add(f);
 536             }
 537         }
 538 
 539         return files.toArray(new File[files.size()]);
 540     }
 541 
 542 
 543 
 544     /**
 545      * Returns the parent directory of <code>dir</code>.
 546      * @param dir the <code>File</code> being queried
 547      * @return the parent directory of <code>dir</code>, or
 548      *   <code>null</code> if <code>dir</code> is <code>null</code>
 549      */
 550     public File getParentDirectory(File dir) {
 551         if (dir == null || !dir.exists()) {
 552             return null;
 553         }
 554 
 555         ShellFolder sf;
 556 
 557         try {
 558             sf = getShellFolder(dir);
 559         } catch (FileNotFoundException e) {
 560             return null;
 561         }
 562 
 563         File psf = sf.getParentFile();
 564 
 565         if (psf == null) {
 566             return null;
 567         }
 568 
 569         if (isFileSystem(psf)) {
 570             File f = psf;
 571             if (!f.exists()) {
 572                 // This could be a node under "Network Neighborhood".
 573                 File ppsf = psf.getParentFile();
 574                 if (ppsf == null || !isFileSystem(ppsf)) {
 575                     // We're mostly after the exists() override for windows below.
 576                     f = createFileSystemRoot(f);
 577                 }
 578             }
 579             return f;
 580         } else {
 581             return psf;
 582         }
 583     }
 584 
 585     /**
 586      * Returns an array of files representing the values to show by default in
 587      * the file chooser selector.
 588      *
 589      * @return an array of {@code File} objects.
 590      * @throws SecurityException if the caller does not have necessary
 591      *                           permissions
 592      * @since 9
 593      */
 594     public File[] getChooserComboBoxFiles() {
 595         return (File[]) ShellFolder.get("fileChooserComboBoxFolders");
 596     }
 597 
 598     /**
 599      * Returns whether the specified file denotes a shell interpreted link which
 600      * can be obtained by the {@link #getLinkLocation(File)}.
 601      *
 602      * @param file a file
 603      * @return whether this is a link
 604      * @throws NullPointerException if {@code file} equals {@code null}
 605      * @throws SecurityException if the caller does not have necessary
 606      *                           permissions
 607      * @see #getLinkLocation(File)
 608      * @since 9
 609      */
 610     public boolean isLink(File file) {
 611         if (file == null) {
 612             throw new NullPointerException("file is null");
 613         }
 614         try {
 615             return ShellFolder.getShellFolder(file).isLink();
 616         } catch (FileNotFoundException e) {
 617             return false;
 618         }
 619     }
 620 
 621     /**
 622      * Returns the regular file referenced by the specified link file if
 623      * the specified file is a shell interpreted link.
 624      * Returns {@code null} if the specified file is not
 625      * a shell interpreted link.
 626      *
 627      * @param file a file
 628      * @return the linked file or {@code null}.
 629      * @throws FileNotFoundException if the linked file does not exist
 630      * @throws NullPointerException if {@code file} equals {@code null}
 631      * @throws SecurityException if the caller does not have necessary
 632      *                           permissions
 633      * @since 9
 634      */
 635     public File getLinkLocation(File file) throws FileNotFoundException {
 636         if (file == null) {
 637             throw new NullPointerException("file is null");
 638         }
 639         ShellFolder shellFolder;
 640         try {
 641             shellFolder = ShellFolder.getShellFolder(file);
 642         } catch (FileNotFoundException e) {
 643             return null;
 644         }
 645         return shellFolder.isLink() ? shellFolder.getLinkLocation() : null;
 646     }
 647 
 648     /**
 649      * Throws {@code FileNotFoundException} if file not found or current thread was interrupted
 650      */
 651     ShellFolder getShellFolder(File f) throws FileNotFoundException {
 652         if (!(f instanceof ShellFolder) && !(f instanceof FileSystemRoot) && isFileSystemRoot(f)) {
 653             f = createFileSystemRoot(f);
 654         }
 655 
 656         try {
 657             return ShellFolder.getShellFolder(f);
 658         } catch (InternalError e) {
 659             System.err.println("FileSystemView.getShellFolder: f="+f);
 660             e.printStackTrace();
 661             return null;
 662         }
 663     }
 664 
 665     /**
 666      * Creates a new <code>File</code> object for <code>f</code> with correct
 667      * behavior for a file system root directory.
 668      *
 669      * @param f a <code>File</code> object representing a file system root
 670      *          directory, for example "/" on Unix or "C:\" on Windows.
 671      * @return a new <code>File</code> object
 672      * @since 1.4
 673      */
 674     protected File createFileSystemRoot(File f) {
 675         return new FileSystemRoot(f);
 676     }
 677 
 678     @SuppressWarnings("serial") // Same-version serialization only
 679     static class FileSystemRoot extends File {
 680         public FileSystemRoot(File f) {
 681             super(f,"");
 682         }
 683 
 684         public FileSystemRoot(String s) {
 685             super(s);
 686         }
 687 
 688         public boolean isDirectory() {
 689             return true;
 690         }
 691 
 692         public String getName() {
 693             return getPath();
 694         }
 695     }
 696 }
 697 
 698 /**
 699  * FileSystemView that handles some specific unix-isms.
 700  */
 701 class UnixFileSystemView extends FileSystemView {
 702 
 703     private static final String newFolderString =
 704             UIManager.getString("FileChooser.other.newFolder");
 705     private static final String newFolderNextString  =
 706             UIManager.getString("FileChooser.other.newFolder.subsequent");
 707 
 708     /**
 709      * Creates a new folder with a default folder name.
 710      */
 711     public File createNewFolder(File containingDir) throws IOException {
 712         if(containingDir == null) {
 713             throw new IOException("Containing directory is null:");
 714         }
 715         File newFolder;
 716         // Unix - using OpenWindows' default folder name. Can't find one for Motif/CDE.
 717         newFolder = createFileObject(containingDir, newFolderString);
 718         int i = 1;
 719         while (newFolder.exists() && i < 100) {
 720             newFolder = createFileObject(containingDir, MessageFormat.format(
 721                     newFolderNextString, i));
 722             i++;
 723         }
 724 
 725         if(newFolder.exists()) {
 726             throw new IOException("Directory already exists:" + newFolder.getAbsolutePath());
 727         } else {
 728             if(!newFolder.mkdirs()) {
 729                 throw new IOException(newFolder.getAbsolutePath());
 730             }
 731         }
 732 
 733         return newFolder;
 734     }
 735 
 736     public boolean isFileSystemRoot(File dir) {
 737         return dir != null && dir.getAbsolutePath().equals("/");
 738     }
 739 
 740     public boolean isDrive(File dir) {
 741         return isFloppyDrive(dir);
 742     }
 743 
 744     public boolean isFloppyDrive(File dir) {
 745         // Could be looking at the path for Solaris, but wouldn't be reliable.
 746         // For example:
 747         // return (dir != null && dir.getAbsolutePath().toLowerCase().startsWith("/floppy"));
 748         return false;
 749     }
 750 
 751     public boolean isComputerNode(File dir) {
 752         if (dir != null) {
 753             String parent = dir.getParent();
 754             if (parent != null && parent.equals("/net")) {
 755                 return true;
 756             }
 757         }
 758         return false;
 759     }
 760 }
 761 
 762 
 763 /**
 764  * FileSystemView that handles some specific windows concepts.
 765  */
 766 class WindowsFileSystemView extends FileSystemView {
 767 
 768     private static final String newFolderString =
 769             UIManager.getString("FileChooser.win32.newFolder");
 770     private static final String newFolderNextString  =
 771             UIManager.getString("FileChooser.win32.newFolder.subsequent");
 772 
 773     public Boolean isTraversable(File f) {
 774         return Boolean.valueOf(isFileSystemRoot(f) || isComputerNode(f) || f.isDirectory());
 775     }
 776 
 777     public File getChild(File parent, String fileName) {
 778         if (fileName.startsWith("\\")
 779             && !fileName.startsWith("\\\\")
 780             && isFileSystem(parent)) {
 781 
 782             //Path is relative to the root of parent's drive
 783             String path = parent.getAbsolutePath();
 784             if (path.length() >= 2
 785                 && path.charAt(1) == ':'
 786                 && Character.isLetter(path.charAt(0))) {
 787 
 788                 return createFileObject(path.substring(0, 2) + fileName);
 789             }
 790         }
 791         return super.getChild(parent, fileName);
 792     }
 793 
 794     /**
 795      * Type description for a file, directory, or folder as it would be displayed in
 796      * a system file browser. Example from Windows: the "Desktop" folder
 797      * is described as "Desktop".
 798      *
 799      * The Windows implementation gets information from the ShellFolder class.
 800      */
 801     public String getSystemTypeDescription(File f) {
 802         if (f == null) {
 803             return null;
 804         }
 805 
 806         try {
 807             return getShellFolder(f).getFolderType();
 808         } catch (FileNotFoundException e) {
 809             return null;
 810         }
 811     }
 812 
 813     /**
 814      * @return the Desktop folder.
 815      */
 816     public File getHomeDirectory() {
 817         File[] roots = getRoots();
 818         return (roots.length == 0) ? null : roots[0];
 819     }
 820 
 821     /**
 822      * Creates a new folder with a default folder name.
 823      */
 824     public File createNewFolder(File containingDir) throws IOException {
 825         if(containingDir == null) {
 826             throw new IOException("Containing directory is null:");
 827         }
 828         // Using NT's default folder name
 829         File newFolder = createFileObject(containingDir, newFolderString);
 830         int i = 2;
 831         while (newFolder.exists() && i < 100) {
 832             newFolder = createFileObject(containingDir, MessageFormat.format(
 833                 newFolderNextString, i));
 834             i++;
 835         }
 836 
 837         if(newFolder.exists()) {
 838             throw new IOException("Directory already exists:" + newFolder.getAbsolutePath());
 839         } else {
 840             if(!newFolder.mkdirs()) {
 841                 throw new IOException(newFolder.getAbsolutePath());
 842             }
 843         }
 844 
 845         return newFolder;
 846     }
 847 
 848     public boolean isDrive(File dir) {
 849         return isFileSystemRoot(dir);
 850     }
 851 
 852     public boolean isFloppyDrive(final File dir) {
 853         String path = AccessController.doPrivileged(new PrivilegedAction<String>() {
 854             public String run() {
 855                 return dir.getAbsolutePath();
 856             }
 857         });
 858 
 859         return path != null && (path.equals("A:\\") || path.equals("B:\\"));
 860     }
 861 
 862     /**
 863      * Returns a File object constructed from the given path string.
 864      */
 865     public File createFileObject(String path) {
 866         // Check for missing backslash after drive letter such as "C:" or "C:filename"
 867         if (path.length() >= 2 && path.charAt(1) == ':' && Character.isLetter(path.charAt(0))) {
 868             if (path.length() == 2) {
 869                 path += "\\";
 870             } else if (path.charAt(2) != '\\') {
 871                 path = path.substring(0, 2) + "\\" + path.substring(2);
 872             }
 873         }
 874         return super.createFileObject(path);
 875     }
 876 
 877     @SuppressWarnings("serial") // anonymous class
 878     protected File createFileSystemRoot(File f) {
 879         // Problem: Removable drives on Windows return false on f.exists()
 880         // Workaround: Override exists() to always return true.
 881         return new FileSystemRoot(f) {
 882             public boolean exists() {
 883                 return true;
 884             }
 885         };
 886     }
 887 
 888 }
 889 
 890 /**
 891  * Fallthrough FileSystemView in case we can't determine the OS.
 892  */
 893 class GenericFileSystemView extends FileSystemView {
 894 
 895     private static final String newFolderString =
 896             UIManager.getString("FileChooser.other.newFolder");
 897 
 898     /**
 899      * Creates a new folder with a default folder name.
 900      */
 901     public File createNewFolder(File containingDir) throws IOException {
 902         if(containingDir == null) {
 903             throw new IOException("Containing directory is null:");
 904         }
 905         // Using NT's default folder name
 906         File newFolder = createFileObject(containingDir, newFolderString);
 907 
 908         if(newFolder.exists()) {
 909             throw new IOException("Directory already exists:" + newFolder.getAbsolutePath());
 910         } else {
 911             if(!newFolder.mkdirs()) {
 912                 throw new IOException(newFolder.getAbsolutePath());
 913             }
 914         }
 915         return newFolder;
 916     }
 917 
 918 }