8003399: JFileChooser gives wrong path to selected file when saving to Libraries folder on Windows 7
authorssadetsky
Mon, 25 May 2015 16:10:12 +0300
changeset 30949 5e54ad419c64
parent 30948 0a0972d3b58d
child 30950 2e6d6c55d109
8003399: JFileChooser gives wrong path to selected file when saving to Libraries folder on Windows 7 Reviewed-by: serb, ant
jdk/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java
jdk/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp
jdk/test/java/awt/FileDialog/8003399/bug8003399.java
--- a/jdk/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java	Fri May 22 23:26:00 2015 +0300
+++ b/jdk/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java	Mon May 25 16:10:12 2015 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -163,6 +163,27 @@
         }
     }
 
+    // Known Folder data
+    static class KnownFolderDefinition {
+        String guid;
+        int category;
+        String name;
+        String description;
+        String parent;
+        String relativePath;
+        String parsingName;
+        String tooltip;
+        String localizedName;
+        String icon;
+        String security;
+        long attributes;
+        int defenitionFlags;
+        String ftidType;
+        String path;
+        String saveLocation;
+        static final List<KnownFolderDefinition> libraries = getLibraries();
+    }
+
     static class FolderDisposer implements sun.java2d.DisposerRecord {
         /*
          * This is cached as a concession to getFolderType(), which needs
@@ -578,7 +599,22 @@
                 return s;
             }
         }
-        return getDisplayNameOf(parentIShellFolder, relativePIDL, SHGDN_FORPARSING);
+        String path = getDisplayNameOf(parentIShellFolder, relativePIDL,
+                        SHGDN_FORPARSING);
+        // if this is a library its default save location is taken as a path
+        // this is a temp fix until java.io starts support Libraries
+        if( path != null && path.startsWith("::{") &&
+                path.toLowerCase().endsWith(".library-ms")) {
+            for (KnownFolderDefinition kf : KnownFolderDefinition.libraries) {
+                if( path.toLowerCase().endsWith(
+                            kf.relativePath.toLowerCase()) &&
+                            path.toUpperCase().startsWith(
+                            kf.parsingName.substring(0, 40).toUpperCase()) ) {
+                    return kf.saveLocation;
+                }
+            }
+        }
+        return path;
     }
 
     // Needs to be accessible to Win32ShellFolderManager2
@@ -848,6 +884,9 @@
                                                   long relativePIDL,
                                                   int attrs);
 
+    // Returns data of all Known Folders registered in the system
+    private static native KnownFolderDefinition[] loadKnownFolders();
+
     /**
      * @return The name used to display this shell folder
      */
@@ -1178,4 +1217,26 @@
             return result == null ? 0 : result;
         }
     }
+
+    // Extracts libraries and their default save locations from Known Folders list
+    private static List<KnownFolderDefinition> getLibraries() {
+        return invoke(new Callable<List<KnownFolderDefinition>>() {
+            @Override
+            public List<KnownFolderDefinition> call() throws Exception {
+                KnownFolderDefinition[] all = loadKnownFolders();
+                List<KnownFolderDefinition> folders = new ArrayList<>();
+                if (all != null) {
+                    for (KnownFolderDefinition kf : all) {
+                        if (kf.relativePath == null || kf.parsingName == null ||
+                                kf.saveLocation == null) {
+                            continue;
+                        }
+                        folders.add(kf);
+                    }
+                }
+                return folders;
+            }
+        });
+    }
+
 }
--- a/jdk/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp	Fri May 22 23:26:00 2015 +0300
+++ b/jdk/src/java.desktop/windows/native/libawt/windows/ShellFolder2.cpp	Mon May 25 16:10:12 2015 +0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -64,6 +64,16 @@
 
 //#include <sun_awt_shell_Win32ShellFolder2.h>
 
