Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/native_NOTIOS/java/util/MacOSXPreferencesFile.m
46972 views
1
/*
2
* Copyright (c) 2011, 2014, 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
/*
27
Hierarchical storage layout:
28
29
<dict>
30
<key>/</key>
31
<dict>
32
<key>foo</key>
33
<string>/foo's value</string>
34
<key>foo/</key>
35
<dict>
36
<key>bar</key>
37
<string>/foo/bar's value</string>
38
</dict>
39
</dict>
40
</dict>
41
42
Java pref nodes are stored in several different files. Pref nodes
43
with at least three components in the node name (e.g. /com/MyCompany/MyApp/)
44
are stored in a CF prefs file with the first three components as the name.
45
This way, all preferences for MyApp end up in com.MyCompany.MyApp.plist .
46
Pref nodes with shorter names are stored in com.apple.java.util.prefs.plist
47
48
The filesystem is assumed to be case-insensitive (like HFS+).
49
Java pref node names are case-sensitive. If two pref node names differ
50
only in case, they may end up in the same pref file. This is ok
51
because the CF keys identifying the node span the entire absolute path
52
to the node and are case-sensitive.
53
54
Java node names may contain '.' . When mapping to the CF file name,
55
these dots are left as-is, even though '/' is mapped to '.' .
56
This is ok because the CF key contains the correct node name.
57
*/
58
59
60
61
#include <CoreFoundation/CoreFoundation.h>
62
63
#include "jni_util.h"
64
#include "jlong.h"
65
#include "jvm.h"
66
67
68
// Throw an OutOfMemoryError with the given message.
69
static void throwOutOfMemoryError(JNIEnv *env, const char *msg)
70
{
71
static jclass exceptionClass = NULL;
72
jclass c;
73
74
(*env)->ExceptionClear(env); // If an exception is pending, clear it before
75
// calling FindClass() and/or ThrowNew().
76
if (exceptionClass) {
77
c = exceptionClass;
78
} else {
79
c = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
80
if ((*env)->ExceptionOccurred(env)) return;
81
exceptionClass = (*env)->NewGlobalRef(env, c);
82
}
83
84
(*env)->ThrowNew(env, c, msg);
85
}
86
87
88
// throwIfNull macro
89
// If var is NULL, throw an OutOfMemoryError and goto badvar.
90
// var must be a variable. env must be the current JNIEnv.
91
// fixme throw BackingStoreExceptions sometimes?
92
#define throwIfNull(var, msg) \
93
do { \
94
if (var == NULL) { \
95
throwOutOfMemoryError(env, msg); \
96
goto bad##var; \
97
} \
98
} while (0)
99
100
101
// Converts CFNumber, CFBoolean, CFString to CFString
102
// returns NULL if value is of some other type
103
// throws and returns NULL on memory error
104
// result must be released (even if value was already a CFStringRef)
105
// value must not be null
106
static CFStringRef copyToCFString(JNIEnv *env, CFTypeRef value)
107
{
108
CFStringRef result;
109
CFTypeID type;
110
111
type = CFGetTypeID(value);
112
113
if (type == CFStringGetTypeID()) {
114
result = (CFStringRef)CFRetain(value);
115
}
116
else if (type == CFBooleanGetTypeID()) {
117
// Java Preferences API expects "true" and "false" for boolean values.
118
result = CFStringCreateCopy(NULL, (value == kCFBooleanTrue) ? CFSTR("true") : CFSTR("false"));
119
throwIfNull(result, "copyToCFString failed");
120
}
121
else if (type == CFNumberGetTypeID()) {
122
CFNumberRef number = (CFNumberRef) value;
123
if (CFNumberIsFloatType(number)) {
124
double d;
125
CFNumberGetValue(number, kCFNumberDoubleType, &d);
126
result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%g"), d);
127
throwIfNull(result, "copyToCFString failed");
128
}
129
else {
130
long l;
131
CFNumberGetValue(number, kCFNumberLongType, &l);
132
result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%ld"), l);
133
throwIfNull(result, "copyToCFString failed");
134
}
135
}
136
else {
137
// unknown type - return NULL
138
result = NULL;
139
}
140
141
badresult:
142
return result;
143
}
144
145
146
// Create a Java string from the given CF string.
147
// returns NULL if cfString is NULL
148
// throws and returns NULL on memory error
149
static jstring toJavaString(JNIEnv *env, CFStringRef cfString)
150
{
151
if (cfString == NULL) {
152
return NULL;
153
} else {
154
jstring javaString = NULL;
155
156
CFIndex length = CFStringGetLength(cfString);
157
const UniChar *constchars = CFStringGetCharactersPtr(cfString);
158
if (constchars) {
159
javaString = (*env)->NewString(env, constchars, length);
160
} else {
161
UniChar *chars = malloc(length * sizeof(UniChar));
162
throwIfNull(chars, "toJavaString failed");
163
CFStringGetCharacters(cfString, CFRangeMake(0, length), chars);
164
javaString = (*env)->NewString(env, chars, length);
165
free(chars);
166
}
167
badchars:
168
return javaString;
169
}
170
}
171
172
173
174
// Create a CF string from the given Java string.
175
// returns NULL if javaString is NULL
176
// throws and returns NULL on memory error
177
static CFStringRef toCF(JNIEnv *env, jstring javaString)
178
{
179
if (javaString == NULL) {
180
return NULL;
181
} else {
182
CFStringRef result = NULL;
183
jsize length = (*env)->GetStringLength(env, javaString);
184
const jchar *chars = (*env)->GetStringChars(env, javaString, NULL);
185
throwIfNull(chars, "toCF failed");
186
result =
187
CFStringCreateWithCharacters(NULL, (const UniChar *)chars, length);
188
(*env)->ReleaseStringChars(env, javaString, chars);
189
throwIfNull(result, "toCF failed");
190
badchars:
191
badresult:
192
return result;
193
}
194
}
195
196
197
// Create an empty Java string array of the given size.
198
// Throws and returns NULL on error.
199
static jarray createJavaStringArray(JNIEnv *env, CFIndex count)
200
{
201
static jclass stringClass = NULL;
202
jclass c;
203
204
if (stringClass) {
205
c = stringClass;
206
} else {
207
c = (*env)->FindClass(env, "java/lang/String");
208
if ((*env)->ExceptionOccurred(env)) return NULL;
209
stringClass = (*env)->NewGlobalRef(env, c);
210
}
211
212
return (*env)->NewObjectArray(env, count, c, NULL); // AWT_THREADING Safe (known object)
213
}
214
215
216
// Java accessors for CF constants.
217
JNIEXPORT jlong JNICALL
218
Java_java_util_prefs_MacOSXPreferencesFile_currentUser(JNIEnv *env,
219
jobject klass)
220
{
221
return ptr_to_jlong(kCFPreferencesCurrentUser);
222
}
223
224
JNIEXPORT jlong JNICALL
225
Java_java_util_prefs_MacOSXPreferencesFile_anyUser(JNIEnv *env, jobject klass)
226
{
227
return ptr_to_jlong(kCFPreferencesAnyUser);
228
}
229
230
JNIEXPORT jlong JNICALL
231
Java_java_util_prefs_MacOSXPreferencesFile_currentHost(JNIEnv *env,
232
jobject klass)
233
{
234
return ptr_to_jlong(kCFPreferencesCurrentHost);
235
}
236
237
JNIEXPORT jlong JNICALL
238
Java_java_util_prefs_MacOSXPreferencesFile_anyHost(JNIEnv *env, jobject klass)
239
{
240
return ptr_to_jlong(kCFPreferencesAnyHost);
241
}
242
243
244
// Create an empty node.
245
// Does not store the node in any prefs file.
246
// returns NULL on memory error
247
static CFMutableDictionaryRef createEmptyNode(void)
248
{
249
return CFDictionaryCreateMutable(NULL, 0,
250
&kCFTypeDictionaryKeyCallBacks,
251
&kCFTypeDictionaryValueCallBacks);
252
}
253
254
255
// Create a string that consists of path minus its last component.
256
// path must end with '/'
257
// The result will end in '/' (unless path itself is '/')
258
static CFStringRef copyParentOf(CFStringRef path)
259
{
260
CFRange searchRange;
261
CFRange slashRange;
262
CFRange parentRange;
263
Boolean found;
264
265
searchRange = CFRangeMake(0, CFStringGetLength(path) - 1);
266
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange,
267
kCFCompareBackwards, &slashRange);
268
if (!found) return CFSTR("");
269
parentRange = CFRangeMake(0, slashRange.location + 1); // include '/'
270
return CFStringCreateWithSubstring(NULL, path, parentRange);
271
}
272
273
274
// Create a string that consists of path's last component.
275
// path must end with '/'
276
// The result will end in '/'.
277
// The result will not start with '/' (unless path itself is '/')
278
static CFStringRef copyChildOf(CFStringRef path)
279
{
280
CFRange searchRange;
281
CFRange slashRange;
282
CFRange childRange;
283
Boolean found;
284
CFIndex length = CFStringGetLength(path);
285
286
searchRange = CFRangeMake(0, length - 1);
287
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange,
288
kCFCompareBackwards, &slashRange);
289
if (!found) return CFSTR("");
290
childRange = CFRangeMake(slashRange.location + 1,
291
length - slashRange.location - 1); // skip '/'
292
return CFStringCreateWithSubstring(NULL, path, childRange);
293
}
294
295
296
// Return the first three components of path, with leading and trailing '/'.
297
// If path does not have three components, return NULL.
298
// path must begin and end in '/'
299
static CFStringRef copyFirstThreeComponentsOf(CFStringRef path)
300
{
301
CFRange searchRange;
302
CFRange slashRange;
303
CFRange prefixRange;
304
CFStringRef prefix;
305
Boolean found;
306
CFIndex length = CFStringGetLength(path);
307
308
searchRange = CFRangeMake(1, length - 1); // skip leading '/'
309
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
310
&slashRange);
311
if (!found) return NULL; // no second slash!
312
313
searchRange = CFRangeMake(slashRange.location + 1,
314
length - slashRange.location - 1);
315
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
316
&slashRange);
317
if (!found) return NULL; // no third slash!
318
319
searchRange = CFRangeMake(slashRange.location + 1,
320
length - slashRange.location - 1);
321
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
322
&slashRange);
323
if (!found) return NULL; // no fourth slash!
324
325
prefixRange = CFRangeMake(0, slashRange.location + 1); // keep last '/'
326
prefix = CFStringCreateWithSubstring(NULL, path, prefixRange);
327
328
return prefix;
329
}
330
331
332
// Copy the CFPreferences key and value at the base of path's tree.
333
// path must end in '/'
334
// topKey or topValue may be NULL
335
// Returns NULL on error or if there is no tree for path in this file.
336
static void copyTreeForPath(CFStringRef path, CFStringRef name,
337
CFStringRef user, CFStringRef host,
338
CFStringRef *topKey, CFDictionaryRef *topValue)
339
{
340
CFStringRef key;
341
CFPropertyListRef value;
342
343
if (topKey) *topKey = NULL;
344
if (topValue) *topValue = NULL;
345
346
if (CFEqual(name, CFSTR("com.apple.java.util.prefs"))) {
347
// Top-level file. Only key "/" is an acceptable root.
348
key = (CFStringRef) CFRetain(CFSTR("/"));
349
} else {
350
// Second-level file. Key must be the first three components of path.
351
key = copyFirstThreeComponentsOf(path);
352
if (!key) return;
353
}
354
355
value = CFPreferencesCopyValue(key, name, user, host);
356
if (value) {
357
if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
358
// (key, value) is acceptable
359
if (topKey) *topKey = (CFStringRef)CFRetain(key);
360
if (topValue) *topValue = (CFDictionaryRef)CFRetain(value);
361
}
362
CFRelease(value);
363
}
364
CFRelease(key);
365
}
366
367
368
// Find the node for path in the given tree.
369
// Returns NULL on error or if path doesn't have a node in this tree.
370
// path must end in '/'
371
static CFDictionaryRef copyNodeInTree(CFStringRef path, CFStringRef topKey,
372
CFDictionaryRef topValue)
373
{
374
CFMutableStringRef p;
375
CFDictionaryRef result = NULL;
376
377
p = CFStringCreateMutableCopy(NULL, 0, path);
378
if (!p) return NULL;
379
CFStringDelete(p, CFRangeMake(0, CFStringGetLength(topKey)));
380
result = topValue;
381
382
while (CFStringGetLength(p) > 0) {
383
CFDictionaryRef child;
384
CFStringRef part = NULL;
385
CFRange slashRange = CFStringFind(p, CFSTR("/"), 0);
386
// guaranteed to succeed because path must end in '/'
387
CFRange partRange = CFRangeMake(0, slashRange.location + 1);
388
part = CFStringCreateWithSubstring(NULL, p, partRange);
389
if (!part) { result = NULL; break; }
390
CFStringDelete(p, partRange);
391
392
child = CFDictionaryGetValue(result, part);
393
CFRelease(part);
394
if (child && CFGetTypeID(child) == CFDictionaryGetTypeID()) {
395
// continue search
396
result = child;
397
} else {
398
// didn't find target node
399
result = NULL;
400
break;
401
}
402
}
403
404
CFRelease(p);
405
if (result) return (CFDictionaryRef)CFRetain(result);
406
else return NULL;
407
}
408
409
410
// Return a retained copy of the node at path from the given file.
411
// path must end in '/'
412
// returns NULL if node doesn't exist.
413
// returns NULL if the value for key "path" isn't a valid node.
414
static CFDictionaryRef copyNodeIfPresent(CFStringRef path, CFStringRef name,
415
CFStringRef user, CFStringRef host)
416
{
417
CFStringRef topKey;
418
CFDictionaryRef topValue;
419
CFDictionaryRef result;
420
421
copyTreeForPath(path, name, user, host, &topKey, &topValue);
422
if (!topKey) return NULL;
423
424
result = copyNodeInTree(path, topKey, topValue);
425
426
CFRelease(topKey);
427
if (topValue) CFRelease(topValue);
428
return result;
429
}
430
431
432
// Create a new tree that would store path in the given file.
433
// Only the root of the tree is created, not all of the links leading to path.
434
// returns NULL on error
435
static void createTreeForPath(CFStringRef path, CFStringRef name,
436
CFStringRef user, CFStringRef host,
437
CFStringRef *outTopKey,
438
CFMutableDictionaryRef *outTopValue)
439
{
440
*outTopKey = NULL;
441
*outTopValue = NULL;
442
443
// if name is "com.apple.java.util.prefs" then create tree "/"
444
// else create tree "/foo/bar/baz/"
445
// "com.apple.java.util.prefs.plist" is also in MacOSXPreferences.java
446
if (CFEqual(name, CFSTR("com.apple.java.util.prefs"))) {
447
*outTopKey = CFSTR("/");
448
*outTopValue = createEmptyNode();
449
} else {
450
CFStringRef prefix = copyFirstThreeComponentsOf(path);
451
if (prefix) {
452
*outTopKey = prefix;
453
*outTopValue = createEmptyNode();
454
}
455
}
456
}
457
458
459
// Return a mutable copy of the tree containing path and the dict for
460
// path itself. *outTopKey and *outTopValue can be used to write the
461
// modified tree back to the prefs file.
462
// *outTopKey and *outTopValue must be released iff the actual return
463
// value is not NULL.
464
static CFMutableDictionaryRef
465
copyMutableNode(CFStringRef path, CFStringRef name,
466
CFStringRef user, CFStringRef host,
467
CFStringRef *outTopKey,
468
CFMutableDictionaryRef *outTopValue)
469
{
470
CFStringRef topKey = NULL;
471
CFDictionaryRef oldTopValue = NULL;
472
CFMutableDictionaryRef topValue;
473
CFMutableDictionaryRef result = NULL;
474
CFMutableStringRef p;
475
476
if (outTopKey) *outTopKey = NULL;
477
if (outTopValue) *outTopValue = NULL;
478
479
copyTreeForPath(path, name, user, host, &topKey, &oldTopValue);
480
if (!topKey) {
481
createTreeForPath(path, name, user, host, &topKey, &topValue);
482
} else {
483
topValue = (CFMutableDictionaryRef)
484
CFPropertyListCreateDeepCopy(NULL, (CFPropertyListRef)oldTopValue,
485
kCFPropertyListMutableContainers);
486
}
487
if (!topValue) goto badtopValue;
488
489
p = CFStringCreateMutableCopy(NULL, 0, path);
490
if (!p) goto badp;
491
CFStringDelete(p, CFRangeMake(0, CFStringGetLength(topKey)));
492
result = topValue;
493
494
while (CFStringGetLength(p) > 0) {
495
CFMutableDictionaryRef child;
496
CFStringRef part = NULL;
497
CFRange slashRange = CFStringFind(p, CFSTR("/"), 0);
498
// guaranteed to succeed because path must end in '/'
499
CFRange partRange = CFRangeMake(0, slashRange.location + 1);
500
part = CFStringCreateWithSubstring(NULL, p, partRange);
501
if (!part) { result = NULL; break; }
502
CFStringDelete(p, partRange);
503
504
child = (CFMutableDictionaryRef)CFDictionaryGetValue(result, part);
505
if (child && CFGetTypeID(child) == CFDictionaryGetTypeID()) {
506
// continue search
507
result = child;
508
} else {
509
// didn't find target node - add it and continue
510
child = createEmptyNode();
511
if (!child) { CFRelease(part); result = NULL; break; }
512
CFDictionaryAddValue(result, part, child);
513
result = child;
514
}
515
CFRelease(part);
516
}
517
518
if (result) {
519
*outTopKey = (CFStringRef)CFRetain(topKey);
520
*outTopValue = (CFMutableDictionaryRef)CFRetain(topValue);
521
CFRetain(result);
522
}
523
524
CFRelease(p);
525
badp:
526
CFRelease(topValue);
527
badtopValue:
528
if (topKey) CFRelease(topKey);
529
if (oldTopValue) CFRelease(oldTopValue);
530
return result;
531
}
532
533
534
JNIEXPORT jboolean JNICALL
535
Java_java_util_prefs_MacOSXPreferencesFile_addNode
536
(JNIEnv *env, jobject klass, jobject jpath,
537
jobject jname, jlong juser, jlong jhost)
538
{
539
CFStringRef path = NULL;
540
CFStringRef name = NULL;
541
542
path = toCF(env, jpath);
543
if (path != NULL) {
544
name = toCF(env, jname);
545
}
546
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
547
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
548
CFDictionaryRef node = NULL;
549
jboolean neededNewNode = false;
550
551
if (!path || !name) goto badparams;
552
553
node = copyNodeIfPresent(path, name, user, host);
554
555
if (node) {
556
neededNewNode = false;
557
CFRelease(node);
558
} else {
559
CFStringRef topKey = NULL;
560
CFMutableDictionaryRef topValue = NULL;
561
562
neededNewNode = true;
563
564
// copyMutableNode creates the node if necessary
565
node = copyMutableNode(path, name, user, host, &topKey, &topValue);
566
throwIfNull(node, "copyMutableNode failed");
567
568
CFPreferencesSetValue(topKey, topValue, name, user, host);
569
570
CFRelease(node);
571
if (topKey) CFRelease(topKey);
572
if (topValue) CFRelease(topValue);
573
}
574
575
badnode:
576
badparams:
577
if (path) CFRelease(path);
578
if (name) CFRelease(name);
579
580
return neededNewNode;
581
}
582
583
584
JNIEXPORT void JNICALL
585
Java_java_util_prefs_MacOSXPreferencesFile_removeNode
586
(JNIEnv *env, jobject klass, jobject jpath,
587
jobject jname, jlong juser, jlong jhost)
588
{
589
CFStringRef path = NULL;
590
CFStringRef name = NULL;
591
592
path = toCF(env, jpath);
593
if (path != NULL) {
594
name = toCF(env, jname);
595
}
596
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
597
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
598
CFStringRef parentName;
599
CFStringRef childName;
600
CFDictionaryRef constParent;
601
602
if (!path || !name) goto badparams;
603
604
parentName = copyParentOf(path);
605
throwIfNull(parentName, "copyParentOf failed");
606
childName = copyChildOf(path);
607
throwIfNull(childName, "copyChildOf failed");
608
609
// root node is not allowed to be removed, so parentName is never empty
610
611
constParent = copyNodeIfPresent(parentName, name, user, host);
612
if (constParent && CFDictionaryContainsKey(constParent, childName)) {
613
CFStringRef topKey;
614
CFMutableDictionaryRef topValue;
615
CFMutableDictionaryRef parent;
616
617
parent = copyMutableNode(parentName, name, user, host,
618
&topKey, &topValue);
619
throwIfNull(parent, "copyMutableNode failed");
620
621
CFDictionaryRemoveValue(parent, childName);
622
CFPreferencesSetValue(topKey, topValue, name, user, host);
623
624
CFRelease(parent);
625
if (topKey) CFRelease(topKey);
626
if (topValue) CFRelease(topValue);
627
} else {
628
// might be trying to remove the root itself in a non-root file
629
CFStringRef topKey;
630
CFDictionaryRef topValue;
631
copyTreeForPath(path, name, user, host, &topKey, &topValue);
632
if (topKey) {
633
if (CFEqual(topKey, path)) {
634
CFPreferencesSetValue(topKey, NULL, name, user, host);
635
}
636
637
if (topKey) CFRelease(topKey);
638
if (topValue) CFRelease(topValue);
639
}
640
}
641
642
643
badparent:
644
if (constParent) CFRelease(constParent);
645
CFRelease(childName);
646
badchildName:
647
CFRelease(parentName);
648
badparentName:
649
badparams:
650
if (path) CFRelease(path);
651
if (name) CFRelease(name);
652
}
653
654
655
// child must end with '/'
656
JNIEXPORT Boolean JNICALL
657
Java_java_util_prefs_MacOSXPreferencesFile_addChildToNode
658
(JNIEnv *env, jobject klass, jobject jpath, jobject jchild,
659
jobject jname, jlong juser, jlong jhost)
660
{
661
// like addNode, but can put a three-level-deep dict into the root file
662
CFStringRef path = NULL;
663
CFStringRef child = NULL;
664
CFStringRef name = NULL;
665
666
path = toCF(env, jpath);
667
if (path != NULL) {
668
child = toCF(env, jchild);
669
}
670
if (child != NULL) {
671
name = toCF(env, jname);
672
}
673
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
674
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
675
CFMutableDictionaryRef parent;
676
CFDictionaryRef node;
677
CFStringRef topKey;
678
CFMutableDictionaryRef topValue;
679
Boolean beforeAdd = false;
680
681
if (!path || !child || !name) goto badparams;
682
683
node = createEmptyNode();
684
throwIfNull(node, "createEmptyNode failed");
685
686
// copyMutableNode creates the node if necessary
687
parent = copyMutableNode(path, name, user, host, &topKey, &topValue);
688
throwIfNull(parent, "copyMutableNode failed");
689
beforeAdd = CFDictionaryContainsKey(parent, child);
690
CFDictionaryAddValue(parent, child, node);
691
if (!beforeAdd)
692
beforeAdd = CFDictionaryContainsKey(parent, child);
693
else
694
beforeAdd = false;
695
CFPreferencesSetValue(topKey, topValue, name, user, host);
696
697
CFRelease(parent);
698
if (topKey) CFRelease(topKey);
699
if (topValue) CFRelease(topValue);
700
badparent:
701
CFRelease(node);
702
badnode:
703
badparams:
704
if (path) CFRelease(path);
705
if (child) CFRelease(child);
706
if (name) CFRelease(name);
707
return beforeAdd;
708
}
709
710
711
JNIEXPORT void JNICALL
712
Java_java_util_prefs_MacOSXPreferencesFile_removeChildFromNode
713
(JNIEnv *env, jobject klass, jobject jpath, jobject jchild,
714
jobject jname, jlong juser, jlong jhost)
715
{
716
CFStringRef path = NULL;
717
CFStringRef child = NULL;
718
CFStringRef name = NULL;
719
720
path = toCF(env, jpath);
721
if (path != NULL) {
722
child = toCF(env, jchild);
723
}
724
if (child != NULL) {
725
name = toCF(env, jname);
726
}
727
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
728
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
729
CFDictionaryRef constParent;
730
731
if (!path || !child || !name) goto badparams;
732
733
constParent = copyNodeIfPresent(path, name, user, host);
734
if (constParent && CFDictionaryContainsKey(constParent, child)) {
735
CFStringRef topKey;
736
CFMutableDictionaryRef topValue;
737
CFMutableDictionaryRef parent;
738
739
parent = copyMutableNode(path, name, user, host, &topKey, &topValue);
740
throwIfNull(parent, "copyMutableNode failed");
741
742
CFDictionaryRemoveValue(parent, child);
743
CFPreferencesSetValue(topKey, topValue, name, user, host);
744
745
CFRelease(parent);
746
if (topKey) CFRelease(topKey);
747
if (topValue) CFRelease(topValue);
748
}
749
750
badparent:
751
if (constParent) CFRelease(constParent);
752
badparams:
753
if (path) CFRelease(path);
754
if (child) CFRelease(child);
755
if (name) CFRelease(name);
756
}
757
758
759
760
JNIEXPORT void JNICALL
761
Java_java_util_prefs_MacOSXPreferencesFile_addKeyToNode
762
(JNIEnv *env, jobject klass, jobject jpath, jobject jkey, jobject jvalue,
763
jobject jname, jlong juser, jlong jhost)
764
{
765
CFStringRef path = NULL;
766
CFStringRef key = NULL;
767
CFStringRef value = NULL;
768
CFStringRef name = NULL;
769
770
path = toCF(env, jpath);
771
if (path != NULL) {
772
key = toCF(env, jkey);
773
}
774
if (key != NULL) {
775
value = toCF(env, jvalue);
776
}
777
if (value != NULL) {
778
name = toCF(env, jname);
779
}
780
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
781
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
782
CFMutableDictionaryRef node = NULL;
783
CFStringRef topKey;
784
CFMutableDictionaryRef topValue;
785
786
if (!path || !key || !value || !name) goto badparams;
787
788
// fixme optimization: check whether old value and new value are identical
789
node = copyMutableNode(path, name, user, host, &topKey, &topValue);
790
throwIfNull(node, "copyMutableNode failed");
791
792
CFDictionarySetValue(node, key, value);
793
CFPreferencesSetValue(topKey, topValue, name, user, host);
794
795
CFRelease(node);
796
if (topKey) CFRelease(topKey);
797
if (topValue) CFRelease(topValue);
798
799
badnode:
800
badparams:
801
if (path) CFRelease(path);
802
if (key) CFRelease(key);
803
if (value) CFRelease(value);
804
if (name) CFRelease(name);
805
}
806
807
808
JNIEXPORT void JNICALL
809
Java_java_util_prefs_MacOSXPreferencesFile_removeKeyFromNode
810
(JNIEnv *env, jobject klass, jobject jpath, jobject jkey,
811
jobject jname, jlong juser, jlong jhost)
812
{
813
CFStringRef path = NULL;
814
CFStringRef key = NULL;
815
CFStringRef name = NULL;
816
817
path = toCF(env, jpath);
818
if (path != NULL) {
819
key = toCF(env, jkey);
820
}
821
if (key != NULL) {
822
name = toCF(env, jname);
823
}
824
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
825
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
826
CFDictionaryRef constNode;
827
828
if (!path || !key || !name) goto badparams;
829
830
constNode = copyNodeIfPresent(path, name, user, host);
831
if (constNode && CFDictionaryContainsKey(constNode, key)) {
832
CFStringRef topKey;
833
CFMutableDictionaryRef topValue;
834
CFMutableDictionaryRef node;
835
836
node = copyMutableNode(path, name, user, host, &topKey, &topValue);
837
throwIfNull(node, "copyMutableNode failed");
838
839
CFDictionaryRemoveValue(node, key);
840
CFPreferencesSetValue(topKey, topValue, name, user, host);
841
842
CFRelease(node);
843
if (topKey) CFRelease(topKey);
844
if (topValue) CFRelease(topValue);
845
}
846
847
badnode:
848
if (constNode) CFRelease(constNode);
849
badparams:
850
if (path) CFRelease(path);
851
if (key) CFRelease(key);
852
if (name) CFRelease(name);
853
}
854
855
856
// path must end in '/'
857
JNIEXPORT jstring JNICALL
858
Java_java_util_prefs_MacOSXPreferencesFile_getKeyFromNode
859
(JNIEnv *env, jobject klass, jobject jpath, jobject jkey,
860
jobject jname, jlong juser, jlong jhost)
861
{
862
CFStringRef path = NULL;
863
CFStringRef key = NULL;
864
CFStringRef name = NULL;
865
866
path = toCF(env, jpath);
867
if (path != NULL) {
868
key = toCF(env, jkey);
869
}
870
if (key != NULL) {
871
name = toCF(env, jname);
872
}
873
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
874
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
875
CFPropertyListRef value;
876
CFDictionaryRef node;
877
jstring result = NULL;
878
879
if (!path || !key || !name) goto badparams;
880
881
node = copyNodeIfPresent(path, name, user, host);
882
if (node) {
883
value = (CFPropertyListRef)CFDictionaryGetValue(node, key);
884
if (!value) {
885
// key doesn't exist, or other error - no Java errors available
886
result = NULL;
887
} else {
888
CFStringRef cfString = copyToCFString(env, value);
889
if ((*env)->ExceptionOccurred(env)) {
890
// memory error in copyToCFString
891
result = NULL;
892
} else if (cfString == NULL) {
893
// bogus value type in prefs file - no Java errors available
894
result = NULL;
895
} else {
896
// good cfString
897
result = toJavaString(env, cfString);
898
CFRelease(cfString);
899
}
900
}
901
CFRelease(node);
902
}
903
904
badparams:
905
if (path) CFRelease(path);
906
if (key) CFRelease(key);
907
if (name) CFRelease(name);
908
909
return result;
910
}
911
912
913
typedef struct {
914
jarray result;
915
JNIEnv *env;
916
CFIndex used;
917
Boolean allowSlash;
918
} BuildJavaArrayArgs;
919
920
// CFDictionary applier function that builds an array of Java strings
921
// from a CFDictionary of CFPropertyListRefs.
922
// If args->allowSlash, only strings that end in '/' are added to the array,
923
// with the slash removed. Otherwise, only strings that do not end in '/'
924
// are added.
925
// args->result must already exist and be large enough to hold all
926
// strings from the dictionary.
927
// After complete application, args->result may not be full because
928
// some of the dictionary values weren't convertible to string. In
929
// this case, args->used will be the count of used elements.
930
static void BuildJavaArrayFn(const void *key, const void *value, void *context)
931
{
932
BuildJavaArrayArgs *args = (BuildJavaArrayArgs *)context;
933
CFPropertyListRef propkey = (CFPropertyListRef)key;
934
CFStringRef cfString = NULL;
935
JNIEnv *env = args->env;
936
937
if ((*env)->ExceptionOccurred(env)) return; // already failed
938
939
cfString = copyToCFString(env, propkey);
940
if ((*env)->ExceptionOccurred(env)) {
941
// memory error in copyToCFString
942
} else if (!cfString) {
943
// bogus value type in prefs file - no Java errors available
944
} else if (args->allowSlash != CFStringHasSuffix(cfString, CFSTR("/"))) {
945
// wrong suffix - ignore
946
} else {
947
// good cfString
948
jstring javaString;
949
if (args->allowSlash) {
950
CFRange range = CFRangeMake(0, CFStringGetLength(cfString) - 1);
951
CFStringRef s = CFStringCreateWithSubstring(NULL, cfString, range);
952
CFRelease(cfString);
953
cfString = s;
954
}
955
if (CFStringGetLength(cfString) <= 0) goto bad; // ignore empty
956
javaString = toJavaString(env, cfString);
957
if ((*env)->ExceptionOccurred(env)) goto bad;
958
(*env)->SetObjectArrayElement(env, args->result,args->used,javaString);
959
if ((*env)->ExceptionOccurred(env)) goto bad;
960
args->used++;
961
}
962
963
bad:
964
if (cfString) CFRelease(cfString);
965
}
966
967
968
static jarray getStringsForNode(JNIEnv *env, jobject klass, jobject jpath,
969
jobject jname, jlong juser, jlong jhost,
970
Boolean allowSlash)
971
{
972
CFStringRef path = NULL;
973
CFStringRef name = NULL;
974
975
path = toCF(env, jpath);
976
if (path != NULL) {
977
name = toCF(env, jname);
978
}
979
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
980
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
981
CFDictionaryRef node;
982
jarray result = NULL;
983
CFIndex count;
984
985
if (!path || !name) goto badparams;
986
987
node = copyNodeIfPresent(path, name, user, host);
988
if (!node) {
989
result = createJavaStringArray(env, 0);
990
} else {
991
count = CFDictionaryGetCount(node);
992
result = createJavaStringArray(env, count);
993
if (result) {
994
BuildJavaArrayArgs args;
995
args.result = result;
996
args.env = env;
997
args.used = 0;
998
args.allowSlash = allowSlash;
999
CFDictionaryApplyFunction(node, BuildJavaArrayFn, &args);
1000
if (!(*env)->ExceptionOccurred(env)) {
1001
// array construction succeeded
1002
if (args.used < count) {
1003
// finished array is smaller than expected.
1004
// Make a new array of precisely the right size.
1005
jarray newresult = createJavaStringArray(env, args.used);
1006
if (newresult) {
1007
JVM_ArrayCopy(env,0, result,0, newresult,0, args.used);
1008
result = newresult;
1009
}
1010
}
1011
}
1012
}
1013
1014
CFRelease(node);
1015
}
1016
1017
badparams:
1018
if (path) CFRelease(path);
1019
if (name) CFRelease(name);
1020
1021
return result;
1022
}
1023
1024
1025
JNIEXPORT jarray JNICALL
1026
Java_java_util_prefs_MacOSXPreferencesFile_getKeysForNode
1027
(JNIEnv *env, jobject klass, jobject jpath,
1028
jobject jname, jlong juser, jlong jhost)
1029
{
1030
return getStringsForNode(env, klass, jpath, jname, juser, jhost, false);
1031
}
1032
1033
JNIEXPORT jarray JNICALL
1034
Java_java_util_prefs_MacOSXPreferencesFile_getChildrenForNode
1035
(JNIEnv *env, jobject klass, jobject jpath,
1036
jobject jname, jlong juser, jlong jhost)
1037
{
1038
return getStringsForNode(env, klass, jpath, jname, juser, jhost, true);
1039
}
1040
1041
1042
// Returns false on error instead of throwing.
1043
JNIEXPORT jboolean JNICALL
1044
Java_java_util_prefs_MacOSXPreferencesFile_synchronize
1045
(JNIEnv *env, jobject klass,
1046
jstring jname, jlong juser, jlong jhost)
1047
{
1048
CFStringRef name = toCF(env, jname);
1049
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
1050
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
1051
jboolean result = 0;
1052
1053
if (name) {
1054
result = CFPreferencesSynchronize(name, user, host);
1055
CFRelease(name);
1056
}
1057
1058
return result;
1059
}
1060
1061