Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java
32288 views
/*1* Copyright (c) 2003, 2019, 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 sun.awt.shell;2627import java.awt.Image;28import java.awt.Toolkit;29import java.awt.image.BufferedImage;3031import java.io.File;32import java.io.FileNotFoundException;33import java.io.IOException;34import java.security.AccessController;35import java.security.PrivilegedAction;36import java.util.ArrayList;37import java.util.Arrays;38import java.util.List;39import java.util.concurrent.Callable;40import java.util.concurrent.ExecutionException;41import java.util.concurrent.Future;42import java.util.concurrent.LinkedBlockingQueue;43import java.util.concurrent.RejectedExecutionException;44import java.util.concurrent.ThreadFactory;45import java.util.concurrent.ThreadPoolExecutor;46import java.util.concurrent.TimeUnit;47import java.util.stream.Stream;4849import sun.awt.OSInfo;50import sun.misc.ThreadGroupUtils;51import sun.util.logging.PlatformLogger;5253import static sun.awt.shell.Win32ShellFolder2.DESKTOP;54import static sun.awt.shell.Win32ShellFolder2.DRIVES;55import static sun.awt.shell.Win32ShellFolder2.Invoker;56import static sun.awt.shell.Win32ShellFolder2.NETWORK;57import static sun.awt.shell.Win32ShellFolder2.PERSONAL;58import static sun.awt.shell.Win32ShellFolder2.RECENT;5960// NOTE: This class supersedes Win32ShellFolderManager, which was removed61// from distribution after version 1.4.2.6263/**64* @author Michael Martak65* @author Leif Samuelsson66* @author Kenneth Russell67* @since 1.468*/6970public class Win32ShellFolderManager2 extends ShellFolderManager {7172private static final PlatformLogger73log = PlatformLogger.getLogger("sun.awt.shell.Win32ShellFolderManager2");7475static {76// Load library here77sun.awt.windows.WToolkit.loadLibraries();78}7980public ShellFolder createShellFolder(File file) throws FileNotFoundException {81try {82return createShellFolder(getDesktop(), file);83} catch (InterruptedException e) {84throw new FileNotFoundException("Execution was interrupted");85}86}8788static Win32ShellFolder2 createShellFolder(Win32ShellFolder2 parent, File file)89throws FileNotFoundException, InterruptedException {90long pIDL;91try {92pIDL = parent.parseDisplayName(file.getCanonicalPath());93} catch (IOException ex) {94pIDL = 0;95}96if (pIDL == 0) {97// Shouldn't happen but watch for it anyway98throw new FileNotFoundException("File " + file.getAbsolutePath() + " not found");99}100101try {102return createShellFolderFromRelativePIDL(parent, pIDL);103} finally {104Win32ShellFolder2.releasePIDL(pIDL);105}106}107108static Win32ShellFolder2 createShellFolderFromRelativePIDL(Win32ShellFolder2 parent, long pIDL)109throws InterruptedException {110// Walk down this relative pIDL, creating new nodes for each of the entries111while (pIDL != 0) {112long curPIDL = Win32ShellFolder2.copyFirstPIDLEntry(pIDL);113if (curPIDL != 0) {114parent = new Win32ShellFolder2(parent, curPIDL);115pIDL = Win32ShellFolder2.getNextPIDLEntry(pIDL);116} else {117// The list is empty if the parent is Desktop and pIDL is a shortcut to Desktop118break;119}120}121return parent;122}123124private static final int VIEW_LIST = 2;125private static final int VIEW_DETAILS = 3;126private static final int VIEW_PARENTFOLDER = 8;127private static final int VIEW_NEWFOLDER = 11;128129private static final Image[] STANDARD_VIEW_BUTTONS = new Image[12];130131private static Image getStandardViewButton(int iconIndex) {132Image result = STANDARD_VIEW_BUTTONS[iconIndex];133134if (result != null) {135return result;136}137138BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);139140img.setRGB(0, 0, 16, 16, Win32ShellFolder2.getStandardViewButton0(iconIndex), 0, 16);141142STANDARD_VIEW_BUTTONS[iconIndex] = img;143144return img;145}146147// Special folders148private static Win32ShellFolder2 desktop;149private static Win32ShellFolder2 drives;150private static Win32ShellFolder2 recent;151private static Win32ShellFolder2 network;152private static Win32ShellFolder2 personal;153154static Win32ShellFolder2 getDesktop() {155if (desktop == null) {156try {157desktop = new Win32ShellFolder2(DESKTOP);158} catch (final SecurityException ignored) {159// Ignore, the message may have sensitive information, not160// accessible other ways161} catch (IOException | InterruptedException e) {162if (log.isLoggable(PlatformLogger.Level.WARNING)) {163log.warning("Cannot access 'Desktop'", e);164}165}166}167return desktop;168}169170static Win32ShellFolder2 getDrives() {171if (drives == null) {172try {173drives = new Win32ShellFolder2(DRIVES);174} catch (final SecurityException ignored) {175// Ignore, the message may have sensitive information, not176// accessible other ways177} catch (IOException | InterruptedException e) {178if (log.isLoggable(PlatformLogger.Level.WARNING)) {179log.warning("Cannot access 'Drives'", e);180}181}182}183return drives;184}185186static Win32ShellFolder2 getRecent() {187if (recent == null) {188try {189String path = Win32ShellFolder2.getFileSystemPath(RECENT);190if (path != null) {191recent = createShellFolder(getDesktop(), new File(path));192}193} catch (final SecurityException ignored) {194// Ignore, the message may have sensitive information, not195// accessible other ways196} catch (InterruptedException | IOException e) {197if (log.isLoggable(PlatformLogger.Level.WARNING)) {198log.warning("Cannot access 'Recent'", e);199}200}201}202return recent;203}204205static Win32ShellFolder2 getNetwork() {206if (network == null) {207try {208network = new Win32ShellFolder2(NETWORK);209} catch (final SecurityException ignored) {210// Ignore, the message may have sensitive information, not211// accessible other ways212} catch (IOException | InterruptedException e) {213if (log.isLoggable(PlatformLogger.Level.WARNING)) {214log.warning("Cannot access 'Network'", e);215}216}217}218return network;219}220221static Win32ShellFolder2 getPersonal() {222if (personal == null) {223try {224String path = Win32ShellFolder2.getFileSystemPath(PERSONAL);225if (path != null) {226Win32ShellFolder2 desktop = getDesktop();227personal = desktop.getChildByPath(path);228if (personal == null) {229personal = createShellFolder(getDesktop(), new File(path));230}231if (personal != null) {232personal.setIsPersonal();233}234}235} catch (final SecurityException ignored) {236// Ignore, the message may have sensitive information, not237// accessible other ways238} catch (InterruptedException | IOException e) {239if (log.isLoggable(PlatformLogger.Level.WARNING)) {240log.warning("Cannot access 'Personal'", e);241}242}243}244return personal;245}246247248private static File[] roots;249250/**251* @param key a <code>String</code>252* "fileChooserDefaultFolder":253* Returns a <code>File</code> - the default shellfolder for a new filechooser254* "roots":255* Returns a <code>File[]</code> - containing the root(s) of the displayable hierarchy256* "fileChooserComboBoxFolders":257* Returns a <code>File[]</code> - an array of shellfolders representing the list to258* show by default in the file chooser's combobox259* "fileChooserShortcutPanelFolders":260* Returns a <code>File[]</code> - an array of shellfolders representing well-known261* folders, such as Desktop, Documents, History, Network, Home, etc.262* This is used in the shortcut panel of the filechooser on Windows 2000263* and Windows Me.264* "fileChooserIcon <icon>":265* Returns an <code>Image</code> - icon can be ListView, DetailsView, UpFolder, NewFolder or266* ViewMenu (Windows only).267* "optionPaneIcon iconName":268* Returns an <code>Image</code> - icon from the system icon list269*270* @return An Object matching the key string.271*/272public Object get(String key) {273if (key.equals("fileChooserDefaultFolder")) {274File file = getPersonal();275if (file == null) {276file = getDesktop();277}278return checkFile(file);279} else if (key.equals("roots")) {280// Should be "History" and "Desktop" ?281if (roots == null) {282File desktop = getDesktop();283if (desktop != null) {284roots = new File[] { desktop };285} else {286roots = (File[])super.get(key);287}288}289return checkFiles(roots);290} else if (key.equals("fileChooserComboBoxFolders")) {291Win32ShellFolder2 desktop = getDesktop();292293if (desktop != null && checkFile(desktop) != null) {294ArrayList<File> folders = new ArrayList<File>();295Win32ShellFolder2 drives = getDrives();296297Win32ShellFolder2 recentFolder = getRecent();298if (recentFolder != null && OSInfo.getWindowsVersion().compareTo(OSInfo.WINDOWS_2000) >= 0) {299folders.add(recentFolder);300}301302folders.add(desktop);303// Add all second level folders304File[] secondLevelFolders = checkFiles(desktop.listFiles());305Arrays.sort(secondLevelFolders);306for (File secondLevelFolder : secondLevelFolders) {307Win32ShellFolder2 folder = (Win32ShellFolder2) secondLevelFolder;308if (!folder.isFileSystem() || (folder.isDirectory() && !folder.isLink())) {309folders.add(folder);310// Add third level for "My Computer"311if (folder.equals(drives)) {312File[] thirdLevelFolders = checkFiles(folder.listFiles());313if (thirdLevelFolders != null && thirdLevelFolders.length > 0) {314List<File> thirdLevelFoldersList = Arrays.asList(thirdLevelFolders);315316folder.sortChildren(thirdLevelFoldersList);317folders.addAll(thirdLevelFoldersList);318}319}320}321}322return checkFiles(folders);323} else {324return super.get(key);325}326} else if (key.equals("fileChooserShortcutPanelFolders")) {327Toolkit toolkit = Toolkit.getDefaultToolkit();328ArrayList<File> folders = new ArrayList<File>();329int i = 0;330Object value;331do {332value = toolkit.getDesktopProperty("win.comdlg.placesBarPlace" + i++);333try {334if (value instanceof Integer) {335// A CSIDL336folders.add(new Win32ShellFolder2((Integer)value));337} else if (value instanceof String) {338// A path339folders.add(createShellFolder(new File((String)value)));340}341} catch (IOException e) {342if (log.isLoggable(PlatformLogger.Level.WARNING)) {343log.warning("Cannot read value = " + value, e);344}345// Skip this value346} catch (InterruptedException e) {347if (log.isLoggable(PlatformLogger.Level.WARNING)) {348log.warning("Cannot read value = " + value, e);349}350// Return empty result351return new File[0];352}353} while (value != null);354355if (folders.size() == 0) {356// Use default list of places357for (File f : new File[] {358getRecent(), getDesktop(), getPersonal(), getDrives(), getNetwork()359}) {360if (f != null) {361folders.add(f);362}363}364}365return checkFiles(folders);366} else if (key.startsWith("fileChooserIcon ")) {367String name = key.substring(key.indexOf(" ") + 1);368369int iconIndex;370371if (name.equals("ListView") || name.equals("ViewMenu")) {372iconIndex = VIEW_LIST;373} else if (name.equals("DetailsView")) {374iconIndex = VIEW_DETAILS;375} else if (name.equals("UpFolder")) {376iconIndex = VIEW_PARENTFOLDER;377} else if (name.equals("NewFolder")) {378iconIndex = VIEW_NEWFOLDER;379} else {380return null;381}382383return getStandardViewButton(iconIndex);384} else if (key.startsWith("optionPaneIcon ")) {385Win32ShellFolder2.SystemIcon iconType;386if (key == "optionPaneIcon Error") {387iconType = Win32ShellFolder2.SystemIcon.IDI_ERROR;388} else if (key == "optionPaneIcon Information") {389iconType = Win32ShellFolder2.SystemIcon.IDI_INFORMATION;390} else if (key == "optionPaneIcon Question") {391iconType = Win32ShellFolder2.SystemIcon.IDI_QUESTION;392} else if (key == "optionPaneIcon Warning") {393iconType = Win32ShellFolder2.SystemIcon.IDI_EXCLAMATION;394} else {395return null;396}397return Win32ShellFolder2.getSystemIcon(iconType);398} else if (key.startsWith("shell32Icon ") || key.startsWith("shell32LargeIcon ")) {399String name = key.substring(key.indexOf(" ") + 1);400try {401int i = Integer.parseInt(name);402if (i >= 0) {403return Win32ShellFolder2.getShell32Icon(i, key.startsWith("shell32LargeIcon "));404}405} catch (NumberFormatException ex) {406}407}408return null;409}410411private static File checkFile(File file) {412SecurityManager sm = System.getSecurityManager();413return (sm == null || file == null) ? file : checkFile(file, sm);414}415416private static File checkFile(File file, SecurityManager sm) {417try {418sm.checkRead(file.getPath());419420if (file instanceof Win32ShellFolder2) {421Win32ShellFolder2 f = (Win32ShellFolder2)file;422if (f.isLink()) {423Win32ShellFolder2 link = (Win32ShellFolder2)f.getLinkLocation();424if (link != null)425sm.checkRead(link.getPath());426}427}428return file;429} catch (SecurityException se) {430return null;431}432}433434static File[] checkFiles(File[] files) {435SecurityManager sm = System.getSecurityManager();436if (sm == null || files == null || files.length == 0) {437return files;438}439return checkFiles(Arrays.stream(files), sm);440}441442private static File[] checkFiles(List<File> files) {443SecurityManager sm = System.getSecurityManager();444if (sm == null || files.isEmpty()) {445return files.toArray(new File[files.size()]);446}447return checkFiles(files.stream(), sm);448}449450private static File[] checkFiles(Stream<File> filesStream, SecurityManager sm) {451return filesStream.filter((file) -> checkFile(file, sm) != null)452.toArray(File[]::new);453}454455/**456* Does <code>dir</code> represent a "computer" such as a node on the network, or457* "My Computer" on the desktop.458*/459public boolean isComputerNode(final File dir) {460if (dir != null && dir == getDrives()) {461return true;462} else {463String path = AccessController.doPrivileged(new PrivilegedAction<String>() {464public String run() {465return dir.getAbsolutePath();466}467});468469return (path.startsWith("\\\\") && path.indexOf("\\", 2) < 0); //Network path470}471}472473public boolean isFileSystemRoot(File dir) {474//Note: Removable drives don't "exist" but are listed in "My Computer"475if (dir != null) {476Win32ShellFolder2 drives = getDrives();477if (dir instanceof Win32ShellFolder2) {478Win32ShellFolder2 sf = (Win32ShellFolder2)dir;479if (sf.isFileSystem()) {480if (sf.parent != null) {481return sf.parent.equals(drives);482}483// else fall through ...484} else {485return false;486}487}488String path = dir.getPath();489490if (path.length() != 3 || path.charAt(1) != ':') {491return false;492}493494File[] files = drives.listFiles();495496return files != null && Arrays.asList(files).contains(dir);497}498return false;499}500501private static List topFolderList = null;502static int compareShellFolders(Win32ShellFolder2 sf1, Win32ShellFolder2 sf2) {503boolean special1 = sf1.isSpecial();504boolean special2 = sf2.isSpecial();505506if (special1 || special2) {507if (topFolderList == null) {508ArrayList tmpTopFolderList = new ArrayList();509tmpTopFolderList.add(Win32ShellFolderManager2.getPersonal());510tmpTopFolderList.add(Win32ShellFolderManager2.getDesktop());511tmpTopFolderList.add(Win32ShellFolderManager2.getDrives());512tmpTopFolderList.add(Win32ShellFolderManager2.getNetwork());513topFolderList = tmpTopFolderList;514}515int i1 = topFolderList.indexOf(sf1);516int i2 = topFolderList.indexOf(sf2);517if (i1 >= 0 && i2 >= 0) {518return (i1 - i2);519} else if (i1 >= 0) {520return -1;521} else if (i2 >= 0) {522return 1;523}524}525526// Non-file shellfolders sort before files527if (special1 && !special2) {528return -1;529} else if (special2 && !special1) {530return 1;531}532533return compareNames(sf1.getAbsolutePath(), sf2.getAbsolutePath());534}535536static int compareNames(String name1, String name2) {537// First ignore case when comparing538int diff = name1.compareToIgnoreCase(name2);539if (diff != 0) {540return diff;541} else {542// May differ in case (e.g. "mail" vs. "Mail")543// We need this test for consistent sorting544return name1.compareTo(name2);545}546}547548@Override549protected Invoker createInvoker() {550return new ComInvoker();551}552553private static class ComInvoker extends ThreadPoolExecutor implements ThreadFactory, ShellFolder.Invoker {554private static Thread comThread;555556private ComInvoker() {557super(1, 1, 0, TimeUnit.DAYS, new LinkedBlockingQueue<Runnable>());558allowCoreThreadTimeOut(false);559setThreadFactory(this);560final Runnable shutdownHook = new Runnable() {561public void run() {562AccessController.doPrivileged(new PrivilegedAction<Void>() {563public Void run() {564shutdownNow();565return null;566}567});568}569};570AccessController.doPrivileged(new PrivilegedAction<Void>() {571public Void run() {572Runtime.getRuntime().addShutdownHook(573new Thread(shutdownHook)574);575return null;576}577});578}579580public synchronized Thread newThread(final Runnable task) {581final Runnable comRun = new Runnable() {582public void run() {583try {584initializeCom();585task.run();586} finally {587uninitializeCom();588}589}590};591comThread = AccessController.doPrivileged((PrivilegedAction<Thread>) () -> {592/* The thread must be a member of a thread group593* which will not get GCed before VM exit.594* Make its parent the top-level thread group.595*/596ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup();597Thread thread = new Thread(rootTG, comRun, "Swing-Shell");598thread.setDaemon(true);599return thread;600}601);602return comThread;603}604605public <T> T invoke(Callable<T> task) throws Exception {606if (Thread.currentThread() == comThread) {607// if it's already called from the COM608// thread, we don't need to delegate the task609return task.call();610} else {611final Future<T> future;612613try {614future = submit(task);615} catch (RejectedExecutionException e) {616throw new InterruptedException(e.getMessage());617}618619try {620return future.get();621} catch (InterruptedException e) {622AccessController.doPrivileged(new PrivilegedAction<Void>() {623public Void run() {624future.cancel(true);625626return null;627}628});629630throw e;631} catch (ExecutionException e) {632Throwable cause = e.getCause();633634if (cause instanceof Exception) {635throw (Exception) cause;636}637638if (cause instanceof Error) {639throw (Error) cause;640}641642throw new RuntimeException("Unexpected error", cause);643}644}645}646}647648static native void initializeCom();649650static native void uninitializeCom();651}652653654