diff --git a/src/main/java/de/jangassen/jfa/FoundationProxy.java b/src/main/java/de/jangassen/jfa/FoundationProxy.java index 014ed4a..ea3efee 100644 --- a/src/main/java/de/jangassen/jfa/FoundationProxy.java +++ b/src/main/java/de/jangassen/jfa/FoundationProxy.java @@ -10,17 +10,26 @@ import java.lang.reflect.Parameter; import java.lang.reflect.Proxy; import java.util.Arrays; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; -import java.util.stream.Stream; import static de.jangassen.jfa.foundation.Foundation.getObjcClass; public class FoundationProxy implements InvocationHandler { - private final ID id; + private static final String EQUALS = "equals"; + private static final String HASH_CODE = "hashCode"; + private static final String TO_STRING = "toString"; + private static final String DESCRIPTION = "description"; + + private final ID id; public static T alloc(Class clazz) { - ID instance = Foundation.invoke(getObjcClass(clazz.getSimpleName()), "alloc"); + return invokeStatic(clazz, "alloc"); + } + + public static T invokeStatic(Class clazz, String selector) { + ID instance = Foundation.invoke(getObjcClass(clazz.getSimpleName()), selector); return wrap(instance, clazz); } @@ -38,25 +47,37 @@ private ID getId() { @Override public Object invoke(Object proxy, Method method, Object[] args) { - if (method.getName().equals("equals")) { - return false; - } else if (method.getName().equals("hashCode")) { - return id.hashCode(); - } - if (method.getName().equals("toString")) { - return Foundation.toStringViaUTF8(Foundation.invoke(id, "description")); - } + switch (method.getName()) { + case EQUALS: + return isFoundationProxy(args[0]) && Objects.equals(getId(), getIdFromProxy(args[0])); + case HASH_CODE: + return id.hashCode(); + case TO_STRING: + return Foundation.toStringViaUTF8(Foundation.invoke(id, DESCRIPTION)); + default: + return invokeNative(method, args); + } + } + private Object invokeNative(Method method, Object[] args) { Object[] foundationArguments = getFoundationArguments(args); String selector = getSelector(method); ID result = Foundation.safeInvoke(id, selector, foundationArguments); - if (Foundation.isNil(result)) { + if (!isPrimitiveType(method.getReturnType()) && Foundation.isNil(result)) { return null; } return wrapReturnValue(method, result); } + private boolean isPrimitiveType(Class returnType) { + return boolean.class == returnType + || int.class == returnType + || long.class == returnType + || double.class == returnType + || float.class == returnType; + } + private String getSelector(Method method) { if (method.getParameterCount() == 0) { return method.getName(); @@ -88,6 +109,12 @@ private Object wrapReturnValue(Method method, ID result) { return result.longValue(); } else if (int.class == returnType || Integer.class == returnType) { return result.longValue(); + } else if (double.class == returnType || Double.class == returnType) { + return result.doubleValue(); + } else if (float.class == returnType || Float.class == returnType) { + return result.floatValue(); + } else if (boolean.class == returnType || Boolean.class == returnType) { + return result.booleanValue(); } return result; @@ -100,13 +127,21 @@ private Object[] getFoundationArguments(Object[] args) { private Object toFoundationArgument(Object arg) { if (arg == null) { return ID.NIL; - } else if (Proxy.isProxyClass(arg.getClass()) && Proxy.getInvocationHandler(arg) instanceof FoundationProxy) { - return ((FoundationProxy) Proxy.getInvocationHandler(arg)).getId(); + } else if (isFoundationProxy(arg)) { + return getIdFromProxy(arg); } else if (arg instanceof String) { return Foundation.nsString((String) arg); } return arg; } + + private ID getIdFromProxy(Object arg) { + return ((FoundationProxy) Proxy.getInvocationHandler(arg)).getId(); + } + + private boolean isFoundationProxy(Object arg) { + return Proxy.isProxyClass(arg.getClass()) && Proxy.getInvocationHandler(arg) instanceof FoundationProxy; + } } diff --git a/src/main/java/de/jangassen/jfa/appkit/NSApplication.java b/src/main/java/de/jangassen/jfa/appkit/NSApplication.java index bdd798f..92e52e2 100644 --- a/src/main/java/de/jangassen/jfa/appkit/NSApplication.java +++ b/src/main/java/de/jangassen/jfa/appkit/NSApplication.java @@ -2,6 +2,7 @@ import de.jangassen.jfa.FoundationProxy; +import de.jangassen.jfa.foundation.ID; import static de.jangassen.jfa.foundation.Foundation.getObjcClass; import static de.jangassen.jfa.foundation.Foundation.invoke; @@ -17,9 +18,7 @@ static NSApplication sharedApplication() { void setMainMenu(NSMenu mainMenu); - void hide(); + void hide(ID sender); - void hideOtherApplications(); - - void unhideAllApplications(); + void unhide(ID sender); } \ No newline at end of file diff --git a/src/main/java/de/jangassen/jfa/appkit/NSMenu.java b/src/main/java/de/jangassen/jfa/appkit/NSMenu.java index 432732d..009d7a7 100644 --- a/src/main/java/de/jangassen/jfa/appkit/NSMenu.java +++ b/src/main/java/de/jangassen/jfa/appkit/NSMenu.java @@ -14,7 +14,7 @@ static NSMenu alloc() { NSMenu init(); - NSMenu initWithTitle(@NamedArg("title") String title); + NSMenu initWithTitle(String title); void setTitle(String title); @@ -26,9 +26,9 @@ static NSMenu alloc() { void addItem(NSMenuItem item); - void insertItem(NSMenuItem item, int index); + void insertItem(NSMenuItem item, @NamedArg("atIndex") int index); NSMenuItem itemAtIndex(int index); - + long numberOfItems(); } diff --git a/src/main/java/de/jangassen/jfa/appkit/NSWorkspace.java b/src/main/java/de/jangassen/jfa/appkit/NSWorkspace.java new file mode 100644 index 0000000..1004567 --- /dev/null +++ b/src/main/java/de/jangassen/jfa/appkit/NSWorkspace.java @@ -0,0 +1,15 @@ +package de.jangassen.jfa.appkit; + + +import de.jangassen.jfa.FoundationProxy; + +import static de.jangassen.jfa.foundation.Foundation.getObjcClass; +import static de.jangassen.jfa.foundation.Foundation.invoke; + +public interface NSWorkspace extends NSObject { + static NSWorkspace sharedWorkspace() { + return FoundationProxy.wrap(invoke(getObjcClass("NSWorkspace"), "sharedWorkspace"), NSWorkspace.class); + } + + void hideOtherApplications(); +} \ No newline at end of file diff --git a/src/main/java/de/jangassen/jfa/foundation/Foundation.java b/src/main/java/de/jangassen/jfa/foundation/Foundation.java index 7022be3..1cad0d1 100644 --- a/src/main/java/de/jangassen/jfa/foundation/Foundation.java +++ b/src/main/java/de/jangassen/jfa/foundation/Foundation.java @@ -19,9 +19,21 @@ public final class Foundation { private static final Function myObjcMsgSend; static { - myFoundationLibrary = Native.load("Foundation", FoundationLibrary.class, Collections.singletonMap("jna.encoding", "UTF8")); - NativeLibrary nativeLibrary = ((Library.Handler) Proxy.getInvocationHandler(myFoundationLibrary)).getNativeLibrary(); - myObjcMsgSend = nativeLibrary.getFunction("objc_msgSend"); + FoundationLibrary foundationLibrary = null; + Function objcMsgSend = null; + try { + foundationLibrary = Native.load("Foundation", FoundationLibrary.class, Collections.singletonMap("jna.encoding", "UTF8")); + NativeLibrary nativeLibrary = ((Library.Handler) Proxy.getInvocationHandler(foundationLibrary)).getNativeLibrary(); + objcMsgSend = nativeLibrary.getFunction("objc_msgSend"); + } catch (RuntimeException e) { + // Foundation not available + } + myFoundationLibrary = foundationLibrary; + myObjcMsgSend = objcMsgSend; + } + + public static boolean isAvailable() { + return myFoundationLibrary != null && myObjcMsgSend != null; } public static void init() { /* fake method to init de.jangassen.foundation */ } diff --git a/src/test/java/de/jangassen/jfa/appkit/NSApplicationTest.java b/src/test/java/de/jangassen/jfa/appkit/NSApplicationTest.java new file mode 100644 index 0000000..51c62a3 --- /dev/null +++ b/src/test/java/de/jangassen/jfa/appkit/NSApplicationTest.java @@ -0,0 +1,13 @@ +package de.jangassen.jfa.appkit; + +import org.junit.Assert; +import org.junit.Test; + +public class NSApplicationTest { + @Test + public void getSharedApplication() { + NSApplication app = NSApplication.sharedApplication(); + + Assert.assertNotNull(app); + } +} diff --git a/src/test/java/de/jangassen/jfa/appkit/NSMenuItemTest.java b/src/test/java/de/jangassen/jfa/appkit/NSMenuItemTest.java new file mode 100644 index 0000000..b2b29d9 --- /dev/null +++ b/src/test/java/de/jangassen/jfa/appkit/NSMenuItemTest.java @@ -0,0 +1,33 @@ +package de.jangassen.jfa.appkit; + +import org.junit.Assert; +import org.junit.Test; + +public class NSMenuItemTest { + + @Test + public void createMenuItem() { + NSMenuItem item = NSMenuItem.alloc(); + + Assert.assertNotNull(item); + Assert.assertFalse(item.hasSubmenu()); + } + + @Test + public void initMenuItem() { + NSMenuItem item = NSMenuItem.alloc().initWithTitle("Test", null, ""); + + Assert.assertNotNull(item); + Assert.assertEquals("Test", item.title()); + } + + @Test + public void addSubmenu() { + NSMenuItem item = NSMenuItem.alloc().initWithTitle("Test", null, ""); + NSMenu submenu = NSMenu.alloc().initWithTitle("test"); + item.setSubmenu(submenu); + + Assert.assertNotNull(item); + Assert.assertTrue(item.hasSubmenu()); + } +} diff --git a/src/test/java/de/jangassen/jfa/appkit/NSMenuTest.java b/src/test/java/de/jangassen/jfa/appkit/NSMenuTest.java new file mode 100644 index 0000000..995b821 --- /dev/null +++ b/src/test/java/de/jangassen/jfa/appkit/NSMenuTest.java @@ -0,0 +1,71 @@ +package de.jangassen.jfa.appkit; + +import org.junit.Assert; +import org.junit.Test; + +public class NSMenuTest { + @Test + public void createMenu() { + NSMenu menu = NSMenu.alloc().initWithTitle("title"); + + Assert.assertNotNull(menu); + Assert.assertEquals("title", menu.title()); + } + + @Test + public void addMenuItem() { + NSMenu menu = NSMenu.alloc().initWithTitle("title"); + NSMenuItem menuItem = NSMenuItem.alloc().initWithTitle("title", null, ""); + + menu.addItem(menuItem); + NSMenuItem item = menu.itemAtIndex(0); + + Assert.assertEquals("title", menuItem.title()); + Assert.assertEquals(item, menuItem); + } + + @Test + public void insertMenuItem() { + NSMenu menu = NSMenu.alloc().initWithTitle("title"); + NSMenuItem menuItem = NSMenuItem.alloc().initWithTitle("title", null, ""); + + menu.insertItem(menuItem, 0); + NSMenuItem item = menu.itemAtIndex(0); + + Assert.assertEquals("title", menuItem.title()); + Assert.assertEquals(item, menuItem); + } + + @Test + public void RemoveMenuItem() { + NSMenu menu = NSMenu.alloc().initWithTitle("title"); + NSMenuItem menuItem = NSMenuItem.alloc().initWithTitle("title", null, ""); + + menu.addItem(menuItem); + menu.removeItem(menuItem); + + Assert.assertEquals(0, menu.numberOfItems()); + } + + @Test + public void removeMenuItemAtIndex() { + NSMenu menu = NSMenu.alloc().initWithTitle("title"); + NSMenuItem menuItem = NSMenuItem.alloc().initWithTitle("title", null, ""); + + menu.addItem(menuItem); + menu.removeItemAtIndex(0); + + Assert.assertEquals(0, menu.numberOfItems()); + } + + @Test + public void removeAllItems() { + NSMenu menu = NSMenu.alloc().initWithTitle("title"); + NSMenuItem menuItem = NSMenuItem.alloc().initWithTitle("title", null, ""); + + menu.addItem(menuItem); + menu.removeAllItems(); + + Assert.assertEquals(0, menu.numberOfItems()); + } +}