Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CFEqual, CFDictionaryRef.ByReference, CFStringRef.ByReference to CoreFoundation #1433

Merged
merged 2 commits into from
May 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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() {
shalupov marked this conversation as resolved.
Show resolved Hide resolved
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() {
dbwiddis marked this conversation as resolved.
Show resolved Hide resolved
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
Original file line number Diff line number Diff line change
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);
}
}