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 */ getFileSystemView()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 */ FileSystemView()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 */ isRoot(File f)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 */ isTraversable(File f)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 */ getSystemDisplayName(File f)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 */ getSystemTypeDescription(File f)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 */ getSystemIcon(File f)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 */ isParent(File folder, File file)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 */ getChild(File parent, String fileName)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 */ isFileSystem(File f)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 */ createNewFolder(File containingDir)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 */ isHiddenFile(File f)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 */ isFileSystemRoot(File dir)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 */ isDrive(File dir)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 */ isFloppyDrive(File dir)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 */ isComputerNode(File dir)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 */ getRoots()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 */ getHomeDirectory()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 */ getDefaultDirectory()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 */ createFileObject(File dir, String filename)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 */ createFileObject(String path)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 */ getFiles(File dir, boolean useFileHiding)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 */ getParentDirectory(File dir)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 which will be shown 587 * in the file chooser selector. 588 * 589 * @return an array of {@code File} objects. The array returned may be 590 * possibly empty if there are no appropriate permissions. 591 * @since 9 592 */ getChooserComboBoxFiles()593 public File[] getChooserComboBoxFiles() { 594 return (File[]) ShellFolder.get("fileChooserComboBoxFolders"); 595 } 596 597 /** 598 * Returns an array of files representing the values to show by default in 599 * the file chooser shortcuts panel. 600 * 601 * @return an array of {@code File} objects. The array returned may be 602 * possibly empty if there are no appropriate permissions. 603 * @since 12 604 */ getChooserShortcutPanelFiles()605 final public File[] getChooserShortcutPanelFiles() { 606 return (File[]) ShellFolder.get("fileChooserShortcutPanelFolders"); 607 } 608 609 /** 610 * Returns whether the specified file denotes a shell interpreted link which 611 * can be obtained by the {@link #getLinkLocation(File)}. 612 * 613 * @param file a file 614 * @return whether this is a link 615 * @throws NullPointerException if {@code file} equals {@code null} 616 * @throws SecurityException if the caller does not have necessary 617 * permissions 618 * @see #getLinkLocation(File) 619 * @since 9 620 */ isLink(File file)621 public boolean isLink(File file) { 622 if (file == null) { 623 throw new NullPointerException("file is null"); 624 } 625 try { 626 return ShellFolder.getShellFolder(file).isLink(); 627 } catch (FileNotFoundException e) { 628 return false; 629 } 630 } 631 632 /** 633 * Returns the regular file referenced by the specified link file if 634 * the specified file is a shell interpreted link. 635 * Returns {@code null} if the specified file is not 636 * a shell interpreted link. 637 * 638 * @param file a file 639 * @return the linked file or {@code null}. 640 * @throws FileNotFoundException if the linked file does not exist 641 * @throws NullPointerException if {@code file} equals {@code null} 642 * @throws SecurityException if the caller does not have necessary 643 * permissions 644 * @since 9 645 */ getLinkLocation(File file)646 public File getLinkLocation(File file) throws FileNotFoundException { 647 if (file == null) { 648 throw new NullPointerException("file is null"); 649 } 650 ShellFolder shellFolder; 651 try { 652 shellFolder = ShellFolder.getShellFolder(file); 653 } catch (FileNotFoundException e) { 654 return null; 655 } 656 return shellFolder.isLink() ? shellFolder.getLinkLocation() : null; 657 } 658 659 /** 660 * Throws {@code FileNotFoundException} if file not found or current thread was interrupted 661 */ getShellFolder(File f)662 ShellFolder getShellFolder(File f) throws FileNotFoundException { 663 if (!(f instanceof ShellFolder) && !(f instanceof FileSystemRoot) && isFileSystemRoot(f)) { 664 f = createFileSystemRoot(f); 665 } 666 667 try { 668 return ShellFolder.getShellFolder(f); 669 } catch (InternalError e) { 670 System.err.println("FileSystemView.getShellFolder: f="+f); 671 e.printStackTrace(); 672 return null; 673 } 674 } 675 676 /** 677 * Creates a new <code>File</code> object for <code>f</code> with correct 678 * behavior for a file system root directory. 679 * 680 * @param f a <code>File</code> object representing a file system root 681 * directory, for example "/" on Unix or "C:\" on Windows. 682 * @return a new <code>File</code> object 683 * @since 1.4 684 */ createFileSystemRoot(File f)685 protected File createFileSystemRoot(File f) { 686 return new FileSystemRoot(f); 687 } 688 689 @SuppressWarnings("serial") // Same-version serialization only 690 static class FileSystemRoot extends File { FileSystemRoot(File f)691 public FileSystemRoot(File f) { 692 super(f,""); 693 } 694 FileSystemRoot(String s)695 public FileSystemRoot(String s) { 696 super(s); 697 } 698 isDirectory()699 public boolean isDirectory() { 700 return true; 701 } 702 getName()703 public String getName() { 704 return getPath(); 705 } 706 } 707 } 708 709 /** 710 * FileSystemView that handles some specific unix-isms. 711 */ 712 class UnixFileSystemView extends FileSystemView { 713 714 private static final String newFolderString = 715 UIManager.getString("FileChooser.other.newFolder"); 716 private static final String newFolderNextString = 717 UIManager.getString("FileChooser.other.newFolder.subsequent"); 718 719 /** 720 * Creates a new folder with a default folder name. 721 */ createNewFolder(File containingDir)722 public File createNewFolder(File containingDir) throws IOException { 723 if(containingDir == null) { 724 throw new IOException("Containing directory is null:"); 725 } 726 File newFolder; 727 // Unix - using OpenWindows' default folder name. Can't find one for Motif/CDE. 728 newFolder = createFileObject(containingDir, newFolderString); 729 int i = 1; 730 while (newFolder.exists() && i < 100) { 731 newFolder = createFileObject(containingDir, MessageFormat.format( 732 newFolderNextString, i)); 733 i++; 734 } 735 736 if(newFolder.exists()) { 737 throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); 738 } else { 739 if(!newFolder.mkdirs()) { 740 throw new IOException(newFolder.getAbsolutePath()); 741 } 742 } 743 744 return newFolder; 745 } 746 isFileSystemRoot(File dir)747 public boolean isFileSystemRoot(File dir) { 748 return dir != null && dir.getAbsolutePath().equals("/"); 749 } 750 isDrive(File dir)751 public boolean isDrive(File dir) { 752 return isFloppyDrive(dir); 753 } 754 isFloppyDrive(File dir)755 public boolean isFloppyDrive(File dir) { 756 // Could be looking at the path for Solaris, but wouldn't be reliable. 757 // For example: 758 // return (dir != null && dir.getAbsolutePath().toLowerCase().startsWith("/floppy")); 759 return false; 760 } 761 isComputerNode(File dir)762 public boolean isComputerNode(File dir) { 763 if (dir != null) { 764 String parent = dir.getParent(); 765 if (parent != null && parent.equals("/net")) { 766 return true; 767 } 768 } 769 return false; 770 } 771 } 772 773 774 /** 775 * FileSystemView that handles some specific windows concepts. 776 */ 777 class WindowsFileSystemView extends FileSystemView { 778 779 private static final String newFolderString = 780 UIManager.getString("FileChooser.win32.newFolder"); 781 private static final String newFolderNextString = 782 UIManager.getString("FileChooser.win32.newFolder.subsequent"); 783 isTraversable(File f)784 public Boolean isTraversable(File f) { 785 return Boolean.valueOf(isFileSystemRoot(f) || isComputerNode(f) || f.isDirectory()); 786 } 787 getChild(File parent, String fileName)788 public File getChild(File parent, String fileName) { 789 if (fileName.startsWith("\\") 790 && !fileName.startsWith("\\\\") 791 && isFileSystem(parent)) { 792 793 //Path is relative to the root of parent's drive 794 String path = parent.getAbsolutePath(); 795 if (path.length() >= 2 796 && path.charAt(1) == ':' 797 && Character.isLetter(path.charAt(0))) { 798 799 return createFileObject(path.substring(0, 2) + fileName); 800 } 801 } 802 return super.getChild(parent, fileName); 803 } 804 805 /** 806 * Type description for a file, directory, or folder as it would be displayed in 807 * a system file browser. Example from Windows: the "Desktop" folder 808 * is described as "Desktop". 809 * 810 * The Windows implementation gets information from the ShellFolder class. 811 */ getSystemTypeDescription(File f)812 public String getSystemTypeDescription(File f) { 813 if (f == null) { 814 return null; 815 } 816 817 try { 818 return getShellFolder(f).getFolderType(); 819 } catch (FileNotFoundException e) { 820 return null; 821 } 822 } 823 824 /** 825 * @return the Desktop folder. 826 */ getHomeDirectory()827 public File getHomeDirectory() { 828 File[] roots = getRoots(); 829 return (roots.length == 0) ? null : roots[0]; 830 } 831 832 /** 833 * Creates a new folder with a default folder name. 834 */ createNewFolder(File containingDir)835 public File createNewFolder(File containingDir) throws IOException { 836 if(containingDir == null) { 837 throw new IOException("Containing directory is null:"); 838 } 839 // Using NT's default folder name 840 File newFolder = createFileObject(containingDir, newFolderString); 841 int i = 2; 842 while (newFolder.exists() && i < 100) { 843 newFolder = createFileObject(containingDir, MessageFormat.format( 844 newFolderNextString, i)); 845 i++; 846 } 847 848 if(newFolder.exists()) { 849 throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); 850 } else { 851 if(!newFolder.mkdirs()) { 852 throw new IOException(newFolder.getAbsolutePath()); 853 } 854 } 855 856 return newFolder; 857 } 858 isDrive(File dir)859 public boolean isDrive(File dir) { 860 return isFileSystemRoot(dir); 861 } 862 isFloppyDrive(final File dir)863 public boolean isFloppyDrive(final File dir) { 864 String path = AccessController.doPrivileged(new PrivilegedAction<String>() { 865 public String run() { 866 return dir.getAbsolutePath(); 867 } 868 }); 869 870 return path != null && (path.equals("A:\\") || path.equals("B:\\")); 871 } 872 873 /** 874 * Returns a File object constructed from the given path string. 875 */ createFileObject(String path)876 public File createFileObject(String path) { 877 // Check for missing backslash after drive letter such as "C:" or "C:filename" 878 if (path.length() >= 2 && path.charAt(1) == ':' && Character.isLetter(path.charAt(0))) { 879 if (path.length() == 2) { 880 path += "\\"; 881 } else if (path.charAt(2) != '\\') { 882 path = path.substring(0, 2) + "\\" + path.substring(2); 883 } 884 } 885 return super.createFileObject(path); 886 } 887 888 @SuppressWarnings("serial") // anonymous class createFileSystemRoot(File f)889 protected File createFileSystemRoot(File f) { 890 // Problem: Removable drives on Windows return false on f.exists() 891 // Workaround: Override exists() to always return true. 892 return new FileSystemRoot(f) { 893 public boolean exists() { 894 return true; 895 } 896 }; 897 } 898 899 } 900 901 /** 902 * Fallthrough FileSystemView in case we can't determine the OS. 903 */ 904 class GenericFileSystemView extends FileSystemView { 905 906 private static final String newFolderString = 907 UIManager.getString("FileChooser.other.newFolder"); 908 909 /** 910 * Creates a new folder with a default folder name. 911 */ 912 public File createNewFolder(File containingDir) throws IOException { 913 if(containingDir == null) { 914 throw new IOException("Containing directory is null:"); 915 } 916 // Using NT's default folder name 917 File newFolder = createFileObject(containingDir, newFolderString); 918 919 if(newFolder.exists()) { 920 throw new IOException("Directory already exists:" + newFolder.getAbsolutePath()); 921 } else { 922 if(!newFolder.mkdirs()) { 923 throw new IOException(newFolder.getAbsolutePath()); 924 } 925 } 926 return newFolder; 927 } 928 929 } 930