Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/classes/com/apple/laf/AquaFileView.java
38831 views
/*1* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.apple.laf;2627import java.io.*;28import java.util.*;29import java.util.Map.Entry;3031import javax.swing.Icon;32import javax.swing.filechooser.FileView;3334import com.apple.laf.AquaUtils.RecyclableSingleton;3536class AquaFileView extends FileView {37private static final boolean DEBUG = false;3839private static final int UNINITALIZED_LS_INFO = -1;4041// Constants from LaunchServices.h42static final int kLSItemInfoIsPlainFile = 0x00000001; /* Not a directory, volume, or symlink*/43static final int kLSItemInfoIsPackage = 0x00000002; /* Packaged directory*/44static final int kLSItemInfoIsApplication = 0x00000004; /* Single-file or packaged application*/45static final int kLSItemInfoIsContainer = 0x00000008; /* Directory (includes packages) or volume*/46static final int kLSItemInfoIsAliasFile = 0x00000010; /* Alias file (includes sym links)*/47static final int kLSItemInfoIsSymlink = 0x00000020; /* UNIX sym link*/48static final int kLSItemInfoIsInvisible = 0x00000040; /* Invisible by any known mechanism*/49static final int kLSItemInfoIsNativeApp = 0x00000080; /* Carbon or Cocoa native app*/50static final int kLSItemInfoIsClassicApp = 0x00000100; /* CFM/68K Classic app*/51static final int kLSItemInfoAppPrefersNative = 0x00000200; /* Carbon app that prefers to be launched natively*/52static final int kLSItemInfoAppPrefersClassic = 0x00000400; /* Carbon app that prefers to be launched in Classic*/53static final int kLSItemInfoAppIsScriptable = 0x00000800; /* App can be scripted*/54static final int kLSItemInfoIsVolume = 0x00001000; /* Item is a volume*/55static final int kLSItemInfoExtensionIsHidden = 0x00100000; /* Item has a hidden extension*/5657static {58java.security.AccessController.doPrivileged(59new java.security.PrivilegedAction<Void>() {60public Void run() {61System.loadLibrary("osxui");62return null;63}64});65}6667// TODO: Un-comment this out when the native version exists68//private static native String getNativePathToRunningJDKBundle();69private static native String getNativePathToSharedJDKBundle();7071private static native String getNativeMachineName();72private static native String getNativeDisplayName(final byte[] pathBytes, final boolean isDirectory);73private static native int getNativeLSInfo(final byte[] pathBytes, final boolean isDirectory);74private static native String getNativePathForResolvedAlias(final byte[] absolutePath, final boolean isDirectory);7576static final RecyclableSingleton<String> machineName = new RecyclableSingleton<String>() {77@Override78protected String getInstance() {79return getNativeMachineName();80}81};82private static String getMachineName() {83return machineName.get();84}8586protected static String getPathToRunningJDKBundle() {87// TODO: Return empty string for now88return "";//getNativePathToRunningJDKBundle();89}9091protected static String getPathToSharedJDKBundle() {92return getNativePathToSharedJDKBundle();93}9495static class FileInfo {96final boolean isDirectory;97final String absolutePath;98byte[] pathBytes;99100String displayName;101Icon icon;102int launchServicesInfo = UNINITALIZED_LS_INFO;103104FileInfo(final File file){105isDirectory = file.isDirectory();106absolutePath = file.getAbsolutePath();107try {108pathBytes = absolutePath.getBytes("UTF-8");109} catch (final UnsupportedEncodingException e) {110pathBytes = new byte[0];111}112}113}114115final int MAX_CACHED_ENTRIES = 256;116protected final Map<File, FileInfo> cache = new LinkedHashMap<File, FileInfo>(){117protected boolean removeEldestEntry(final Entry<File, FileInfo> eldest) {118return size() > MAX_CACHED_ENTRIES;119}120};121122FileInfo getFileInfoFor(final File file) {123final FileInfo info = cache.get(file);124if (info != null) return info;125final FileInfo newInfo = new FileInfo(file);126cache.put(file, newInfo);127return newInfo;128}129130131final AquaFileChooserUI fFileChooserUI;132public AquaFileView(final AquaFileChooserUI fileChooserUI) {133fFileChooserUI = fileChooserUI;134}135136String _directoryDescriptionText() {137return fFileChooserUI.directoryDescriptionText;138}139140String _fileDescriptionText() {141return fFileChooserUI.fileDescriptionText;142}143144boolean _packageIsTraversable() {145return fFileChooserUI.fPackageIsTraversable == AquaFileChooserUI.kOpenAlways;146}147148boolean _applicationIsTraversable() {149return fFileChooserUI.fApplicationIsTraversable == AquaFileChooserUI.kOpenAlways;150}151152public String getName(final File f) {153final FileInfo info = getFileInfoFor(f);154if (info.displayName != null) return info.displayName;155156final String nativeDisplayName = getNativeDisplayName(info.pathBytes, info.isDirectory);157if (nativeDisplayName != null) {158info.displayName = nativeDisplayName;159return nativeDisplayName;160}161162final String displayName = f.getName();163if (f.isDirectory() && fFileChooserUI.getFileChooser().getFileSystemView().isRoot(f)) {164final String localMachineName = getMachineName();165info.displayName = localMachineName;166return localMachineName;167}168169info.displayName = displayName;170return displayName;171}172173public String getDescription(final File f) {174return f.getName();175}176177public String getTypeDescription(final File f) {178if (f.isDirectory()) return _directoryDescriptionText();179return _fileDescriptionText();180}181182public Icon getIcon(final File f) {183final FileInfo info = getFileInfoFor(f);184if (info.icon != null) return info.icon;185186if (f == null) {187info.icon = AquaIcon.SystemIcon.getDocumentIconUIResource();188} else {189// Look for the document's icon190final AquaIcon.FileIcon fileIcon = new AquaIcon.FileIcon(f);191info.icon = fileIcon;192if (!fileIcon.hasIconRef()) {193// Fall back on the default icons194if (f.isDirectory()) {195if (fFileChooserUI.getFileChooser().getFileSystemView().isRoot(f)) {196info.icon = AquaIcon.SystemIcon.getComputerIconUIResource();197} else if (f.getParent() == null || f.getParent().equals("/")) {198info.icon = AquaIcon.SystemIcon.getHardDriveIconUIResource();199} else {200info.icon = AquaIcon.SystemIcon.getFolderIconUIResource();201}202} else {203info.icon = AquaIcon.SystemIcon.getDocumentIconUIResource();204}205}206}207208return info.icon;209}210211// aliases are traversable though they aren't directories212public Boolean isTraversable(final File f) {213if (f.isDirectory()) {214// Doesn't matter if it's a package or app, because they're traversable215if (_packageIsTraversable() && _applicationIsTraversable()) {216return Boolean.TRUE;217} else if (!_packageIsTraversable() && !_applicationIsTraversable()) {218if (isPackage(f) || isApplication(f)) return Boolean.FALSE;219} else if (!_applicationIsTraversable()) {220if (isApplication(f)) return Boolean.FALSE;221} else if (!_packageIsTraversable()) {222// [3101730] All applications are packages, but not all packages are applications.223if (isPackage(f) && !isApplication(f)) return Boolean.FALSE;224}225226// We're allowed to traverse it227return Boolean.TRUE;228}229230if (isAlias(f)) {231final File realFile = resolveAlias(f);232return realFile.isDirectory() ? Boolean.TRUE : Boolean.FALSE;233}234235return Boolean.FALSE;236}237238int getLSInfoFor(final File f) {239final FileInfo info = getFileInfoFor(f);240241if (info.launchServicesInfo == UNINITALIZED_LS_INFO) {242info.launchServicesInfo = getNativeLSInfo(info.pathBytes, info.isDirectory);243}244245return info.launchServicesInfo;246}247248boolean isAlias(final File f) {249final int lsInfo = getLSInfoFor(f);250return ((lsInfo & kLSItemInfoIsAliasFile) != 0) && ((lsInfo & kLSItemInfoIsSymlink) == 0);251}252253boolean isApplication(final File f) {254return (getLSInfoFor(f) & kLSItemInfoIsApplication) != 0;255}256257boolean isPackage(final File f) {258return (getLSInfoFor(f) & kLSItemInfoIsPackage) != 0;259}260261/**262* Things that need to be handled:263* -Change getFSRef to use CFURLRef instead of FSPathMakeRef264* -Use the HFS-style path from CFURLRef in resolveAlias() to avoid265* path length limitations266* -In resolveAlias(), simply resolve immediately if this is an alias267*/268269/**270* Returns the actual file represented by this object. This will271* resolve any aliases in the path, including this file if it is an272* alias. No alias resolution requiring user interaction (e.g.273* mounting servers) will occur. Note that aliases to servers may274* take a significant amount of time to resolve. This method275* currently does not have any provisions for a more fine-grained276* timeout for alias resolution beyond that used by the system.277*278* In the event of a path that does not contain any aliases, or if the file279* does not exist, this method will return the file that was passed in.280* @return The canonical path to the file281* @throws IOException If an I/O error occurs while attempting to282* construct the path283*/284File resolveAlias(final File mFile) {285// If the file exists and is not an alias, there aren't286// any aliases along its path, so the standard version287// of getCanonicalPath() will work.288if (mFile.exists() && !isAlias(mFile)) {289if (DEBUG) System.out.println("not an alias");290return mFile;291}292293// If it doesn't exist, either there's an alias in the294// path or this is an alias. Traverse the path and295// resolve all aliases in it.296final LinkedList<String> components = getPathComponents(mFile);297if (components == null) {298if (DEBUG) System.out.println("getPathComponents is null ");299return mFile;300}301302File file = new File("/");303for (final String nextComponent : components) {304file = new File(file, nextComponent);305final FileInfo info = getFileInfoFor(file);306307// If any point along the way doesn't exist,308// just return the file.309if (!file.exists()) { return mFile; }310311if (isAlias(file)) {312// Resolve it!313final String path = getNativePathForResolvedAlias(info.pathBytes, info.isDirectory);314315// <rdar://problem/3582601> If the alias doesn't resolve (on a non-existent volume, for example)316// just return the file.317if (path == null) return mFile;318319file = new File(path);320}321}322323return file;324}325326/**327* Returns a linked list of Strings consisting of the components of328* the path of this file, in order, including the filename as the329* last element. The first element in the list will be the first330* directory in the path, or "".331* @return A linked list of the components of this file's path332*/333private static LinkedList<String> getPathComponents(final File mFile) {334final LinkedList<String> componentList = new LinkedList<String>();335String parent;336337File file = new File(mFile.getAbsolutePath());338componentList.add(0, file.getName());339while ((parent = file.getParent()) != null) {340file = new File(parent);341componentList.add(0, file.getName());342}343return componentList;344}345}346347348