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