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 * Icon for a file, directory, or folder as it would be displayed in 260 * a system file browser for the requested size. 261 * 262 * The default implementation gets information from the ShellFolder class. 263 * 264 * @param f a <code>File</code> object 265 * @param size width and height of the icon in pixels to be scaled(valid range: 1 to 256) 266 * @return an icon as it would be displayed by a native file chooser 267 * @see JFileChooser#getIcon 268 * @since 12 269 */ 270 protected Icon getSystemIcon(File f, int size) { 271 if (f == null) { 272 return null; 273 } 274 275 if(size > 256 || size < 1) { 276 return null; 277 } 278 279 ShellFolder sf; 280 try { 281 sf = getShellFolder(f); 282 } catch (FileNotFoundException e) { 283 return null; 284 } 285 286 Image img = sf.getIcon(size); 287 if (img != null) { 288 return new ImageIcon(img, sf.getFolderType()); 289 } else { 290 return UIManager.getIcon(f.isDirectory() ? "FileView.directoryIcon" : "FileView.fileIcon"); 291 } 292 } 293 294 /** 295 * On Windows, a file can appear in multiple folders, other than its 296 * parent directory in the filesystem. Folder could for example be the 297 * "Desktop" folder which is not the same as file.getParentFile(). 298 * 299 * @param folder a <code>File</code> object representing a directory or special folder 300 * @param file a <code>File</code> object 301 * @return <code>true</code> if <code>folder</code> is a directory or special folder and contains <code>file</code>. 302 * @since 1.4 303 */ 304 public boolean isParent(File folder, File file) { 305 if (folder == null || file == null) { 306 return false; 307 } else if (folder instanceof ShellFolder) { 308 File parent = file.getParentFile(); 309 if (parent != null && parent.equals(folder)) { 310 return true; 311 } 312 File[] children = getFiles(folder, false); 313 for (File child : children) { 314 if (file.equals(child)) { 315 return true; 316 } 317 } 318 return false; 319 } else { 320 return folder.equals(file.getParentFile()); 321 } 322 } 323 324 /** 325 * 326 * @param parent a <code>File</code> object representing a directory or special folder 327 * @param fileName a name of a file or folder which exists in <code>parent</code> 328 * @return a File object. This is normally constructed with <code>new 329 * File(parent, fileName)</code> except when parent and child are both 330 * special folders, in which case the <code>File</code> is a wrapper containing 331 * a <code>ShellFolder</code> object. 332 * @since 1.4 333 */ 334 public File getChild(File parent, String fileName) { 335 if (parent instanceof ShellFolder) { 336 File[] children = getFiles(parent, false); 337 for (File child : children) { 338 if (child.getName().equals(fileName)) { 339 return child; 340 } 341 } 342 } 343 return createFileObject(parent, fileName); 344 } 345 346 347 /** 348 * Checks if <code>f</code> represents a real directory or file as opposed to a 349 * special folder such as <code>"Desktop"</code>. Used by UI classes to decide if 350 * a folder is selectable when doing directory choosing. 351 * 352 * @param f a <code>File</code> object 353 * @return <code>true</code> if <code>f</code> is a real file or directory. 354 * @since 1.4 355 */ 356 public boolean isFileSystem(File f) { 357 if (f instanceof ShellFolder) { 358 ShellFolder sf = (ShellFolder)f; 359 // Shortcuts to directories are treated as not being file system objects, 360 // so that they are never returned by JFileChooser. 361 return sf.isFileSystem() && !(sf.isLink() && sf.isDirectory()); 362 } else { 363 return true; 364 } 365 } 366 367 /** 368 * Creates a new folder with a default folder name. 369 * 370 * @param containingDir a {@code File} object denoting directory to contain the new folder 371 * @return a {@code File} object denoting the newly created folder 372 * @throws IOException if new folder could not be created 373 */ 374 public abstract File createNewFolder(File containingDir) throws IOException; 375 376 /** 377 * Returns whether a file is hidden or not. 378 * 379 * @param f a {@code File} object 380 * @return true if the given {@code File} denotes a hidden file 381 */ 382 public boolean isHiddenFile(File f) { 383 return f.isHidden(); 384 } 385 386 387 /** 388 * Is dir the root of a tree in the file system, such as a drive 389 * or partition. Example: Returns true for "C:\" on Windows 98. 390 * 391 * @param dir a <code>File</code> object representing a directory 392 * @return <code>true</code> if <code>f</code> is a root of a filesystem 393 * @see #isRoot 394 * @since 1.4 395 */ 396 public boolean isFileSystemRoot(File dir) { 397 return ShellFolder.isFileSystemRoot(dir); 398 } 399 400 /** 401 * Used by UI classes to decide whether to display a special icon 402 * for drives or partitions, e.g. a "hard disk" icon. 403 * 404 * The default implementation has no way of knowing, so always returns false. 405 * 406 * @param dir a directory 407 * @return <code>false</code> always 408 * @since 1.4 409 */ 410 public boolean isDrive(File dir) { 411 return false; 412 } 413 414 /** 415 * Used by UI classes to decide whether to display a special icon 416 * for a floppy disk. Implies isDrive(dir). 417 * 418 * The default implementation has no way of knowing, so always returns false. 419 * 420 * @param dir a directory 421 * @return <code>false</code> always 422 * @since 1.4 423 */ 424 public boolean isFloppyDrive(File dir) { 425 return false; 426 } 427 428 /** 429 * Used by UI classes to decide whether to display a special icon 430 * for a computer node, e.g. "My Computer" or a network server. 431 * 432 * The default implementation has no way of knowing, so always returns false. 433 * 434 * @param dir a directory 435 * @return <code>false</code> always 436 * @since 1.4 437 */ 438 public boolean isComputerNode(File dir) { 439 return ShellFolder.isComputerNode(dir); 440 } 441 442 443 /** 444 * Returns all root partitions on this system. For example, on 445 * Windows, this would be the "Desktop" folder, while on DOS this 446 * would be the A: through Z: drives. 447 * 448 * @return an array of {@code File} objects representing all root partitions 449 * on this system 450 */ 451 public File[] getRoots() { 452 // Don't cache this array, because filesystem might change 453 File[] roots = (File[])ShellFolder.get("roots"); 454 455 for (int i = 0; i < roots.length; i++) { 456 if (isFileSystemRoot(roots[i])) { 457 roots[i] = createFileSystemRoot(roots[i]); 458 } 459 } 460 return roots; 461 } 462 463 464 // Providing default implementations for the remaining methods 465 // because most OS file systems will likely be able to use this 466 // code. If a given OS can't, override these methods in its 467 // implementation. 468 469 /** 470 * Returns the home directory. 471 * @return the home directory 472 */ 473 public File getHomeDirectory() { 474 return createFileObject(System.getProperty("user.home")); 475 } 476 477 /** 478 * Return the user's default starting directory for the file chooser. 479 * 480 * @return a <code>File</code> object representing the default 481 * starting folder 482 * @since 1.4 483 */ 484 public File getDefaultDirectory() { 485 File f = (File)ShellFolder.get("fileChooserDefaultFolder"); 486 if (isFileSystemRoot(f)) { 487 f = createFileSystemRoot(f); 488 } 489 return f; 490 } 491 492 /** 493 * Returns a File object constructed in dir from the given filename. 494 * 495 * @param dir an abstract pathname denoting a directory 496 * @param filename a {@code String} representation of a pathname 497 * @return a {@code File} object created from {@code dir} and {@code filename} 498 */ 499 public File createFileObject(File dir, String filename) { 500 if(dir == null) { 501 return new File(filename); 502 } else { 503 return new File(dir, filename); 504 } 505 } 506 507 /** 508 * Returns a File object constructed from the given path string. 509 * 510 * @param path {@code String} representation of path 511 * @return a {@code File} object created from the given {@code path} 512 */ 513 public File createFileObject(String path) { 514 File f = new File(path); 515 if (isFileSystemRoot(f)) { 516 f = createFileSystemRoot(f); 517 } 518 return f; 519 } 520 521 522 /** 523 * Gets the list of shown (i.e. not hidden) files. 524 * 525 * @param dir the root directory of files to be returned 526 * @param useFileHiding determine if hidden files are returned 527 * @return an array of {@code File} objects representing files and 528 * directories in the given {@code dir}. It includes hidden 529 * files if {@code useFileHiding} is false. 530 */ 531 public File[] getFiles(File dir, boolean useFileHiding) { 532 List<File> files = new ArrayList<File>(); 533 534 // add all files in dir 535 if (!(dir instanceof ShellFolder)) { 536 try { 537 dir = getShellFolder(dir); 538 } catch (FileNotFoundException e) { 539 return new File[0]; 540 } 541 } 542 543 File[] names = ((ShellFolder) dir).listFiles(!useFileHiding); 544 545 if (names == null) { 546 return new File[0]; 547 } 548 549 for (File f : names) { 550 if (Thread.currentThread().isInterrupted()) { 551 break; 552 } 553 554 if (!(f instanceof ShellFolder)) { 555 if (isFileSystemRoot(f)) { 556 f = createFileSystemRoot(f); 557 } 558 try { 559 f = ShellFolder.getShellFolder(f); 560 } catch (FileNotFoundException e) { 561 // Not a valid file (wouldn't show in native file chooser) 562 // Example: C:\pagefile.sys 563 continue; 564 } catch (InternalError e) { 565 // Not a valid file (wouldn't show in native file chooser) 566 // Example C:\Winnt\Profiles\joe\history\History.IE5 567 continue; 568 } 569 } 570 if (!useFileHiding || !isHiddenFile(f)) { 571 files.add(f); 572 } 573 } 574 575 return files.toArray(new File[files.size()]); 576 } 577 578 579 580 /** 581 * Returns the parent directory of <code>dir</code>. 582 * @param dir the <code>File</code> being queried 583 * @return the parent directory of <code>dir</code>, or 584 * <code>null</code> if <code>dir</code> is <code>null</code> 585 */ 586 public File getParentDirectory(File dir) { 587 if (dir == null || !dir.exists()) { 588 return null; 589 } 590 591 ShellFolder sf; 592 593 try { 594 sf = getShellFolder(dir); 595 } catch (FileNotFoundException e) { 596 return null; 597 } 598 599 File psf = sf.getParentFile(); 600 601 if (psf == null) { 602 return null; 603 } 604 605 if (isFileSystem(psf)) { 606 File f = psf; 607 if (!f.exists()) { 608 // This could be a node under "Network Neighborhood". 609 File ppsf = psf.getParentFile(); 610 if (ppsf == null || !isFileSystem(ppsf)) { 611 // We're mostly after the exists() override for windows below. 612 f = createFileSystemRoot(f); 613 } 614 } 615 return f; 616 } else { 617 return psf; 618 } 619 } 620 621 /** 622 * Returns an array of files representing the values to show by default in 623 * the file chooser selector. 624 * 625 * @return an array of {@code File} objects. 626 * @throws SecurityException if the caller does not have necessary 627 * permissions 628 * @since 9 629 */ 630 public File[] getChooserComboBoxFiles() { 631 return (File[]) ShellFolder.get("fileChooserComboBoxFolders"); 632 } 633 634 /** 635 * Returns whether the specified file denotes a shell interpreted link which 636 * can be obtained by the {@link #getLinkLocation(File)}. 637 * 638 * @param file a file 639 * @return whether this is a link 640 * @throws NullPointerException if {@code file} equals {@code null} 641 * @throws SecurityException if the caller does not have necessary 642 * permissions 643 * @see #getLinkLocation(File) 644 * @since 9 645 */ 646 public boolean isLink(File file) { 647 if (file == null) { 648 throw new NullPointerException("file is null"); 649 } 650 try { 651 return ShellFolder.getShellFolder(file).isLink(); 652 } catch (FileNotFoundException e) { 653 return false; 654 } 655 } 656 657 /** 658 * Returns the regular file referenced by the specified link file if 659 * the specified file is a shell interpreted link. 660 * Returns {@code null} if the specified file is not 661 * a shell interpreted link. 662 * 663 * @param file a file 664 * @return the linked file or {@code null}. 665 * @throws FileNotFoundException if the linked file does not exist 666 * @throws NullPointerException if {@code file} equals {@code null} 667 * @throws SecurityException if the caller does not have necessary 668 * permissions 669 * @since 9 670 */ 671 public File getLinkLocation(File file) throws FileNotFoundException { 672 if (file == null) { 673 throw new NullPointerException("file is null"); 674 } 675 ShellFolder shellFolder; 676 try { 677 shellFolder = ShellFolder.getShellFolder(file); 678 } catch (FileNotFoundException e) { 679 return null; 680 } 681 return shellFolder.isLink() ? shellFolder.getLinkLocation() : null; 682 } 683 684 /** 685 * Throws {@code FileNotFoundException} if file not found or current thread was interrupted 686 */ 687 ShellFolder getShellFolder(File f) throws FileNotFoundException { 688 if (!(f instanceof ShellFolder) && !(f instanceof FileSystemRoot) && isFileSystemRoot(f)) { 689 f = createFileSystemRoot(f); 690 } 691 692 try { 693 return ShellFolder.getShellFolder(f); 694 } catch (InternalError e) { 695 System.err.println("FileSystemView.getShellFolder: f="+f); 696 e.printStackTrace(); 697 return null; 698 } 699 } 700 701 /** 702 * Creates a new <code>File</code> object for <code>f</code> with correct 703 * behavior for a file system root directory. 704 * 705 * @param f a <code>File</code> object representing a file system root 706 * directory, for example "/" on Unix or "C:\" on Windows. 707 * @return a new <code>File</code> object 708 * @since 1.4 709 */ 710 protected File createFileSystemRoot(File f) { 711 return new FileSystemRoot(f); 712 } 713 714 @SuppressWarnings("serial") // Same-version serialization only 715 static class FileSystemRoot extends File { 716 public FileSystemRoot(File f) { 717 super(f,""); 718 } 719 720 public FileSystemRoot(String s) { 721 super(s); 722 } 723 724 public boolean isDirectory() { 725 return true; 726 } 727 728 public String getName() { 729 return getPath(); 730 } 731 } 732 } 733 734 /** 735 * FileSystemView that handles some specific unix-isms. 736 */ 737 class UnixFileSystemView extends FileSystemView { 738 739 private static final String newFolderString = 740 UIManager.getString("FileChooser.other.newFolder"); 741 private static final String newFolderNextString = 742 UIManager.getString("FileChooser.other.newFolder.subsequent"); 743 744 /** 745 * Creates a new folder with a default folder name. 746 */ 747 public File createNewFolder(File containingDir) throws IOException { 748 if(containingDir == null) { 749 throw new IOException("Containing directory is null:"); 750 } 751 File newFolder; 752 // Unix - using OpenWindows' default folder name. Can't find one for Motif/CDE. 753 newFolder = createFileObject(containingDir, newFolderString); 754 int i = 1; 755 while (newFolder.exists() && i < 100) { 756 newFolder = createFileObject(containingDir, MessageFormat.format( 757 newFolderNextString, i)); 758 i++; 759 } 760 761 if(newFolder.exists()) { 762 throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); 763 } else { 764 if(!newFolder.mkdirs()) { 765 throw new IOException(newFolder.getAbsolutePath()); 766 } 767 } 768 769 return newFolder; 770 } 771 772 public boolean isFileSystemRoot(File dir) { 773 return dir != null && dir.getAbsolutePath().equals("/"); 774 } 775 776 public boolean isDrive(File dir) { 777 return isFloppyDrive(dir); 778 } 779 780 public boolean isFloppyDrive(File dir) { 781 // Could be looking at the path for Solaris, but wouldn't be reliable. 782 // For example: 783 // return (dir != null && dir.getAbsolutePath().toLowerCase().startsWith("/floppy")); 784 return false; 785 } 786 787 public boolean isComputerNode(File dir) { 788 if (dir != null) { 789 String parent = dir.getParent(); 790 if (parent != null && parent.equals("/net")) { 791 return true; 792 } 793 } 794 return false; 795 } 796 } 797 798 799 /** 800 * FileSystemView that handles some specific windows concepts. 801 */ 802 class WindowsFileSystemView extends FileSystemView { 803 804 private static final String newFolderString = 805 UIManager.getString("FileChooser.win32.newFolder"); 806 private static final String newFolderNextString = 807 UIManager.getString("FileChooser.win32.newFolder.subsequent"); 808 809 public Boolean isTraversable(File f) { 810 return Boolean.valueOf(isFileSystemRoot(f) || isComputerNode(f) || f.isDirectory()); 811 } 812 813 public File getChild(File parent, String fileName) { 814 if (fileName.startsWith("\\") 815 && !fileName.startsWith("\\\\") 816 && isFileSystem(parent)) { 817 818 //Path is relative to the root of parent's drive 819 String path = parent.getAbsolutePath(); 820 if (path.length() >= 2 821 && path.charAt(1) == ':' 822 && Character.isLetter(path.charAt(0))) { 823 824 return createFileObject(path.substring(0, 2) + fileName); 825 } 826 } 827 return super.getChild(parent, fileName); 828 } 829 830 /** 831 * Type description for a file, directory, or folder as it would be displayed in 832 * a system file browser. Example from Windows: the "Desktop" folder 833 * is described as "Desktop". 834 * 835 * The Windows implementation gets information from the ShellFolder class. 836 */ 837 public String getSystemTypeDescription(File f) { 838 if (f == null) { 839 return null; 840 } 841 842 try { 843 return getShellFolder(f).getFolderType(); 844 } catch (FileNotFoundException e) { 845 return null; 846 } 847 } 848 849 /** 850 * @return the Desktop folder. 851 */ 852 public File getHomeDirectory() { 853 File[] roots = getRoots(); 854 return (roots.length == 0) ? null : roots[0]; 855 } 856 857 /** 858 * Creates a new folder with a default folder name. 859 */ 860 public File createNewFolder(File containingDir) throws IOException { 861 if(containingDir == null) { 862 throw new IOException("Containing directory is null:"); 863 } 864 // Using NT's default folder name 865 File newFolder = createFileObject(containingDir, newFolderString); 866 int i = 2; 867 while (newFolder.exists() && i < 100) { 868 newFolder = createFileObject(containingDir, MessageFormat.format( 869 newFolderNextString, i)); 870 i++; 871 } 872 873 if(newFolder.exists()) { 874 throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); 875 } else { 876 if(!newFolder.mkdirs()) { 877 throw new IOException(newFolder.getAbsolutePath()); 878 } 879 } 880 881 return newFolder; 882 } 883 884 public boolean isDrive(File dir) { 885 return isFileSystemRoot(dir); 886 } 887 888 public boolean isFloppyDrive(final File dir) { 889 String path = AccessController.doPrivileged(new PrivilegedAction<String>() { 890 public String run() { 891 return dir.getAbsolutePath(); 892 } 893 }); 894 895 return path != null && (path.equals("A:\\") || path.equals("B:\\")); 896 } 897 898 /** 899 * Returns a File object constructed from the given path string. 900 */ 901 public File createFileObject(String path) { 902 // Check for missing backslash after drive letter such as "C:" or "C:filename" 903 if (path.length() >= 2 && path.charAt(1) == ':' && Character.isLetter(path.charAt(0))) { 904 if (path.length() == 2) { 905 path += "\\"; 906 } else if (path.charAt(2) != '\\') { 907 path = path.substring(0, 2) + "\\" + path.substring(2); 908 } 909 } 910 return super.createFileObject(path); 911 } 912 913 @SuppressWarnings("serial") // anonymous class 914 protected File createFileSystemRoot(File f) { 915 // Problem: Removable drives on Windows return false on f.exists() 916 // Workaround: Override exists() to always return true. 917 return new FileSystemRoot(f) { 918 public boolean exists() { 919 return true; 920 } 921 }; 922 } 923 924 } 925 926 /** 927 * Fallthrough FileSystemView in case we can't determine the OS. 928 */ 929 class GenericFileSystemView extends FileSystemView { 930 931 private static final String newFolderString = 932 UIManager.getString("FileChooser.other.newFolder"); 933 934 /** 935 * Creates a new folder with a default folder name. 936 */ 937 public File createNewFolder(File containingDir) throws IOException { 938 if(containingDir == null) { 939 throw new IOException("Containing directory is null:"); 940 } 941 // Using NT's default folder name 942 File newFolder = createFileObject(containingDir, newFolderString); 943 944 if(newFolder.exists()) { 945 throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); 946 } else { 947 if(!newFolder.mkdirs()) { 948 throw new IOException(newFolder.getAbsolutePath()); 949 } 950 } 951 return newFolder; 952 } 953 954 }