Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolderManager2.java
41159 views
1
/*
2
* Copyright (c) 2003, 2021, 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 sun.awt.shell;
27
28
import java.awt.Image;
29
import java.awt.Toolkit;
30
import java.awt.image.BufferedImage;
31
import java.io.File;
32
import java.io.FileNotFoundException;
33
import java.io.IOException;
34
import java.security.AccessController;
35
import java.security.PrivilegedAction;
36
import java.util.ArrayList;
37
import java.util.Arrays;
38
import java.util.List;
39
import java.util.concurrent.Callable;
40
import java.util.concurrent.ExecutionException;
41
import java.util.concurrent.Future;
42
import java.util.concurrent.LinkedBlockingQueue;
43
import java.util.concurrent.RejectedExecutionException;
44
import java.util.concurrent.ThreadFactory;
45
import java.util.concurrent.ThreadPoolExecutor;
46
import java.util.concurrent.TimeUnit;
47
import java.util.stream.Stream;
48
49
import sun.awt.OSInfo;
50
import sun.awt.util.ThreadGroupUtils;
51
import sun.util.logging.PlatformLogger;
52
53
import static sun.awt.shell.Win32ShellFolder2.DESKTOP;
54
import static sun.awt.shell.Win32ShellFolder2.DRIVES;
55
import static sun.awt.shell.Win32ShellFolder2.Invoker;
56
import static sun.awt.shell.Win32ShellFolder2.LARGE_ICON_SIZE;
57
import static sun.awt.shell.Win32ShellFolder2.MultiResolutionIconImage;
58
import static sun.awt.shell.Win32ShellFolder2.NETWORK;
59
import static sun.awt.shell.Win32ShellFolder2.PERSONAL;
60
import static sun.awt.shell.Win32ShellFolder2.RECENT;
61
import static sun.awt.shell.Win32ShellFolder2.SMALL_ICON_SIZE;
62
// NOTE: This class supersedes Win32ShellFolderManager, which was removed
63
// from distribution after version 1.4.2.
64
65
/**
66
* @author Michael Martak
67
* @author Leif Samuelsson
68
* @author Kenneth Russell
69
* @since 1.4
70
*/
71
72
final class Win32ShellFolderManager2 extends ShellFolderManager {
73
74
private static final PlatformLogger
75
log = PlatformLogger.getLogger("sun.awt.shell.Win32ShellFolderManager2");
76
77
static {
78
// Load library here
79
sun.awt.windows.WToolkit.loadLibraries();
80
}
81
82
public ShellFolder createShellFolder(File file) throws FileNotFoundException {
83
try {
84
return createShellFolder(getDesktop(), file);
85
} catch (InterruptedException e) {
86
throw new FileNotFoundException("Execution was interrupted");
87
}
88
}
89
90
static Win32ShellFolder2 createShellFolder(Win32ShellFolder2 parent, File file)
91
throws FileNotFoundException, InterruptedException {
92
long pIDL;
93
try {
94
pIDL = parent.parseDisplayName(file.getCanonicalPath());
95
} catch (IOException ex) {
96
pIDL = 0;
97
}
98
if (pIDL == 0) {
99
// Shouldn't happen but watch for it anyway
100
throw new FileNotFoundException("File " + file.getAbsolutePath() + " not found");
101
}
102
103
try {
104
return createShellFolderFromRelativePIDL(parent, pIDL);
105
} finally {
106
Win32ShellFolder2.releasePIDL(pIDL);
107
}
108
}
109
110
static Win32ShellFolder2 createShellFolderFromRelativePIDL(Win32ShellFolder2 parent, long pIDL)
111
throws InterruptedException {
112
// Walk down this relative pIDL, creating new nodes for each of the entries
113
while (pIDL != 0) {
114
long curPIDL = Win32ShellFolder2.copyFirstPIDLEntry(pIDL);
115
if (curPIDL != 0) {
116
parent = Win32ShellFolder2.createShellFolder(parent, curPIDL);
117
pIDL = Win32ShellFolder2.getNextPIDLEntry(pIDL);
118
} else {
119
// The list is empty if the parent is Desktop and pIDL is a shortcut to Desktop
120
break;
121
}
122
}
123
return parent;
124
}
125
126
private static final int VIEW_LIST = 2;
127
private static final int VIEW_DETAILS = 3;
128
private static final int VIEW_PARENTFOLDER = 8;
129
private static final int VIEW_NEWFOLDER = 11;
130
131
private static final Image[] STANDARD_VIEW_BUTTONS = new Image[12];
132
133
private static Image getStandardViewButton(int iconIndex) {
134
Image result = STANDARD_VIEW_BUTTONS[iconIndex];
135
136
if (result != null) {
137
return result;
138
}
139
140
final int[] iconBits = Win32ShellFolder2
141
.getStandardViewButton0(iconIndex, true);
142
if (iconBits != null) {
143
// icons are always square
144
final int size = (int) Math.sqrt(iconBits.length);
145
final BufferedImage img =
146
new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
147
img.setRGB(0, 0, size, size, iconBits, 0, size);
148
149
STANDARD_VIEW_BUTTONS[iconIndex] = (size == SMALL_ICON_SIZE)
150
? img
151
: new MultiResolutionIconImage(SMALL_ICON_SIZE, img);
152
}
153
154
return STANDARD_VIEW_BUTTONS[iconIndex];
155
}
156
157
// Special folders
158
private static Win32ShellFolder2 desktop;
159
private static Win32ShellFolder2 drives;
160
private static Win32ShellFolder2 recent;
161
private static Win32ShellFolder2 network;
162
private static Win32ShellFolder2 personal;
163
164
static Win32ShellFolder2 getDesktop() {
165
if (desktop == null) {
166
try {
167
desktop = new Win32ShellFolder2(DESKTOP);
168
} catch (final SecurityException ignored) {
169
// Ignore, the message may have sensitive information, not
170
// accessible other ways
171
} catch (IOException | InterruptedException e) {
172
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
173
log.warning("Cannot access 'Desktop'", e);
174
}
175
}
176
}
177
return desktop;
178
}
179
180
static Win32ShellFolder2 getDrives() {
181
if (drives == null) {
182
try {
183
drives = new Win32ShellFolder2(DRIVES);
184
} catch (final SecurityException ignored) {
185
// Ignore, the message may have sensitive information, not
186
// accessible other ways
187
} catch (IOException | InterruptedException e) {
188
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
189
log.warning("Cannot access 'Drives'", e);
190
}
191
}
192
}
193
return drives;
194
}
195
196
static Win32ShellFolder2 getRecent() {
197
if (recent == null) {
198
try {
199
String path = Win32ShellFolder2.getFileSystemPath(RECENT);
200
if (path != null) {
201
recent = createShellFolder(getDesktop(), new File(path));
202
}
203
} catch (final SecurityException ignored) {
204
// Ignore, the message may have sensitive information, not
205
// accessible other ways
206
} catch (InterruptedException | IOException e) {
207
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
208
log.warning("Cannot access 'Recent'", e);
209
}
210
}
211
}
212
return recent;
213
}
214
215
static Win32ShellFolder2 getNetwork() {
216
if (network == null) {
217
try {
218
network = new Win32ShellFolder2(NETWORK);
219
} catch (final SecurityException ignored) {
220
// Ignore, the message may have sensitive information, not
221
// accessible other ways
222
} catch (IOException | InterruptedException e) {
223
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
224
log.warning("Cannot access 'Network'", e);
225
}
226
}
227
}
228
return network;
229
}
230
231
static Win32ShellFolder2 getPersonal() {
232
if (personal == null) {
233
try {
234
String path = Win32ShellFolder2.getFileSystemPath(PERSONAL);
235
if (path != null) {
236
Win32ShellFolder2 desktop = getDesktop();
237
personal = desktop.getChildByPath(path);
238
if (personal == null) {
239
personal = createShellFolder(getDesktop(), new File(path));
240
}
241
if (personal != null) {
242
personal.setIsPersonal();
243
}
244
}
245
} catch (final SecurityException ignored) {
246
// Ignore, the message may have sensitive information, not
247
// accessible other ways
248
} catch (InterruptedException | IOException e) {
249
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
250
log.warning("Cannot access 'Personal'", e);
251
}
252
}
253
}
254
return personal;
255
}
256
257
258
private static File[] roots;
259
260
/**
261
* @param key a {@code String}
262
* "fileChooserDefaultFolder":
263
* Returns a {@code File} - the default shellfolder for a new filechooser
264
* "roots":
265
* Returns a {@code File[]} - containing the root(s) of the displayable hierarchy
266
* "fileChooserComboBoxFolders":
267
* Returns a {@code File[]} - an array of shellfolders representing the list to
268
* show by default in the file chooser's combobox
269
* "fileChooserShortcutPanelFolders":
270
* Returns a {@code File[]} - an array of shellfolders representing well-known
271
* folders, such as Desktop, Documents, History, Network, Home, etc.
272
* This is used in the shortcut panel of the filechooser on Windows 2000
273
* and Windows Me.
274
* "fileChooserIcon <icon>":
275
* Returns an {@code Image} - icon can be ListView, DetailsView, UpFolder, NewFolder or
276
* ViewMenu (Windows only).
277
* "optionPaneIcon iconName":
278
* Returns an {@code Image} - icon from the system icon list
279
*
280
* @return An Object matching the key string.
281
*/
282
public Object get(String key) {
283
if (key.equals("fileChooserDefaultFolder")) {
284
File file = getPersonal();
285
if (file == null) {
286
file = getDesktop();
287
}
288
return checkFile(file);
289
} else if (key.equals("roots")) {
290
// Should be "History" and "Desktop" ?
291
if (roots == null) {
292
File desktop = getDesktop();
293
if (desktop != null) {
294
roots = new File[] { desktop };
295
} else {
296
roots = (File[])super.get(key);
297
}
298
}
299
return checkFiles(roots);
300
} else if (key.equals("fileChooserComboBoxFolders")) {
301
Win32ShellFolder2 desktop = getDesktop();
302
303
if (desktop != null && checkFile(desktop) != null) {
304
ArrayList<File> folders = new ArrayList<File>();
305
Win32ShellFolder2 drives = getDrives();
306
307
Win32ShellFolder2 recentFolder = getRecent();
308
if (recentFolder != null && OSInfo.getWindowsVersion().compareTo(OSInfo.WINDOWS_2000) >= 0) {
309
folders.add(recentFolder);
310
}
311
312
folders.add(desktop);
313
// Add all second level folders
314
File[] secondLevelFolders = checkFiles(desktop.listFiles());
315
Arrays.sort(secondLevelFolders);
316
for (File secondLevelFolder : secondLevelFolders) {
317
Win32ShellFolder2 folder = (Win32ShellFolder2) secondLevelFolder;
318
if (!folder.isFileSystem() || (folder.isDirectory() && !folder.isLink())) {
319
folders.add(folder);
320
// Add third level for "My Computer"
321
if (folder.equals(drives)) {
322
File[] thirdLevelFolders = checkFiles(folder.listFiles());
323
if (thirdLevelFolders != null && thirdLevelFolders.length > 0) {
324
List<File> thirdLevelFoldersList = Arrays.asList(thirdLevelFolders);
325
326
folder.sortChildren(thirdLevelFoldersList);
327
folders.addAll(thirdLevelFoldersList);
328
}
329
}
330
}
331
}
332
return checkFiles(folders);
333
} else {
334
return super.get(key);
335
}
336
} else if (key.equals("fileChooserShortcutPanelFolders")) {
337
Toolkit toolkit = Toolkit.getDefaultToolkit();
338
ArrayList<File> folders = new ArrayList<File>();
339
int i = 0;
340
Object value;
341
do {
342
value = toolkit.getDesktopProperty("win.comdlg.placesBarPlace" + i++);
343
try {
344
if (value instanceof Integer) {
345
// A CSIDL
346
folders.add(new Win32ShellFolder2((Integer)value));
347
} else if (value instanceof String) {
348
// A path
349
folders.add(createShellFolder(new File((String)value)));
350
}
351
} catch (IOException e) {
352
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
353
log.warning("Cannot read value = " + value, e);
354
}
355
// Skip this value
356
} catch (InterruptedException e) {
357
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
358
log.warning("Cannot read value = " + value, e);
359
}
360
// Return empty result
361
return new File[0];
362
}
363
} while (value != null);
364
365
if (folders.size() == 0) {
366
// Use default list of places
367
for (File f : new File[] {
368
getRecent(), getDesktop(), getPersonal(), getDrives(), getNetwork()
369
}) {
370
if (f != null) {
371
folders.add(f);
372
}
373
}
374
}
375
return checkFiles(folders);
376
} else if (key.startsWith("fileChooserIcon ")) {
377
String name = key.substring(key.indexOf(" ") + 1);
378
379
int iconIndex;
380
381
if (name.equals("ListView") || name.equals("ViewMenu")) {
382
iconIndex = VIEW_LIST;
383
} else if (name.equals("DetailsView")) {
384
iconIndex = VIEW_DETAILS;
385
} else if (name.equals("UpFolder")) {
386
iconIndex = VIEW_PARENTFOLDER;
387
} else if (name.equals("NewFolder")) {
388
iconIndex = VIEW_NEWFOLDER;
389
} else {
390
return null;
391
}
392
393
return getStandardViewButton(iconIndex);
394
} else if (key.startsWith("optionPaneIcon ")) {
395
Win32ShellFolder2.SystemIcon iconType;
396
if (key == "optionPaneIcon Error") {
397
iconType = Win32ShellFolder2.SystemIcon.IDI_ERROR;
398
} else if (key == "optionPaneIcon Information") {
399
iconType = Win32ShellFolder2.SystemIcon.IDI_INFORMATION;
400
} else if (key == "optionPaneIcon Question") {
401
iconType = Win32ShellFolder2.SystemIcon.IDI_QUESTION;
402
} else if (key == "optionPaneIcon Warning") {
403
iconType = Win32ShellFolder2.SystemIcon.IDI_EXCLAMATION;
404
} else {
405
return null;
406
}
407
return Win32ShellFolder2.getSystemIcon(iconType);
408
} else if (key.startsWith("shell32Icon ") || key.startsWith("shell32LargeIcon ")) {
409
String name = key.substring(key.indexOf(" ") + 1);
410
try {
411
int i = Integer.parseInt(name);
412
if (i >= 0) {
413
return Win32ShellFolder2.getShell32Icon(i,
414
key.startsWith("shell32LargeIcon ") ? LARGE_ICON_SIZE : SMALL_ICON_SIZE);
415
}
416
} catch (NumberFormatException ex) {
417
}
418
}
419
return null;
420
}
421
422
private static File checkFile(File file) {
423
@SuppressWarnings("removal")
424
SecurityManager sm = System.getSecurityManager();
425
return (sm == null || file == null) ? file : checkFile(file, sm);
426
}
427
428
private static File checkFile(File file, @SuppressWarnings("removal") SecurityManager sm) {
429
try {
430
sm.checkRead(file.getPath());
431
432
if (file instanceof Win32ShellFolder2) {
433
Win32ShellFolder2 f = (Win32ShellFolder2)file;
434
if (f.isLink()) {
435
Win32ShellFolder2 link = (Win32ShellFolder2)f.getLinkLocation();
436
if (link != null)
437
sm.checkRead(link.getPath());
438
}
439
}
440
return file;
441
} catch (SecurityException se) {
442
return null;
443
}
444
}
445
446
static File[] checkFiles(File[] files) {
447
@SuppressWarnings("removal")
448
SecurityManager sm = System.getSecurityManager();
449
if (sm == null || files == null || files.length == 0) {
450
return files;
451
}
452
return checkFiles(Arrays.stream(files), sm);
453
}
454
455
private static File[] checkFiles(List<File> files) {
456
@SuppressWarnings("removal")
457
SecurityManager sm = System.getSecurityManager();
458
if (sm == null || files.isEmpty()) {
459
return files.toArray(new File[files.size()]);
460
}
461
return checkFiles(files.stream(), sm);
462
}
463
464
private static File[] checkFiles(Stream<File> filesStream, @SuppressWarnings("removal") SecurityManager sm) {
465
return filesStream.filter((file) -> checkFile(file, sm) != null)
466
.toArray(File[]::new);
467
}
468
469
/**
470
* Does {@code dir} represent a "computer" such as a node on the network, or
471
* "My Computer" on the desktop.
472
*/
473
public boolean isComputerNode(final File dir) {
474
if (dir != null && dir == getDrives()) {
475
return true;
476
} else {
477
@SuppressWarnings("removal")
478
String path = AccessController.doPrivileged(new PrivilegedAction<String>() {
479
public String run() {
480
return dir.getAbsolutePath();
481
}
482
});
483
484
return (path.startsWith("\\\\") && path.indexOf("\\", 2) < 0); //Network path
485
}
486
}
487
488
public boolean isFileSystemRoot(File dir) {
489
//Note: Removable drives don't "exist" but are listed in "My Computer"
490
if (dir != null) {
491
492
if (dir instanceof Win32ShellFolder2) {
493
Win32ShellFolder2 sf = (Win32ShellFolder2)dir;
494
495
//This includes all the drives under "My PC" or "My Computer.
496
// On windows 10, "External Drives" are listed under "Desktop"
497
// also
498
return (sf.isFileSystem() && sf.parent != null &&
499
(sf.parent.equals (getDrives()) ||
500
(sf.parent.equals (getDesktop()) && isDrive(dir))));
501
}
502
return isDrive(dir);
503
}
504
return false;
505
}
506
507
private boolean isDrive(File dir) {
508
String path = dir.getPath();
509
if (path.length() != 3 || path.charAt(1) != ':') {
510
return false;
511
}
512
File[] roots = Win32ShellFolder2.listRoots();
513
return roots != null && Arrays.asList(roots).contains(dir);
514
}
515
516
private static List<Win32ShellFolder2> topFolderList = null;
517
static int compareShellFolders(Win32ShellFolder2 sf1, Win32ShellFolder2 sf2) {
518
boolean special1 = sf1.isSpecial();
519
boolean special2 = sf2.isSpecial();
520
521
if (special1 || special2) {
522
if (topFolderList == null) {
523
ArrayList<Win32ShellFolder2> tmpTopFolderList = new ArrayList<>();
524
tmpTopFolderList.add(Win32ShellFolderManager2.getPersonal());
525
tmpTopFolderList.add(Win32ShellFolderManager2.getDesktop());
526
tmpTopFolderList.add(Win32ShellFolderManager2.getDrives());
527
tmpTopFolderList.add(Win32ShellFolderManager2.getNetwork());
528
topFolderList = tmpTopFolderList;
529
}
530
int i1 = topFolderList.indexOf(sf1);
531
int i2 = topFolderList.indexOf(sf2);
532
if (i1 >= 0 && i2 >= 0) {
533
return (i1 - i2);
534
} else if (i1 >= 0) {
535
return -1;
536
} else if (i2 >= 0) {
537
return 1;
538
}
539
}
540
541
// Non-file shellfolders sort before files
542
if (special1 && !special2) {
543
return -1;
544
} else if (special2 && !special1) {
545
return 1;
546
}
547
548
return compareNames(sf1.getAbsolutePath(), sf2.getAbsolutePath());
549
}
550
551
static int compareNames(String name1, String name2) {
552
// First ignore case when comparing
553
int diff = name1.compareToIgnoreCase(name2);
554
if (diff != 0) {
555
return diff;
556
} else {
557
// May differ in case (e.g. "mail" vs. "Mail")
558
// We need this test for consistent sorting
559
return name1.compareTo(name2);
560
}
561
}
562
563
@Override
564
protected Invoker createInvoker() {
565
return new ComInvoker();
566
}
567
568
private static class ComInvoker extends ThreadPoolExecutor implements ThreadFactory, ShellFolder.Invoker {
569
private static Thread comThread;
570
571
@SuppressWarnings("removal")
572
private ComInvoker() {
573
super(1, 1, 0, TimeUnit.DAYS, new LinkedBlockingQueue<>());
574
allowCoreThreadTimeOut(false);
575
setThreadFactory(this);
576
final Runnable shutdownHook = () -> AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
577
shutdownNow();
578
return null;
579
});
580
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
581
Thread t = new Thread(
582
ThreadGroupUtils.getRootThreadGroup(), shutdownHook,
583
"ShellFolder", 0, false);
584
Runtime.getRuntime().addShutdownHook(t);
585
return null;
586
});
587
}
588
589
@SuppressWarnings("removal")
590
public synchronized Thread newThread(final Runnable task) {
591
final Runnable comRun = new Runnable() {
592
public void run() {
593
try {
594
initializeCom();
595
task.run();
596
} finally {
597
uninitializeCom();
598
}
599
}
600
};
601
comThread = AccessController.doPrivileged((PrivilegedAction<Thread>) () -> {
602
String name = "Swing-Shell";
603
/* The thread must be a member of a thread group
604
* which will not get GCed before VM exit.
605
* Make its parent the top-level thread group.
606
*/
607
Thread thread = new Thread(
608
ThreadGroupUtils.getRootThreadGroup(), comRun, name,
609
0, false);
610
thread.setDaemon(true);
611
/* This is important, since this thread running at lower priority
612
leads to memory consumption when listDrives() function is called
613
repeatedly.
614
*/
615
thread.setPriority(Thread.MAX_PRIORITY);
616
return thread;
617
});
618
return comThread;
619
}
620
621
@SuppressWarnings("removal")
622
public <T> T invoke(Callable<T> task) throws Exception {
623
if (Thread.currentThread() == comThread) {
624
// if it's already called from the COM
625
// thread, we don't need to delegate the task
626
return task.call();
627
} else {
628
final Future<T> future;
629
630
try {
631
future = submit(task);
632
} catch (RejectedExecutionException e) {
633
throw new InterruptedException(e.getMessage());
634
}
635
636
try {
637
return future.get();
638
} catch (InterruptedException e) {
639
AccessController.doPrivileged(new PrivilegedAction<Void>() {
640
public Void run() {
641
future.cancel(true);
642
643
return null;
644
}
645
});
646
647
throw e;
648
} catch (ExecutionException e) {
649
Throwable cause = e.getCause();
650
651
if (cause instanceof Exception) {
652
throw (Exception) cause;
653
}
654
655
if (cause instanceof Error) {
656
throw (Error) cause;
657
}
658
659
throw new RuntimeException("Unexpected error", cause);
660
}
661
}
662
}
663
}
664
665
static native void initializeCom();
666
667
static native void uninitializeCom();
668
}
669
670