+#define DEFINE_FIELD_ID(var, cls, field, type)                            \
+    jfieldID var = env->GetFieldID(cls, field, type);                     \
+    DASSERT(var != NULL);                                                 \
+    CHECK_NULL_RETURN(var, NULL);
+
+#define EXCEPTION_CHECK                                                   \
+   if(env->ExceptionCheck()) {                                            \
+        throw std::bad_alloc();                                           \
+   }
+
 // Shell Functions
 typedef BOOL (WINAPI *DestroyIconType)(HICON);
 typedef HINSTANCE (WINAPI *FindExecutableType)(LPCTSTR,LPCTSTR,LPTSTR);
@@ -1263,5 +1273,216 @@
     return 0;
 }
 
+/*
+ * Class:     sun_awt_shell_Win32ShellFolder2
+ * Method:    loadKnownFolders
+ * Signature: (V)[BLsun/awt/shell/Win32ShellFolder2$KnownfolderDefenition;
+ */
+JNIEXPORT jobjectArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_loadKnownFolders
+    (JNIEnv* env, jclass cls )
+{
+    CoInitialize(NULL);
+    IKnownFolderManager* pkfm = NULL;
+    HRESULT hr = CoCreateInstance(CLSID_KnownFolderManager, NULL,
+                                CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pkfm));
+    if (!SUCCEEDED(hr)) return NULL;
+
+    TRY;
+
+    jclass cl = env->FindClass("sun/awt/shell/Win32ShellFolder2$KnownFolderDefinition");
+    CHECK_NULL_RETURN(cl, NULL);
+    DEFINE_FIELD_ID(field_guid, cl, "guid", "Ljava/lang/String;")
+    DEFINE_FIELD_ID(field_name, cl, "name", "Ljava/lang/String;");
+    DEFINE_FIELD_ID(field_description, cl, "description", "Ljava/lang/String;");
+    DEFINE_FIELD_ID(field_parent, cl, "parent", "Ljava/lang/String;");
+    DEFINE_FIELD_ID(field_relativePath, cl, "relativePath", "Ljava/lang/String;");
+    DEFINE_FIELD_ID(field_parsingName, cl, "parsingName", "Ljava/lang/String;");
+    DEFINE_FIELD_ID(field_tooltip, cl, "tooltip", "Ljava/lang/String;");
+    DEFINE_FIELD_ID(field_localizedName, cl, "localizedName", "Ljava/lang/String;");
+    DEFINE_FIELD_ID(field_icon, cl, "icon", "Ljava/lang/String;");
+    DEFINE_FIELD_ID(field_security, cl, "security", "Ljava/lang/String;");
+    DEFINE_FIELD_ID(field_path, cl, "path", "Ljava/lang/String;");
+    DEFINE_FIELD_ID(field_saveLocation, cl, "saveLocation", "Ljava/lang/String;");
+    DEFINE_FIELD_ID(field_category, cl, "category", "I");
+    DEFINE_FIELD_ID(field_attributes, cl, "attributes", "J");
+    DEFINE_FIELD_ID(field_defenitionFlags, cl, "defenitionFlags", "I");
+    DEFINE_FIELD_ID(field_ftidType, cl, "ftidType", "Ljava/lang/String;");
+
+    jobjectArray result;
+    KNOWNFOLDERID* pFoldersIds = NULL;
+    UINT count = 0;
+    if (SUCCEEDED(pkfm->GetFolderIds(&pFoldersIds, &count))) {
+        jmethodID initMethod;
+        try {
+            result = env->NewObjectArray(count, cl, NULL);
+            initMethod = env->GetMethodID(cl, "<init>", "()V");
+            EXCEPTION_CHECK
+        } catch (std::bad_alloc&) {
+            CoTaskMemFree(pFoldersIds);
+            pkfm->Release();
+            throw;
+        }
+        for(UINT i = 0; i < count; ++i)
+        {
+            jobject fld;
+            const KNOWNFOLDERID& folderId = pFoldersIds[i];
+            LPOLESTR guid = NULL;
+            try {
+                fld = env->NewObject(cl, initMethod);
+                if (fld) {
+                    env->SetObjectArrayElement(result, i, fld);
+                }
+                EXCEPTION_CHECK
+
+                if (SUCCEEDED(StringFromCLSID(folderId, &guid))) {
+                    jstring jstr = JNU_NewStringPlatform(env, guid);
+                    if (jstr) {
+                        env->SetObjectField(fld, field_guid, jstr);
+                    }
+                    CoTaskMemFree(guid);
+                    EXCEPTION_CHECK
+                }
+            } catch (std::bad_alloc&) {
+                CoTaskMemFree(pFoldersIds);
+                pkfm->Release();
+                throw;
+            }
+
+            IKnownFolder* pFolder = NULL;
+            if (SUCCEEDED(pkfm->GetFolder(folderId, &pFolder))) {
+                KNOWNFOLDER_DEFINITION kfDef;
+                if (SUCCEEDED(pFolder->GetFolderDefinition(&kfDef)))
+                {
+                    try {
+                        jstring jstr = JNU_NewStringPlatform(env, kfDef.pszName);
+                        if(jstr) {
+                            env->SetObjectField(fld, field_name, jstr);
+                        }
+                        EXCEPTION_CHECK
+                        if (kfDef.pszDescription) {
+                            jstr = JNU_NewStringPlatform(env, kfDef.pszDescription);
+                            if (jstr) {
+                                env->SetObjectField(fld, field_description, jstr);
+                            }
+                            EXCEPTION_CHECK
+                        }
+                        EXCEPTION_CHECK
+                        if (SUCCEEDED(StringFromCLSID(kfDef.fidParent, &guid))) {
+                            jstr = JNU_NewStringPlatform(env, guid);
+                            if (jstr) {
+                                env->SetObjectField(fld, field_parent, jstr);
+                            }
+                            CoTaskMemFree(guid);
+                            EXCEPTION_CHECK
+                        }
+                        if (kfDef.pszRelativePath) {
+                            jstr = JNU_NewStringPlatform(env, kfDef.pszRelativePath);
+                            if (jstr) {
+                                env->SetObjectField(fld, field_relativePath, jstr);
+                            }
+                            EXCEPTION_CHECK
+                        }
+                        if (kfDef.pszParsingName) {
+                            jstr = JNU_NewStringPlatform(env, kfDef.pszParsingName);
+                            if (jstr) {
+                                env->SetObjectField(fld, field_parsingName, jstr);
+                            }
+                            EXCEPTION_CHECK
+                        }
+                        if (kfDef.pszTooltip) {
+                            jstr = JNU_NewStringPlatform(env, kfDef.pszTooltip);
+                            if (jstr) {
+                                env->SetObjectField(fld, field_tooltip, jstr);
+                            }
+                            EXCEPTION_CHECK
+                        }
+                        if (kfDef.pszLocalizedName) {
+                            jstr = JNU_NewStringPlatform(env, kfDef.pszLocalizedName);
+                            if (jstr) {
+                                env->SetObjectField(fld, field_localizedName, jstr);
+                            }
+                            EXCEPTION_CHECK
+                        }
+                        if (kfDef.pszIcon) {
+                            jstr = JNU_NewStringPlatform(env, kfDef.pszIcon);
+                            if (jstr) {
+                                env->SetObjectField(fld, field_icon, jstr);
+                            }
+                            EXCEPTION_CHECK
+                        }
+                        if (kfDef.pszSecurity) {
+                            jstr = JNU_NewStringPlatform(env, kfDef.pszSecurity);
+                            if (jstr) {
+                                env->SetObjectField(fld, field_security, jstr);
+                            }
+                            EXCEPTION_CHECK
+                        }
+                        if (SUCCEEDED(StringFromCLSID(kfDef.ftidType, &guid))) {
+                            jstr = JNU_NewStringPlatform(env, guid);
+                            if (jstr) {
+                                env->SetObjectField(fld, field_ftidType, jstr);
+                            }
+                            CoTaskMemFree(guid);
+                            EXCEPTION_CHECK
+                        }
+                        env->SetIntField(fld, field_category, kfDef.category);
+                        env->SetIntField(fld, field_defenitionFlags, kfDef.kfdFlags);
+                        env->SetLongField(fld, field_attributes, kfDef.dwAttributes);
+
+                        LPWSTR folderPath = NULL;
+                        if (SUCCEEDED(pFolder->GetPath(KF_FLAG_NO_ALIAS, &folderPath))
+                                    && folderPath) {
+                            jstr = JNU_NewStringPlatform(env, folderPath);
+                            if (jstr) {
+                                env->SetObjectField(fld, field_path, jstr);
+                            }
+                            CoTaskMemFree(folderPath);
+                            EXCEPTION_CHECK
+                        }
+
+                        IShellLibrary *plib = NULL;
+                        hr = CoCreateInstance(CLSID_ShellLibrary, NULL,
+                                         CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&plib));
+                        if (SUCCEEDED(hr)) {
+                            hr = plib->LoadLibraryFromKnownFolder(folderId, STGM_READWRITE);
+                            if (SUCCEEDED(hr)) {
+                                IShellItem *item = NULL;
+                                hr = plib->GetDefaultSaveFolder(DSFT_DETECT,
+                                        IID_PPV_ARGS(&item));
+                                if (SUCCEEDED(hr) && item) {
+                                    LPWSTR loc = NULL;
+                                    hr = item->GetDisplayName(SIGDN_FILESYSPATH, &loc);
+                                    if (SUCCEEDED(hr) && loc)
+                                    {
+                                        jstr = JNU_NewStringPlatform(env, loc);
+                                        if (jstr) {
+                                            env->SetObjectField(fld, field_saveLocation, jstr);
+                                        }
+                                        CoTaskMemFree(loc);
+                                    }
+                                    item->Release();
+                                }
+                            }
+                            plib->Release();
+                            EXCEPTION_CHECK
+                        }
+                        FreeKnownFolderDefinitionFields(&kfDef);
+                    } catch (std::bad_alloc&) {
+                        FreeKnownFolderDefinitionFields(&kfDef);
+                        pFolder->Release();
+                        CoTaskMemFree(pFoldersIds);
+                        pkfm->Release();
+                        throw;
+                    }
+                }
+            }
+            pFolder->Release();
+        }
+        CoTaskMemFree(pFoldersIds);
+    }
+    pkfm->Release();
+    return result;
+    CATCH_BAD_ALLOC_RET(NULL);
+}
 
 } // extern "C"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/FileDialog/8003399/bug8003399.java	Mon May 25 16:10:12 2015 +0300
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+   @bug 8003399
+   @summary JFileChooser gives wrong path to selected file when saving to Libraries folder on Windows 7
+   @author Semyon Sadetsky
+   @library /lib/testlibrary
+   @build jdk.testlibrary.OSInfo
+   @run main bug8003399
+  */
+
+import jdk.testlibrary.OSInfo;
+
+import javax.swing.filechooser.FileSystemView;
+import java.io.File;
+
+public class bug8003399 {
+
+    public static void main(String[] args) throws Exception {
+        if (OSInfo.getOSType() == OSInfo.OSType.WINDOWS &&
+                OSInfo.getWindowsVersion().compareTo(OSInfo.WINDOWS_VISTA) > 0 ) {
+            FileSystemView fsv = FileSystemView.getFileSystemView();
+            for (File file : fsv.getFiles(fsv.getHomeDirectory(), false)) {
+                if(file.isDirectory()) {
+                    for (File file1 : fsv.getFiles(file, false)) {
+                        if(file1.isDirectory())
+                        {
+                            String path = file1.getPath();
+                            if(path.startsWith("::{") &&
+                                    path.toLowerCase().endsWith(".library-ms")) {
+                                throw new RuntimeException("Unconverted library link found");
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        System.out.println("ok");
+    }
+}