Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/sun/awt/shell/Win32ShellFolder2.java
32288 views
/*1* Copyright (c) 2003, 2018, 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;30import java.io.File;31import java.io.IOException;32import java.util.*;33import java.util.concurrent.*;34import javax.swing.SwingConstants;3536// NOTE: This class supersedes Win32ShellFolder, which was removed from37// distribution after version 1.4.2.3839/**40* Win32 Shell Folders41* <P>42* <BR>43* There are two fundamental types of shell folders : file system folders44* and non-file system folders. File system folders are relatively easy45* to deal with. Non-file system folders are items such as My Computer,46* Network Neighborhood, and the desktop. Some of these non-file system47* folders have special values and properties.48* <P>49* <BR>50* Win32 keeps two basic data structures for shell folders. The first51* of these is called an ITEMIDLIST. Usually a pointer, called an52* LPITEMIDLIST, or more frequently just "PIDL". This structure holds53* a series of identifiers and can be either relative to the desktop54* (an absolute PIDL), or relative to the shell folder that contains them.55* Some Win32 functions can take absolute or relative PIDL values, and56* others can only accept relative values.57* <BR>58* The second data structure is an IShellFolder COM interface. Using59* this interface, one can enumerate the relative PIDLs in a shell60* folder, get attributes, etc.61* <BR>62* All Win32ShellFolder2 objects which are folder types (even non-file63* system folders) contain an IShellFolder object. Files are named in64* directories via relative PIDLs.65*66* @author Michael Martak67* @author Leif Samuelsson68* @author Kenneth Russell69* @since 1.4 */7071final class Win32ShellFolder2 extends ShellFolder {7273private static native void initIDs();7475static {76initIDs();77}7879// Win32 Shell Folder Constants80public static final int DESKTOP = 0x0000;81public static final int INTERNET = 0x0001;82public static final int PROGRAMS = 0x0002;83public static final int CONTROLS = 0x0003;84public static final int PRINTERS = 0x0004;85public static final int PERSONAL = 0x0005;86public static final int FAVORITES = 0x0006;87public static final int STARTUP = 0x0007;88public static final int RECENT = 0x0008;89public static final int SENDTO = 0x0009;90public static final int BITBUCKET = 0x000a;91public static final int STARTMENU = 0x000b;92public static final int DESKTOPDIRECTORY = 0x0010;93public static final int DRIVES = 0x0011;94public static final int NETWORK = 0x0012;95public static final int NETHOOD = 0x0013;96public static final int FONTS = 0x0014;97public static final int TEMPLATES = 0x0015;98public static final int COMMON_STARTMENU = 0x0016;99public static final int COMMON_PROGRAMS = 0X0017;100public static final int COMMON_STARTUP = 0x0018;101public static final int COMMON_DESKTOPDIRECTORY = 0x0019;102public static final int APPDATA = 0x001a;103public static final int PRINTHOOD = 0x001b;104public static final int ALTSTARTUP = 0x001d;105public static final int COMMON_ALTSTARTUP = 0x001e;106public static final int COMMON_FAVORITES = 0x001f;107public static final int INTERNET_CACHE = 0x0020;108public static final int COOKIES = 0x0021;109public static final int HISTORY = 0x0022;110111// Win32 shell folder attributes112public static final int ATTRIB_CANCOPY = 0x00000001;113public static final int ATTRIB_CANMOVE = 0x00000002;114public static final int ATTRIB_CANLINK = 0x00000004;115public static final int ATTRIB_CANRENAME = 0x00000010;116public static final int ATTRIB_CANDELETE = 0x00000020;117public static final int ATTRIB_HASPROPSHEET = 0x00000040;118public static final int ATTRIB_DROPTARGET = 0x00000100;119public static final int ATTRIB_LINK = 0x00010000;120public static final int ATTRIB_SHARE = 0x00020000;121public static final int ATTRIB_READONLY = 0x00040000;122public static final int ATTRIB_GHOSTED = 0x00080000;123public static final int ATTRIB_HIDDEN = 0x00080000;124public static final int ATTRIB_FILESYSANCESTOR = 0x10000000;125public static final int ATTRIB_FOLDER = 0x20000000;126public static final int ATTRIB_FILESYSTEM = 0x40000000;127public static final int ATTRIB_HASSUBFOLDER = 0x80000000;128public static final int ATTRIB_VALIDATE = 0x01000000;129public static final int ATTRIB_REMOVABLE = 0x02000000;130public static final int ATTRIB_COMPRESSED = 0x04000000;131public static final int ATTRIB_BROWSABLE = 0x08000000;132public static final int ATTRIB_NONENUMERATED = 0x00100000;133public static final int ATTRIB_NEWCONTENT = 0x00200000;134135// IShellFolder::GetDisplayNameOf constants136public static final int SHGDN_NORMAL = 0;137public static final int SHGDN_INFOLDER = 1;138public static final int SHGDN_INCLUDE_NONFILESYS= 0x2000;139public static final int SHGDN_FORADDRESSBAR = 0x4000;140public static final int SHGDN_FORPARSING = 0x8000;141142// Values for system call LoadIcon()143public enum SystemIcon {144IDI_APPLICATION(32512),145IDI_HAND(32513),146IDI_ERROR(32513),147IDI_QUESTION(32514),148IDI_EXCLAMATION(32515),149IDI_WARNING(32515),150IDI_ASTERISK(32516),151IDI_INFORMATION(32516),152IDI_WINLOGO(32517);153154private final int iconID;155156SystemIcon(int iconID) {157this.iconID = iconID;158}159160public int getIconID() {161return iconID;162}163}164165static class FolderDisposer implements sun.java2d.DisposerRecord {166/*167* This is cached as a concession to getFolderType(), which needs168* an absolute PIDL.169*/170long absolutePIDL;171/*172* We keep track of shell folders through the IShellFolder173* interface of their parents plus their relative PIDL.174*/175long pIShellFolder;176long relativePIDL;177178boolean disposed;179public void dispose() {180if (disposed) return;181invoke(new Callable<Void>() {182public Void call() {183if (relativePIDL != 0) {184releasePIDL(relativePIDL);185}186if (absolutePIDL != 0) {187releasePIDL(absolutePIDL);188}189if (pIShellFolder != 0) {190releaseIShellFolder(pIShellFolder);191}192return null;193}194});195disposed = true;196}197}198FolderDisposer disposer = new FolderDisposer();199private void setIShellFolder(long pIShellFolder) {200disposer.pIShellFolder = pIShellFolder;201}202private void setRelativePIDL(long relativePIDL) {203disposer.relativePIDL = relativePIDL;204}205/*206* The following are for caching various shell folder properties.207*/208private long pIShellIcon = -1L;209private String folderType = null;210private String displayName = null;211private Image smallIcon = null;212private Image largeIcon = null;213private Boolean isDir = null;214215/*216* The following is to identify the My Documents folder as being special217*/218private boolean isPersonal;219220private static String composePathForCsidl(int csidl) throws IOException, InterruptedException {221String path = getFileSystemPath(csidl);222return path == null223? ("ShellFolder: 0x" + Integer.toHexString(csidl))224: path;225}226227/**228* Create a system special shell folder, such as the229* desktop or Network Neighborhood.230*/231Win32ShellFolder2(final int csidl) throws IOException, InterruptedException {232// Desktop is parent of DRIVES and NETWORK, not necessarily233// other special shell folders.234super(null, composePathForCsidl(csidl));235236invoke(new Callable<Void>() {237public Void call() throws InterruptedException {238if (csidl == DESKTOP) {239initDesktop();240} else {241initSpecial(getDesktop().getIShellFolder(), csidl);242// At this point, the native method initSpecial() has set our relativePIDL243// relative to the Desktop, which may not be our immediate parent. We need244// to traverse this ID list and break it into a chain of shell folders from245// the top, with each one having an immediate parent and a relativePIDL246// relative to that parent.247long pIDL = disposer.relativePIDL;248parent = getDesktop();249while (pIDL != 0) {250// Get a child pidl relative to 'parent'251long childPIDL = copyFirstPIDLEntry(pIDL);252if (childPIDL != 0) {253// Get a handle to the the rest of the ID list254// i,e, parent's grandchilren and down255pIDL = getNextPIDLEntry(pIDL);256if (pIDL != 0) {257// Now we know that parent isn't immediate to 'this' because it258// has a continued ID list. Create a shell folder for this child259// pidl and make it the new 'parent'.260parent = new Win32ShellFolder2((Win32ShellFolder2) parent, childPIDL);261} else {262// No grandchildren means we have arrived at the parent of 'this',263// and childPIDL is directly relative to parent.264disposer.relativePIDL = childPIDL;265}266} else {267break;268}269}270}271return null;272}273}, InterruptedException.class);274275sun.java2d.Disposer.addRecord(this, disposer);276}277278279/**280* Create a system shell folder281*/282Win32ShellFolder2(Win32ShellFolder2 parent, long pIShellFolder, long relativePIDL, String path) {283super(parent, (path != null) ? path : "ShellFolder: ");284this.disposer.pIShellFolder = pIShellFolder;285this.disposer.relativePIDL = relativePIDL;286sun.java2d.Disposer.addRecord(this, disposer);287}288289290/**291* Creates a shell folder with a parent and relative PIDL292*/293Win32ShellFolder2(final Win32ShellFolder2 parent, final long relativePIDL) throws InterruptedException {294super(parent,295invoke(new Callable<String>() {296public String call() {297return getFileSystemPath(parent.getIShellFolder(), relativePIDL);298}299}, RuntimeException.class)300);301this.disposer.relativePIDL = relativePIDL;302sun.java2d.Disposer.addRecord(this, disposer);303}304305// Initializes the desktop shell folder306// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details307private native void initDesktop();308309// Initializes a special, non-file system shell folder310// from one of the above constants311// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details312private native void initSpecial(long desktopIShellFolder, int csidl);313314/** Marks this folder as being the My Documents (Personal) folder */315public void setIsPersonal() {316isPersonal = true;317}318319/**320* This method is implemented to make sure that no instances321* of <code>ShellFolder</code> are ever serialized. If <code>isFileSystem()</code> returns322* <code>true</code>, then the object is representable with an instance of323* <code>java.io.File</code> instead. If not, then the object depends324* on native PIDL state and should not be serialized.325*326* @return a <code>java.io.File</code> replacement object. If the folder327* is a not a normal directory, then returns the first non-removable328* drive (normally "C:\").329*/330protected Object writeReplace() throws java.io.ObjectStreamException {331return invoke(new Callable<File>() {332public File call() {333if (isFileSystem()) {334return new File(getPath());335} else {336Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives();337if (drives != null) {338File[] driveRoots = drives.listFiles();339if (driveRoots != null) {340for (int i = 0; i < driveRoots.length; i++) {341if (driveRoots[i] instanceof Win32ShellFolder2) {342Win32ShellFolder2 sf = (Win32ShellFolder2) driveRoots[i];343if (sf.isFileSystem() && !sf.hasAttribute(ATTRIB_REMOVABLE)) {344return new File(sf.getPath());345}346}347}348}349}350// Ouch, we have no hard drives. Return something "valid" anyway.351return new File("C:\\");352}353}354});355}356357358/**359* Finalizer to clean up any COM objects or PIDLs used by this object.360*/361protected void dispose() {362disposer.dispose();363}364365366// Given a (possibly multi-level) relative PIDL (with respect to367// the desktop, at least in all of the usage cases in this code),368// return a pointer to the next entry. Does not mutate the PIDL in369// any way. Returns 0 if the null terminator is reached.370// Needs to be accessible to Win32ShellFolderManager2371static native long getNextPIDLEntry(long pIDL);372373// Given a (possibly multi-level) relative PIDL (with respect to374// the desktop, at least in all of the usage cases in this code),375// copy the first entry into a newly-allocated PIDL. Returns 0 if376// the PIDL is at the end of the list.377// Needs to be accessible to Win32ShellFolderManager2378static native long copyFirstPIDLEntry(long pIDL);379380// Given a parent's absolute PIDL and our relative PIDL, build an absolute PIDL381private static native long combinePIDLs(long ppIDL, long pIDL);382383// Release a PIDL object384// Needs to be accessible to Win32ShellFolderManager2385static native void releasePIDL(long pIDL);386387// Release an IShellFolder object388// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details389private static native void releaseIShellFolder(long pIShellFolder);390391/**392* Accessor for IShellFolder393*/394private long getIShellFolder() {395if (disposer.pIShellFolder == 0) {396try {397disposer.pIShellFolder = invoke(new Callable<Long>() {398public Long call() {399assert(isDirectory());400assert(parent != null);401long parentIShellFolder = getParentIShellFolder();402if (parentIShellFolder == 0) {403throw new InternalError("Parent IShellFolder was null for "404+ getAbsolutePath());405}406// We are a directory with a parent and a relative PIDL.407// We want to bind to the parent so we get an408// IShellFolder instance associated with us.409long pIShellFolder = bindToObject(parentIShellFolder,410disposer.relativePIDL);411if (pIShellFolder == 0) {412throw new InternalError("Unable to bind "413+ getAbsolutePath() + " to parent");414}415return pIShellFolder;416}417}, RuntimeException.class);418} catch (InterruptedException e) {419// Ignore error420}421}422return disposer.pIShellFolder;423}424425/**426* Get the parent ShellFolder's IShellFolder interface427*/428public long getParentIShellFolder() {429Win32ShellFolder2 parent = (Win32ShellFolder2)getParentFile();430if (parent == null) {431// Parent should only be null if this is the desktop, whose432// relativePIDL is relative to its own IShellFolder.433return getIShellFolder();434}435return parent.getIShellFolder();436}437438/**439* Accessor for relative PIDL440*/441public long getRelativePIDL() {442if (disposer.relativePIDL == 0) {443throw new InternalError("Should always have a relative PIDL");444}445return disposer.relativePIDL;446}447448private long getAbsolutePIDL() {449if (parent == null) {450// This is the desktop451return getRelativePIDL();452} else {453if (disposer.absolutePIDL == 0) {454disposer.absolutePIDL = combinePIDLs(((Win32ShellFolder2)parent).getAbsolutePIDL(), getRelativePIDL());455}456457return disposer.absolutePIDL;458}459}460461/**462* Helper function to return the desktop463*/464public Win32ShellFolder2 getDesktop() {465return Win32ShellFolderManager2.getDesktop();466}467468/**469* Helper function to return the desktop IShellFolder interface470*/471public long getDesktopIShellFolder() {472return getDesktop().getIShellFolder();473}474475private static boolean pathsEqual(String path1, String path2) {476// Same effective implementation as Win32FileSystem477return path1.equalsIgnoreCase(path2);478}479480/**481* Check to see if two ShellFolder objects are the same482*/483public boolean equals(Object o) {484if (o == null || !(o instanceof Win32ShellFolder2)) {485// Short-circuit circuitous delegation path486if (!(o instanceof File)) {487return super.equals(o);488}489return pathsEqual(getPath(), ((File) o).getPath());490}491Win32ShellFolder2 rhs = (Win32ShellFolder2) o;492if ((parent == null && rhs.parent != null) ||493(parent != null && rhs.parent == null)) {494return false;495}496497if (isFileSystem() && rhs.isFileSystem()) {498// Only folders with identical parents can be equal499return (pathsEqual(getPath(), rhs.getPath()) &&500(parent == rhs.parent || parent.equals(rhs.parent)));501}502503if (parent == rhs.parent || parent.equals(rhs.parent)) {504try {505return pidlsEqual(getParentIShellFolder(), disposer.relativePIDL, rhs.disposer.relativePIDL);506} catch (InterruptedException e) {507return false;508}509}510511return false;512}513514private static boolean pidlsEqual(final long pIShellFolder, final long pidl1, final long pidl2)515throws InterruptedException {516return invoke(new Callable<Boolean>() {517public Boolean call() {518return compareIDs(pIShellFolder, pidl1, pidl2) == 0;519}520}, RuntimeException.class);521}522523// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details524private static native int compareIDs(long pParentIShellFolder, long pidl1, long pidl2);525526private volatile Boolean cachedIsFileSystem;527528/**529* @return Whether this is a file system shell folder530*/531public boolean isFileSystem() {532if (cachedIsFileSystem == null) {533cachedIsFileSystem = hasAttribute(ATTRIB_FILESYSTEM);534}535536return cachedIsFileSystem;537}538539/**540* Return whether the given attribute flag is set for this object541*/542public boolean hasAttribute(final int attribute) {543Boolean result = invoke(new Callable<Boolean>() {544public Boolean call() {545// Caching at this point doesn't seem to be cost efficient546return (getAttributes0(getParentIShellFolder(),547getRelativePIDL(), attribute)548& attribute) != 0;549}550});551552return result != null && result;553}554555/**556* Returns the queried attributes specified in attrsMask.557*558* Could plausibly be used for attribute caching but have to be559* very careful not to touch network drives and file system roots560* with a full attrsMask561* NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details562*/563564private static native int getAttributes0(long pParentIShellFolder, long pIDL, int attrsMask);565566// Return the path to the underlying file system object567// Should be called from the COM thread568private static String getFileSystemPath(final long parentIShellFolder, final long relativePIDL) {569int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;570if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&571getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {572573String s =574getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),575getLinkLocation(parentIShellFolder, relativePIDL, false));576if (s != null && s.startsWith("\\\\")) {577return s;578}579}580return getDisplayNameOf(parentIShellFolder, relativePIDL, SHGDN_FORPARSING);581}582583// Needs to be accessible to Win32ShellFolderManager2584static String getFileSystemPath(final int csidl) throws IOException, InterruptedException {585String path = invoke(new Callable<String>() {586public String call() throws IOException {587return getFileSystemPath0(csidl);588}589}, IOException.class);590if (path != null) {591SecurityManager security = System.getSecurityManager();592if (security != null) {593security.checkRead(path);594}595}596return path;597}598599// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details600private static native String getFileSystemPath0(int csidl) throws IOException;601602// Return whether the path is a network root.603// Path is assumed to be non-null604private static boolean isNetworkRoot(String path) {605return (path.equals("\\\\") || path.equals("\\") || path.equals("//") || path.equals("/"));606}607608/**609* @return The parent shell folder of this shell folder, null if610* there is no parent611*/612public File getParentFile() {613return parent;614}615616public boolean isDirectory() {617if (isDir == null) {618// Folders with SFGAO_BROWSABLE have "shell extension" handlers and are619// not traversable in JFileChooser.620if (hasAttribute(ATTRIB_FOLDER) && !hasAttribute(ATTRIB_BROWSABLE)) {621isDir = Boolean.TRUE;622} else if (isLink()) {623ShellFolder linkLocation = getLinkLocation(false);624isDir = Boolean.valueOf(linkLocation != null && linkLocation.isDirectory());625} else {626isDir = Boolean.FALSE;627}628}629return isDir.booleanValue();630}631632/*633* Functions for enumerating an IShellFolder's children634*/635// Returns an IEnumIDList interface for an IShellFolder. The value636// returned must be released using releaseEnumObjects().637private long getEnumObjects(final boolean includeHiddenFiles) throws InterruptedException {638return invoke(new Callable<Long>() {639public Long call() {640boolean isDesktop = disposer.pIShellFolder == getDesktopIShellFolder();641642return getEnumObjects(disposer.pIShellFolder, isDesktop, includeHiddenFiles);643}644}, RuntimeException.class);645}646647// Returns an IEnumIDList interface for an IShellFolder. The value648// returned must be released using releaseEnumObjects().649// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details650private native long getEnumObjects(long pIShellFolder, boolean isDesktop,651boolean includeHiddenFiles);652// Returns the next sequential child as a relative PIDL653// from an IEnumIDList interface. The value returned must654// be released using releasePIDL().655// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details656private native long getNextChild(long pEnumObjects);657// Releases the IEnumIDList interface658// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details659private native void releaseEnumObjects(long pEnumObjects);660661// Returns the IShellFolder of a child from a parent IShellFolder662// and a relative PIDL. The value returned must be released663// using releaseIShellFolder().664// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details665private static native long bindToObject(long parentIShellFolder, long pIDL);666667/**668* @return An array of shell folders that are children of this shell folder669* object. The array will be empty if the folder is empty. Returns670* <code>null</code> if this shellfolder does not denote a directory.671*/672public File[] listFiles(final boolean includeHiddenFiles) {673SecurityManager security = System.getSecurityManager();674if (security != null) {675security.checkRead(getPath());676}677678try {679File[] files = invoke(new Callable<File[]>() {680public File[] call() throws InterruptedException {681if (!isDirectory()) {682return null;683}684// Links to directories are not directories and cannot be parents.685// This does not apply to folders in My Network Places (NetHood)686// because they are both links and real directories!687if (isLink() && !hasAttribute(ATTRIB_FOLDER)) {688return new File[0];689}690691Win32ShellFolder2 desktop = Win32ShellFolderManager2.getDesktop();692Win32ShellFolder2 personal = Win32ShellFolderManager2.getPersonal();693694// If we are a directory, we have a parent and (at least) a695// relative PIDL. We must first ensure we are bound to the696// parent so we have an IShellFolder to query.697long pIShellFolder = getIShellFolder();698// Now we can enumerate the objects in this folder.699ArrayList<Win32ShellFolder2> list = new ArrayList<Win32ShellFolder2>();700long pEnumObjects = getEnumObjects(includeHiddenFiles);701if (pEnumObjects != 0) {702try {703long childPIDL;704int testedAttrs = ATTRIB_FILESYSTEM | ATTRIB_FILESYSANCESTOR;705do {706childPIDL = getNextChild(pEnumObjects);707boolean releasePIDL = true;708if (childPIDL != 0 &&709(getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) {710Win32ShellFolder2 childFolder;711if (Win32ShellFolder2.this.equals(desktop)712&& personal != null713&& pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) {714childFolder = personal;715} else {716childFolder = new Win32ShellFolder2(Win32ShellFolder2.this, childPIDL);717releasePIDL = false;718}719list.add(childFolder);720}721if (releasePIDL) {722releasePIDL(childPIDL);723}724} while (childPIDL != 0 && !Thread.currentThread().isInterrupted());725} finally {726releaseEnumObjects(pEnumObjects);727}728}729return Thread.currentThread().isInterrupted()730? new File[0]731: list.toArray(new ShellFolder[list.size()]);732}733}, InterruptedException.class);734735return Win32ShellFolderManager2.checkFiles(files);736737} catch (InterruptedException e) {738return new File[0];739}740}741742743/**744* Look for (possibly special) child folder by it's path745*746* @return The child shellfolder, or null if not found.747*/748Win32ShellFolder2 getChildByPath(final String filePath) throws InterruptedException {749return invoke(new Callable<Win32ShellFolder2>() {750public Win32ShellFolder2 call() throws InterruptedException {751long pIShellFolder = getIShellFolder();752long pEnumObjects = getEnumObjects(true);753Win32ShellFolder2 child = null;754long childPIDL;755756while ((childPIDL = getNextChild(pEnumObjects)) != 0) {757if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {758String path = getFileSystemPath(pIShellFolder, childPIDL);759if (path != null && path.equalsIgnoreCase(filePath)) {760long childIShellFolder = bindToObject(pIShellFolder, childPIDL);761child = new Win32ShellFolder2(Win32ShellFolder2.this,762childIShellFolder, childPIDL, path);763break;764}765}766releasePIDL(childPIDL);767}768releaseEnumObjects(pEnumObjects);769return child;770}771}, InterruptedException.class);772}773774private volatile Boolean cachedIsLink;775776/**777* @return Whether this shell folder is a link778*/779public boolean isLink() {780if (cachedIsLink == null) {781cachedIsLink = hasAttribute(ATTRIB_LINK);782}783784return cachedIsLink;785}786787/**788* @return Whether this shell folder is marked as hidden789*/790public boolean isHidden() {791return hasAttribute(ATTRIB_HIDDEN);792}793794795// Return the link location of a shell folder796// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details797private static native long getLinkLocation(long parentIShellFolder,798long relativePIDL, boolean resolve);799800/**801* @return The shell folder linked to by this shell folder, or null802* if this shell folder is not a link or is a broken or invalid link803*/804public ShellFolder getLinkLocation() {805return getLinkLocation(true);806}807808private ShellFolder getLinkLocation(final boolean resolve) {809return invoke(new Callable<ShellFolder>() {810public ShellFolder call() {811if (!isLink()) {812return null;813}814815ShellFolder location = null;816long linkLocationPIDL = getLinkLocation(getParentIShellFolder(),817getRelativePIDL(), resolve);818if (linkLocationPIDL != 0) {819try {820location =821Win32ShellFolderManager2.createShellFolderFromRelativePIDL(getDesktop(),822linkLocationPIDL);823} catch (InterruptedException e) {824// Return null825} catch (InternalError e) {826// Could be a link to a non-bindable object, such as a network connection827// TODO: getIShellFolder() should throw FileNotFoundException instead828}829}830return location;831}832});833}834835// Parse a display name into a PIDL relative to the current IShellFolder.836long parseDisplayName(final String name) throws IOException, InterruptedException {837return invoke(new Callable<Long>() {838public Long call() throws IOException {839return parseDisplayName0(getIShellFolder(), name);840}841}, IOException.class);842}843844// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details845private static native long parseDisplayName0(long pIShellFolder, String name) throws IOException;846847// Return the display name of a shell folder848// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details849private static native String getDisplayNameOf(long parentIShellFolder,850long relativePIDL,851int attrs);852853/**854* @return The name used to display this shell folder855*/856public String getDisplayName() {857if (displayName == null) {858displayName =859invoke(new Callable<String>() {860public String call() {861return getDisplayNameOf(getParentIShellFolder(),862getRelativePIDL(), SHGDN_NORMAL);863}864});865}866return displayName;867}868869// Return the folder type of a shell folder870// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details871private static native String getFolderType(long pIDL);872873/**874* @return The type of shell folder as a string875*/876public String getFolderType() {877if (folderType == null) {878final long absolutePIDL = getAbsolutePIDL();879folderType =880invoke(new Callable<String>() {881public String call() {882return getFolderType(absolutePIDL);883}884});885}886return folderType;887}888889// Return the executable type of a file system shell folder890private native String getExecutableType(String path);891892/**893* @return The executable type as a string894*/895public String getExecutableType() {896if (!isFileSystem()) {897return null;898}899return getExecutableType(getAbsolutePath());900}901902903904// Icons905906private static Map smallSystemImages = new HashMap();907private static Map largeSystemImages = new HashMap();908private static Map smallLinkedSystemImages = new HashMap();909private static Map largeLinkedSystemImages = new HashMap();910911// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details912private static native long getIShellIcon(long pIShellFolder);913914// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details915private static native int getIconIndex(long parentIShellIcon, long relativePIDL);916917// Return the icon of a file system shell folder in the form of an HICON918private static native long getIcon(String absolutePath, boolean getLargeIcon);919920// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details921private static native long extractIcon(long parentIShellFolder, long relativePIDL,922boolean getLargeIcon);923924// Returns an icon from the Windows system icon list in the form of an HICON925private static native long getSystemIcon(int iconID);926private static native long getIconResource(String libName, int iconID,927int cxDesired, int cyDesired,928boolean useVGAColors);929// Note: useVGAColors is ignored on XP and later930931// Return the bits from an HICON. This has a side effect of setting932// the imageHash variable for efficient caching / comparing.933private static native int[] getIconBits(long hIcon, int iconSize);934// Dispose the HICON935private static native void disposeIcon(long hIcon);936937static native int[] getStandardViewButton0(int iconIndex);938939// Should be called from the COM thread940private long getIShellIcon() {941if (pIShellIcon == -1L) {942pIShellIcon = getIShellIcon(getIShellFolder());943}944945return pIShellIcon;946}947948private static Image makeIcon(long hIcon, boolean getLargeIcon) {949if (hIcon != 0L && hIcon != -1L) {950// Get the bits. This has the side effect of setting the imageHash value for this object.951int size = getLargeIcon ? 32 : 16;952int[] iconBits = getIconBits(hIcon, size);953if (iconBits != null) {954BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);955img.setRGB(0, 0, size, size, iconBits, 0, size);956return img;957}958}959return null;960}961962963/**964* @return The icon image used to display this shell folder965*/966public Image getIcon(final boolean getLargeIcon) {967Image icon = getLargeIcon ? largeIcon : smallIcon;968if (icon == null) {969icon =970invoke(new Callable<Image>() {971public Image call() {972Image newIcon = null;973if (isFileSystem()) {974long parentIShellIcon = (parent != null)975? ((Win32ShellFolder2) parent).getIShellIcon()976: 0L;977long relativePIDL = getRelativePIDL();978979// These are cached per type (using the index in the system image list)980int index = getIconIndex(parentIShellIcon, relativePIDL);981if (index > 0) {982Map imageCache;983if (isLink()) {984imageCache = getLargeIcon ? largeLinkedSystemImages : smallLinkedSystemImages;985} else {986imageCache = getLargeIcon ? largeSystemImages : smallSystemImages;987}988newIcon = (Image) imageCache.get(Integer.valueOf(index));989if (newIcon == null) {990long hIcon = getIcon(getAbsolutePath(), getLargeIcon);991newIcon = makeIcon(hIcon, getLargeIcon);992disposeIcon(hIcon);993if (newIcon != null) {994imageCache.put(Integer.valueOf(index), newIcon);995}996}997}998}9991000if (newIcon == null) {1001// These are only cached per object1002long hIcon = extractIcon(getParentIShellFolder(),1003getRelativePIDL(), getLargeIcon);1004newIcon = makeIcon(hIcon, getLargeIcon);1005disposeIcon(hIcon);1006}10071008if (newIcon == null) {1009newIcon = Win32ShellFolder2.super.getIcon(getLargeIcon);1010}1011return newIcon;1012}1013});1014if (getLargeIcon) {1015largeIcon = icon;1016} else {1017smallIcon = icon;1018}1019}1020return icon;1021}10221023/**1024* Gets an icon from the Windows system icon list as an <code>Image</code>1025*/1026static Image getSystemIcon(SystemIcon iconType) {1027long hIcon = getSystemIcon(iconType.getIconID());1028Image icon = makeIcon(hIcon, true);1029disposeIcon(hIcon);1030return icon;1031}10321033/**1034* Gets an icon from the Windows system icon list as an <code>Image</code>1035*/1036static Image getShell32Icon(int iconID, boolean getLargeIcon) {1037boolean useVGAColors = true; // Will be ignored on XP and later10381039int size = getLargeIcon ? 32 : 16;10401041Toolkit toolkit = Toolkit.getDefaultToolkit();1042String shellIconBPP = (String)toolkit.getDesktopProperty("win.icon.shellIconBPP");1043if (shellIconBPP != null) {1044useVGAColors = shellIconBPP.equals("4");1045}10461047long hIcon = getIconResource("shell32.dll", iconID, size, size, useVGAColors);1048if (hIcon != 0) {1049Image icon = makeIcon(hIcon, getLargeIcon);1050disposeIcon(hIcon);1051return icon;1052}1053return null;1054}10551056/**1057* Returns the canonical form of this abstract pathname. Equivalent to1058* <code>new Win32ShellFolder2(getParentFile(), this.{@link java.io.File#getCanonicalPath}())</code>.1059*1060* @see java.io.File#getCanonicalFile1061*/1062public File getCanonicalFile() throws IOException {1063return this;1064}10651066/*1067* Indicates whether this is a special folder (includes My Documents)1068*/1069public boolean isSpecial() {1070return isPersonal || !isFileSystem() || (this == getDesktop());1071}10721073/**1074* Compares this object with the specified object for order.1075*1076* @see sun.awt.shell.ShellFolder#compareTo(File)1077*/1078public int compareTo(File file2) {1079if (!(file2 instanceof Win32ShellFolder2)) {1080if (isFileSystem() && !isSpecial()) {1081return super.compareTo(file2);1082} else {1083return -1; // Non-file shellfolders sort before files1084}1085}1086return Win32ShellFolderManager2.compareShellFolders(this, (Win32ShellFolder2) file2);1087}10881089// native constants from commctrl.h1090private static final int LVCFMT_LEFT = 0;1091private static final int LVCFMT_RIGHT = 1;1092private static final int LVCFMT_CENTER = 2;10931094public ShellFolderColumnInfo[] getFolderColumns() {1095return invoke(new Callable<ShellFolderColumnInfo[]>() {1096public ShellFolderColumnInfo[] call() {1097ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());10981099if (columns != null) {1100List<ShellFolderColumnInfo> notNullColumns =1101new ArrayList<ShellFolderColumnInfo>();1102for (int i = 0; i < columns.length; i++) {1103ShellFolderColumnInfo column = columns[i];1104if (column != null) {1105column.setAlignment(column.getAlignment() == LVCFMT_RIGHT1106? SwingConstants.RIGHT1107: column.getAlignment() == LVCFMT_CENTER1108? SwingConstants.CENTER1109: SwingConstants.LEADING);11101111column.setComparator(new ColumnComparator(Win32ShellFolder2.this, i));11121113notNullColumns.add(column);1114}1115}1116columns = new ShellFolderColumnInfo[notNullColumns.size()];1117notNullColumns.toArray(columns);1118}1119return columns;1120}1121});1122}11231124public Object getFolderColumnValue(final int column) {1125return invoke(new Callable<Object>() {1126public Object call() {1127return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column);1128}1129});1130}11311132// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1133private native ShellFolderColumnInfo[] doGetColumnInfo(long iShellFolder2);11341135// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1136private native Object doGetColumnValue(long parentIShellFolder2, long childPIDL, int columnIdx);11371138// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details1139private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx);114011411142public void sortChildren(final List<? extends File> files) {1143// To avoid loads of synchronizations with Invoker and improve performance we1144// synchronize the whole code of the sort method once1145invoke(new Callable<Void>() {1146public Void call() {1147Collections.sort(files, new ColumnComparator(Win32ShellFolder2.this, 0));11481149return null;1150}1151});1152}11531154private static class ColumnComparator implements Comparator<File> {1155private final Win32ShellFolder2 shellFolder;11561157private final int columnIdx;11581159public ColumnComparator(Win32ShellFolder2 shellFolder, int columnIdx) {1160this.shellFolder = shellFolder;1161this.columnIdx = columnIdx;1162}11631164// compares 2 objects within this folder by the specified column1165public int compare(final File o, final File o1) {1166Integer result = invoke(new Callable<Integer>() {1167public Integer call() {1168if (o instanceof Win32ShellFolder21169&& o1 instanceof Win32ShellFolder2) {1170// delegates comparison to native method1171return compareIDsByColumn(shellFolder.getIShellFolder(),1172((Win32ShellFolder2) o).getRelativePIDL(),1173((Win32ShellFolder2) o1).getRelativePIDL(),1174columnIdx);1175}1176return 0;1177}1178});11791180return result == null ? 0 : result;1181}1182}1183}118411851186