Skip to content

Commit

Permalink
Add CFEqual, CFDictionaryRef.ByReference, CFStringRef.ByReference to …
Browse files Browse the repository at this point in the history
…CoreFoundation (#1433)

* Add CFEqual, CFDictionaryRef.ByReference, CFStringRef.ByReference to CoreFoundation

* fixes after review #1433
  • Loading branch information
shalupov committed May 7, 2022
1 parent ca465ee commit 19c66f3
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Expand Up @@ -7,6 +7,7 @@ Next Release (5.12.0)

Features
--------
* [#1433](https://github.com/java-native-access/jna/pull/1433): Add `CFEqual`, `CFDictionaryRef.ByReference`, `CFStringRef.ByReference` to `c.s.j.p.mac.CoreFoundation` - [@shalupov](https://github.com/shalupov)

Bug Fixes
---------
Expand Down
89 changes: 89 additions & 0 deletions contrib/platform/src/com/sun/jna/platform/mac/CoreFoundation.java
Expand Up @@ -377,6 +377,41 @@ public Pointer getBytePtr() {
* A reference to an immutable {@code CFDictionary} object.
*/
class CFDictionaryRef extends CFTypeRef {

/**
* Placeholder for a reference to a {@code CFDictionary} object.
*/
public static class ByReference extends PointerByReference {
public ByReference() {
this(null);
}

public ByReference(CoreFoundation.CFDictionaryRef value) {
super(value != null ? value.getPointer() : null);
}

@Override
public void setValue(Pointer value) {
if (value != null) {
CFTypeID typeId = INSTANCE.CFGetTypeID(value);
if (!DICTIONARY_TYPE_ID.equals(typeId)) {
throw new ClassCastException("Unable to cast to CFDictionary. Type ID: " + typeId);
}
}

super.setValue(value);
}

public CoreFoundation.CFDictionaryRef getDictionaryRefValue() {
Pointer value = super.getValue();
if (value == null) {
return null;
}

return new CoreFoundation.CFDictionaryRef(value);
}
}

public CFDictionaryRef() {
super();
}
Expand Down Expand Up @@ -460,6 +495,41 @@ public void setValue(PointerType key, PointerType value) {
* the characteristics and behavior of {@code CFString} objects.
*/
class CFStringRef extends CFTypeRef {

/**
* Placeholder for a reference to a {@code CFString} object.
*/
public static class ByReference extends PointerByReference {
public ByReference() {
this(null);
}

public ByReference(CoreFoundation.CFStringRef value) {
super(value != null ? value.getPointer() : null);
}

@Override
public void setValue(Pointer value) {
if (value != null) {
CFTypeID typeId = INSTANCE.CFGetTypeID(value);
if (!STRING_TYPE_ID.equals(typeId)) {
throw new ClassCastException("Unable to cast to CFString. Type ID: " + typeId);
}
}

super.setValue(value);
}

public CoreFoundation.CFStringRef getStringRefValue() {
Pointer value = super.getValue();
if (value == null) {
return null;
}

return new CoreFoundation.CFStringRef(value);
}
}

public CFStringRef() {
super();
}
Expand Down Expand Up @@ -974,6 +1044,14 @@ CFMutableDictionaryRef CFDictionaryCreateMutable(CFAllocatorRef alloc, CFIndex c
*/
CFIndex CFStringGetMaximumSizeForEncoding(CFIndex length, int encoding);

/**
* Determines whether two Core Foundation objects are considered equal.
* @param cf1 A CFType object to compare to cf2.
* @param cf2 A CFType object to compare to cf1.
* @return true if cf1 and cf2 are of the same type and considered equal, otherwise false.
*/
boolean CFEqual(CFTypeRef cf1, CFTypeRef cf2);

/**
* Gets the default allocator object for the current thread.
*
Expand Down Expand Up @@ -1013,6 +1091,17 @@ CFMutableDictionaryRef CFDictionaryCreateMutable(CFAllocatorRef alloc, CFIndex c
*/
CFTypeID CFGetTypeID(CFTypeRef theObject);

/**
* Returns the type of a {@code CFType} object presented as a pointer.
* Allows to inspect object type without creating a {@link CFTypeRef} wrapper.
*
* @param theObject
* The pointer to {@code CFData} object to examine.
* @return A value of type {@link CFTypeID} that identifies the opaque type of
* {@code cf}.
*/
CFTypeID CFGetTypeID(Pointer theObject);

/**
* @return The type identifier for the {@code CFArray} opaque type.
*/
Expand Down
126 changes: 126 additions & 0 deletions contrib/platform/test/com/sun/jna/platform/mac/CoreFoundationTest.java
Expand Up @@ -28,6 +28,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
Expand All @@ -38,6 +39,8 @@
import java.util.List;
import java.util.Random;

import com.sun.jna.platform.mac.CoreFoundation.CFDictionaryRef;
import org.junit.Assert;
import org.junit.Test;

import com.sun.jna.Memory;
Expand Down Expand Up @@ -248,4 +251,127 @@ public void testCFDictionary() {
cfOne.release();
dict.release();
}

@Test
public void testCFStringRefByReference() {
CFStringRef key = CFStringRef.createCFString("key");
CFStringRef value = CFStringRef.createCFString("value");

CFMutableDictionaryRef dict = CF.CFDictionaryCreateMutable(null, new CFIndex(2), null, null);
dict.setValue(key, value);

// test getStringRefValue()
CFStringRef.ByReference byRef = new CFStringRef.ByReference();
assertTrue(dict.getValueIfPresent(key, byRef));
assertTrue(CF.CFEqual(value, byRef.getStringRefValue()));

// test constructor()
assertNull(new CFStringRef.ByReference().getValue());

// test constructor(null)
assertNull(new CFStringRef.ByReference(null).getValue());

// test setValue(null)
assertNotNull(byRef.getStringRefValue());
byRef.setValue(null);
assertNull(byRef.getStringRefValue());

// test setValue(CFStringRef), getValue()
byRef.setValue(value.getPointer());
assertTrue(CF.CFEqual(value, byRef.getStringRefValue()));
assertEquals(value.getPointer(), byRef.getValue());

// test setValue(CFDictionaryRef)
try {
byRef.setValue(dict.getPointer());
Assert.fail("must fail");
} catch (ClassCastException cce) {
// as it should be
}

CF.CFRelease(key);
CF.CFRelease(value);
CF.CFRelease(dict);
}

@Test
public void testCFDictionaryRefByReference() {
CFStringRef key = CFStringRef.createCFString("key");

CFMutableDictionaryRef value = CF.CFDictionaryCreateMutable(null, new CFIndex(2), null, null);
value.setValue(key, key);

CFMutableDictionaryRef dict = CF.CFDictionaryCreateMutable(null, new CFIndex(2), null, null);
dict.setValue(key, value);

// test getDictionaryRefValue()
CFDictionaryRef.ByReference byRef = new CFDictionaryRef.ByReference();
assertTrue(dict.getValueIfPresent(key, byRef));
assertTrue(CF.CFEqual(value, byRef.getDictionaryRefValue()));

// test constructor()
assertNull(new CFDictionaryRef.ByReference().getValue());

// test constructor(null)
assertNull(new CFDictionaryRef.ByReference(null).getValue());

// test setValue(null)
assertNotNull(byRef.getDictionaryRefValue());
byRef.setValue(null);
assertNull(byRef.getDictionaryRefValue());

// test setValue(CFDictionaryRef), getValue()
byRef.setValue(value.getPointer());
assertTrue(CF.CFEqual(value, byRef.getDictionaryRefValue()));
assertEquals(value.getPointer(), byRef.getValue());

// test setValue(CFStringRef)
try {
byRef.setValue(key.getPointer());
Assert.fail("must fail");
} catch (ClassCastException cce) {
// as it should be
}

CF.CFRelease(key);
CF.CFRelease(value);
CF.CFRelease(dict);
}

@Test
public void testCFGetTypeID() {
CFStringRef s1 = CFStringRef.createCFString("s1");
assertEquals(CF.CFStringGetTypeID(), CF.CFGetTypeID(s1.getPointer()));
assertEquals(CF.CFStringGetTypeID(), CF.CFGetTypeID(s1));
s1.release();
}

@Test
public void testCFEqual() {
CFStringRef s1 = CFStringRef.createCFString("s1");
CFStringRef s1_the_same = CFStringRef.createCFString("s1");
CFStringRef s2 = CFStringRef.createCFString("s2");

assertTrue(CF.CFEqual(s1, s1));
assertTrue(CF.CFEqual(s1, s1_the_same));

assertFalse(CF.CFEqual(s1, s2));

CFMutableDictionaryRef dict1 = CF.CFDictionaryCreateMutable(null, new CFIndex(2), null, null);
dict1.setValue(s1, s1);
CFMutableDictionaryRef dict2 = CF.CFDictionaryCreateMutable(null, new CFIndex(2), null, null);
dict2.setValue(s1, s1);

assertNotEquals(dict1.getPointer(), dict2.getPointer());
assertTrue(CF.CFEqual(dict1, dict2));

dict2.setValue(s1, s2);
assertFalse(CF.CFEqual(dict1, dict2));

CF.CFRelease(dict1);
CF.CFRelease(dict2);
CF.CFRelease(s1);
CF.CFRelease(s1_the_same);
CF.CFRelease(s2);
}
}

0 comments on commit 19c66f3

Please sign in to comment.