2
* Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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.
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
27
Hierarchical storage layout:
33
<string>/foo's value</string>
37
<string>/foo/bar's value</string>
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
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.
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.
61
#include <CoreFoundation/CoreFoundation.h>
66
#include "java_util_prefs_MacOSXPreferencesFile.h"
69
* Declare library specific JNI_Onload entry if static build
74
// Throw an OutOfMemoryError with the given message.
75
static void throwOutOfMemoryError(JNIEnv *env, const char *msg)
77
static jclass exceptionClass = NULL;
80
(*env)->ExceptionClear(env); // If an exception is pending, clear it before
81
// calling FindClass() and/or ThrowNew().
85
c = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
86
if ((*env)->ExceptionOccurred(env)) return;
87
exceptionClass = (*env)->NewGlobalRef(env, c);
90
(*env)->ThrowNew(env, c, msg);
95
// If var is NULL, throw an OutOfMemoryError and goto badvar.
96
// var must be a variable. env must be the current JNIEnv.
97
// fixme throw BackingStoreExceptions sometimes?
98
#define throwIfNull(var, msg) \
101
throwOutOfMemoryError(env, msg); \
107
// Converts CFNumber, CFBoolean, CFString to CFString
108
// returns NULL if value is of some other type
109
// throws and returns NULL on memory error
110
// result must be released (even if value was already a CFStringRef)
111
// value must not be null
112
static CFStringRef copyToCFString(JNIEnv *env, CFTypeRef value)
117
type = CFGetTypeID(value);
119
if (type == CFStringGetTypeID()) {
120
result = (CFStringRef)CFRetain(value);
122
else if (type == CFBooleanGetTypeID()) {
123
// Java Preferences API expects "true" and "false" for boolean values.
124
result = CFStringCreateCopy(NULL, (value == kCFBooleanTrue) ? CFSTR("true") : CFSTR("false"));
125
throwIfNull(result, "copyToCFString failed");
127
else if (type == CFNumberGetTypeID()) {
128
CFNumberRef number = (CFNumberRef) value;
129
if (CFNumberIsFloatType(number)) {
131
CFNumberGetValue(number, kCFNumberDoubleType, &d);
132
result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%g"), d);
133
throwIfNull(result, "copyToCFString failed");
137
CFNumberGetValue(number, kCFNumberLongType, &l);
138
result = CFStringCreateWithFormat(NULL, NULL, CFSTR("%ld"), l);
139
throwIfNull(result, "copyToCFString failed");
143
// unknown type - return NULL
152
// Create a Java string from the given CF string.
153
// returns NULL if cfString is NULL
154
// throws and returns NULL on memory error
155
static jstring toJavaString(JNIEnv *env, CFStringRef cfString)
157
if (cfString == NULL) {
160
jstring javaString = NULL;
162
CFIndex length = CFStringGetLength(cfString);
163
const UniChar *constchars = CFStringGetCharactersPtr(cfString);
165
javaString = (*env)->NewString(env, constchars, length);
167
UniChar *chars = malloc(length * sizeof(UniChar));
168
throwIfNull(chars, "toJavaString failed");
169
CFStringGetCharacters(cfString, CFRangeMake(0, length), chars);
170
javaString = (*env)->NewString(env, chars, length);
180
// Create a CF string from the given Java string.
181
// returns NULL if javaString is NULL
182
// throws and returns NULL on memory error
183
static CFStringRef toCF(JNIEnv *env, jstring javaString)
185
if (javaString == NULL) {
188
CFStringRef result = NULL;
189
jsize length = (*env)->GetStringLength(env, javaString);
190
const jchar *chars = (*env)->GetStringChars(env, javaString, NULL);
191
throwIfNull(chars, "toCF failed");
193
CFStringCreateWithCharacters(NULL, (const UniChar *)chars, length);
194
(*env)->ReleaseStringChars(env, javaString, chars);
195
throwIfNull(result, "toCF failed");
203
// Create an empty Java string array of the given size.
204
// Throws and returns NULL on error.
205
static jarray createJavaStringArray(JNIEnv *env, CFIndex count)
207
static jclass stringClass = NULL;
213
c = (*env)->FindClass(env, "java/lang/String");
214
if ((*env)->ExceptionOccurred(env)) return NULL;
215
stringClass = (*env)->NewGlobalRef(env, c);
218
return (*env)->NewObjectArray(env, count, c, NULL); // AWT_THREADING Safe (known object)
222
// Java accessors for CF constants.
223
JNIEXPORT jlong JNICALL
224
Java_java_util_prefs_MacOSXPreferencesFile_currentUser(JNIEnv *env,
227
return ptr_to_jlong(kCFPreferencesCurrentUser);
230
JNIEXPORT jlong JNICALL
231
Java_java_util_prefs_MacOSXPreferencesFile_anyUser(JNIEnv *env, jobject klass)
233
return ptr_to_jlong(kCFPreferencesAnyUser);
236
JNIEXPORT jlong JNICALL
237
Java_java_util_prefs_MacOSXPreferencesFile_currentHost(JNIEnv *env,
240
return ptr_to_jlong(kCFPreferencesCurrentHost);
243
JNIEXPORT jlong JNICALL
244
Java_java_util_prefs_MacOSXPreferencesFile_anyHost(JNIEnv *env, jobject klass)
246
return ptr_to_jlong(kCFPreferencesAnyHost);
250
// Create an empty node.
251
// Does not store the node in any prefs file.
252
// returns NULL on memory error
253
static CFMutableDictionaryRef createEmptyNode(void)
255
return CFDictionaryCreateMutable(NULL, 0,
256
&kCFTypeDictionaryKeyCallBacks,
257
&kCFTypeDictionaryValueCallBacks);
261
// Create a string that consists of path minus its last component.
262
// path must end with '/'
263
// The result will end in '/' (unless path itself is '/')
264
static CFStringRef copyParentOf(CFStringRef path)
271
searchRange = CFRangeMake(0, CFStringGetLength(path) - 1);
272
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange,
273
kCFCompareBackwards, &slashRange);
274
if (!found) return CFSTR("");
275
parentRange = CFRangeMake(0, slashRange.location + 1); // include '/'
276
return CFStringCreateWithSubstring(NULL, path, parentRange);
280
// Create a string that consists of path's last component.
281
// path must end with '/'
282
// The result will end in '/'.
283
// The result will not start with '/' (unless path itself is '/')
284
static CFStringRef copyChildOf(CFStringRef path)
290
CFIndex length = CFStringGetLength(path);
292
searchRange = CFRangeMake(0, length - 1);
293
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange,
294
kCFCompareBackwards, &slashRange);
295
if (!found) return CFSTR("");
296
childRange = CFRangeMake(slashRange.location + 1,
297
length - slashRange.location - 1); // skip '/'
298
return CFStringCreateWithSubstring(NULL, path, childRange);
302
// Return the first three components of path, with leading and trailing '/'.
303
// If path does not have three components, return NULL.
304
// path must begin and end in '/'
305
static CFStringRef copyFirstThreeComponentsOf(CFStringRef path)
312
CFIndex length = CFStringGetLength(path);
314
searchRange = CFRangeMake(1, length - 1); // skip leading '/'
315
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
317
if (!found) return NULL; // no second slash!
319
searchRange = CFRangeMake(slashRange.location + 1,
320
length - slashRange.location - 1);
321
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
323
if (!found) return NULL; // no third slash!
325
searchRange = CFRangeMake(slashRange.location + 1,
326
length - slashRange.location - 1);
327
found = CFStringFindWithOptions(path, CFSTR("/"), searchRange, 0,
329
if (!found) return NULL; // no fourth slash!
331
prefixRange = CFRangeMake(0, slashRange.location + 1); // keep last '/'
332
prefix = CFStringCreateWithSubstring(NULL, path, prefixRange);
338
// Copy the CFPreferences key and value at the base of path's tree.
339
// path must end in '/'
340
// topKey or topValue may be NULL
341
// Returns NULL on error or if there is no tree for path in this file.
342
static void copyTreeForPath(CFStringRef path, CFStringRef name,
343
CFStringRef user, CFStringRef host,
344
CFStringRef *topKey, CFDictionaryRef *topValue)
347
CFPropertyListRef value;
349
if (topKey) *topKey = NULL;
350
if (topValue) *topValue = NULL;
352
if (CFEqual(name, CFSTR("com.apple.java.util.prefs"))) {
353
// Top-level file. Only key "/" is an acceptable root.
354
key = (CFStringRef) CFRetain(CFSTR("/"));
356
// Second-level file. Key must be the first three components of path.
357
key = copyFirstThreeComponentsOf(path);
361
value = CFPreferencesCopyValue(key, name, user, host);
363
if (CFGetTypeID(value) == CFDictionaryGetTypeID()) {
364
// (key, value) is acceptable
365
if (topKey) *topKey = (CFStringRef)CFRetain(key);
366
if (topValue) *topValue = (CFDictionaryRef)CFRetain(value);
374
// Find the node for path in the given tree.
375
// Returns NULL on error or if path doesn't have a node in this tree.
376
// path must end in '/'
377
static CFDictionaryRef copyNodeInTree(CFStringRef path, CFStringRef topKey,
378
CFDictionaryRef topValue)
380
CFMutableStringRef p;
381
CFDictionaryRef result = NULL;
383
p = CFStringCreateMutableCopy(NULL, 0, path);
385
CFStringDelete(p, CFRangeMake(0, CFStringGetLength(topKey)));
388
while (CFStringGetLength(p) > 0) {
389
CFDictionaryRef child;
390
CFStringRef part = NULL;
391
CFRange slashRange = CFStringFind(p, CFSTR("/"), 0);
392
// guaranteed to succeed because path must end in '/'
393
CFRange partRange = CFRangeMake(0, slashRange.location + 1);
394
part = CFStringCreateWithSubstring(NULL, p, partRange);
395
if (!part) { result = NULL; break; }
396
CFStringDelete(p, partRange);
398
child = CFDictionaryGetValue(result, part);
400
if (child && CFGetTypeID(child) == CFDictionaryGetTypeID()) {
404
// didn't find target node
411
if (result) return (CFDictionaryRef)CFRetain(result);
416
// Return a retained copy of the node at path from the given file.
417
// path must end in '/'
418
// returns NULL if node doesn't exist.
419
// returns NULL if the value for key "path" isn't a valid node.
420
static CFDictionaryRef copyNodeIfPresent(CFStringRef path, CFStringRef name,
421
CFStringRef user, CFStringRef host)
424
CFDictionaryRef topValue;
425
CFDictionaryRef result;
427
copyTreeForPath(path, name, user, host, &topKey, &topValue);
428
if (!topKey) return NULL;
430
result = copyNodeInTree(path, topKey, topValue);
433
if (topValue) CFRelease(topValue);
438
// Create a new tree that would store path in the given file.
439
// Only the root of the tree is created, not all of the links leading to path.
440
// returns NULL on error
441
static void createTreeForPath(CFStringRef path, CFStringRef name,
442
CFStringRef user, CFStringRef host,
443
CFStringRef *outTopKey,
444
CFMutableDictionaryRef *outTopValue)
449
// if name is "com.apple.java.util.prefs" then create tree "/"
450
// else create tree "/foo/bar/baz/"
451
// "com.apple.java.util.prefs.plist" is also in MacOSXPreferences.java
452
if (CFEqual(name, CFSTR("com.apple.java.util.prefs"))) {
453
*outTopKey = CFSTR("/");
454
*outTopValue = createEmptyNode();
456
CFStringRef prefix = copyFirstThreeComponentsOf(path);
459
*outTopValue = createEmptyNode();
465
// Return a mutable copy of the tree containing path and the dict for
466
// path itself. *outTopKey and *outTopValue can be used to write the
467
// modified tree back to the prefs file.
468
// *outTopKey and *outTopValue must be released iff the actual return
470
static CFMutableDictionaryRef
471
copyMutableNode(CFStringRef path, CFStringRef name,
472
CFStringRef user, CFStringRef host,
473
CFStringRef *outTopKey,
474
CFMutableDictionaryRef *outTopValue)
476
CFStringRef topKey = NULL;
477
CFDictionaryRef oldTopValue = NULL;
478
CFMutableDictionaryRef topValue;
479
CFMutableDictionaryRef result = NULL;
480
CFMutableStringRef p;
482
if (outTopKey) *outTopKey = NULL;
483
if (outTopValue) *outTopValue = NULL;
485
copyTreeForPath(path, name, user, host, &topKey, &oldTopValue);
487
createTreeForPath(path, name, user, host, &topKey, &topValue);
489
topValue = (CFMutableDictionaryRef)
490
CFPropertyListCreateDeepCopy(NULL, (CFPropertyListRef)oldTopValue,
491
kCFPropertyListMutableContainers);
493
if (!topValue) goto badtopValue;
495
p = CFStringCreateMutableCopy(NULL, 0, path);
497
CFStringDelete(p, CFRangeMake(0, CFStringGetLength(topKey)));
500
while (CFStringGetLength(p) > 0) {
501
CFMutableDictionaryRef child;
502
CFStringRef part = NULL;
503
CFRange slashRange = CFStringFind(p, CFSTR("/"), 0);
504
// guaranteed to succeed because path must end in '/'
505
CFRange partRange = CFRangeMake(0, slashRange.location + 1);
506
part = CFStringCreateWithSubstring(NULL, p, partRange);
507
if (!part) { result = NULL; break; }
508
CFStringDelete(p, partRange);
510
child = (CFMutableDictionaryRef)CFDictionaryGetValue(result, part);
511
if (child && CFGetTypeID(child) == CFDictionaryGetTypeID()) {
515
// didn't find target node - add it and continue
516
child = createEmptyNode();
517
if (!child) { CFRelease(part); result = NULL; break; }
518
CFDictionaryAddValue(result, part, child);
525
*outTopKey = (CFStringRef)CFRetain(topKey);
526
*outTopValue = (CFMutableDictionaryRef)CFRetain(topValue);
534
if (topKey) CFRelease(topKey);
535
if (oldTopValue) CFRelease(oldTopValue);
540
JNIEXPORT jboolean JNICALL
541
Java_java_util_prefs_MacOSXPreferencesFile_addNode
542
(JNIEnv *env, jobject klass, jobject jpath,
543
jobject jname, jlong juser, jlong jhost)
545
CFStringRef path = NULL;
546
CFStringRef name = NULL;
548
path = toCF(env, jpath);
550
name = toCF(env, jname);
552
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
553
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
554
CFDictionaryRef node = NULL;
555
jboolean neededNewNode = false;
557
if (!path || !name) goto badparams;
559
node = copyNodeIfPresent(path, name, user, host);
562
neededNewNode = false;
565
CFStringRef topKey = NULL;
566
CFMutableDictionaryRef topValue = NULL;
568
neededNewNode = true;
570
// copyMutableNode creates the node if necessary
571
node = copyMutableNode(path, name, user, host, &topKey, &topValue);
572
throwIfNull(node, "copyMutableNode failed");
574
CFPreferencesSetValue(topKey, topValue, name, user, host);
577
if (topKey) CFRelease(topKey);
578
if (topValue) CFRelease(topValue);
583
if (path) CFRelease(path);
584
if (name) CFRelease(name);
586
return neededNewNode;
590
JNIEXPORT void JNICALL
591
Java_java_util_prefs_MacOSXPreferencesFile_removeNode
592
(JNIEnv *env, jobject klass, jobject jpath,
593
jobject jname, jlong juser, jlong jhost)
595
CFStringRef path = NULL;
596
CFStringRef name = NULL;
598
path = toCF(env, jpath);
600
name = toCF(env, jname);
602
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
603
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
604
CFStringRef parentName;
605
CFStringRef childName;
606
CFDictionaryRef constParent;
608
if (!path || !name) goto badparams;
610
parentName = copyParentOf(path);
611
throwIfNull(parentName, "copyParentOf failed");
612
childName = copyChildOf(path);
613
throwIfNull(childName, "copyChildOf failed");
615
// root node is not allowed to be removed, so parentName is never empty
617
constParent = copyNodeIfPresent(parentName, name, user, host);
618
if (constParent && CFDictionaryContainsKey(constParent, childName)) {
620
CFMutableDictionaryRef topValue;
621
CFMutableDictionaryRef parent;
623
parent = copyMutableNode(parentName, name, user, host,
625
throwIfNull(parent, "copyMutableNode failed");
627
CFDictionaryRemoveValue(parent, childName);
628
CFPreferencesSetValue(topKey, topValue, name, user, host);
631
if (topKey) CFRelease(topKey);
632
if (topValue) CFRelease(topValue);
634
// might be trying to remove the root itself in a non-root file
636
CFDictionaryRef topValue;
637
copyTreeForPath(path, name, user, host, &topKey, &topValue);
639
if (CFEqual(topKey, path)) {
640
CFPreferencesSetValue(topKey, NULL, name, user, host);
643
if (topKey) CFRelease(topKey);
644
if (topValue) CFRelease(topValue);
650
if (constParent) CFRelease(constParent);
651
CFRelease(childName);
653
CFRelease(parentName);
656
if (path) CFRelease(path);
657
if (name) CFRelease(name);
661
// child must end with '/'
662
JNIEXPORT jboolean JNICALL
663
Java_java_util_prefs_MacOSXPreferencesFile_addChildToNode
664
(JNIEnv *env, jobject klass, jobject jpath, jobject jchild,
665
jobject jname, jlong juser, jlong jhost)
667
// like addNode, but can put a three-level-deep dict into the root file
668
CFStringRef path = NULL;
669
CFStringRef child = NULL;
670
CFStringRef name = NULL;
672
path = toCF(env, jpath);
674
child = toCF(env, jchild);
677
name = toCF(env, jname);
679
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
680
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
681
CFMutableDictionaryRef parent;
682
CFDictionaryRef node;
684
CFMutableDictionaryRef topValue;
685
jboolean beforeAdd = JNI_FALSE;
687
if (!path || !child || !name) goto badparams;
689
node = createEmptyNode();
690
throwIfNull(node, "createEmptyNode failed");
692
// copyMutableNode creates the node if necessary
693
parent = copyMutableNode(path, name, user, host, &topKey, &topValue);
694
throwIfNull(parent, "copyMutableNode failed");
695
beforeAdd = CFDictionaryContainsKey(parent, child) ? JNI_TRUE : JNI_FALSE;
696
CFDictionaryAddValue(parent, child, node);
698
beforeAdd = CFDictionaryContainsKey(parent, child) ? JNI_TRUE : JNI_FALSE;
700
beforeAdd = JNI_FALSE;
701
CFPreferencesSetValue(topKey, topValue, name, user, host);
704
if (topKey) CFRelease(topKey);
705
if (topValue) CFRelease(topValue);
710
if (path) CFRelease(path);
711
if (child) CFRelease(child);
712
if (name) CFRelease(name);
717
JNIEXPORT void JNICALL
718
Java_java_util_prefs_MacOSXPreferencesFile_removeChildFromNode
719
(JNIEnv *env, jobject klass, jobject jpath, jobject jchild,
720
jobject jname, jlong juser, jlong jhost)
722
CFStringRef path = NULL;
723
CFStringRef child = NULL;
724
CFStringRef name = NULL;
726
path = toCF(env, jpath);
728
child = toCF(env, jchild);
731
name = toCF(env, jname);
733
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
734
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
735
CFDictionaryRef constParent;
737
if (!path || !child || !name) goto badparams;
739
constParent = copyNodeIfPresent(path, name, user, host);
740
if (constParent && CFDictionaryContainsKey(constParent, child)) {
742
CFMutableDictionaryRef topValue;
743
CFMutableDictionaryRef parent;
745
parent = copyMutableNode(path, name, user, host, &topKey, &topValue);
746
throwIfNull(parent, "copyMutableNode failed");
748
CFDictionaryRemoveValue(parent, child);
749
CFPreferencesSetValue(topKey, topValue, name, user, host);
752
if (topKey) CFRelease(topKey);
753
if (topValue) CFRelease(topValue);
757
if (constParent) CFRelease(constParent);
759
if (path) CFRelease(path);
760
if (child) CFRelease(child);
761
if (name) CFRelease(name);
766
JNIEXPORT void JNICALL
767
Java_java_util_prefs_MacOSXPreferencesFile_addKeyToNode
768
(JNIEnv *env, jobject klass, jobject jpath, jobject jkey, jobject jvalue,
769
jobject jname, jlong juser, jlong jhost)
771
CFStringRef path = NULL;
772
CFStringRef key = NULL;
773
CFStringRef value = NULL;
774
CFStringRef name = NULL;
776
path = toCF(env, jpath);
778
key = toCF(env, jkey);
781
value = toCF(env, jvalue);
784
name = toCF(env, jname);
786
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
787
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
788
CFMutableDictionaryRef node = NULL;
790
CFMutableDictionaryRef topValue;
792
if (!path || !key || !value || !name) goto badparams;
794
// fixme optimization: check whether old value and new value are identical
795
node = copyMutableNode(path, name, user, host, &topKey, &topValue);
796
throwIfNull(node, "copyMutableNode failed");
798
CFDictionarySetValue(node, key, value);
799
CFPreferencesSetValue(topKey, topValue, name, user, host);
802
if (topKey) CFRelease(topKey);
803
if (topValue) CFRelease(topValue);
807
if (path) CFRelease(path);
808
if (key) CFRelease(key);
809
if (value) CFRelease(value);
810
if (name) CFRelease(name);
814
JNIEXPORT void JNICALL
815
Java_java_util_prefs_MacOSXPreferencesFile_removeKeyFromNode
816
(JNIEnv *env, jobject klass, jobject jpath, jobject jkey,
817
jobject jname, jlong juser, jlong jhost)
819
CFStringRef path = NULL;
820
CFStringRef key = NULL;
821
CFStringRef name = NULL;
823
path = toCF(env, jpath);
825
key = toCF(env, jkey);
828
name = toCF(env, jname);
830
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
831
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
832
CFDictionaryRef constNode;
834
if (!path || !key || !name) goto badparams;
836
constNode = copyNodeIfPresent(path, name, user, host);
837
if (constNode && CFDictionaryContainsKey(constNode, key)) {
839
CFMutableDictionaryRef topValue;
840
CFMutableDictionaryRef node;
842
node = copyMutableNode(path, name, user, host, &topKey, &topValue);
843
throwIfNull(node, "copyMutableNode failed");
845
CFDictionaryRemoveValue(node, key);
846
CFPreferencesSetValue(topKey, topValue, name, user, host);
849
if (topKey) CFRelease(topKey);
850
if (topValue) CFRelease(topValue);
854
if (constNode) CFRelease(constNode);
856
if (path) CFRelease(path);
857
if (key) CFRelease(key);
858
if (name) CFRelease(name);
862
// path must end in '/'
863
JNIEXPORT jstring JNICALL
864
Java_java_util_prefs_MacOSXPreferencesFile_getKeyFromNode
865
(JNIEnv *env, jobject klass, jobject jpath, jobject jkey,
866
jobject jname, jlong juser, jlong jhost)
868
CFStringRef path = NULL;
869
CFStringRef key = NULL;
870
CFStringRef name = NULL;
872
path = toCF(env, jpath);
874
key = toCF(env, jkey);
877
name = toCF(env, jname);
879
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
880
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
881
CFPropertyListRef value;
882
CFDictionaryRef node;
883
jstring result = NULL;
885
if (!path || !key || !name) goto badparams;
887
node = copyNodeIfPresent(path, name, user, host);
889
value = (CFPropertyListRef)CFDictionaryGetValue(node, key);
891
// key doesn't exist, or other error - no Java errors available
894
CFStringRef cfString = copyToCFString(env, value);
895
if ((*env)->ExceptionOccurred(env)) {
896
// memory error in copyToCFString
898
} else if (cfString == NULL) {
899
// bogus value type in prefs file - no Java errors available
903
result = toJavaString(env, cfString);
911
if (path) CFRelease(path);
912
if (key) CFRelease(key);
913
if (name) CFRelease(name);
926
// CFDictionary applier function that builds an array of Java strings
927
// from a CFDictionary of CFPropertyListRefs.
928
// If args->allowSlash, only strings that end in '/' are added to the array,
929
// with the slash removed. Otherwise, only strings that do not end in '/'
931
// args->result must already exist and be large enough to hold all
932
// strings from the dictionary.
933
// After complete application, args->result may not be full because
934
// some of the dictionary values weren't convertible to string. In
935
// this case, args->used will be the count of used elements.
936
static void BuildJavaArrayFn(const void *key, const void *value, void *context)
938
BuildJavaArrayArgs *args = (BuildJavaArrayArgs *)context;
939
CFPropertyListRef propkey = (CFPropertyListRef)key;
940
CFStringRef cfString = NULL;
941
JNIEnv *env = args->env;
943
if ((*env)->ExceptionOccurred(env)) return; // already failed
945
cfString = copyToCFString(env, propkey);
946
if ((*env)->ExceptionOccurred(env)) {
947
// memory error in copyToCFString
948
} else if (!cfString) {
949
// bogus value type in prefs file - no Java errors available
950
} else if (args->allowSlash != CFStringHasSuffix(cfString, CFSTR("/"))) {
951
// wrong suffix - ignore
955
if (args->allowSlash) {
956
CFRange range = CFRangeMake(0, CFStringGetLength(cfString) - 1);
957
CFStringRef s = CFStringCreateWithSubstring(NULL, cfString, range);
961
if (CFStringGetLength(cfString) <= 0) goto bad; // ignore empty
962
javaString = toJavaString(env, cfString);
963
if ((*env)->ExceptionOccurred(env)) goto bad;
964
(*env)->SetObjectArrayElement(env, args->result,args->used,javaString);
965
if ((*env)->ExceptionOccurred(env)) goto bad;
970
if (cfString) CFRelease(cfString);
974
static jarray getStringsForNode(JNIEnv *env, jobject klass, jobject jpath,
975
jobject jname, jlong juser, jlong jhost,
978
CFStringRef path = NULL;
979
CFStringRef name = NULL;
981
path = toCF(env, jpath);
983
name = toCF(env, jname);
985
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
986
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
987
CFDictionaryRef node;
988
jarray result = NULL;
991
if (!path || !name) goto badparams;
993
node = copyNodeIfPresent(path, name, user, host);
995
result = createJavaStringArray(env, 0);
997
count = CFDictionaryGetCount(node);
998
result = createJavaStringArray(env, count);
1000
BuildJavaArrayArgs args;
1001
args.result = result;
1004
args.allowSlash = allowSlash;
1005
CFDictionaryApplyFunction(node, BuildJavaArrayFn, &args);
1006
if (!(*env)->ExceptionOccurred(env)) {
1007
// array construction succeeded
1008
if (args.used < count) {
1009
// finished array is smaller than expected.
1010
// Make a new array of precisely the right size.
1011
jarray newresult = createJavaStringArray(env, args.used);
1013
JVM_ArrayCopy(env,0, result,0, newresult,0, args.used);
1024
if (path) CFRelease(path);
1025
if (name) CFRelease(name);
1031
JNIEXPORT jarray JNICALL
1032
Java_java_util_prefs_MacOSXPreferencesFile_getKeysForNode
1033
(JNIEnv *env, jobject klass, jobject jpath,
1034
jobject jname, jlong juser, jlong jhost)
1036
return getStringsForNode(env, klass, jpath, jname, juser, jhost, false);
1039
JNIEXPORT jarray JNICALL
1040
Java_java_util_prefs_MacOSXPreferencesFile_getChildrenForNode
1041
(JNIEnv *env, jobject klass, jobject jpath,
1042
jobject jname, jlong juser, jlong jhost)
1044
return getStringsForNode(env, klass, jpath, jname, juser, jhost, true);
1048
// Returns false on error instead of throwing.
1049
JNIEXPORT jboolean JNICALL
1050
Java_java_util_prefs_MacOSXPreferencesFile_synchronize
1051
(JNIEnv *env, jobject klass,
1052
jstring jname, jlong juser, jlong jhost)
1054
CFStringRef name = toCF(env, jname);
1055
CFStringRef user = (CFStringRef)jlong_to_ptr(juser);
1056
CFStringRef host = (CFStringRef)jlong_to_ptr(jhost);
1057
jboolean result = 0;
1060
result = CFPreferencesSynchronize(name, user, host);