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 }