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