├── .classpath ├── .github └── workflows │ └── maven.yml ├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── encodings.xml ├── jarRepositories.xml ├── libraries │ ├── Maven__net_java_dev_jna_jna_5_10_0.xml │ ├── Maven__net_java_dev_jna_jna_5_11_0_SNAPSHOT.xml │ ├── Maven__org_apiguardian_apiguardian_api_1_1_0.xml │ ├── Maven__org_junit_jupiter_junit_jupiter_5_7_0.xml │ ├── Maven__org_junit_jupiter_junit_jupiter_api_5_7_0.xml │ ├── Maven__org_junit_jupiter_junit_jupiter_engine_5_7_0.xml │ ├── Maven__org_junit_jupiter_junit_jupiter_params_5_7_0.xml │ ├── Maven__org_junit_platform_junit_platform_commons_1_7_0.xml │ ├── Maven__org_junit_platform_junit_platform_engine_1_7_0.xml │ └── Maven__org_opentest4j_opentest4j_1_2_0.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── vcs.xml ├── .project ├── .settings ├── org.eclipse.jdt.core.prefs └── org.eclipse.m2e.core.prefs ├── .travis.settings.xml ├── .travis.yml ├── README.md ├── generate_runtime_mappings.sh ├── java-objc-bridge.iml ├── libjcocoa.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── xcshareddata │ └── xcschemes │ │ └── libjcocoa.xcscheme └── xcuserdata │ ├── matthias.fuchs.xcuserdatad │ └── xcschemes │ │ └── xcschememanagement.plist │ ├── matthias.xcuserdatad │ └── xcschemes │ │ └── xcschememanagement.plist │ └── shannah.xcuserdatad │ ├── xcdebugger │ └── Breakpoints.xcbkptlist │ └── xcschemes │ ├── libjcocoa.xcscheme │ └── xcschememanagement.plist ├── overview.html ├── pom.xml └── src ├── main ├── java │ ├── ca │ │ └── weblite │ │ │ ├── nativeutils │ │ │ └── NativeUtils.java │ │ │ └── objc │ │ │ ├── Client.java │ │ │ ├── Message.java │ │ │ ├── NSMessageInvocationException.java │ │ │ ├── NSObject.java │ │ │ ├── Peerable.java │ │ │ ├── PeerableRecipient.java │ │ │ ├── Proxy.java │ │ │ ├── Recipient.java │ │ │ ├── Runtime.java │ │ │ ├── RuntimeMappings.java │ │ │ ├── RuntimeUtils.java │ │ │ ├── RuntmeArm64Extensions.java │ │ │ ├── TypeMapper.java │ │ │ ├── TypeMapping.java │ │ │ ├── annotations │ │ │ └── Msg.java │ │ │ ├── foundation │ │ │ └── NSRange.java │ │ │ ├── jna │ │ │ └── PointerTool.java │ │ │ ├── mappers │ │ │ ├── NSObjectMapping.java │ │ │ ├── PointerMapping.java │ │ │ ├── ScalarMapping.java │ │ │ ├── StringMapping.java │ │ │ └── StructureMapping.java │ │ │ └── util │ │ │ └── CocoaUtils.java │ └── overview.html ├── objectivec │ ├── implementation │ │ ├── JavaUtil.h │ │ ├── JavaUtil.m │ │ ├── WLJavaProxy.h │ │ ├── WLJavaProxy.m │ │ ├── jnaexample_NativeClient_Setup.h │ │ ├── jnaexample_NativeClient_Setup.m │ │ ├── jni.h │ │ ├── jni_md.h │ │ ├── libjcocoa.h │ │ └── libjcocoa.m │ ├── lib │ │ ├── classfile_constants.h │ │ ├── jawt.h │ │ ├── jawt_md.h │ │ ├── jdwpTransport.h │ │ ├── jni.h │ │ ├── jni_md.h │ │ ├── jvmti.h │ │ └── jvmticmlr.h │ └── libjcocoa-Prefix.pch ├── php │ └── generate_mappings.php └── resources │ ├── Application.xib │ ├── Info.plist │ └── Window.xib ├── sample └── java │ └── ca │ └── weblite │ └── objc │ └── samples │ ├── BringToFrontSample.java │ ├── LoadNibSample.java │ ├── NSOpenPanelSample.java │ ├── NSSavePanelSample.java │ └── TestWebView.java └── test ├── java └── ca │ └── weblite │ └── objc │ ├── ClientTest.java │ ├── FiveArgTest.java │ ├── NSObjectTest.java │ ├── NSProcessInfoUtils.java │ ├── ProxyTest.java │ ├── RuntimeTest.java │ └── RuntimeUtilsTest.java ├── objectivec ├── libjcocoaTests.h └── libjcocoaTests.m └── resources ├── TestBundle.bundle └── Contents │ ├── Info.plist │ ├── MacOS │ └── TestBundle │ └── Resources │ ├── TestWindow.nib │ ├── TestWindowController.nib │ └── en.lproj │ └── InfoPlist.strings ├── en.lproj └── InfoPlist.strings └── libjcocoaTests-Info.plist /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI with Maven 2 | 3 | on: [push, pull_request] 4 | env: 5 | DEVELOPER_DIR: /Applications/Xcode_13.2.app/Contents/Developer 6 | jobs: 7 | build: 8 | runs-on: macos-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - name: Cache local Maven repository 14 | uses: actions/cache@v2 15 | with: 16 | path: ~/.m2/repository 17 | key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} 18 | restore-keys: | 19 | ${{ runner.os }}-maven- 20 | 21 | - name: Set up JDK 11 22 | uses: actions/setup-java@v1 23 | with: 24 | java-version: 11 25 | 26 | - name: Build with Maven 27 | run: mvn --batch-mode --no-transfer-progress --update-snapshots verify 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /java/nbproject/private/ 2 | /java/build/ 3 | /java/dist/ 4 | /samples/BringToFrontSample/nbproject/private/ 5 | /samples/BringToFrontSample/build/ 6 | .DS_Store 7 | private 8 | /bin/ 9 | /target/ 10 | libjcocoa.xcodeproj/project.xcworkspace/xcuserdata 11 | Build 12 | Index 13 | dist/Release/libjcocoa.dylib.dSYM 14 | dist 15 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__net_java_dev_jna_jna_5_10_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__net_java_dev_jna_jna_5_11_0_SNAPSHOT.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_apiguardian_apiguardian_api_1_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_5_7_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_api_5_7_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_engine_5_7_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_jupiter_junit_jupiter_params_5_7_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_platform_junit_platform_commons_1_7_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_junit_platform_junit_platform_engine_1_7_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_opentest4j_opentest4j_1_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | Java-Objective-C-Bridge 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.m2e.core.maven2Nature 21 | org.eclipse.jdt.core.javanature 22 | 23 | 24 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 3 | org.eclipse.jdt.core.compiler.compliance=11 4 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled 5 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 6 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore 7 | org.eclipse.jdt.core.compiler.release=disabled 8 | org.eclipse.jdt.core.compiler.source=11 9 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /.travis.settings.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | ossrh 8 | ${env.OSSRH_USER} 9 | ${env.OSSRH_PASSWORD} 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk11 5 | 6 | env: 7 | global: 8 | # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created 9 | # via the "travis encrypt" command using the project repo's public key 10 | 11 | 12 | cache: 13 | directories: 14 | - $HOME/.m2/repository 15 | 16 | before_cache: 17 | - rm -R $HOME/.m2/repository/ca/weblite 18 | 19 | before_install: 20 | - openssl aes-256-cbc -K $encrypted_2dffe6d9f24e_key -iv $encrypted_2dffe6d9f24e_iv -in codesigning.asc.enc -out codesigning.asc -d 21 | - gpg --fast-import codesigning.asc 22 | - gpg --list-keys 23 | - cp .travis.settings.xml $HOME/.m2/settings.xml 24 | 25 | install: true 26 | 27 | script: "mvn -e clean install" 28 | 29 | deploy: 30 | - provider: script 31 | script: "mvn -e -q -P travis,release -Dmaven.test.skip=true clean deploy" 32 | skip_cleanup: true 33 | on: 34 | branch: develop 35 | tags: false 36 | - provider: script 37 | # add "`git log --pretty=format:'%h' -n 1`" for git commit hash 38 | script: "mvn -e -P travis,release -Dmaven.test.skip=true clean deploy -Drevision=$TRAVIS_TAG-`date +%Y%m%d`" 39 | skip_cleanup: true 40 | on: 41 | branch: master 42 | tags: true 43 | 44 | notifications: 45 | email: 46 | recipients: 47 | - meister.fuchs@gmail.com 48 | on_success: change 49 | on_failure: always 50 | webhooks: 51 | urls: 52 | # - https://webhooks.gitter.im/e/02860416326b2469fdcc 53 | on_success: change # options: [always|never|change] default: always 54 | on_failure: always # options: [always|never|change] default: always 55 | on_start: never # options: [always|never|change] default: always 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java-Objective-C-Bridge 2 | 3 | ## Synopsis 4 | 5 | A thin bridge that allows for two-way communication from Java to Objective-C. 6 | 7 | ## License 8 | 9 | [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0.html) 10 | 11 | ## Requirements 12 | 13 | 1. Java 11 or Higher on OS X 14 | 2. JNA 15 | 16 | ## Getting Started 17 | 18 | Add the following dependency to your pom.xml: 19 | ```xml 20 | 21 | ca.weblite 22 | java-objc-bridge 23 | 1.2 24 | 25 | ``` 26 | 27 | ### Working with Sources 28 | 29 | 1. Check out the project and use `mvn clean install` (for debug builds) or `mvn clean install -Drelease=true` (for release builds) to build it 30 | 2. Include it as a Maven depencency in your project: 31 | ```xml 32 | 33 | ca.weblite 34 | java-objc-bridge 35 | 1.3-SNAPSHOT 36 | 37 | ``` 38 | 39 | ## Examples 40 | 41 | * [Sample wrapper of NSOpenPanel and NSSavePanel](https://gist.github.com/shannah/65007754c2b0f8add4f7) 42 | * [Example using WebKit and .nib file](src/test/java/ca/weblite/objc/TestWebView.java) 43 | * [Loading Nib File](src/test/java/ca/weblite/objc/LoadNibSample.java) 44 | * [Wrapper for NSProcessInfo to Solve App Nap Problem](src/test/java/ca/weblite/objc/NSProcessInfoUtils.java) 45 | 46 | ## JavaDocs & Documentation 47 | 48 | * [Documentation](https://solutions.weblite.ca/maven/java-objc-bridge/apidocs/index.html) 49 | * Read a [blog post](https://sjhannah.com/blog/2012/10/29/speaking-cocoa-from-java/) about the motivation for this project. 50 | 51 | ## Contact 52 | 53 | * Post your questions in the [Java-Objective-C Google Group](https://groups.google.com/forum/#!forum/java-objective-c-bridge) 54 | * Post bugs and feature requests to the [issue tracker](https://github.com/shannah/Java-Objective-C-Bridge/issues) 55 | 56 | ## Credits 57 | 58 | Created by [Steve Hannah](https://sjhannah.com) 59 | -------------------------------------------------------------------------------- /generate_runtime_mappings.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Use this script to regenerate the runtime mappings. 3 | # Runtime mappings are necessary to generate the JNA mappings for the objc_msgSend() method 4 | # with all of the different parameter permutations. A single mapping is insufficient 5 | # because JNA requires you to explicitly declare whether a Structure parameter is passed by 6 | # value. 7 | 8 | php src/main/php/generate_mappings.php > src/main/java/ca/weblite/objc/RuntimeMappings.java -------------------------------------------------------------------------------- /java-objc-bridge.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /libjcocoa.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /libjcocoa.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /libjcocoa.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /libjcocoa.xcodeproj/xcshareddata/xcschemes/libjcocoa.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 71 | 72 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /libjcocoa.xcodeproj/xcuserdata/matthias.fuchs.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | libjcocoa.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | libjcocoaTests.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /libjcocoa.xcodeproj/xcuserdata/matthias.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | libjcocoa.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | libjcocoaTests.xcscheme 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /libjcocoa.xcodeproj/xcuserdata/shannah.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /libjcocoa.xcodeproj/xcuserdata/shannah.xcuserdatad/xcschemes/libjcocoa.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 52 | 53 | 54 | 55 | 61 | 62 | 64 | 65 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /libjcocoa.xcodeproj/xcuserdata/shannah.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | libjcocoa.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 65152903163458B4002A41D8 16 | 17 | primary 18 | 19 | 20 | 65152917163458B4002A41D8 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /overview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Java/Objective-C Bridge

4 | 5 |

This class library enables you to use any Objective-C library directly from Java without 6 | having to generate any Java class stubs. It is built on top of the powerful JNA library, 7 | and interacts with Objective-C through the C functions in the Objective-C runtime. 8 |

9 | 10 |

Features

11 | 12 |
    13 |
  1. Access To All Native Libraries and Frameworks
  2. 14 |
  3. No need to generate class stubs
  4. 15 |
  5. Two-Way Communication - Objective-C can use your Java objects 16 | just as your Java objects can use Objective-C objects.
  6. 17 |
  7. Annotations to mark Java methods that should be callable by Objective-C
  8. 18 |
  9. Very Thin Wrapper - so you don't have to learn a whole new way of working 19 | with Objective-C.
  10. 20 |
21 | 22 |

An Example

23 | 24 |

The following example shows the bridge being used to display an NSOpenPanel, 25 | and add a Java class as its delegate to listen for changes to the user selection:

26 | 27 | 28 |

Requirements

29 |
    30 |
  1. Java SE6 or Higher
  2. 31 |
  3. Mac OS X 10.6 or Higher
  4. 32 |
33 | 34 |

License

35 | 36 |

Apache License, Version 2.0

37 | 38 | 39 |

Source Code and Downloads

40 | 41 |

Available On Git Hub

42 | 43 |

Contact

44 | 45 |

Steve Hannah @shannah78

46 | 47 |

Getting Started

48 | 49 |

Installation

50 |
    51 |
  1. Add the ObjCBridge.jar and jna.jar files into your class path
  2. 52 |
  3. Add libjcocoa.dylib to your java.library.path
  4. 53 |
54 | 55 |

The Basics

56 | 57 |

The Java Cocoa bridge can be used at three levels of abstraction:

58 |
    59 |
  1. Low level : Direct calling of Objective-C runtime functions. E.g. objc_msgSend(), object_getClass(), etc... 60 | See Apple's documentation 61 | on the Objective-C runtime for details of what these methods do. You would use the {@link ca.weblite.Runtime Runtime.INSTANCE} singleton object 62 | to access these functions through the Java Cocoa Bridge. 63 |
  2. 64 | 65 |
  3. Mid level : Calling simplified procedural wrappers 66 | around the Objective-C runtime functions via the {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} class and its 67 | set of static methods. 68 |
  4. 69 | 70 |
  5. High level : Using the object-oriented API to interact with 71 | Objective-C objects and classes via Java wrapper objects. This method makes 72 | use of the {@link ca.weblite.objc.Client Client}, {@link ca.weblite.objc.Proxy Proxy}, 73 | and {@link ca.weblite.objc.NSObject NSObject} classes primarily. 74 |
  6. 75 |
76 | 77 | 78 |

The Low-Level API

79 | 80 |

The low-level API is nothing more than a JNA interface 81 | for the Objective-C runtime functions. 82 | If you are familiar with these functions, and you are comfortable calling them directly, then you are free to call this API directly. 83 | In most cases, however, you will likely want to use a higher level API that handles a bit more of the plumbing. 84 |

85 | 86 |

The following is a sample from the {@link ca.weblite.objc.RuntimeTest RuntimeTest} Unit test that makes use of this low-level API:

87 | 88 | 89 |

A few things to comment on here:

90 |
    91 |
  1. The {@code com.sun.jna.Pointer} class is used throughout this API to represent 92 | a C pointer. 93 |
  2. 94 |
  3. Many of the low-level API functions take a Pointer as a parameter and return pointers, however the objc_msgSend() method 95 | and its variants return a long rather than a Pointer. This is because the value returned by this method can be 96 | either a value or a pointer. 97 |
  4. 98 |
  5. 99 | Messages that take C strings (i.e. {@code const *char}) expect to receive a java string as a parameter. Messages that 100 | take NSStrings, however, will not accept Strings of this type. You need to provide a pointer to an NSString object 101 | in these cases. 102 |
  6. 103 |
104 | 105 |

The Mid-Level API

106 | 107 |

The mid-level API consists of a set of static convenience methods around commonly used functions of the Objective-C runtime API. 108 | These include:

109 | 118 | 119 |

Even if your are working with the high-level API, you will still likely need to use the mid-level API in some instances. For convenience, you may want to 120 | use a static import of all of the {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} static methods so that you can access them quickly and easily: 121 |

122 |
{@code import static ca.weblite.objc.RuntimeUtils.*;}
123 | 124 |

The following sample code is taken from one of the unit tests and gives a good example of how to use the mid-level API to interact with 125 | the Objective-C runtime:

126 | 127 | 128 | 129 |

In order to provide a contrast with the low-level API, this example performs the same logic as the low-level 130 | example above. It makes use of the {@link ca.weblite.objc.RuntimeUtils#sel RuntimeUtils.sel()} function 131 | to obtain a selector rather than longer Runtime.INSTANCE.sel_getUid(). It also uses the cls() and clsName() 132 | functions to go back and forth between a class object and its class name. 133 |

134 |

However, we can shorten this still more. The {@code msg()} function includes a variant that accepts {@code String}s 135 | as their first two arguments. If the first argument is a {@code String}, then it is assumed to be a class name. If 136 | the second argument is a {@code String}, then it is assumed to be a selector name. Using this knowledge we can 137 | compress the above code significantly: 138 |

139 | 140 | 141 | 142 |
Using Type-Mapping
143 | 144 |

The {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} class also includes a few methods that act as a link and foundation 145 | to the higher level APIs. It includes a variant of the {@code msg()} function that can optionally perform type mapping 146 | of input arguments and their return values.

147 |

The method signature is as follows:

148 | {@code Object msg(boolean coerceOutput, 149 | boolean coerceInput, 150 | Pointer receiver, 151 | Pointer selector, 152 | Object... args)} 153 | 154 |

Note, that there are variants that allow you to specify the receiver or selector as strings also.

155 | 156 |

If you pass {@code true} for both {@code coerceOutput} and {@code coerceInput}, then the method will 157 | perform type mapping on the input {@code args} and the return value so that you are able to pass 158 | more familiar Java objects in. It will also perform type-checking to ensure that the correct 159 | variant of {@code objc_msgSend()} is sent to the Objective-C runtime (e.g. messages that return {@code float}s 160 | and {@code doubles} need to use the {@code objc_msgSend_fret()} function, and messages 161 | that return structures need to use the {@code objc_msgSend_sret()} function).

162 |

The type mapping is minimal and non-complex. There are just a few key mappings that are performed by 163 | this method:

164 |
    165 |
  1. Converts Java {@code String} objects to Objective-C {@code NSString} objects for arguments 166 | that are passed to messages that are expecting {@code NSStrings}. It uses the method signature 167 | of the method to determine if the transformation needs to be made. I.e. if a Java {@code String} 168 | is passed as an argument to a message that is expecting an Objective-C object (i.e. type encoding "{@literal @}"), 169 | then the string will be converted to an {@code NSString} automatically. 170 |
  2. 171 |
  3. Converts {@link ca.weblite.objc.Proxy Proxy} objects into {@code Pointer} objects for arguments 172 | that are expecting a Pointer or an Objective-C object. 173 |
  4. 174 |
  5. 175 | Automatically determines the correct variant of {@code objc_msgSend*} depending on the return type of 176 | the message (as specified in the Objective-C message signature). 177 |
  6. 178 |
  7. 179 | Converts return {@code NSString} return values to Java {@code String} objects. 180 |
  8. 181 |
  9. 182 | Wraps {@code NSObject} return values in {@link ca.weblite.objc.Proxy Proxy} objects so that 183 | they can be used more easily. It performs this conversion in a smart way using the {@link ca.weblite.objc.Proxy#load Proxy.load()} 184 | method to make sure that there is ever only one instance of a Java proxy for each instance of an Objective-C 185 | object. I.e. If a {@code Proxy} object already exists for a particular Objective-C object, then this method 186 | will look up the existing {@code Proxy} object and return that. Otherwise it creates a new {@code Proxy} object 187 | to wrap the return value and registers this proxy with the central proxy registry. 188 |
  10. 189 |
  11. Messages that return Structures will return a subclass of 190 | {@code com.sun.jna.Structure} 191 |
  12. 192 |
193 | 194 |

The following is some sample code taken from one of the unit tests to demonstrate how this variant of {@code msg()} can 195 | be used to enable automatic type mapping in the return values of Objective-c messages.

196 | 197 | 198 | 199 |

High-Level API (Object Oriented API)

200 | 201 |

Now that you see the foundation upon which the Java/Objective-C bridge sits, we can jump straight into the 202 | higher-level object oriented API. This API consists of three main classes that you'll wish to use.:

203 |
    204 |
  1. {@link ca.weblite.objc.Client Client} - An object that allows you to send messages to the Objective-C runtime. You'll generally 205 | use this to instantiate new Objective-C objects and obtain {@link ca.weblite.objc.Proxy Proxies} to them. 206 |
  2. 207 | 208 |
  3. {@link ca.weblite.objc.Proxy Proxy} - A wrapper around an Objective-C object that enables you to easily send messages 209 | to the native object. It includes convenience methods for sending messages that return various types. E.g. the {@link ca.weblite.objc.Proxy#sendInt sentInt()} 210 | method sends a message to which you expect an {@code int} to be returned. It returns a Java {@code int} so that 211 | you don't need to perform any ugly type casting. 212 |
  4. 213 | 214 |
  5. {@link ca.weblite.objc.NSObject NSObject} - A Java wrapper around an Objective-C object that enables two-way communication. Objective-C 215 | can call methods on this Java class, and you are able to send messages to the object's native Peer from Java. This is a subclass 216 | of {@link ca.weblite.objc.Proxy Proxy} so that it includes all of the same capabilities for sending messages. Typically you would subclass 217 | {@link ca.weblite.objc.NSObject NSObject} if you need to create a delegate for an objective-C object. You can define Java methods 218 | in your subclass that can be used in Objective-C via the {@link ca.weblite.objc.annotations.Msg Msg} annotation. 219 |
  6. 220 |
221 | 222 |

The following snippet is taken from a unit test. It shows the use of the {@link ca.weblite.objc.Client Client} class and {@link ca.weblite.objc.Proxy} 223 | class to create an NSMutable array and add some strings to it.

224 | 225 | 226 | 227 |

Writing a Delegate Class In Java

228 | 229 |

The {@link ca.weblite.objc.NSObject NSObject} class can be extended to define your own Java classes that can be called from Objective-C. 230 | You specify which methods can handle Objective-C messages using the {@link ca.weblite.objc.annotations.Msg Msg} annotation. There 231 | is an example at the top of the page that uses this method to add a delegate to the {@code NSOpenPanel} dialog. 232 |

233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | ca.weblite 6 | java-objc-bridge 7 | 1.3-SNAPSHOT 8 | Java-Objective-C-Bridge 9 | A thin bridge that allows for two-way communication from Java to Objective-C. 10 | 11 | 12 | 13 | 14 | 15 | release 16 | 17 | 18 | 19 | Release 20 | 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-javadoc-plugin 26 | 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-source-plugin 31 | 32 | 33 | org.apache.maven.plugins 34 | maven-gpg-plugin 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | UTF-8 43 | 11 44 | Debug 45 | 46 | 47 | 48 | 49 | Steve Hannah 50 | 51 | Creator 52 | 53 | https://github.com/shannah 54 | 55 | 56 | Matthias Fuchs 57 | 58 | Developer 59 | 60 | https://github.com/mojo2012 61 | 62 | 63 | 64 | 65 | 66 | Apache License, Version 2.0 67 | https://www.apache.org/licenses/LICENSE-2.0.txt 68 | repo 69 | 70 | 71 | 72 | 2012 73 | 74 | 75 | Web Lite Solutions Corp. 76 | https://solutions.weblite.ca/ 77 | 78 | 79 | 80 | github 81 | https://github.com/shannah/Java-Objective-C-Bridge/issues 82 | 83 | 84 | https://github.com/shannah/Java-Objective-C-Bridge 85 | 86 | 87 | scm:git:https://github.com/shannah/Java-Objective-C-Bridge.git 88 | scm:git:https://github.com/shannah/Java-Objective-C-Bridge.git 89 | HEAD 90 | https://github.com/shannah/Java-Objective-C-Bridge 91 | 92 | 93 | 94 | 95 | nexus-staging 96 | Nexus Release Repository 97 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.apache.maven.wagon 105 | wagon-ssh 106 | 3.4.2 107 | 108 | 109 | 110 | 111 | 112 | 113 | org.codehaus.mojo 114 | exec-maven-plugin 115 | 3.0.0 116 | 117 | 118 | xcodebuild 119 | generate-resources 120 | 121 | exec 122 | 123 | 124 | 125 | 126 | xcodebuild 127 | 128 | -scheme 129 | libjcocoa 130 | -configuration 131 | ${xcodeScheme} 132 | CONFIGURATION_BUILD_DIR=${project.build.outputDirectory} 133 | build 134 | 135 | 136 | 137 | 138 | 139 | 140 | org.apache.maven.plugins 141 | maven-jar-plugin 142 | 3.2.0 143 | 144 | 145 | 146 | javaobjectivecbridge 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | com.github.spotbugs 155 | spotbugs-maven-plugin 156 | 4.2.0 157 | 158 | more 159 | --add-opens java.base/java.lang=ALL-UNNAMED 160 | --illegal-access=deny 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | org.apache.maven.plugins 181 | maven-compiler-plugin 182 | 3.8.1 183 | 184 | 185 | org.apache.maven.plugins 186 | maven-surefire-plugin 187 | 3.0.0-M5 188 | 189 | false 190 | 191 | 192 | 193 | 194 | org.apache.maven.plugins 195 | maven-source-plugin 196 | 3.2.1 197 | 198 | 199 | attach-sources 200 | 201 | jar-no-fork 202 | 203 | 204 | 205 | 206 | 207 | 208 | org.apache.maven.plugins 209 | maven-javadoc-plugin 210 | 3.2.0 211 | 212 | false 213 | 214 | --allow-script-in-comments 215 | false 216 | 217 | true 218 | 219 | true 220 | 221 | false 222 | false 223 | all,-html,-syntax 224 | false 225 | true 226 | false 227 | mojo2012 228 | 1.0 229 | public 230 | 231 | 232 | 233 | attach-javadocs 234 | 235 | jar 236 | 237 | 238 | 239 | 240 | 241 | 242 | org.apache.maven.plugins 243 | maven-gpg-plugin 244 | 1.6 245 | 246 | 247 | 248 | --pinentry-mode 249 | loopback 250 | 251 | 252 | 253 | 254 | sign-artifacts 255 | verify 256 | 257 | sign 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | net.java.dev.jna 269 | jna 270 | 5.10.0 271 | 272 | 273 | 274 | org.junit.jupiter 275 | junit-jupiter 276 | 5.7.0 277 | test 278 | 279 | 280 | 281 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/nativeutils/NativeUtils.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.nativeutils; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.nio.file.StandardOpenOption; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | /** 14 | * Simple library class for working with JNI (Java Native Interface) 15 | * 16 | * @see http://frommyplayground.com/how-to-load-native-jni-library-from-jar 17 | * @author Adam Heirnich , 18 | * @version $Id: $Id 19 | * @since 1.1 20 | */ 21 | public class NativeUtils { 22 | 23 | /** 24 | * Private constructor - this class will never be instanced 25 | */ 26 | private NativeUtils() { 27 | } 28 | 29 | /** 30 | * Loads a library from current JAR archive, using the class loader of the {@code NativeUtils} 31 | * class to find the resource in the JAR. 32 | * 33 | * @param path a {@link java.lang.String} object. 34 | * @throws java.io.IOException if any. 35 | * @throws UnsatisfiedLinkError if loading the native library fails. 36 | */ 37 | public static void loadLibraryFromJar(String path) throws IOException { 38 | loadLibraryFromJar(path, NativeUtils.class); 39 | } 40 | 41 | /** 42 | * Loads a library from current JAR archive. 43 | * 44 | * The file from JAR is copied into system temporary directory and then loaded. The temporary file is deleted after exiting. 45 | * Method uses String as filename because the pathname is "abstract", not system-dependent. 46 | * 47 | * @throws java.lang.IllegalArgumentException If the path is not absolute or if the filename is shorter than three characters (restriction of @see File#createTempFile(java.lang.String, java.lang.String)). 48 | * @param path a {@link java.lang.String} object. 49 | * @param source {@code Class} whose class loader should be used to look up the resource in the JAR file 50 | * @throws java.io.IOException if any. 51 | * @throws UnsatisfiedLinkError if loading the native library fails. 52 | */ 53 | public static void loadLibraryFromJar(String path, Class source) throws IOException, UnsatisfiedLinkError { 54 | // Finally, load the library 55 | System.load(extractFromJar(path, source).toAbsolutePath().toString()); 56 | } 57 | 58 | /** 59 | * Extracts a resource from the JAR and stores it as temporary file 60 | * in the file system. 61 | * 62 | * @param path path of the resource, must begin with {@code '/'}, see {@link Class#getResourceAsStream(String)} 63 | * @param source {@code Class} whose class loader should be used to look up the resource in the JAR file 64 | * @return file path of the temporary file extracted from this JAR 65 | * @throws java.io.IOException if any. 66 | */ 67 | public static Path extractFromJar(String path, Class source) throws IOException { 68 | if (!path.startsWith("/")) { 69 | throw new IllegalArgumentException("The path has to be absolute (start with '/')."); 70 | } 71 | 72 | String filename = path.substring(path.lastIndexOf('/') + 1); 73 | 74 | // Split filename to prefix and suffix (extension) 75 | String prefix; 76 | String suffix; 77 | int lastDot = filename.lastIndexOf('.'); 78 | if (lastDot == -1) { 79 | // No file extension; use complete filename as prefix 80 | prefix = filename; 81 | suffix = null; 82 | } else { 83 | prefix = filename.substring(0, lastDot); 84 | suffix = filename.substring(lastDot); 85 | } 86 | 87 | // Check if the filename is okay 88 | if (prefix.length() < 3) { 89 | throw new IllegalArgumentException("The filename has to be at least 3 characters long."); 90 | } 91 | 92 | // Prepare temporary file 93 | Path temp = Files.createTempFile(prefix, suffix); 94 | temp.toFile().deleteOnExit(); 95 | 96 | // Open and check input stream 97 | InputStream is = source.getResourceAsStream(path); 98 | if (is == null) { 99 | throw new FileNotFoundException("File " + path + " was not found inside JAR."); 100 | } 101 | 102 | try (is; OutputStream out = Files.newOutputStream(temp, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) { 103 | is.transferTo(out); 104 | } 105 | 106 | return temp; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/Message.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import com.sun.jna.Pointer; 7 | 8 | /** 9 | * A structure the encapsulates a message. This is an optional alternative 10 | * way of sending messages to the Objective-C runtime. 11 | * 12 | * @see Client#send(Message...) 13 | * @see RuntimeUtils#msg(Message...) 14 | * @see Proxy#send(Message...) 15 | * @author shannah 16 | * @version $Id: $Id 17 | * @since 1.1 18 | */ 19 | public class Message { 20 | 21 | /** 22 | * Status identifier of a message to indicate that it has been skipped. 23 | * 24 | */ 25 | public static final int STATUS_SKIPPED=1; 26 | 27 | /** 28 | * Status identifier of a message to indicate that is has been cancelled. 29 | */ 30 | public static final int STATUS_CANCELLED=2; 31 | 32 | /** 33 | * Status identifier of a message to indicated that it has been completed. 34 | */ 35 | public static final int STATUS_COMPLETED=3; 36 | 37 | /** 38 | * Status identifier of a message to indicate that it is ready to be sent. 39 | */ 40 | public static final int STATUS_READY=0; 41 | 42 | /** 43 | * The target of the message. 44 | */ 45 | public Pointer receiver; 46 | 47 | /** 48 | * The selector of the message. 49 | */ 50 | public Pointer selector; 51 | 52 | /** 53 | * List of arguments to pass to the method invocation. 54 | */ 55 | public List args = new ArrayList(); 56 | 57 | /** 58 | * Placeholder for the result of the message. (i.e. return value). 59 | */ 60 | public Object result; 61 | 62 | /** 63 | * If there was en error in the message handling, the error will be saved 64 | * here. 65 | */ 66 | public Exception error; 67 | 68 | /** 69 | * The current status of the message. Before running, its status should 70 | * be STATUS_READY, and after running, it should be STATUS_COMPLETED. If, 71 | * for some reason it has been cancelled or skipped, then it could have 72 | * those statuses also. 73 | */ 74 | public int status = 0; 75 | 76 | /** 77 | * Whether to coerce the input of the message. 78 | */ 79 | public boolean coerceInput; 80 | /** 81 | * Whether to coerce the output of the message. 82 | */ 83 | public boolean coerceOutput; 84 | 85 | 86 | /** 87 | * Whether the input was, in fact coerced. Set when the message 88 | * is run. 89 | */ 90 | public boolean inputWasCoerced; 91 | /** 92 | * Whether the output was, in fact, coerced. Set when the message 93 | * is run. 94 | */ 95 | public boolean outputWasCoerced; 96 | 97 | /** 98 | * Reference to the next message in the message chain. 99 | */ 100 | public Message next; 101 | /** 102 | * Reference to the previous message in the message chain. 103 | */ 104 | public Message previous; 105 | 106 | 107 | /** 108 | * Method that is called just before the message is sent. This can be 109 | * overridden to change the parameters, skip the message, or cancel the message 110 | * chain altogether. 111 | */ 112 | public void beforeRequest(){ 113 | 114 | } 115 | 116 | /** 117 | * Method that is called just after the message is send and response received. 118 | * This can be overridden to do post processing, like changing the settings 119 | * of subsequent messages in the chain or doing processing based on the 120 | * output of the message. 121 | */ 122 | public void afterResponse(){ 123 | 124 | } 125 | 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/NSMessageInvocationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Web Lite Solutions Corp.. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package ca.weblite.objc; 17 | 18 | import java.lang.reflect.Method; 19 | 20 | /** 21 | * Exception that may be thrown when failure occurs while trying to invoke 22 | * a method in response to a selector. 23 | * @author shannah 24 | */ 25 | public class NSMessageInvocationException extends RuntimeException { 26 | private static final long serialVersionUID = 1L; 27 | 28 | private final String selectorName; 29 | private final Method method; 30 | 31 | /** 32 | * Creates a new instance of this exception. 33 | * @param selectorName The objective-c selector that was being handled. 34 | * @param method The method that was being executed. 35 | * @param cause The cause of the exception. 36 | */ 37 | public NSMessageInvocationException(String selectorName, Method method, Throwable cause) { 38 | super(String.format("Method invocation for selector %s caused exception. Method: %s", selectorName, method), cause); 39 | this.selectorName = selectorName; 40 | this.method = method; 41 | } 42 | 43 | /** 44 | * The name of the Objective-C selector that was being called. 45 | * @return 46 | */ 47 | public String getSelectorName() { 48 | return selectorName; 49 | } 50 | 51 | /** 52 | * The method that was being executed when this exception occurred. 53 | * @return 54 | */ 55 | public Method getMethod() { 56 | return method; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/Peerable.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import com.sun.jna.Pointer; 4 | 5 | /** 6 | * An interface for an object that has an Objective-C peer. 7 | * 8 | * @author shannah 9 | * @version $Id: $Id 10 | * @since 1.1 11 | */ 12 | public interface Peerable { 13 | /** 14 | *

getPeer.

15 | * 16 | * @return a {@link com.sun.jna.Pointer} object. 17 | */ 18 | public Pointer getPeer(); 19 | /** 20 | *

setPeer.

21 | * 22 | * @param peer a {@link com.sun.jna.Pointer} object. 23 | */ 24 | public void setPeer(Pointer peer); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/PeerableRecipient.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | /** 4 | * An interface for objects that have an Objective-C peer, and that can 5 | * be called from objective-C. 6 | * 7 | * @author shannah 8 | * @version $Id: $Id 9 | * @since 1.1 10 | */ 11 | public interface PeerableRecipient extends Peerable, Recipient { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/Recipient.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | 4 | /** 5 | * An interface for an object that can receive messages from the Objective-C 6 | * runtime. In order to receive messages, the object should be passed to the 7 | * RuntimeUtils.createProxy() method. 8 | * 9 | *

The NSObject class is a concrete implementation of this interface that 10 | * contains all of the plumbing necessary to operate in the world of the 11 | * Objective-C runtime. It is probably best to just subclass NSObject rather 12 | * than implement your own Recipient class.

13 | * 14 | * @author shannah 15 | * @see NSObject 16 | * @see NSProxy Class Reference 17 | * @version $Id: $Id 18 | * @since 1.1 19 | */ 20 | public interface Recipient { 21 | 22 | /** 23 | * Returns the method signature for a specified selector. 24 | * 25 | * @param selector The pointer to the selector to check. 26 | * @return Pointer to the NSMethodSignature object for the specified selector. 27 | * @see NSMethodSignature Class Reference 28 | * @see forwardInvocation: Method reference (from NSProxy) 29 | */ 30 | public long methodSignatureForSelector(long selector); 31 | 32 | /** 33 | * Handles the invocation of a method on the recipient. Typically this should 34 | * either be handled by a java method, or routed to some parent object that 35 | * is being proxied. 36 | * 37 | * @param invocation The NSInvocation object. 38 | * @see NSInvocation Class Reference 39 | * @see forwardInvocation: Method reference (from NSProxy) 40 | * @see NSObject#methodSignatureForSelector(long) For a concrete imlementation. 41 | */ 42 | public void forwardInvocation(long invocation); 43 | 44 | /** 45 | * Checks to see if this object responds to the specified selector. 46 | * 47 | * @param selector a long. 48 | * @return True if the object responds to the specified selector. 49 | */ 50 | public boolean respondsToSelector(long selector); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/RuntmeArm64Extensions.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import com.sun.jna.Library; 4 | import com.sun.jna.Native; 5 | import com.sun.jna.Pointer; 6 | 7 | public interface RuntmeArm64Extensions extends Library { 8 | public static RuntmeArm64Extensions INSTANCE = (RuntmeArm64Extensions) Native.loadLibrary("objc.A", RuntmeArm64Extensions.class); 9 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector); 10 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg); 11 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2); 12 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2, Object arg3); 13 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2, Object arg3, Object arg4); 14 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2, Object arg3, Object arg4, Object arg5); 15 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6); 16 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/TypeMapper.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import ca.weblite.objc.mappers.NSObjectMapping; 4 | import ca.weblite.objc.mappers.PointerMapping; 5 | import ca.weblite.objc.mappers.ScalarMapping; 6 | import ca.weblite.objc.mappers.StringMapping; 7 | import ca.weblite.objc.mappers.StructureMapping; 8 | 9 | /** 10 | * 11 | * Maps Objective-C types to Java types. This provides automatic conversion 12 | * to message inputs and outputs (unless coercion is disabled in the message 13 | * request). In many cases, it just passes the values straight through (e.g. 14 | * primitive types. Notably, Java Strings are mapped to NSStrings 15 | * if the signature of the argument context is an NSString, and NSObjects 16 | * are mapped as Proxy wrapper objects. 17 | * 18 | * @author shannah 19 | * @version $Id: $Id 20 | * @since 1.1 21 | */ 22 | public class TypeMapper implements TypeMapping { 23 | /** 24 | * Singleton instance of the TypeMapper 25 | */ 26 | public static final TypeMapper INSTANCE = new TypeMapper(); 27 | 28 | /** 29 | * Obtains the singleton instance of the TypeMapper, i.e. {@link #INSTANCE}. 30 | * 31 | * @return singleton {@code TypeMapper} object. 32 | */ 33 | public static TypeMapper getInstance(){ 34 | return INSTANCE; 35 | } 36 | 37 | private TypeMapper() { } 38 | 39 | /** 40 | * Maps signatures to the corresponding TypeMapping object. Signatures 41 | * are Objective-C type encodings. 42 | */ 43 | private static TypeMapping getMapping(char typeChar) { 44 | switch (typeChar) { 45 | case 'c': case 'C': 46 | case 'i': case 'I': 47 | case 's': case 'S': 48 | case 'f': case 'd': 49 | case 'l': case 'L': 50 | case 'q': case 'Q': 51 | case 'b': case 'B': 52 | case '[': case ':': case '?': case '#': case 'v': 53 | return ScalarMapping.INSTANCE; 54 | case '*': 55 | return StringMapping.INSTANCE; 56 | case '^': 57 | return PointerMapping.INSTANCE; 58 | case '@': 59 | return NSObjectMapping.INSTANCE; 60 | case '{': 61 | return StructureMapping.INSTANCE; 62 | default: 63 | throw new IllegalArgumentException("Unknown type: " + typeChar); 64 | } 65 | } 66 | 67 | /** 68 | * {@inheritDoc} 69 | * 70 | * Converts a C variable to the corresponding Java type based on the 71 | * specified signature. By default, this will map scalars straight 72 | * across without change. Strings are mapped to NSStrings. 73 | */ 74 | @Override 75 | public Object cToJ(Object cVar, String signature, TypeMapping root) { 76 | String prefixes = "rnNoORV"; 77 | int offset = 0; 78 | while ( prefixes.indexOf(signature.charAt(offset)) != -1 ){ 79 | offset++; 80 | if ( offset > signature.length()-1 ){ 81 | break; 82 | } 83 | } 84 | if ( offset > 0 ){ 85 | signature = signature.substring(offset); 86 | } 87 | 88 | char typeChar = signature.charAt(0); 89 | TypeMapping mapping = getMapping(typeChar); 90 | return mapping.cToJ(cVar, signature, root); 91 | } 92 | 93 | /** 94 | * {@inheritDoc} 95 | * 96 | * Converts a Java variable to the corresponding C type based on the 97 | * specified signature. By default, this will map scalars straight 98 | * across without change. Strings are mapped to NSStrings. 99 | * 100 | *

Example Usage

101 | *

The following is a modified snippet from the NSObject class that shows 102 | * (roughly) how the jToC method is used to take the output of a Java method 103 | * and set the return value in an NSInvocation object to a corresponding 104 | * C type.:

105 | * 106 | */ 107 | @Override 108 | public Object jToC(Object jVar, String signature, TypeMapping root) { 109 | String prefixes = "rnNoORV"; 110 | int offset = 0; 111 | while ( prefixes.indexOf(signature.charAt(offset)) != -1 ){ 112 | offset++; 113 | if ( offset > signature.length()-1 ){ 114 | break; 115 | } 116 | } 117 | if ( offset > 0 ){ 118 | signature = signature.substring(offset); 119 | } 120 | 121 | char typeChar = signature.charAt(0); 122 | TypeMapping mapping = getMapping(typeChar); 123 | return mapping.jToC(jVar, signature, root); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/TypeMapping.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | /** 4 | * Interface to be implemented by objects that map Java types to Objective-C 5 | * types. 6 | * 7 | * @author shannah 8 | * @version $Id: $Id 9 | * @since 1.1 10 | */ 11 | public interface TypeMapping { 12 | /** 13 | * Converts a C variable to the corresponding Java variable given the 14 | * context of the specified signature. 15 | * 16 | * @param cVar The C variable to be converted. 17 | * @param signature The signature that tells what type of variable we are dealing with according to Objective-C Type Encoding conventions. 18 | * @param root The root TypeMapping object 19 | * @return The converted Java object. 20 | */ 21 | public Object cToJ(Object cVar, String signature, TypeMapping root); 22 | 23 | /** 24 | * Converts a Java variable to the corresponding Java variable given the 25 | * context of the specified signature. 26 | * 27 | * @param jVar The Java variable to be converted. 28 | * @param signature The signature that tells what type of variable we are dealing with according to Objective-C Type Encoding conventions. 29 | * @param root a {@link ca.weblite.objc.TypeMapping} object. 30 | * @return The converted C variable 31 | */ 32 | public Object jToC(Object jVar, String signature, TypeMapping root); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/annotations/Msg.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * An annotation that allows a Java method to receive and process Objective-C 10 | * messages. This annotation is only useful for subclasses of ca.weblite.objc.NSObject. 11 | * 12 | * 13 | *

Example Class Using Annotation TO Handle Objective-C messages

14 | * 15 | * 16 | * 17 | *

The following is some test code that uses the TestClass. Notice that this 18 | * class can be interacted with the same as if it was an Objective-C object. Indeed, 19 | * Objective-C can call any of the methods marked by the {@literal @}Msg annotation 20 | * by simply sending it the appropriate message.

21 | * 22 | * 23 | * 24 | * @author shannah 25 | * @version $Id: $Id 26 | * @since 1.1 27 | */ 28 | @Retention(RetentionPolicy.RUNTIME) 29 | @Target(ElementType.METHOD) 30 | public @interface Msg { 31 | 32 | /** 33 | * The name of the objective-c selector that can be used to target this method. 34 | * Selectors should be the full method name, including colons. If you're overriding 35 | * the NSSavePanel's -setTitle: message, then the selector would be "setTitle:". 36 | * The -title message, on the other hand would just be "title" (without colon) 37 | * because it takes no parameters. 38 | * @return the selector name 39 | */ 40 | String selector(); 41 | 42 | /** 43 | * The signature of this message. This should be a valid Objective-C type 44 | * encoding. If this parameter is omitted, then you can optionally use 45 | * the like() directive specify that the signature should be the same 46 | * as another Class' method. 47 | * 48 | * @return The signature of the message. 49 | * 50 | * 51 | * 52 | *

Example Signatures

53 | * 54 | * 55 | * 56 | * 57 | * 58 | * 59 | * 60 | * 61 | * 62 | * 63 | * 64 | * 65 | * 70 | * 71 | * 72 | * 73 | * 74 | * 80 | * 81 | * 82 | * 83 | * 84 | * 89 | * 90 | * 91 | *
Method TypeSignatureExplanation
Returns NSString, takes no parameters{@literal @@}:The first "{@literal @}" indicates the return value, which is an Object, 66 | * The second "{@literal @}" is a mandatory hidden parameter that refers 67 | * to the receiver of the message. The ":" is a hidden parameter 68 | * that refers to the selector of the message. 69 | *
Returns void, takes an NSString as a parameter{@literal v@:@}The "v" indicates that there is no return value (i.e. void). 75 | * The first "{@literal @}" marks the hidden parameter that is the target 76 | * of the message. The ":" marks the hidden selector. The final 77 | * "{@literal @}" indicates that it takes an object as a parameter (specifically 78 | * an NSString, but the signature doesn't distinguish. 79 | *
Returns void, takes an C String (i.e. *char) as a parameter{@literal v@:*}The "v" indicates that there is no return value (i.e. void). 85 | * The first "{@literal @}" marks the hidden parameter that is the target 86 | * of the message. The ":" marks the hidden selector. The final 87 | * "{@literal *}" indicates that it takes a C String as the first parameter. 88 | *
92 | * @see Objective-C Type Encodings 93 | */ 94 | String signature() default ""; 95 | 96 | /** 97 | * Specifies that the signature should match that of another object's method. 98 | * This can make it easier for you to define methods without having to know 99 | * how to formulate the signature. 100 | * 101 | *

E.g., if you have a method that takes no input, and returns an int, 102 | * you might just say that your method is like the NSArray.count selector: 103 | * {@code 104 | * {@literal @}Msg(selector="myIntMessage", like="NSArray.count") 105 | * } 106 | *

107 | * 108 | * @return The name of a method whose signature that this message should 109 | * copy. 110 | */ 111 | String like() default ""; 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/foundation/NSRange.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc.foundation; 2 | 3 | import com.sun.jna.Structure; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * A structure mapping to the Foundation structure NSRange. If you need to call a method that 9 | * receives an NSRange as an input, you can use this structure. 10 | */ 11 | public class NSRange extends Structure { 12 | 13 | public static class ByReference extends NSRange implements Structure.ByReference{ 14 | } 15 | public static class ByValue extends NSRange implements Structure.ByValue { 16 | } 17 | 18 | /** 19 | * The location. WARNING: This stores an unsigned integer value. Use {@link #getLocation()} and {@link #setLocation(int)} 20 | * to properly convert to/from Java's signed ints. 21 | */ 22 | public long location; 23 | 24 | /** 25 | * The length. WARNING: This stores an unsigned integer value. Use {@link #getLength()} and {@link #setLength(int)} 26 | * to properly convert to/from Java's signed ints. 27 | */ 28 | public long length; 29 | 30 | /** 31 | * Sets the location of the range. 32 | * @param loc The location. 33 | */ 34 | public void setLocation(int loc) { 35 | location = Integer.toUnsignedLong(loc); 36 | } 37 | 38 | /** 39 | * Sets the length of the range. 40 | * @param len The length of the range. 41 | */ 42 | public void setLength(int len) { 43 | length = Integer.toUnsignedLong(len); 44 | } 45 | 46 | /** 47 | * Gets the location. Prefer this accessor to direct access of {@link #location} because {@link #location} stores 48 | * is unsigned, so the value will seem nonsensical. 49 | * @return The location as a signed int. 50 | */ 51 | public int getLocation() { 52 | return (int)Integer.toUnsignedLong((int)location); 53 | } 54 | 55 | /** 56 | * Gets the length as a signed int. 57 | * @return The length as a signed int. 58 | */ 59 | public int getLength() { 60 | return (int)Integer.toUnsignedLong((int)length); 61 | } 62 | 63 | @Override 64 | protected List getFieldOrder() { 65 | return List.of("location","length"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/jna/PointerTool.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc.jna; 2 | 3 | import com.sun.jna.Pointer; 4 | 5 | /** 6 | *

PointerTool class.

7 | * 8 | * @author shannah 9 | * @version $Id: $Id 10 | * @since 1.1 11 | */ 12 | public class PointerTool { 13 | /** 14 | *

getPeer.

15 | * 16 | * @param ptr a {@link com.sun.jna.Pointer} object. 17 | * @return a long. 18 | */ 19 | public static long getPeer(Pointer ptr){ 20 | return Pointer.nativeValue(ptr); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/mappers/NSObjectMapping.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc.mappers; 2 | 3 | import com.sun.jna.Pointer; 4 | 5 | import ca.weblite.objc.Peerable; 6 | import ca.weblite.objc.Proxy; 7 | import ca.weblite.objc.Runtime; 8 | import ca.weblite.objc.RuntimeUtils; 9 | import ca.weblite.objc.TypeMapping; 10 | import ca.weblite.objc.jna.PointerTool; 11 | 12 | /** 13 | *

NSObjectMapping class.

14 | * 15 | * @author shannah 16 | * @version $Id: $Id 17 | * @since 1.1 18 | */ 19 | public class NSObjectMapping implements TypeMapping { 20 | /** 21 | * Singleton instance. 22 | */ 23 | public static final NSObjectMapping INSTANCE = new NSObjectMapping(); 24 | 25 | private NSObjectMapping() { } 26 | 27 | @Override 28 | public Object cToJ(Object cVar, String signature, TypeMapping root) { 29 | //System.out.println("Mapping NSObject to Java "+cVar+" sig: "+signature); 30 | Pointer cObj = Pointer.NULL; 31 | if (cVar instanceof Pointer) { 32 | cObj = (Pointer)cVar; 33 | } else if (cVar instanceof Long) { 34 | cObj = new Pointer((Long) cVar); 35 | } else { 36 | return cVar; 37 | } 38 | if ( (Pointer.NULL == cObj) || (cVar == null) || (cObj == null) || (PointerTool.getPeer(cObj) == 0L ) ){ 39 | //System.out.println("The java value will be null"); 40 | return null; 41 | } 42 | String clsName = Runtime.INSTANCE.object_getClassName(cObj); 43 | boolean isString = "NSString".equals(clsName) || "NSTaggedPointerString".equals(clsName) || "NSMutableString".equals(clsName) || "__NSCFString".equals(clsName); 44 | 45 | 46 | ////System.out.println("Checking if object is a string "+isString+", "+clsName); 47 | if ( isString ){ 48 | return RuntimeUtils.str(cObj); 49 | } 50 | Object peer = RuntimeUtils.getJavaPeer(PointerTool.getPeer(cObj)); 51 | if ( peer == null ){ 52 | return Proxy.load(cObj); 53 | } else { 54 | return peer; 55 | } 56 | } 57 | 58 | @Override 59 | public Object jToC(Object jVar, String signature, TypeMapping root) { 60 | if ( jVar == null ){ 61 | return Pointer.NULL; 62 | } 63 | if (jVar instanceof String) { 64 | return RuntimeUtils.str((String)jVar); 65 | } 66 | if (jVar instanceof Peerable) { 67 | return ((Peerable)jVar).getPeer(); 68 | } else if (jVar instanceof Pointer) { 69 | return jVar; 70 | } else { 71 | throw new IllegalArgumentException("Unsupported value: " + jVar); 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/mappers/PointerMapping.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc.mappers; 2 | 3 | import com.sun.jna.Pointer; 4 | 5 | import ca.weblite.objc.TypeMapping; 6 | 7 | /** 8 | *

PointerMapping class.

9 | * 10 | * @author shannah 11 | * @version $Id: $Id 12 | * @since 1.1 13 | */ 14 | public class PointerMapping implements TypeMapping { 15 | /** 16 | * Singleton instance. 17 | */ 18 | public static final PointerMapping INSTANCE = new PointerMapping(); 19 | 20 | private PointerMapping() { } 21 | 22 | @Override 23 | public Object cToJ(Object cVar, String signature, TypeMapping root) { 24 | if (cVar instanceof Pointer) return cVar; 25 | return new Pointer((Long) cVar); 26 | } 27 | 28 | @Override 29 | public Object jToC(Object jVar, String signature, TypeMapping root) { 30 | // After some difficult deliberation I've decided that it is 31 | // better to require a Pointer or long to be passed in places 32 | // where Objective-C expects a pointer. 33 | return jVar; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/mappers/ScalarMapping.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc.mappers; 2 | 3 | import ca.weblite.objc.TypeMapping; 4 | 5 | /** 6 | *

ScalarMapping class.

7 | * 8 | * @author shannah 9 | * @version $Id: $Id 10 | * @since 1.1 11 | */ 12 | public class ScalarMapping implements TypeMapping { 13 | /** 14 | * Singleton instance. 15 | */ 16 | public static final ScalarMapping INSTANCE = new ScalarMapping(); 17 | 18 | private ScalarMapping() { } 19 | 20 | @Override 21 | public Object cToJ(Object cVar, String signature, TypeMapping root) { 22 | //System.out.println("C to J for signature "+signature); 23 | char firstChar = signature.charAt(0); 24 | if (cVar instanceof Long) { 25 | long cObj = (Long) cVar; 26 | switch (firstChar){ 27 | case 'i': 28 | case 'I': 29 | case 's': 30 | case 'S': 31 | return (int) cObj; 32 | case 'c': 33 | return (byte) cObj; 34 | case 'B': 35 | return cObj > 0L; 36 | } 37 | } 38 | 39 | return cVar; 40 | } 41 | 42 | @Override 43 | public Object jToC(Object jVar, String signature, TypeMapping root) { 44 | return jVar; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/mappers/StringMapping.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc.mappers; 2 | 3 | import com.sun.jna.Pointer; 4 | 5 | import ca.weblite.objc.TypeMapping; 6 | 7 | /** 8 | *

StringMapping class.

9 | * 10 | * @author shannah 11 | * @version $Id: $Id 12 | * @since 1.1 13 | */ 14 | public class StringMapping implements TypeMapping { 15 | /** 16 | * Singleton instance. 17 | */ 18 | public static final StringMapping INSTANCE = new StringMapping(); 19 | 20 | private StringMapping() { } 21 | 22 | @Override 23 | public Object cToJ(Object cVar, String signature, TypeMapping root) { 24 | return new Pointer((Long)cVar).getString(0); 25 | } 26 | 27 | @Override 28 | public Object jToC(Object jVar, String signature, TypeMapping root) { 29 | return jVar; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/mappers/StructureMapping.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc.mappers; 2 | 3 | import ca.weblite.objc.TypeMapping; 4 | 5 | /** 6 | *

StructureMapping class.

7 | * 8 | * @author shannah 9 | * @version $Id: $Id 10 | * @since 1.1 11 | */ 12 | public class StructureMapping implements TypeMapping { 13 | /** 14 | * Singleton instance. 15 | */ 16 | public static final StructureMapping INSTANCE = new StructureMapping(); 17 | 18 | private StructureMapping() { } 19 | 20 | @Override 21 | public Object cToJ(Object cVar, String signature, TypeMapping root) { 22 | return cVar; 23 | } 24 | 25 | @Override 26 | public Object jToC(Object jVar, String signature, TypeMapping root) { 27 | return jVar; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/ca/weblite/objc/util/CocoaUtils.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc.util; 2 | 3 | import ca.weblite.objc.NSObject; 4 | import static ca.weblite.objc.RuntimeUtils.sel; 5 | import ca.weblite.objc.annotations.Msg; 6 | 7 | /** 8 | *

CocoaUtils class.

9 | * 10 | * @author shannah 11 | * @version $Id: $Id 12 | * @since 1.1 13 | */ 14 | public class CocoaUtils { 15 | 16 | 17 | /** 18 | *

dispatch_async.

19 | * 20 | * @param r a {@link java.lang.Runnable} object. 21 | */ 22 | public static void dispatch_async(final Runnable r){ 23 | (new NSObject("NSObject"){ 24 | @Msg(selector="run", like="NSObject.finalize") 25 | public void run(){ 26 | r.run(); 27 | } 28 | }).send("performSelectorOnMainThread:withObject:waitUntilDone:", sel("run"), null, false); 29 | } 30 | 31 | 32 | /** 33 | *

dispatch_sync.

34 | * 35 | * @param r a {@link java.lang.Runnable} object. 36 | */ 37 | public static void dispatch_sync(final Runnable r){ 38 | (new NSObject("NSObject"){ 39 | @Msg(selector="run", like="NSObject.finalize") 40 | public void run(){ 41 | r.run(); 42 | } 43 | }).send("performSelectorOnMainThread:withObject:waitUntilDone:", sel("run"), null, true); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/overview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Java/Objective-C Bridge

4 | 5 |

This class library enables you to use any Objective-C library directly from Java without 6 | having to generate any Java class stubs. It is built on top of the powerful JNA library, 7 | and interacts with Objective-C through the C functions in the Objective-C runtime. 8 |

9 | 10 |

Features

11 | 12 |
    13 |
  1. Access To All Native Libraries and Frameworks
  2. 14 |
  3. No need to generate class stubs
  4. 15 |
  5. Two-Way Communication - Objective-C can use your Java objects 16 | just as your Java objects can use Objective-C objects.
  6. 17 |
  7. Annotations to mark Java methods that should be callable by Objective-C
  8. 18 |
  9. Very Thin Wrapper - so you don't have to learn a whole new way of working 19 | with Objective-C.
  10. 20 |
21 | 22 |

An Example

23 | 24 |

The following example shows the bridge being used to display an NSOpenPanel, 25 | and add a Java class as its delegate to listen for changes to the user selection:

26 | 27 | 28 |

Requirements

29 |
    30 |
  1. Java SE6 or Higher
  2. 31 |
  3. Mac OS X 10.6 or Higher
  4. 32 |
33 | 34 |

License

35 | 36 |

Apache License, Version 2.0

37 | 38 | 39 |

Source Code and Downloads

40 | 41 |

Available On Git Hub

42 | 43 |

Contact

44 | 45 |

Steve Hannah @shannah78

46 | 47 |

Getting Started

48 | 49 |

Installation

50 |
    51 |
  1. Add the ObjCBridge.jar and jna.jar files into your class path
  2. 52 |
  3. Add libjcocoa.dylib to your java.library.path
  4. 53 |
54 | 55 |

The Basics

56 | 57 |

The Java Cocoa bridge can be used at three levels of abstraction:

58 |
    59 |
  1. Low level : Direct calling of Objective-C runtime functions. E.g. objc_msgSend(), object_getClass(), etc... 60 | See Apple's documentation 61 | on the Objective-C runtime for details of what these methods do. You would use the {@link ca.weblite.Runtime Runtime.INSTANCE} singleton object 62 | to access these functions through the Java Cocoa Bridge. 63 |
  2. 64 | 65 |
  3. Mid level : Calling simplified procedural wrappers 66 | around the Objective-C runtime functions via the {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} class and its 67 | set of static methods. 68 |
  4. 69 | 70 |
  5. High level : Using the object-oriented API to interact with 71 | Objective-C objects and classes via Java wrapper objects. This method makes 72 | use of the {@link ca.weblite.objc.Client Client}, {@link ca.weblite.objc.Proxy Proxy}, 73 | and {@link ca.weblite.objc.NSObject NSObject} classes primarily. 74 |
  6. 75 |
76 | 77 | 78 |

The Low-Level API

79 | 80 |

The low-level API is nothing more than a JNA interface 81 | for the Objective-C runtime functions. 82 | If you are familiar with these functions, and you are comfortable calling them directly, then you are free to call this API directly. 83 | In most cases, however, you will likely want to use a higher level API that handles a bit more of the plumbing. 84 |

85 | 86 |

The following is a sample from the {@link ca.weblite.objc.RuntimeTest RuntimeTest} Unit test that makes use of this low-level API:

87 | 88 | 89 |

A few things to comment on here:

90 |
    91 |
  1. The {@code com.sun.jna.Pointer} class is used throughout this API to represent 92 | a C pointer. 93 |
  2. 94 |
  3. Many of the low-level API functions take a Pointer as a parameter and return pointers, however the objc_msgSend() method 95 | and its variants return a long rather than a Pointer. This is because the value returned by this method can be 96 | either a value or a pointer. 97 |
  4. 98 |
  5. 99 | Messages that take C strings (i.e. {@code const *char}) expect to receive a java string as a parameter. Messages that 100 | take NSStrings, however, will not accept Strings of this type. You need to provide a pointer to an NSString object 101 | in these cases. 102 |
  6. 103 |
104 | 105 |

The Mid-Level API

106 | 107 |

The mid-level API consists of a set of static convenience methods around commonly used functions of the Objective-C runtime API. 108 | These include:

109 | 118 | 119 |

Even if your are working with the high-level API, you will still likely need to use the mid-level API in some instances. For convenience, you may want to 120 | use a static import of all of the {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} static methods so that you can access them quickly and easily: 121 |

122 |
{@code import static ca.weblite.objc.RuntimeUtils.*;}
123 | 124 |

The following sample code is taken from one of the unit tests and gives a good example of how to use the mid-level API to interact with 125 | the Objective-C runtime:

126 | 127 | 128 | 129 |

In order to provide a contrast with the low-level API, this example performs the same logic as the low-level 130 | example above. It makes use of the {@link ca.weblite.objc.RuntimeUtils#sel RuntimeUtils.sel()} function 131 | to obtain a selector rather than longer Runtime.INSTANCE.sel_getUid(). It also uses the cls() and clsName() 132 | functions to go back and forth between a class object and its class name. 133 |

134 |

However, we can shorten this still more. The {@code msg()} function includes a variant that accepts {@code String}s 135 | as their first two arguments. If the first argument is a {@code String}, then it is assumed to be a class name. If 136 | the second argument is a {@code String}, then it is assumed to be a selector name. Using this knowledge we can 137 | compress the above code significantly: 138 |

139 | 140 | 141 | 142 |
Using Type-Mapping
143 | 144 |

The {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} class also includes a few methods that act as a link and foundation 145 | to the higher level APIs. It includes a variant of the {@code msg()} function that can optionally perform type mapping 146 | of input arguments and their return values.

147 |

The method signature is as follows:

148 | {@code Object msg(boolean coerceOutput, 149 | boolean coerceInput, 150 | Pointer receiver, 151 | Pointer selector, 152 | Object... args)} 153 | 154 |

Note, that there are variants that allow you to specify the receiver or selector as strings also.

155 | 156 |

If you pass {@code true} for both {@code coerceOutput} and {@code coerceInput}, then the method will 157 | perform type mapping on the input {@code args} and the return value so that you are able to pass 158 | more familiar Java objects in. It will also perform type-checking to ensure that the correct 159 | variant of {@code objc_msgSend()} is sent to the Objective-C runtime (e.g. messages that return {@code float}s 160 | and {@code doubles} need to use the {@code objc_msgSend_fret()} function, and messages 161 | that return structures need to use the {@code objc_msgSend_sret()} function).

162 |

The type mapping is minimal and non-complex. There are just a few key mappings that are performed by 163 | this method:

164 |
    165 |
  1. Converts Java {@code String} objects to Objective-C {@code NSString} objects for arguments 166 | that are passed to messages that are expecting {@code NSStrings}. It uses the method signature 167 | of the method to determine if the transformation needs to be made. I.e. if a Java {@code String} 168 | is passed as an argument to a message that is expecting an Objective-C object (i.e. type encoding "{@literal @}"), 169 | then the string will be converted to an {@code NSString} automatically. 170 |
  2. 171 |
  3. Converts {@link ca.weblite.objc.Proxy Proxy} objects into {@code Pointer} objects for arguments 172 | that are expecting a Pointer or an Objective-C object. 173 |
  4. 174 |
  5. 175 | Automatically determines the correct variant of {@code objc_msgSend*} depending on the return type of 176 | the message (as specified in the Objective-C message signature). 177 |
  6. 178 |
  7. 179 | Converts return {@code NSString} return values to Java {@code String} objects. 180 |
  8. 181 |
  9. 182 | Wraps {@code NSObject} return values in {@link ca.weblite.objc.Proxy Proxy} objects so that 183 | they can be used more easily. It performs this conversion in a smart way using the {@link ca.weblite.objc.Proxy#load Proxy.load()} 184 | method to make sure that there is ever only one instance of a Java proxy for each instance of an Objective-C 185 | object. I.e. If a {@code Proxy} object already exists for a particular Objective-C object, then this method 186 | will look up the existing {@code Proxy} object and return that. Otherwise it creates a new {@code Proxy} object 187 | to wrap the return value and registers this proxy with the central proxy registry. 188 |
  10. 189 |
  11. Messages that return Structures will return a subclass of 190 | {@code com.sun.jna.Structure} 191 |
  12. 192 |
193 | 194 |

The following is some sample code taken from one of the unit tests to demonstrate how this variant of {@code msg()} can 195 | be used to enable automatic type mapping in the return values of Objective-c messages.

196 | 197 | 198 | 199 |

High-Level API (Object Oriented API)

200 | 201 |

Now that you see the foundation upon which the Java/Objective-C bridge sits, we can jump straight into the 202 | higher-level object oriented API. This API consists of three main classes that you'll wish to use.:

203 |
    204 |
  1. {@link ca.weblite.objc.Client Client} - An object that allows you to send messages to the Objective-C runtime. You'll generally 205 | use this to instantiate new Objective-C objects and obtain {@link ca.weblite.objc.Proxy Proxies} to them. 206 |
  2. 207 | 208 |
  3. {@link ca.weblite.objc.Proxy Proxy} - A wrapper around an Objective-C object that enables you to easily send messages 209 | to the native object. It includes convenience methods for sending messages that return various types. E.g. the {@link ca.weblite.objc.Proxy#sendInt sentInt()} 210 | method sends a message to which you expect an {@code int} to be returned. It returns a Java {@code int} so that 211 | you don't need to perform any ugly type casting. 212 |
  4. 213 | 214 |
  5. {@link ca.weblite.objc.NSObject NSObject} - A Java wrapper around an Objective-C object that enables two-way communication. Objective-C 215 | can call methods on this Java class, and you are able to send messages to the object's native Peer from Java. This is a subclass 216 | of {@link ca.weblite.objc.Proxy Proxy} so that it includes all of the same capabilities for sending messages. Typically you would subclass 217 | {@link ca.weblite.objc.NSObject NSObject} if you need to create a delegate for an objective-C object. You can define Java methods 218 | in your subclass that can be used in Objective-C via the {@link ca.weblite.objc.annotations.Msg Msg} annotation. 219 |
  6. 220 |
221 | 222 |

The following snippet is taken from a unit test. It shows the use of the {@link ca.weblite.objc.Client Client} class and {@link ca.weblite.objc.Proxy} 223 | class to create an NSMutable array and add some strings to it.

224 | 225 | 226 | 227 |

Writing a Delegate Class In Java

228 | 229 |

The {@link ca.weblite.objc.NSObject NSObject} class can be extended to define your own Java classes that can be called from Objective-C. 230 | You specify which methods can handle Objective-C messages using the {@link ca.weblite.objc.annotations.Msg Msg} annotation. There 231 | is an example at the top of the page that uses this method to add a delegate to the {@code NSOpenPanel} dialog. 232 |

233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /src/main/objectivec/implementation/JavaUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // JavaUtil.h 3 | // libjcocoa 4 | // 5 | // Created by Matthias Fuchs on 04.06.19. 6 | // Copyright © 2019 Web Lite Solutions. All rights reserved. 7 | // 8 | 9 | #ifndef JavaUtil_h 10 | #define JavaUtil_h 11 | #import 12 | 13 | @interface JavaUtil : NSObject 14 | 15 | +(void) throwJavaException:(JNIEnv *)env withMessage: (const char *)msg; 16 | 17 | @end 18 | 19 | #endif /* JavaUtil_h */ 20 | -------------------------------------------------------------------------------- /src/main/objectivec/implementation/JavaUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // JavaUtil.m 3 | // libjcocoa 4 | // 5 | // Created by Matthias Fuchs on 04.06.19. 6 | // Copyright © 2019 Web Lite Solutions. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JavaUtil.h" 11 | 12 | @implementation JavaUtil 13 | 14 | +(void) throwJavaException:(JNIEnv *)env withMessage: (const char *)msg 15 | { 16 | // You can put your own exception here 17 | jclass c = (*env)->FindClass(env, "java/lang/RuntimeException"); 18 | 19 | (*env)->ThrowNew(env, c, msg); 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /src/main/objectivec/implementation/WLJavaProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // WLJavaProxy.h 3 | // libjcocoa 4 | // 5 | // Created by Steve Hannah on 2012-10-21. 6 | // Copyright (c) 2012 Web Lite Solutions. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface WLJavaProxy : NSObject { 13 | jobject peer; 14 | jclass peerClass; 15 | jmethodID jMethodSignatureForSelector; 16 | jmethodID jForwardInvocation; 17 | jmethodID jRespondsToSelector; 18 | 19 | } 20 | +(void)setJVM:(JavaVM*)theJvm; 21 | -(WLJavaProxy*)init:(jobject)peer; 22 | -(NSMethodSignature*)methodSignatureForSelector:(SEL)sel; 23 | -(void)forwardInvocation:(NSInvocation *)invocation; 24 | -(BOOL)respondsToSelector:(SEL)aSelector; 25 | -(jobject)javaPeer; 26 | 27 | 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /src/main/objectivec/implementation/WLJavaProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // WLJavaProxy.m 3 | // libjcocoa 4 | // 5 | // Created by Steve Hannah on 2012-10-21. 6 | // Copyright (c) 2012 Web Lite Solutions. All rights reserved. 7 | // 8 | 9 | #import "WLJavaProxy.h" 10 | #include "JavaUtil.h" 11 | 12 | static JavaVM *jvm = NULL; 13 | 14 | unsigned long acceptNSRange(NSRange range) { 15 | return range.length + range.location; 16 | } 17 | 18 | @implementation WLJavaProxy 19 | 20 | -(WLJavaProxy*)init:(jobject)thePeer 21 | { 22 | JNIEnv *env=0; 23 | 24 | @try { 25 | int attach = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); 26 | if ( attach == 0 ){ 27 | //JNF_COCOA_ENTER(env); 28 | (*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_6); 29 | peer = (*env)->NewGlobalRef(env,thePeer); 30 | jclass cls = (*env)->GetObjectClass(env, peer); 31 | peerClass = (*env)->NewGlobalRef(env, cls); 32 | (*env)->DeleteLocalRef(env, cls); 33 | 34 | jMethodSignatureForSelector = (*env)->GetMethodID(env, peerClass, "methodSignatureForSelector", "(J)J" ); 35 | jForwardInvocation = (*env)->GetMethodID(env, peerClass, "forwardInvocation", "(J)V" ); 36 | jRespondsToSelector = (*env)->GetMethodID(env, peerClass, "respondsToSelector", "(J)Z" ); 37 | //JNF_COCOA_EXIT(env); 38 | } 39 | //(*jvm)->DetachCurrentThread(jvm); 40 | 41 | return self; 42 | } @catch (NSException *e) { 43 | NSLog(@"Exception: %@", e); 44 | [JavaUtil throwJavaException: env withMessage: [[e reason] UTF8String] ]; 45 | } 46 | } 47 | 48 | -(void)dealloc 49 | { 50 | JNIEnv *env=0; 51 | 52 | @try { 53 | int attach = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); 54 | 55 | if ( attach == 0 ){ 56 | //JNF_COCOA_ENTER(env); 57 | (*env)->DeleteGlobalRef(env, peerClass); 58 | (*env)->DeleteGlobalRef(env, peer); 59 | [super dealloc]; 60 | //JNF_COCOA_EXIT(env); 61 | } 62 | } @catch (NSException *e) { 63 | NSLog(@"Exception: %@", e); 64 | [JavaUtil throwJavaException: env withMessage: [[e reason] UTF8String] ]; 65 | } 66 | } 67 | 68 | +(void)setJVM:(JavaVM*)theJvm 69 | { 70 | JNIEnv *env=0; 71 | 72 | @try { 73 | int attach = (*theJvm)->AttachCurrentThread(theJvm, (void**)&env, NULL); 74 | if ( attach == 0 ){ 75 | jvm = theJvm; 76 | } 77 | } @catch (NSException *e) { 78 | NSLog(@"Exception: %@", e); 79 | [JavaUtil throwJavaException: env withMessage: [[e reason] UTF8String] ]; 80 | } 81 | } 82 | 83 | -(jobject)javaPeer 84 | { 85 | JNIEnv *env=0; 86 | 87 | @try { 88 | int attach = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); 89 | if ( attach == 0 ){ 90 | return peer; 91 | } 92 | return NULL; 93 | } @catch (NSException *e) { 94 | NSLog(@"Exception: %@", e); 95 | [JavaUtil throwJavaException: env withMessage: [[e reason] UTF8String] ]; 96 | } 97 | } 98 | 99 | -(NSMethodSignature*)methodSignatureForSelector:(SEL)sel 100 | { 101 | NSMethodSignature* signature; 102 | JNIEnv *env=0; 103 | 104 | @try { 105 | int attach = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); 106 | if ( attach == 0 ){ 107 | //JNF_COCOA_ENTER(env); 108 | signature = (NSMethodSignature*)(*env)->CallLongMethod(env, peer, jMethodSignatureForSelector, sel); 109 | 110 | if (!signature) { 111 | signature = [@"" methodSignatureForSelector:sel]; 112 | } 113 | 114 | //JNF_COCOA_EXIT(env); 115 | } 116 | //(*jvm)->DetachCurrentThread(jvm); 117 | return signature; 118 | } @catch (NSException *e) { 119 | NSLog(@"Exception: %@", e); 120 | [JavaUtil throwJavaException: env withMessage: [[e reason] UTF8String] ]; 121 | } 122 | 123 | // return [NSMethodSignature methodSignatureForSelector:sel]; 124 | } 125 | 126 | -(void)forwardInvocation:(NSInvocation *)invocation 127 | { 128 | NSLog(@"forwardInvocation: %@", invocation); 129 | 130 | JNIEnv *env=0; 131 | SEL aSelector = [invocation selector]; 132 | int attach = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); 133 | 134 | @try { 135 | if ( attach == 0 ) { 136 | NSLog(@"Before CallBooleanMethod"); 137 | 138 | //JNF_COCOA_ENTER(env); 139 | (*env)->CallVoidMethod(env, peer, jForwardInvocation, invocation); 140 | //JNF_COCOA_EXIT(env); 141 | 142 | NSLog(@"After CallBooleanMethod"); 143 | } 144 | //(*jvm)->DetachCurrentThread(jvm); 145 | } @catch (NSException *e) { 146 | NSString *message = [NSString stringWithFormat:@"Selector '%@' caused exception: %@", NSStringFromSelector(aSelector), e.reason]; 147 | NSLog(@"%@", message); 148 | [JavaUtil throwJavaException: env withMessage: [message UTF8String] ]; 149 | } 150 | } 151 | 152 | + (BOOL)instancesRespondToSelector:(SEL)aSelector { 153 | return YES; 154 | } 155 | 156 | -(BOOL)respondsToSelector:(SEL)aSelector 157 | { 158 | BOOL response = FALSE; 159 | JNIEnv *env=0; 160 | 161 | @try { 162 | int attach = (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); 163 | if ( attach == 0 ){ 164 | //JNF_COCOA_ENTER(env); 165 | response = (*env)->CallBooleanMethod(env, peer, jRespondsToSelector, aSelector); 166 | //JNF_COCOA_EXIT(env); 167 | } 168 | //(*jvm)->DetachCurrentThread(jvm); 169 | return response==1?TRUE:FALSE; 170 | } @catch (NSException *e) { 171 | NSLog(@"Exception: %@", e); 172 | [JavaUtil throwJavaException: env withMessage: [[e reason] UTF8String] ]; 173 | } 174 | 175 | // return YES; 176 | } 177 | 178 | - (id)valueForKey:(NSString *)key { 179 | return [self forwardInvocationForSelector: _cmd withTarget:self withArguments: [NSArray arrayWithObject: key]]; 180 | } 181 | 182 | - (id)valueForUndefinedKey:(NSString *)key { 183 | return [self forwardInvocationForSelector: _cmd withTarget:self withArguments: [NSArray arrayWithObject: key]]; 184 | } 185 | 186 | - (void)setValue:(id)value forKey:(NSString *)key { 187 | [self forwardInvocationForSelector: _cmd withTarget:self withArguments: [NSArray arrayWithObjects: value, key, nil]]; 188 | } 189 | 190 | - (void)setValue:(id)value forKeyPath:(NSString *)keyPath { 191 | [self forwardInvocationForSelector: _cmd withTarget:self withArguments: [NSArray arrayWithObjects: value, keyPath, nil]]; 192 | } 193 | 194 | - (void)setValue:(id)value forUndefinedKey:(NSString *)key { 195 | [self forwardInvocationForSelector: _cmd withTarget:self withArguments: [NSArray arrayWithObjects: value, key, nil]]; 196 | } 197 | 198 | + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { 199 | NSLog(@"Calling keyPathsForValuesAffectingValueForKey: %@", key); 200 | return [super keyPathsForValuesAffectingValueForKey: key]; 201 | } 202 | 203 | - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context { 204 | 205 | [self forwardInvocationForSelector: _cmd withTarget:self withArguments: [NSArray arrayWithObjects: observer, keyPath, options, context, nil]]; 206 | } 207 | 208 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 209 | { 210 | [self forwardInvocationForSelector: _cmd withTarget:self withArguments: [NSArray arrayWithObjects: keyPath, object, change, context, nil]]; 211 | } 212 | 213 | - (void)doesNotRecognizeSelector:(SEL)aSelector { 214 | NSString* message = [NSString stringWithFormat:@"Unrecognized selector called: %@", NSStringFromSelector(aSelector)]; 215 | NSLog(@"%@", message); 216 | JNIEnv *env=0; 217 | (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); 218 | [JavaUtil throwJavaException: env withMessage: [message UTF8String] ]; 219 | } 220 | 221 | 222 | /** 223 | Forwards an unhandled call to the java proxy. 224 | */ 225 | - (id)forwardInvocationForSelector: (SEL)aSelector withTarget: (id _Nullable)aTarget withArguments: (NSArray*)args { 226 | @try { 227 | NSString* sel = NSStringFromSelector(aSelector); 228 | NSLog(@"Forwarding selector: %@, %@", sel, args); 229 | 230 | NSMethodSignature *aSignature = [self methodSignatureForSelector:aSelector]; 231 | NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature:aSignature]; 232 | 233 | [anInvocation setTarget:aTarget]; 234 | [anInvocation setSelector:aSelector]; 235 | 236 | NSLog(@"Settings arguments for selector: %@", sel); 237 | int i = 2; 238 | for (__unsafe_unretained id arg in args) { 239 | NSLog(@"Settings argument %d to: %@", i, arg); 240 | [anInvocation setArgument:&arg atIndex:i]; 241 | i++; 242 | } 243 | 244 | [anInvocation retainArguments]; 245 | 246 | NSLog(@"Forwarding selector to java object: %@", sel); 247 | [self forwardInvocation:anInvocation]; 248 | 249 | NSLog(@"Getting return value for selector: %@", sel); 250 | NSUInteger length = [[anInvocation methodSignature] methodReturnLength]; 251 | 252 | if (length > 0 ) { 253 | void *result = (void *) malloc(length); 254 | [anInvocation getReturnValue:&result]; 255 | return result; 256 | } else { 257 | return nil; 258 | } 259 | } @catch(NSException *e) { 260 | NSLog(@"Exception: %@", e); 261 | 262 | JNIEnv *env=0; 263 | (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); 264 | [JavaUtil throwJavaException: env withMessage: [[e reason] UTF8String] ]; 265 | } 266 | 267 | return nil; 268 | } 269 | 270 | @end 271 | -------------------------------------------------------------------------------- /src/main/objectivec/implementation/jnaexample_NativeClient_Setup.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class jnaexample_NativeClient_Setup */ 4 | 5 | #ifndef _Included_jnaexample_NativeClient_Setup 6 | #define _Included_jnaexample_NativeClient_Setup 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: jnaexample_NativeClient_Setup 12 | * Method: init 13 | * Signature: ()V 14 | */ 15 | JNIEXPORT void JNICALL Java_ca_weblite_objc_RuntimeUtils_init 16 | (JNIEnv *, jclass); 17 | 18 | /* 19 | * Class: jnaexample_NativeClient_Setup 20 | * Method: createProxy 21 | * Signature: (Ljnaexample/NativeClient;)J 22 | */ 23 | JNIEXPORT jlong JNICALL Java_ca_weblite_objc_RuntimeUtils_createProxy 24 | (JNIEnv *, jclass, jobject); 25 | 26 | /* 27 | * Class: jnaexample_NativeClient_Setup 28 | * Method: getJavaPeer 29 | * Signature: (Ljnaexample/NativeClient;)J 30 | */ 31 | JNIEXPORT jobject JNICALL Java_ca_weblite_objc_RuntimeUtils_getJavaPeer 32 | (JNIEnv *, jclass, jlong); 33 | 34 | /* 35 | JNIEXPORT jobject JNICALL Java_ca_weblite_objc_RuntimeUtils_invokeWithSelfAndTarget 36 | (JNIEnv *, jclass, jlong, jlong, jlong); 37 | */ 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | #endif 42 | -------------------------------------------------------------------------------- /src/main/objectivec/implementation/jnaexample_NativeClient_Setup.m: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | #include 4 | #include "JavaUtil.h" 5 | 6 | 7 | void exceptionHandler(NSException *exception) 8 | { 9 | NSLog(@"%@",[exception name]); 10 | NSLog(@"%@",[exception reason]); 11 | NSLog(@"%@",[exception userInfo]); 12 | NSLog(@"%@",[exception callStackSymbols]); 13 | NSLog(@"%@",[exception callStackReturnAddresses]); 14 | } 15 | 16 | JNIEXPORT void JNICALL Java_ca_weblite_objc_RuntimeUtils_init 17 | (JNIEnv *env, jclass cls) 18 | { 19 | // enable better error logging? 20 | NSSetUncaughtExceptionHandler(&exceptionHandler); 21 | 22 | @try { 23 | JavaVM *jvm; 24 | (*env)->GetJavaVM(env, &jvm); 25 | [WLJavaProxy setJVM:jvm]; 26 | } @catch (NSException *e) { 27 | [JavaUtil throwJavaException: env withMessage: [[e reason] UTF8String] ]; 28 | NSLog(@"Exception: %@", e); 29 | } 30 | } 31 | 32 | #ifndef ptr_to_jlong 33 | #define ptr_to_jlong(a) ((jlong)(uintptr_t)(a)) 34 | #endif 35 | 36 | /* 37 | * Class: jnaexample_NativeClient_Setup 38 | * Method: createProxy 39 | * Signature: (Ljnaexample/NativeClient;)J 40 | */ 41 | JNIEXPORT jlong JNICALL Java_ca_weblite_objc_RuntimeUtils_createProxy 42 | (JNIEnv *env, jclass jcls, jobject jclient) 43 | { 44 | @try { 45 | return ptr_to_jlong([[WLJavaProxy alloc] init:jclient]); 46 | } @catch (NSException *e) { 47 | [JavaUtil throwJavaException: env withMessage: [[e reason] UTF8String] ]; 48 | NSLog(@"Exception: %@", e); 49 | } 50 | } 51 | 52 | /* 53 | * Class: jnaexample_NativeClient_Setup 54 | * Method: getJavaPeer 55 | * Signature: (Ljnaexample/NativeClient;)J 56 | */ 57 | JNIEXPORT jobject JNICALL Java_ca_weblite_objc_RuntimeUtils_getJavaPeer 58 | (JNIEnv *env, jclass jcls, jlong nsObject) 59 | { 60 | @try { 61 | NSObject* proxy = (NSObject*)nsObject; 62 | if ( [proxy respondsToSelector:@selector(javaPeer)] ){ 63 | WLJavaProxy* proxy2 = (WLJavaProxy*)proxy; 64 | return [proxy2 javaPeer]; 65 | } else { 66 | return NULL; 67 | } 68 | } @catch (NSException *e) { 69 | [JavaUtil throwJavaException: env withMessage: [[e reason] UTF8String] ]; 70 | NSLog(@"Exception: %@", e); 71 | } 72 | } 73 | 74 | /* 75 | JNIEXPORT jobject JNICALL Java_ca_weblite_objc_RuntimeUtils_invokeWithSelfAndTarget 76 | (JNIEnv *env, jclass jcls, jlong selfPtr, jlong target, jlong invocation) 77 | { 78 | id selfO = (id)selfPtr; 79 | id targetO = (id)target; 80 | NSInvocation* inv = (NSInvocation*)invocation; 81 | NSMethodSignature *sig = [inv methodSignature]; 82 | NSUInteger numArgs = [sig numberOfArguments]; 83 | const char* returnType = [sig methodReturnType]; 84 | 85 | // OK If anyone knows how to call a function with a variable number of 86 | // arguments, Please correct this, but I don't know how!! So I'm just 87 | // going to use a Switch statement and cover most of the reasonable 88 | // cases 89 | 90 | IMP impl = [targetO methodForSelector:[inv selector]]; 91 | impl(selfO, [inv selector], [inv ]) 92 | 93 | } 94 | */ 95 | -------------------------------------------------------------------------------- /src/main/objectivec/implementation/jni_md.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1996, 2013, 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 | #ifndef _JAVASOFT_JNI_MD_H_ 27 | #define _JAVASOFT_JNI_MD_H_ 28 | 29 | #ifndef __has_attribute 30 | #define __has_attribute(x) 0 31 | #endif 32 | #if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) 33 | #ifdef ARM 34 | #define JNIEXPORT __attribute__((externally_visible,visibility("default"))) 35 | #define JNIIMPORT __attribute__((externally_visible,visibility("default"))) 36 | #else 37 | #define JNIEXPORT __attribute__((visibility("default"))) 38 | #define JNIIMPORT __attribute__((visibility("default"))) 39 | #endif 40 | #else 41 | #define JNIEXPORT 42 | #define JNIIMPORT 43 | #endif 44 | 45 | #define JNICALL 46 | 47 | typedef int jint; 48 | #ifdef _LP64 49 | typedef long jlong; 50 | #else 51 | typedef long long jlong; 52 | #endif 53 | 54 | typedef signed char jbyte; 55 | 56 | #endif /* !_JAVASOFT_JNI_MD_H_ */ 57 | -------------------------------------------------------------------------------- /src/main/objectivec/implementation/libjcocoa.h: -------------------------------------------------------------------------------- 1 | // 2 | // libjcocoa.h 3 | // libjcocoa 4 | // 5 | // Created by Steve Hannah on 2012-10-21. 6 | // Copyright (c) 2012 Web Lite Solutions. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface libjcocoa : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /src/main/objectivec/implementation/libjcocoa.m: -------------------------------------------------------------------------------- 1 | // 2 | // libjcocoa.m 3 | // libjcocoa 4 | // 5 | // Created by Steve Hannah on 2012-10-21. 6 | // Copyright (c) 2012 Web Lite Solutions. All rights reserved. 7 | // 8 | 9 | #import "libjcocoa.h" 10 | 11 | @implementation libjcocoa 12 | 13 | @end 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/objectivec/lib/jawt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2001, 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 | #ifndef _JAVASOFT_JAWT_H_ 27 | #define _JAVASOFT_JAWT_H_ 28 | 29 | #include "jni.h" 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | /* 36 | * AWT native interface (new in JDK 1.3) 37 | * 38 | * The AWT native interface allows a native C or C++ application a means 39 | * by which to access native structures in AWT. This is to facilitate moving 40 | * legacy C and C++ applications to Java and to target the needs of the 41 | * community who, at present, wish to do their own native rendering to canvases 42 | * for performance reasons. Standard extensions such as Java3D also require a 43 | * means to access the underlying native data structures of AWT. 44 | * 45 | * There may be future extensions to this API depending on demand. 46 | * 47 | * A VM does not have to implement this API in order to pass the JCK. 48 | * It is recommended, however, that this API is implemented on VMs that support 49 | * standard extensions, such as Java3D. 50 | * 51 | * Since this is a native API, any program which uses it cannot be considered 52 | * 100% pure java. 53 | */ 54 | 55 | /* 56 | * AWT Native Drawing Surface (JAWT_DrawingSurface). 57 | * 58 | * For each platform, there is a native drawing surface structure. This 59 | * platform-specific structure can be found in jawt_md.h. It is recommended 60 | * that additional platforms follow the same model. It is also recommended 61 | * that VMs on Win32 and Solaris support the existing structures in jawt_md.h. 62 | * 63 | ******************* 64 | * EXAMPLE OF USAGE: 65 | ******************* 66 | * 67 | * In Win32, a programmer wishes to access the HWND of a canvas to perform 68 | * native rendering into it. The programmer has declared the paint() method 69 | * for their canvas subclass to be native: 70 | * 71 | * 72 | * MyCanvas.java: 73 | * 74 | * import java.awt.*; 75 | * 76 | * public class MyCanvas extends Canvas { 77 | * 78 | * static { 79 | * System.loadLibrary("mylib"); 80 | * } 81 | * 82 | * public native void paint(Graphics g); 83 | * } 84 | * 85 | * 86 | * myfile.c: 87 | * 88 | * #include "jawt_md.h" 89 | * #include 90 | * 91 | * JNIEXPORT void JNICALL 92 | * Java_MyCanvas_paint(JNIEnv* env, jobject canvas, jobject graphics) 93 | * { 94 | * JAWT awt; 95 | * JAWT_DrawingSurface* ds; 96 | * JAWT_DrawingSurfaceInfo* dsi; 97 | * JAWT_Win32DrawingSurfaceInfo* dsi_win; 98 | * jboolean result; 99 | * jint lock; 100 | * 101 | * // Get the AWT 102 | * awt.version = JAWT_VERSION_1_3; 103 | * result = JAWT_GetAWT(env, &awt); 104 | * assert(result != JNI_FALSE); 105 | * 106 | * // Get the drawing surface 107 | * ds = awt.GetDrawingSurface(env, canvas); 108 | * assert(ds != NULL); 109 | * 110 | * // Lock the drawing surface 111 | * lock = ds->Lock(ds); 112 | * assert((lock & JAWT_LOCK_ERROR) == 0); 113 | * 114 | * // Get the drawing surface info 115 | * dsi = ds->GetDrawingSurfaceInfo(ds); 116 | * 117 | * // Get the platform-specific drawing info 118 | * dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo; 119 | * 120 | * ////////////////////////////// 121 | * // !!! DO PAINTING HERE !!! // 122 | * ////////////////////////////// 123 | * 124 | * // Free the drawing surface info 125 | * ds->FreeDrawingSurfaceInfo(dsi); 126 | * 127 | * // Unlock the drawing surface 128 | * ds->Unlock(ds); 129 | * 130 | * // Free the drawing surface 131 | * awt.FreeDrawingSurface(ds); 132 | * } 133 | * 134 | */ 135 | 136 | /* 137 | * JAWT_Rectangle 138 | * Structure for a native rectangle. 139 | */ 140 | typedef struct jawt_Rectangle { 141 | jint x; 142 | jint y; 143 | jint width; 144 | jint height; 145 | } JAWT_Rectangle; 146 | 147 | struct jawt_DrawingSurface; 148 | 149 | /* 150 | * JAWT_DrawingSurfaceInfo 151 | * Structure for containing the underlying drawing information of a component. 152 | */ 153 | typedef struct jawt_DrawingSurfaceInfo { 154 | /* 155 | * Pointer to the platform-specific information. This can be safely 156 | * cast to a JAWT_Win32DrawingSurfaceInfo on Windows or a 157 | * JAWT_X11DrawingSurfaceInfo on Solaris. See jawt_md.h for details. 158 | */ 159 | void* platformInfo; 160 | /* Cached pointer to the underlying drawing surface */ 161 | struct jawt_DrawingSurface* ds; 162 | /* Bounding rectangle of the drawing surface */ 163 | JAWT_Rectangle bounds; 164 | /* Number of rectangles in the clip */ 165 | jint clipSize; 166 | /* Clip rectangle array */ 167 | JAWT_Rectangle* clip; 168 | } JAWT_DrawingSurfaceInfo; 169 | 170 | #define JAWT_LOCK_ERROR 0x00000001 171 | #define JAWT_LOCK_CLIP_CHANGED 0x00000002 172 | #define JAWT_LOCK_BOUNDS_CHANGED 0x00000004 173 | #define JAWT_LOCK_SURFACE_CHANGED 0x00000008 174 | 175 | /* 176 | * JAWT_DrawingSurface 177 | * Structure for containing the underlying drawing information of a component. 178 | * All operations on a JAWT_DrawingSurface MUST be performed from the same 179 | * thread as the call to GetDrawingSurface. 180 | */ 181 | typedef struct jawt_DrawingSurface { 182 | /* 183 | * Cached reference to the Java environment of the calling thread. 184 | * If Lock(), Unlock(), GetDrawingSurfaceInfo() or 185 | * FreeDrawingSurfaceInfo() are called from a different thread, 186 | * this data member should be set before calling those functions. 187 | */ 188 | JNIEnv* env; 189 | /* Cached reference to the target object */ 190 | jobject target; 191 | /* 192 | * Lock the surface of the target component for native rendering. 193 | * When finished drawing, the surface must be unlocked with 194 | * Unlock(). This function returns a bitmask with one or more of the 195 | * following values: 196 | * 197 | * JAWT_LOCK_ERROR - When an error has occurred and the surface could not 198 | * be locked. 199 | * 200 | * JAWT_LOCK_CLIP_CHANGED - When the clip region has changed. 201 | * 202 | * JAWT_LOCK_BOUNDS_CHANGED - When the bounds of the surface have changed. 203 | * 204 | * JAWT_LOCK_SURFACE_CHANGED - When the surface itself has changed 205 | */ 206 | jint (JNICALL *Lock) 207 | (struct jawt_DrawingSurface* ds); 208 | /* 209 | * Get the drawing surface info. 210 | * The value returned may be cached, but the values may change if 211 | * additional calls to Lock() or Unlock() are made. 212 | * Lock() must be called before this can return a valid value. 213 | * Returns NULL if an error has occurred. 214 | * When finished with the returned value, FreeDrawingSurfaceInfo must be 215 | * called. 216 | */ 217 | JAWT_DrawingSurfaceInfo* (JNICALL *GetDrawingSurfaceInfo) 218 | (struct jawt_DrawingSurface* ds); 219 | /* 220 | * Free the drawing surface info. 221 | */ 222 | void (JNICALL *FreeDrawingSurfaceInfo) 223 | (JAWT_DrawingSurfaceInfo* dsi); 224 | /* 225 | * Unlock the drawing surface of the target component for native rendering. 226 | */ 227 | void (JNICALL *Unlock) 228 | (struct jawt_DrawingSurface* ds); 229 | } JAWT_DrawingSurface; 230 | 231 | /* 232 | * JAWT 233 | * Structure for containing native AWT functions. 234 | */ 235 | typedef struct jawt { 236 | /* 237 | * Version of this structure. This must always be set before 238 | * calling JAWT_GetAWT() 239 | */ 240 | jint version; 241 | /* 242 | * Return a drawing surface from a target jobject. This value 243 | * may be cached. 244 | * Returns NULL if an error has occurred. 245 | * Target must be a java.awt.Component (should be a Canvas 246 | * or Window for native rendering). 247 | * FreeDrawingSurface() must be called when finished with the 248 | * returned JAWT_DrawingSurface. 249 | */ 250 | JAWT_DrawingSurface* (JNICALL *GetDrawingSurface) 251 | (JNIEnv* env, jobject target); 252 | /* 253 | * Free the drawing surface allocated in GetDrawingSurface. 254 | */ 255 | void (JNICALL *FreeDrawingSurface) 256 | (JAWT_DrawingSurface* ds); 257 | /* 258 | * Since 1.4 259 | * Locks the entire AWT for synchronization purposes 260 | */ 261 | void (JNICALL *Lock)(JNIEnv* env); 262 | /* 263 | * Since 1.4 264 | * Unlocks the entire AWT for synchronization purposes 265 | */ 266 | void (JNICALL *Unlock)(JNIEnv* env); 267 | /* 268 | * Since 1.4 269 | * Returns a reference to a java.awt.Component from a native 270 | * platform handle. On Windows, this corresponds to an HWND; 271 | * on Solaris and Linux, this is a Drawable. For other platforms, 272 | * see the appropriate machine-dependent header file for a description. 273 | * The reference returned by this function is a local 274 | * reference that is only valid in this environment. 275 | * This function returns a NULL reference if no component could be 276 | * found with matching platform information. 277 | */ 278 | jobject (JNICALL *GetComponent)(JNIEnv* env, void* platformInfo); 279 | 280 | } JAWT; 281 | 282 | /* 283 | * Get the AWT native structure. This function returns JNI_FALSE if 284 | * an error occurs. 285 | */ 286 | _JNI_IMPORT_OR_EXPORT_ 287 | jboolean JNICALL JAWT_GetAWT(JNIEnv* env, JAWT* awt); 288 | 289 | #define JAWT_VERSION_1_3 0x00010003 290 | #define JAWT_VERSION_1_4 0x00010004 291 | #define JAWT_VERSION_1_7 0x00010007 292 | 293 | #ifdef __cplusplus 294 | } /* extern "C" */ 295 | #endif 296 | 297 | #endif /* !_JAVASOFT_JAWT_H_ */ 298 | -------------------------------------------------------------------------------- /src/main/objectivec/lib/jawt_md.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999, 2001, 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 | #ifndef _JAVASOFT_JAWT_MD_H_ 27 | #define _JAVASOFT_JAWT_MD_H_ 28 | 29 | #include 30 | #include 31 | #include 32 | #include "jawt.h" 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | /* 39 | * X11-specific declarations for AWT native interface. 40 | * See notes in jawt.h for an example of use. 41 | */ 42 | typedef struct jawt_X11DrawingSurfaceInfo { 43 | Drawable drawable; 44 | Display* display; 45 | VisualID visualID; 46 | Colormap colormapID; 47 | int depth; 48 | /* 49 | * Since 1.4 50 | * Returns a pixel value from a set of RGB values. 51 | * This is useful for paletted color (256 color) modes. 52 | */ 53 | int (JNICALL *GetAWTColor)(JAWT_DrawingSurface* ds, 54 | int r, int g, int b); 55 | } JAWT_X11DrawingSurfaceInfo; 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | 61 | #endif /* !_JAVASOFT_JAWT_MD_H_ */ 62 | -------------------------------------------------------------------------------- /src/main/objectivec/lib/jdwpTransport.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003, 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 | * Java Debug Wire Protocol Transport Service Provider Interface. 28 | */ 29 | 30 | #ifndef JDWPTRANSPORT_H 31 | #define JDWPTRANSPORT_H 32 | 33 | #include "jni.h" 34 | 35 | enum { 36 | JDWPTRANSPORT_VERSION_1_0 = 0x00010000 37 | }; 38 | 39 | #ifdef __cplusplus 40 | extern "C" { 41 | #endif 42 | 43 | struct jdwpTransportNativeInterface_; 44 | 45 | struct _jdwpTransportEnv; 46 | 47 | #ifdef __cplusplus 48 | typedef _jdwpTransportEnv jdwpTransportEnv; 49 | #else 50 | typedef const struct jdwpTransportNativeInterface_ *jdwpTransportEnv; 51 | #endif /* __cplusplus */ 52 | 53 | /* 54 | * Errors. Universal errors with JVMTI/JVMDI equivalents keep the 55 | * values the same. 56 | */ 57 | typedef enum { 58 | JDWPTRANSPORT_ERROR_NONE = 0, 59 | JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT = 103, 60 | JDWPTRANSPORT_ERROR_OUT_OF_MEMORY = 110, 61 | JDWPTRANSPORT_ERROR_INTERNAL = 113, 62 | JDWPTRANSPORT_ERROR_ILLEGAL_STATE = 201, 63 | JDWPTRANSPORT_ERROR_IO_ERROR = 202, 64 | JDWPTRANSPORT_ERROR_TIMEOUT = 203, 65 | JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE = 204 66 | } jdwpTransportError; 67 | 68 | 69 | /* 70 | * Structure to define capabilities 71 | */ 72 | typedef struct { 73 | unsigned int can_timeout_attach :1; 74 | unsigned int can_timeout_accept :1; 75 | unsigned int can_timeout_handshake :1; 76 | unsigned int reserved3 :1; 77 | unsigned int reserved4 :1; 78 | unsigned int reserved5 :1; 79 | unsigned int reserved6 :1; 80 | unsigned int reserved7 :1; 81 | unsigned int reserved8 :1; 82 | unsigned int reserved9 :1; 83 | unsigned int reserved10 :1; 84 | unsigned int reserved11 :1; 85 | unsigned int reserved12 :1; 86 | unsigned int reserved13 :1; 87 | unsigned int reserved14 :1; 88 | unsigned int reserved15 :1; 89 | } JDWPTransportCapabilities; 90 | 91 | 92 | /* 93 | * Structures to define packet layout. 94 | * 95 | * See: http://java.sun.com/j2se/1.5/docs/guide/jpda/jdwp-spec.html 96 | */ 97 | 98 | enum { 99 | JDWPTRANSPORT_FLAGS_NONE = 0x0, 100 | JDWPTRANSPORT_FLAGS_REPLY = 0x80 101 | }; 102 | 103 | typedef struct { 104 | jint len; 105 | jint id; 106 | jbyte flags; 107 | jbyte cmdSet; 108 | jbyte cmd; 109 | jbyte *data; 110 | } jdwpCmdPacket; 111 | 112 | typedef struct { 113 | jint len; 114 | jint id; 115 | jbyte flags; 116 | jshort errorCode; 117 | jbyte *data; 118 | } jdwpReplyPacket; 119 | 120 | typedef struct { 121 | union { 122 | jdwpCmdPacket cmd; 123 | jdwpReplyPacket reply; 124 | } type; 125 | } jdwpPacket; 126 | 127 | /* 128 | * JDWP functions called by the transport. 129 | */ 130 | typedef struct jdwpTransportCallback { 131 | void *(*alloc)(jint numBytes); /* Call this for all allocations */ 132 | void (*free)(void *buffer); /* Call this for all deallocations */ 133 | } jdwpTransportCallback; 134 | 135 | typedef jint (JNICALL *jdwpTransport_OnLoad_t)(JavaVM *jvm, 136 | jdwpTransportCallback *callback, 137 | jint version, 138 | jdwpTransportEnv** env); 139 | 140 | 141 | 142 | /* Function Interface */ 143 | 144 | struct jdwpTransportNativeInterface_ { 145 | /* 1 : RESERVED */ 146 | void *reserved1; 147 | 148 | /* 2 : Get Capabilities */ 149 | jdwpTransportError (JNICALL *GetCapabilities)(jdwpTransportEnv* env, 150 | JDWPTransportCapabilities *capabilities_ptr); 151 | 152 | /* 3 : Attach */ 153 | jdwpTransportError (JNICALL *Attach)(jdwpTransportEnv* env, 154 | const char* address, 155 | jlong attach_timeout, 156 | jlong handshake_timeout); 157 | 158 | /* 4: StartListening */ 159 | jdwpTransportError (JNICALL *StartListening)(jdwpTransportEnv* env, 160 | const char* address, 161 | char** actual_address); 162 | 163 | /* 5: StopListening */ 164 | jdwpTransportError (JNICALL *StopListening)(jdwpTransportEnv* env); 165 | 166 | /* 6: Accept */ 167 | jdwpTransportError (JNICALL *Accept)(jdwpTransportEnv* env, 168 | jlong accept_timeout, 169 | jlong handshake_timeout); 170 | 171 | /* 7: IsOpen */ 172 | jboolean (JNICALL *IsOpen)(jdwpTransportEnv* env); 173 | 174 | /* 8: Close */ 175 | jdwpTransportError (JNICALL *Close)(jdwpTransportEnv* env); 176 | 177 | /* 9: ReadPacket */ 178 | jdwpTransportError (JNICALL *ReadPacket)(jdwpTransportEnv* env, 179 | jdwpPacket *pkt); 180 | 181 | /* 10: Write Packet */ 182 | jdwpTransportError (JNICALL *WritePacket)(jdwpTransportEnv* env, 183 | const jdwpPacket* pkt); 184 | 185 | /* 11: GetLastError */ 186 | jdwpTransportError (JNICALL *GetLastError)(jdwpTransportEnv* env, 187 | char** error); 188 | 189 | }; 190 | 191 | 192 | /* 193 | * Use inlined functions so that C++ code can use syntax such as 194 | * env->Attach("mymachine:5000", 10*1000, 0); 195 | * 196 | * rather than using C's :- 197 | * 198 | * (*env)->Attach(env, "mymachine:5000", 10*1000, 0); 199 | */ 200 | struct _jdwpTransportEnv { 201 | const struct jdwpTransportNativeInterface_ *functions; 202 | #ifdef __cplusplus 203 | 204 | jdwpTransportError GetCapabilities(JDWPTransportCapabilities *capabilities_ptr) { 205 | return functions->GetCapabilities(this, capabilities_ptr); 206 | } 207 | 208 | jdwpTransportError Attach(const char* address, jlong attach_timeout, 209 | jlong handshake_timeout) { 210 | return functions->Attach(this, address, attach_timeout, handshake_timeout); 211 | } 212 | 213 | jdwpTransportError StartListening(const char* address, 214 | char** actual_address) { 215 | return functions->StartListening(this, address, actual_address); 216 | } 217 | 218 | jdwpTransportError StopListening(void) { 219 | return functions->StopListening(this); 220 | } 221 | 222 | jdwpTransportError Accept(jlong accept_timeout, jlong handshake_timeout) { 223 | return functions->Accept(this, accept_timeout, handshake_timeout); 224 | } 225 | 226 | jboolean IsOpen(void) { 227 | return functions->IsOpen(this); 228 | } 229 | 230 | jdwpTransportError Close(void) { 231 | return functions->Close(this); 232 | } 233 | 234 | jdwpTransportError ReadPacket(jdwpPacket *pkt) { 235 | return functions->ReadPacket(this, pkt); 236 | } 237 | 238 | jdwpTransportError WritePacket(const jdwpPacket* pkt) { 239 | return functions->WritePacket(this, pkt); 240 | } 241 | 242 | jdwpTransportError GetLastError(char** error) { 243 | return functions->GetLastError(this, error); 244 | } 245 | 246 | 247 | #endif /* __cplusplus */ 248 | }; 249 | 250 | #ifdef __cplusplus 251 | } /* extern "C" */ 252 | #endif /* __cplusplus */ 253 | 254 | #endif /* JDWPTRANSPORT_H */ 255 | -------------------------------------------------------------------------------- /src/main/objectivec/lib/jni_md.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1996, 2000, 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 | #ifndef _JAVASOFT_JNI_MD_H_ 27 | #define _JAVASOFT_JNI_MD_H_ 28 | 29 | #define JNIEXPORT 30 | #define JNIIMPORT 31 | #define JNICALL 32 | 33 | typedef int jint; 34 | #ifdef _LP64 /* 64-bit Solaris */ 35 | typedef long jlong; 36 | #else 37 | typedef long long jlong; 38 | #endif 39 | 40 | typedef signed char jbyte; 41 | 42 | #endif /* !_JAVASOFT_JNI_MD_H_ */ 43 | -------------------------------------------------------------------------------- /src/main/objectivec/lib/jvmticmlr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010, 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 | * This header file defines the data structures sent by the VM 28 | * through the JVMTI CompiledMethodLoad callback function via the 29 | * "void * compile_info" parameter. The memory pointed to by the 30 | * compile_info parameter may not be referenced after returning from 31 | * the CompiledMethodLoad callback. These are VM implementation 32 | * specific data structures that may evolve in future releases. A 33 | * JVMTI agent should interpret a non-NULL compile_info as a pointer 34 | * to a region of memory containing a list of records. In a typical 35 | * usage scenario, a JVMTI agent would cast each record to a 36 | * jvmtiCompiledMethodLoadRecordHeader, a struct that represents 37 | * arbitrary information. This struct contains a kind field to indicate 38 | * the kind of information being passed, and a pointer to the next 39 | * record. If the kind field indicates inlining information, then the 40 | * agent would cast the record to a jvmtiCompiledMethodLoadInlineRecord. 41 | * This record contains an array of PCStackInfo structs, which indicate 42 | * for every pc address what are the methods on the invocation stack. 43 | * The "methods" and "bcis" fields in each PCStackInfo struct specify a 44 | * 1-1 mapping between these inlined methods and their bytecode indices. 45 | * This can be used to derive the proper source lines of the inlined 46 | * methods. 47 | */ 48 | 49 | #ifndef _JVMTI_CMLR_H_ 50 | #define _JVMTI_CMLR_H_ 51 | 52 | enum { 53 | JVMTI_CMLR_MAJOR_VERSION_1 = 0x00000001, 54 | JVMTI_CMLR_MINOR_VERSION_0 = 0x00000000, 55 | 56 | JVMTI_CMLR_MAJOR_VERSION = 0x00000001, 57 | JVMTI_CMLR_MINOR_VERSION = 0x00000000 58 | 59 | /* 60 | * This comment is for the "JDK import from HotSpot" sanity check: 61 | * version: 1.0.0 62 | */ 63 | }; 64 | 65 | typedef enum { 66 | JVMTI_CMLR_DUMMY = 1, 67 | JVMTI_CMLR_INLINE_INFO = 2 68 | } jvmtiCMLRKind; 69 | 70 | /* 71 | * Record that represents arbitrary information passed through JVMTI 72 | * CompiledMethodLoadEvent void pointer. 73 | */ 74 | typedef struct _jvmtiCompiledMethodLoadRecordHeader { 75 | jvmtiCMLRKind kind; /* id for the kind of info passed in the record */ 76 | jint majorinfoversion; /* major and minor info version values. Init'ed */ 77 | jint minorinfoversion; /* to current version value in jvmtiExport.cpp. */ 78 | 79 | struct _jvmtiCompiledMethodLoadRecordHeader* next; 80 | } jvmtiCompiledMethodLoadRecordHeader; 81 | 82 | /* 83 | * Record that gives information about the methods on the compile-time 84 | * stack at a specific pc address of a compiled method. Each element in 85 | * the methods array maps to same element in the bcis array. 86 | */ 87 | typedef struct _PCStackInfo { 88 | void* pc; /* the pc address for this compiled method */ 89 | jint numstackframes; /* number of methods on the stack */ 90 | jmethodID* methods; /* array of numstackframes method ids */ 91 | jint* bcis; /* array of numstackframes bytecode indices */ 92 | } PCStackInfo; 93 | 94 | /* 95 | * Record that contains inlining information for each pc address of 96 | * an nmethod. 97 | */ 98 | typedef struct _jvmtiCompiledMethodLoadInlineRecord { 99 | jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */ 100 | jint numpcs; /* number of pc descriptors in this nmethod */ 101 | PCStackInfo* pcinfo; /* array of numpcs pc descriptors */ 102 | } jvmtiCompiledMethodLoadInlineRecord; 103 | 104 | /* 105 | * Dummy record used to test that we can pass records with different 106 | * information through the void pointer provided that they can be cast 107 | * to a jvmtiCompiledMethodLoadRecordHeader. 108 | */ 109 | 110 | typedef struct _jvmtiCompiledMethodLoadDummyRecord { 111 | jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */ 112 | char message[50]; 113 | } jvmtiCompiledMethodLoadDummyRecord; 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /src/main/objectivec/libjcocoa-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'libjcocoa' target in the 'libjcocoa' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /src/main/php/generate_mappings.php: -------------------------------------------------------------------------------- 1 | /* 2 | * WARNING: This file was automatically generated by the generate_mappings.php script. 3 | * NO DONT MODIFY THIS FILE. 4 | * 5 | * UPDATE INSTRUCTIONS: 6 | * bash generate_runtime_mappings.sh 7 | */ 8 | 'Runtime', 13 | 'args' => [] 14 | ]; 15 | //$out[] = $baseMapping; 16 | generateMappingsRecursive($out, $baseMapping, $num); 17 | return $out; 18 | } 19 | 20 | function generateMappingsRecursive(&$out, $baseMapping, $num) { 21 | if ($num === 0) return; 22 | $out[] = generateNextMapping($baseMapping, false); 23 | generateMappingsRecursive($out, $out[count($out)-1], $num-1); 24 | $out[] = generateNextMapping($baseMapping, true); 25 | generateMappingsRecursive($out, $out[count($out)-1], $num-1); 26 | } 27 | 28 | function generateNextMapping($mapping, $structParam) { 29 | $out = $mapping; 30 | $out['name'] .= ($structParam ? '1' : '0'); 31 | $out['args'][] = ($structParam ? 'Structure.ByValue' : 'Object'); 32 | 33 | return $out; 34 | } 35 | 36 | ?> 37 | package ca.weblite.objc; 38 | 39 | import com.sun.jna.Library; 40 | import com.sun.jna.Native; 41 | import com.sun.jna.Pointer; 42 | import com.sun.jna.Structure; 43 | 44 | /** 45 | * A JNA wrapper around the objective-c runtime. This contains all of the functions 46 | * needed to interact with the runtime (e.g. send messages, etc..). 47 | * 48 | *

Sample Usage

49 | * 50 | * 51 | * @author shannah 52 | * @see Objective-C Runtime Reference 53 | * @version $Id: $Id 54 | * @since 1.1 55 | */ 56 | public interface RuntimeMappings { 57 | 58 | 59 | 60 | public static interface extends Library { 61 | public static INSTANCE = () Native.loadLibrary("objc.A", .class); 62 | 63 | public long objc_msgSend(Pointer theReceiver, Pointer theSelector, 64 | 73 | arg 74 | 75 | 76 | ); 77 | public double objc_msgSend_fpret(Pointer theReceiver, Pointer theSelector, 78 | 87 | arg 88 | 89 | 90 | ); 91 | 92 | } 93 | 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/main/resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | libjli.dylib 9 | CFBundleGetInfoString 10 | OpenJDK 11.0.2+9 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 7.0 15 | CFBundleName 16 | OpenJDK 11.0.2 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 11.0.2 25 | JavaVM 26 | 27 | JVMCapabilities 28 | 29 | CommandLine 30 | 31 | JVMMinimumFrameworkVersion 32 | 13.2.9 33 | JVMMinimumSystemVersion 34 | 10.6.0 35 | JVMPlatformVersion 36 | 11.0 37 | JVMVendor 38 | Oracle Corporation 39 | JVMVersion 40 | 11.0.2 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/resources/Window.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1070 5 | 11G63 6 | 2549 7 | 1138.51 8 | 569.00 9 | 10 | com.apple.InterfaceBuilder.CocoaPlugin 11 | 2549 12 | 13 | 14 | IBNSLayoutConstraint 15 | NSCustomObject 16 | NSTextField 17 | NSTextFieldCell 18 | NSView 19 | NSWindowTemplate 20 | 21 | 22 | com.apple.InterfaceBuilder.CocoaPlugin 23 | 24 | 25 | PluginDependencyRecalculationVersion 26 | 27 | 28 | 29 | 30 | NSObject 31 | 32 | 33 | FirstResponder 34 | 35 | 36 | NSApplication 37 | 38 | 39 | 15 40 | 2 41 | {{196, 240}, {480, 270}} 42 | 544735232 43 | Window 44 | NSWindow 45 | 46 | 47 | 48 | 49 | 256 50 | 51 | 52 | 53 | 268 54 | {{168, 146}, {77, 17}} 55 | 56 | 57 | _NS:1535 58 | YES 59 | 60 | 68157504 61 | 272630784 62 | Hello World 63 | 64 | LucidaGrande 65 | 13 66 | 1044 67 | 68 | _NS:1535 69 | 70 | 71 | 6 72 | System 73 | controlColor 74 | 75 | 3 76 | MC42NjY2NjY2NjY3AA 77 | 78 | 79 | 80 | 6 81 | System 82 | controlTextColor 83 | 84 | 3 85 | MAA 86 | 87 | 88 | 89 | 90 | 91 | {480, 270} 92 | 93 | 94 | 95 | 96 | {{0, 0}, {1920, 1058}} 97 | {10000000000000, 10000000000000} 98 | YES 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 0 107 | 108 | 109 | 110 | 111 | 112 | -2 113 | 114 | 115 | File's Owner 116 | 117 | 118 | -1 119 | 120 | 121 | First Responder 122 | 123 | 124 | -3 125 | 126 | 127 | Application 128 | 129 | 130 | 1 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 2 139 | 140 | 141 | 142 | 143 | 144 | 3 145 | 0 146 | 147 | 3 148 | 1 149 | 150 | 107 151 | 152 | 1000 153 | 154 | 3 155 | 9 156 | 3 157 | 158 | 159 | 160 | 5 161 | 0 162 | 163 | 5 164 | 1 165 | 166 | 171 167 | 168 | 1000 169 | 170 | 3 171 | 9 172 | 3 173 | 174 | 175 | 176 | 177 | 178 | 3 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 4 187 | 188 | 189 | 190 | 191 | 5 192 | 193 | 194 | 195 | 196 | 6 197 | 198 | 199 | 200 | 201 | 202 | 203 | com.apple.InterfaceBuilder.CocoaPlugin 204 | com.apple.InterfaceBuilder.CocoaPlugin 205 | com.apple.InterfaceBuilder.CocoaPlugin 206 | com.apple.InterfaceBuilder.CocoaPlugin 207 | {{357, 418}, {480, 270}} 208 | 209 | 210 | 211 | 212 | 213 | com.apple.InterfaceBuilder.CocoaPlugin 214 | 215 | com.apple.InterfaceBuilder.CocoaPlugin 216 | com.apple.InterfaceBuilder.CocoaPlugin 217 | com.apple.InterfaceBuilder.CocoaPlugin 218 | com.apple.InterfaceBuilder.CocoaPlugin 219 | 220 | 221 | 222 | 223 | 224 | 6 225 | 226 | 227 | 228 | 229 | NSLayoutConstraint 230 | NSObject 231 | 232 | IBProjectSource 233 | ./Classes/NSLayoutConstraint.h 234 | 235 | 236 | 237 | 238 | 0 239 | IBCocoaFramework 240 | YES 241 | 3 242 | YES 243 | 244 | 245 | -------------------------------------------------------------------------------- /src/sample/java/ca/weblite/objc/samples/BringToFrontSample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package ca.weblite.objc.samples; 7 | 8 | import ca.weblite.objc.Client; 9 | import java.awt.BorderLayout; 10 | import java.awt.Dimension; 11 | import java.awt.EventQueue; 12 | import java.io.IOException; 13 | import java.net.ServerSocket; 14 | import java.net.Socket; 15 | import javax.swing.JFrame; 16 | import javax.swing.JLabel; 17 | 18 | /** 19 | * 20 | * @author shannah 21 | */ 22 | public class BringToFrontSample extends JFrame { 23 | ServerSocket sock; 24 | 25 | 26 | BringToFrontSample() { 27 | initUI(); 28 | } 29 | private void initUI() { 30 | setMinimumSize(new Dimension(640, 480)); 31 | setLayout(new BorderLayout()); 32 | add(new JLabel("Test Bring to Front"), BorderLayout.CENTER); 33 | 34 | } 35 | 36 | private void listen() throws IOException { 37 | sock = new ServerSocket(0); 38 | System.out.println("Listening on port "+sock.getLocalPort()); 39 | while (true) { 40 | Socket s = sock.accept(); 41 | System.out.println("Connection received"); 42 | Client c = Client.getInstance(); 43 | c.sendProxy("NSApplication", "sharedApplication").send("activateIgnoringOtherApps:", Boolean.TRUE); 44 | s.close(); 45 | 46 | } 47 | 48 | } 49 | 50 | /** 51 | * @param args the command line arguments 52 | */ 53 | public static void main(String[] args) throws Exception { 54 | // TODO code application logic here 55 | EventQueue.invokeLater(()->{ 56 | BringToFrontSample sample = new BringToFrontSample(); 57 | sample.pack(); 58 | sample.setVisible(true); 59 | new Thread(()->{ 60 | try { 61 | sample.listen(); 62 | } catch (Exception x) { 63 | x.printStackTrace(); 64 | } 65 | }).start(); 66 | }); 67 | 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/sample/java/ca/weblite/objc/samples/LoadNibSample.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import static ca.weblite.objc.RuntimeUtils.*; 4 | 5 | import ca.weblite.objc.annotations.Msg; 6 | 7 | /** 8 | * 9 | * @author shannah 10 | */ 11 | public class LoadNibSample extends NSObject { 12 | 13 | Proxy window; 14 | 15 | public LoadNibSample(){ 16 | super(); 17 | init("NSObject"); 18 | 19 | } 20 | 21 | @Msg(selector="setWindow:", like="NSSavePanel.setTitle:") 22 | public void setWindow(Proxy window){ 23 | this.window = window; 24 | } 25 | 26 | @Msg(selector="window", like="NSObject.description") 27 | public Proxy window(){ 28 | return this.window; 29 | } 30 | 31 | @Msg(selector="windowDidLoad", like="NSWindowController.windowDidLoad") 32 | public void windowDidLoad(){ 33 | System.out.println("Window did load"); 34 | } 35 | 36 | @Msg(selector="applicationDidFinishLaunching:", like="NSWindow.setTitle:") 37 | public void applicationDidFinishLaunching(Proxy notification){ 38 | System.out.println("App did finish launching"); 39 | } 40 | 41 | @Msg(selector="doMyAction:", like="NSWindow.setTitle") 42 | public void doMyAction(Proxy sender){ 43 | System.out.println("Action was performed"); 44 | } 45 | 46 | @Msg(selector="startApplication", like="NSObject.finalize") 47 | public void startApplication(){ 48 | Client c = getClient(); 49 | Proxy app = c.chain("NSApplication", "sharedApplication"); 50 | 51 | Proxy topLevelObjects = c.chain("NSMutableArray", "array"); 52 | 53 | 54 | Proxy filesOwner = c.chain( 55 | "NSDictionary", 56 | "dictionaryWithObjectsAndKeys:", 57 | this.getPeer(), 58 | str("NSOwner"), 59 | topLevelObjects.getPeer(), 60 | str("NSTopLevelObjects"), 61 | null 62 | ); 63 | 64 | 65 | app.send("setDelegate:", this); 66 | 67 | 68 | 69 | long res = (Long)c.send("NSBundle", "loadNibFile:externalNameTable:withZone:", "MainMenu.nib", filesOwner.getPeer(), null); 70 | int numTopLevelObjects = ((Long)topLevelObjects.send("count")).intValue(); 71 | 72 | Proxy mainWindow = null; 73 | 74 | for ( int i=0; i 0 ){ 77 | mainWindow = obj; 78 | } else { 79 | 80 | obj.dispose(false); 81 | } 82 | } 83 | 84 | //Proxy windowController = c.chain("NSWindowController", "alloc").chain("initWithWindow:", mainWindow.getPeer()); 85 | //System.out.println("Window loaded? "+windowController.send("isWindowLoaded")); 86 | //System.out.println("Window owner: "+windowController.send("owner")); 87 | //System.out.println("Bundle loaded "+res); 88 | //System.out.println("Num top level objects "+topLevelObjects.send("count")); 89 | //System.out.println("Main window "+app.send("mainWindow")); 90 | 91 | 92 | 93 | app.send("run"); 94 | 95 | } 96 | public static void main(String[] args){ 97 | //NativeLibrary library = NativeLibrary.getInstance("AppKit"); 98 | //Pointer addr = library.getGlobalVariableAddress("NSNibOwner"); 99 | 100 | //System.out.println("Top level objects :"+str(addr.getLong(0)));System.exit(0); 101 | LoadNibSample app = new LoadNibSample(); 102 | app.send("performSelectorOnMainThread:withObject:waitUntilDone:", sel("startApplication"), app, 1); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/sample/java/ca/weblite/objc/samples/NSOpenPanelSample.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import ca.weblite.objc.annotations.Msg; 4 | import static ca.weblite.objc.RuntimeUtils.*; 5 | 6 | /** 7 | * 8 | * @author shannah 9 | */ 10 | public class NSOpenPanelSample extends NSObject { 11 | 12 | public NSOpenPanelSample(){ 13 | super(); 14 | init("NSObject"); 15 | } 16 | @Msg(selector="panelSelectionDidChange:", signature="v@:@") 17 | public void panelSelectionDidChange(Proxy sender){ 18 | System.out.println("---------In panel selection did change---------"); 19 | } 20 | 21 | 22 | @Msg(selector="start", signature="v@:") 23 | public void start(){ 24 | Proxy openPanel = getClient().sendProxy("NSOpenPanel", "openPanel"); 25 | openPanel.send("setDelegate:", this); 26 | int result = openPanel.sendInt("runModal"); 27 | if ( result == 1 ){ 28 | // File was selected 29 | // Use the -[URLs] message on NSOpen panel to get an NSArray 30 | // of the selected files 31 | Proxy selectedUrls = openPanel.sendProxy("URLs"); 32 | System.out.println("The following urls were selected :"+selectedUrls); 33 | } else { 34 | // File was not selected. 35 | System.out.println("No file was selected"); 36 | } 37 | 38 | } 39 | 40 | public static void main(String[] args){ 41 | NSOpenPanelSample sample = new NSOpenPanelSample(); 42 | 43 | // Any interaction with the GUI must happen on the main thread 44 | // for cocoa, so we'll use NSObject's performSelectorOnMainThread: 45 | // message to 46 | sample.send("performSelectorOnMainThread:withObject:waitUntilDone:", sel("start"), sample, true); 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/sample/java/ca/weblite/objc/samples/NSSavePanelSample.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import ca.weblite.objc.annotations.Msg; 4 | import static ca.weblite.objc.RuntimeUtils.*; 5 | 6 | /** 7 | * 8 | * @author shannah 9 | */ 10 | public class NSSavePanelSample extends NSObject { 11 | 12 | public NSSavePanelSample(){ 13 | super(); 14 | init("NSObject"); 15 | } 16 | @Msg(selector="panelSelectionDidChange:", signature="v@:@") 17 | public void panelSelectionDidChange(Proxy sender){ 18 | System.out.println("---------In panel selection did change---------"); 19 | } 20 | 21 | 22 | @Msg(selector="start", signature="v@:") 23 | public void start(){ 24 | Proxy savePanel = getClient().sendProxy("NSSavePanel", "savePanel"); 25 | savePanel.send("setDelegate:", this); 26 | savePanel.send("runModal"); 27 | 28 | } 29 | 30 | public static void main(String[] args){ 31 | NSSavePanelSample sample = new NSSavePanelSample(); 32 | 33 | // Any interaction with the GUI must happen on the main thread 34 | // for cocoa, so we'll use NSObject's performSelectorOnMainThread: 35 | // message to 36 | sample.send("performSelectorOnMainThread:withObject:waitUntilDone:", sel("start"), sample, true); 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/sample/java/ca/weblite/objc/samples/TestWebView.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import static ca.weblite.objc.RuntimeUtils.*; 4 | 5 | import javax.swing.JFrame; 6 | 7 | import com.sun.jna.Library; 8 | import com.sun.jna.Native; 9 | 10 | import ca.weblite.objc.annotations.Msg; 11 | 12 | /** 13 | * 14 | * @author shannah 15 | */ 16 | public class TestWebView extends NSObject { 17 | 18 | // JNA interface for webkit framework 19 | public interface WebKit extends Library { 20 | TestWebView.WebKit INSTANCE = Native.loadLibrary("WebKit", TestWebView.WebKit.class); 21 | } 22 | 23 | public TestWebView(){ 24 | super("NSObject"); 25 | 26 | } 27 | 28 | // Initialization method 29 | @Msg(selector="myInit", like="NSObject.finalize") 30 | public void myInit(){ 31 | 32 | // Get objective-c client 33 | Client c = getClient(); 34 | 35 | // Load the bundle code 36 | // Includes TestWindowController class 37 | Proxy bundle = c.sendProxy("NSBundle", "bundleWithPath:", "TestBundle.bundle"); 38 | bundle.send("load"); 39 | 40 | // Initialize TestWindowController object used to work with the TestWindowController nib file. 41 | Proxy window = c.sendProxy("TestWindowController","alloc"); 42 | window = window.sendProxy("initWithWindowNibPath:owner:", "TestBundle.bundle/Contents/Resources/TestWindowController.nib", window.getPeer()); 43 | 44 | // Open the window 45 | window.send("showWindow:", window); 46 | 47 | // Get the webView in the window and load a URL in it 48 | Proxy webView = window.sendProxy("webView"); 49 | webView.send("setMainFrameURL:", "https://www.google.com"); 50 | 51 | } 52 | 53 | public void someFuncWithMultipleArgs(String arg1, int arg2){ 54 | System.out.println("Arg 1 is "+arg1+"; arg 2 is "+arg2); 55 | } 56 | 57 | public static void main(String[] args){ 58 | 59 | // Easy way to start event loop so app doesn't exit... start swing. 60 | new JFrame(); 61 | 62 | // Load the WebKit framework 63 | Native.loadLibrary("WebKit", TestWebView.WebKit.class); 64 | 65 | 66 | final TestWebView v = new TestWebView(); 67 | final String arg1 = "A first argument"; 68 | final int arg2 = 100; 69 | 70 | (new NSObject("NSObject"){ 71 | 72 | @Msg(selector="doBlock", like="NSObject.finalize") 73 | public void doBlock(){ 74 | v.myInit(); 75 | v.someFuncWithMultipleArgs(arg1, arg2); 76 | } 77 | }).send("performSelectorOnMainThread:withObject:waitUntilDone:", sel("doBlock"), null, 1); 78 | 79 | 80 | // Run on the main thread 81 | //v.send("performSelectorOnMainThread:withObject:waitUntilDone:", sel("myInit"), v, 1); 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/ca/weblite/objc/ClientTest.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | /** 8 | * 9 | * @author shannah 10 | */ 11 | public class ClientTest { 12 | /** 13 | * Test of objc_lookUpClass method, of class Runtime. 14 | */ 15 | @Test 16 | public void testObjc_lookUpClass() { 17 | 18 | // Obtain reference to Singleton instance of Objective-C client 19 | Client c = Client.getInstance(); 20 | 21 | // Create a new mutable array 22 | Proxy array = c.sendProxy("NSMutableArray", "array"); 23 | array.send("addObject:", "Hello"); 24 | array.send("addObject:", "World"); 25 | array.send("addObject:", "Test String"); 26 | 27 | assertEquals(3, array.sendInt("count")); 28 | 29 | String lastString = array.sendString("lastObject"); 30 | assertEquals("Test String", lastString); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/ca/weblite/objc/FiveArgTest.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | /** 6 | * 7 | * @author shannah 8 | */ 9 | public class FiveArgTest { 10 | 11 | /** 12 | * https://github.com/shannah/Java-Objective-C-Bridge/issues/44 13 | */ 14 | @Test 15 | public void testFiveArgs44() { 16 | Client.getInstance().sendProxy("NSURL", "URLByResolvingBookmarkData:options:relativeToURL:bookmarkDataIsStale:error:", null, 1024, null, false, null); 17 | 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/ca/weblite/objc/NSObjectTest.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import static ca.weblite.objc.RuntimeUtils.*; 4 | import static org.junit.jupiter.api.Assertions.*; 5 | 6 | import java.util.List; 7 | 8 | import ca.weblite.objc.foundation.NSRange; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import com.sun.jna.Pointer; 12 | import com.sun.jna.Structure; 13 | 14 | import ca.weblite.objc.annotations.Msg; 15 | 16 | /** 17 | * 18 | * @author shannah 19 | */ 20 | public class NSObjectTest { 21 | 22 | 23 | 24 | /** 25 | * Test of load method, of class Proxy. 26 | */ 27 | @Test 28 | public void testNSArray() { 29 | 30 | NSObject o = new NSObject(msgPointer("NSMutableArray", "array")); 31 | o.init("NSObject"); 32 | 33 | 34 | long expectedCount = 0; 35 | long actualCount = (Long)o.send("count"); 36 | assertEquals(expectedCount, actualCount); 37 | 38 | // Add a string to the array and check that 39 | // the last object matches that string. 40 | // Strings should be automatically converted to NSStrings 41 | String aString = "foobar"; 42 | o.send("addObject:", aString); 43 | 44 | String obj = (String)o.send("lastObject"); 45 | assertEquals(aString, obj); 46 | 47 | // There should be one object in the array 48 | expectedCount = 1; 49 | actualCount = (Long)o.send("count"); 50 | assertEquals(expectedCount, actualCount); 51 | 52 | //Now the string is there 53 | boolean expectedContains = true; 54 | boolean actualContains = o.sendBoolean("containsObject:", aString); 55 | assertEquals(expectedContains, actualContains); 56 | 57 | // Let's try to call a method that takes a structure as one of the 58 | // parameters 59 | Pointer[] buffer = new Pointer[1]; 60 | NSRange.ByValue range = new NSRange.ByValue(); 61 | range.setLength(1); 62 | range.setLocation(0); 63 | 64 | 65 | 66 | o.send("getObjects:range:", buffer, range); 67 | 68 | assertEquals(1, buffer.length); 69 | 70 | // Make sure that the first (and only entry) in the 71 | // buffer is the same string that we added previously. 72 | assertEquals(aString, str(buffer[0])); 73 | 74 | Proxy enumerator = o.sendProxy("objectEnumerator"); 75 | 76 | String placeHolder = (String)enumerator.send("nextObject"); 77 | assertEquals(aString, placeHolder); 78 | 79 | Proxy newArray = o.sendProxy("arrayByAddingObject:", "Another String"); 80 | //System.out.println(newArray); 81 | assertEquals(2, newArray.sendInt("count")); 82 | 83 | 84 | 85 | } 86 | 87 | @Test 88 | public void testCustomClass(){ 89 | TestCustomClass cls = new TestCustomClass(); 90 | cls.init("NSObject"); 91 | 92 | String res = (String)cls.send("myCustomString"); 93 | assertEquals("My custom string", res); 94 | cls.send("setMyCustomString:", "Changed String"); 95 | res = (String)cls.send("myCustomString"); 96 | assertEquals("Changed String", res); 97 | 98 | int iRes = cls.sendInt("getCustomInt"); 99 | assertEquals(34, iRes); 100 | 101 | cls.send("setCustomInt:", 12); 102 | assertEquals(12, cls.sendInt("getCustomInt")); 103 | 104 | double dRes = cls.sendDouble("getMyDouble"); 105 | 106 | assertEquals(1.5, dRes); 107 | 108 | cls.send("setMyDouble:", 3.0); 109 | dRes = cls.sendDouble("getMyDouble"); 110 | assertEquals(3.0, dRes); 111 | assertEquals(3.0, cls.dNum); 112 | 113 | Proxy myObj = (Proxy)cls.send("getMyObj"); 114 | assertNull(myObj); 115 | 116 | Proxy array = Client.getInstance().sendProxy("NSMutableArray", "array"); 117 | cls.send("setMyObj:", array); 118 | 119 | myObj = cls.sendProxy("getMyObj"); 120 | assertEquals(array, myObj); 121 | } 122 | 123 | public static class TestCustomClass extends NSObject { 124 | private String str = "My custom string"; 125 | 126 | private int iNum = 34; 127 | private double dNum = 1.5; 128 | private Proxy myObj; 129 | 130 | @Msg(selector="myCustomString", like="NSObject.description") 131 | public String myCustomString(){ 132 | return str; 133 | } 134 | 135 | @Msg(selector="setMyCustomString:", signature="v@:@") 136 | public void setMyCustomString(String str){ 137 | this.str = str; 138 | } 139 | 140 | @Msg(selector="getCustomInt", signature="i@:") 141 | public int getCustomInt(){ 142 | return iNum; 143 | } 144 | @Msg(selector="setCustomInt:", signature="v@:i") 145 | public void setCustomInt(int i){ 146 | this.iNum = i; 147 | } 148 | 149 | @Msg(selector="getMyDouble", signature="d@:") 150 | public double getMyDouble(){ 151 | return dNum; 152 | } 153 | 154 | @Msg(selector="setMyDouble:", signature="v@:d") 155 | public void setMyDouble(double d){ 156 | dNum = d; 157 | } 158 | 159 | @Msg(selector="getMyObj", signature="@@:") 160 | public Proxy getMyObj(){ 161 | return myObj; 162 | } 163 | @Msg(selector="setMyObj:", signature="v@:@") 164 | public void setMyObj(Proxy obj){ 165 | myObj = obj; 166 | } 167 | } 168 | 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/test/java/ca/weblite/objc/NSProcessInfoUtils.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | /** 4 | * Created by IntelliJ IDEA. 5 | * User: remicartier (remi.cartier@gmail.com) 6 | * Date: 2014-08-06 7 | * Time: 8:13 PM 8 | */ 9 | public class NSProcessInfoUtils { 10 | 11 | private final static long NSActivityUserInitiated = (0x00FFFFFFL | (1L << 20)); 12 | 13 | /** 14 | * To ensure Mac OS X doesn't slow down your app because of App Nap, call this method 15 | * @param reason the reason for allowing the app to work at full speed 16 | * @return the activity id as a Proxy object 17 | */ 18 | public static Proxy beginActivityWithOptions(String reason) { 19 | 20 | Client c = Client.getInstance(); 21 | Proxy processInfo = c.sendProxy("NSProcessInfo", "processInfo"); 22 | return processInfo.sendProxy("beginActivityWithOptions:reason:", NSActivityUserInitiated, reason); 23 | } 24 | 25 | /** 26 | * When the activity is finished, to re-enable app napping call this method 27 | * @param activity previously returned by beginActivityWithOptions() 28 | */ 29 | public static void endActivity(Proxy activity) { 30 | 31 | if (activity != null) { 32 | Client c = Client.getInstance(); 33 | Proxy processInfo = c.sendProxy("NSProcessInfo", "processInfo"); 34 | processInfo.send("endActivity:", activity); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/ca/weblite/objc/ProxyTest.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import static ca.weblite.objc.RuntimeUtils.*; 4 | import static org.junit.jupiter.api.Assertions.*; 5 | 6 | import java.util.List; 7 | 8 | import ca.weblite.objc.foundation.NSRange; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import com.sun.jna.Pointer; 12 | import com.sun.jna.Structure; 13 | 14 | /** 15 | * 16 | * @author shannah 17 | */ 18 | public class ProxyTest { 19 | 20 | 21 | /** 22 | * Test of load method, of class Proxy. 23 | */ 24 | @Test 25 | public void testNSArray() { 26 | 27 | Proxy o = new Proxy(msgPointer("NSMutableArray", "array")); 28 | long expectedCount = 0; 29 | long actualCount = (Long)o.send("count"); 30 | assertEquals(expectedCount, actualCount); 31 | 32 | // Add a string to the array and check that 33 | // the last object matches that string. 34 | // Strings should be automatically converted to NSStrings 35 | String aString = "foobar"; 36 | o.send("addObject:", aString); 37 | 38 | String obj = (String)o.send("lastObject"); 39 | assertEquals(aString, obj); 40 | 41 | // There should be one object in the array 42 | expectedCount = 1; 43 | actualCount = (Long)o.send("count"); 44 | assertEquals(expectedCount, actualCount); 45 | 46 | //Now the string is there 47 | boolean expectedContains = true; 48 | boolean actualContains = o.sendBoolean("containsObject:", aString); 49 | assertEquals(expectedContains, actualContains); 50 | 51 | // Let's try to call a method that takes a structure as one of the 52 | // parameters 53 | 54 | Pointer[] buffer = new Pointer[1]; 55 | NSRange range = new NSRange.ByValue(); 56 | range.setLength(1); 57 | range.setLocation(0); 58 | o.send("getObjects:range:", buffer, range); 59 | 60 | assertEquals(1, buffer.length); 61 | 62 | // Make sure that the first (and only entry) in the 63 | // buffer is the same string that we added previously. 64 | assertEquals(aString, str(buffer[0])); 65 | 66 | Proxy enumerator = o.sendProxy("objectEnumerator"); 67 | 68 | String placeHolder = (String)enumerator.send("nextObject"); 69 | assertEquals(aString, placeHolder); 70 | 71 | Proxy newArray = o.sendProxy("arrayByAddingObject:", "Another String"); 72 | 73 | assertEquals(2, newArray.sendInt("count")); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/ca/weblite/objc/RuntimeTest.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import static org.junit.jupiter.api.Assertions.*; 4 | 5 | import org.junit.jupiter.api.Test; 6 | 7 | import com.sun.jna.Pointer; 8 | 9 | /** 10 | * 11 | * @author shannah 12 | */ 13 | public class RuntimeTest { 14 | 15 | /** 16 | * Test of objc_lookUpClass method, of class Runtime. 17 | */ 18 | @Test 19 | public void testObjc_lookUpClass() { 20 | // Load NSString class 21 | Pointer nsString = Runtime.INSTANCE.objc_lookUpClass("NSString"); 22 | 23 | // Get the name of the class that we just loaded (should be NSString) 24 | String clsName = Runtime.INSTANCE.class_getName(nsString); 25 | 26 | // Assert that the class name is what we expect 27 | assertEquals("NSString", clsName); 28 | 29 | // Get the UID for the stringWithUTF8String: selector 30 | Pointer strWithUTF8StringSelector = Runtime.INSTANCE 31 | .sel_getUid("stringWithUTF8String:"); 32 | 33 | 34 | // Get the name of the selector from its UID 35 | String selName = Runtime.INSTANCE.sel_getName(strWithUTF8StringSelector); 36 | 37 | // Assert that the selector name is what we expect 38 | assertEquals("stringWithUTF8String:", selName); 39 | 40 | 41 | // Create a new string with the stringWithUTF8String: message 42 | // We are sending the message directly to the NSString class. 43 | long string = Runtime.INSTANCE 44 | .objc_msgSend(nsString, strWithUTF8StringSelector, "Test String"); 45 | 46 | // Now that we have our string let's send a message to it 47 | Pointer utf8StringSelector = Runtime.INSTANCE.sel_getUid("UTF8String"); 48 | 49 | // objc_msgSend takes a pointer, not a long so we need to wrap our string 50 | // inside a com.sun.jna.Pointer object 51 | Pointer stringPtr = new Pointer(string); 52 | 53 | 54 | long outStringPtr = Runtime.INSTANCE.objc_msgSend(stringPtr, utf8StringSelector); 55 | 56 | //outStringPtr is a pointer to a CString, so let's convert it into 57 | // a Java string so we can check to make sure it matches what 58 | // we expect 59 | 60 | String outString = new Pointer(outStringPtr).getString(0); 61 | assertEquals("Test String", outString); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/ca/weblite/objc/RuntimeUtilsTest.java: -------------------------------------------------------------------------------- 1 | package ca.weblite.objc; 2 | 3 | import static ca.weblite.objc.RuntimeUtils.*; 4 | import static org.junit.jupiter.api.Assertions.*; 5 | 6 | import org.junit.jupiter.api.AfterEach; 7 | import org.junit.jupiter.api.BeforeEach; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import com.sun.jna.Pointer; 11 | 12 | /** 13 | * 14 | * @author shannah 15 | */ 16 | public class RuntimeUtilsTest { 17 | 18 | private Pointer autoreleasePool; 19 | 20 | @BeforeEach 21 | public void setup() { 22 | autoreleasePool = msgPointer("NSAutoreleasePool", "alloc"); 23 | msg(autoreleasePool, "init"); 24 | } 25 | 26 | @AfterEach 27 | public void tearDown() { 28 | if (autoreleasePool != null) { 29 | msg(autoreleasePool, "release"); 30 | autoreleasePool = null; 31 | } 32 | } 33 | 34 | /** 35 | * Test of objc_lookUpClass method, of class Runtime. 36 | */ 37 | @Test 38 | public void testObjc_lookUpClass() { 39 | 40 | // Load NSString class 41 | Pointer nsString = cls("NSString"); 42 | 43 | // Get the name of the class that we just loaded (should be NSString) 44 | String clsName = clsName(nsString); 45 | 46 | // Assert that the class name is what we expect 47 | assertEquals("NSString", clsName); 48 | 49 | // Get the UID for the stringWithUTF8String: selector 50 | Pointer strWithUTF8StringSelector = sel("stringWithUTF8String:"); 51 | 52 | 53 | // Get the name of the selector from its UID 54 | String selName = selName(strWithUTF8StringSelector); 55 | 56 | // Assert that the selector name is what we expect 57 | assertEquals("stringWithUTF8String:", selName); 58 | 59 | 60 | // Create a new string with the stringWithUTF8String: message 61 | // We are sending the message directly to the NSString class. 62 | long string = msg(nsString, strWithUTF8StringSelector, "Test String"); 63 | assertNotEquals(0L, string, "stringWithUTF8String should return non-null string"); 64 | msg(new Pointer(string), "retain"); 65 | // Now that we have our string let's send a message to it 66 | 67 | assertEquals(11, msg(new Pointer(string), "length"), "Test String should be length 11"); 68 | 69 | Pointer utf8StringSelector = sel("UTF8String"); 70 | 71 | // objc_msgSend takes a pointer, not a long so we need to wrap our string 72 | // inside a com.sun.jna.Pointer object 73 | Pointer stringPtr = new Pointer(string); 74 | 75 | 76 | 77 | long outStringPtr = msg(stringPtr, utf8StringSelector); 78 | assertNotEquals(0L, outStringPtr, "UTF8String selector expected to be non null"); 79 | 80 | 81 | //outStringPtr is a pointer to a CString, so let's convert it into 82 | // a Java string so we can check to make sure it matches what 83 | // we expect 84 | 85 | String outString = new Pointer(outStringPtr).getString(0); 86 | assertEquals("Test String", outString); 87 | msg(new Pointer(string), "release"); 88 | 89 | } 90 | 91 | @Test 92 | public void testObjc_lookUpClass2() { 93 | // Create a new string with the stringWithUTF8String: message 94 | // We are sending the message directly to the NSString class. 95 | long string = msg("NSString", "stringWithUTF8String:", "Test String"); 96 | 97 | long outStringPtr = msg(new Pointer(string), "UTF8String"); 98 | 99 | //outStringPtr is a pointer to a CString, so let's convert it into 100 | // a Java string so we can check to make sure it matches what 101 | // we expect 102 | 103 | String outString = new Pointer(outStringPtr).getString(0); 104 | assertEquals("Test String", outString); 105 | } 106 | 107 | @Test 108 | public void testObjc_lookUpClass3() { 109 | // Create a new string with the stringWithUTF8String: message 110 | // We are sending the message directly to the NSString class. 111 | // Because we are asking to coerce outputs, this will simply 112 | // map the resulting NSString object to a Java string so 113 | // we end where we started. 114 | String outString = (String)msg( 115 | true, // Coerce Outputs 116 | true, // Coerce Inputs 117 | cls("NSString"), //Receiver 118 | sel("stringWithUTF8String:"), // Selector 119 | "Test String" // Argument to message 120 | ); 121 | 122 | assertEquals("Test String", outString); 123 | 124 | 125 | // Same thing without coercing outputs. We'll instead receive 126 | // a pointer to an NSString. 127 | long nsString = (Long)msg( 128 | false, 129 | true, 130 | cls("NSString"), 131 | sel("stringWithUTF8String:"), 132 | "Test String" 133 | ); 134 | 135 | // Confirm that this is a pointer to an NSString 136 | long res = msg(new Pointer(nsString), "isKindOfClass:", cls("NSString")); 137 | assertEquals(1L, res); 138 | 139 | 140 | // Now let's get the string as a Java string. 141 | outString = (String)msg( 142 | true, 143 | true, 144 | new Pointer(nsString), 145 | sel("UTF8String") 146 | ); 147 | 148 | assertEquals("Test String", outString); 149 | 150 | // Now create an NSMutableArray to show the msg() method 151 | // automatically wrap it in a Proxy object 152 | 153 | Proxy array = (Proxy)msg( 154 | true, 155 | true, 156 | cls("NSMutableArray"), 157 | sel("array") 158 | ); 159 | 160 | 161 | // Add out test string to the array 162 | // This will convert it to an NSString 163 | // and store it in the array. 164 | array.send("addObject:", outString); 165 | 166 | 167 | // Use the Proxy's sendInt() method to send a message to count 168 | // in which we expect an int as output 169 | int sizeOfArray = array.sendInt("count"); 170 | assertEquals(1, sizeOfArray); 171 | 172 | 173 | // The Proxy object has type mapping enabled, so we can send 174 | // directly and have it return a string. 175 | String firstItem = array.sendString("objectAtIndex:", 0); 176 | assertEquals(outString, firstItem); 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /src/test/objectivec/libjcocoaTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // libjcocoaTests.h 3 | // libjcocoaTests 4 | // 5 | // Created by Steve Hannah on 2012-10-21. 6 | // Copyright (c) 2012 Web Lite Solutions. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface libjcocoaTests : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /src/test/objectivec/libjcocoaTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // libjcocoaTests.m 3 | // libjcocoaTests 4 | // 5 | // Created by Steve Hannah on 2012-10-21. 6 | // Copyright (c) 2012 Web Lite Solutions. All rights reserved. 7 | // 8 | 9 | #import "libjcocoaTests.h" 10 | 11 | @implementation libjcocoaTests 12 | 13 | - (void)setUp 14 | { 15 | [super setUp]; 16 | 17 | // Set-up code here. 18 | } 19 | 20 | - (void)tearDown 21 | { 22 | // Tear-down code here. 23 | 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample 28 | { 29 | STFail(@"Unit tests are not implemented yet in libjcocoaTests"); 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /src/test/resources/TestBundle.bundle/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildMachineOSBuild 6 | 11G63b 7 | CFBundleDevelopmentRegion 8 | English 9 | CFBundleExecutable 10 | TestBundle 11 | CFBundleIdentifier 12 | ca.weblite.TestBundle 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | TestBundle 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | DTCompiler 26 | 27 | DTPlatformBuild 28 | 4G2008a 29 | DTPlatformVersion 30 | GM 31 | DTSDKBuild 32 | 12C37 33 | DTSDKName 34 | macosx10.8 35 | DTXcode 36 | 0452 37 | DTXcodeBuild 38 | 4G2008a 39 | NSHumanReadableCopyright 40 | Copyright © 2013 Web Lite Solutions Corp. All rights reserved. 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/test/resources/TestBundle.bundle/Contents/MacOS/TestBundle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannah/Java-Objective-C-Bridge/dbd12377aee75acf3afd6f99b4ff92e8faf56058/src/test/resources/TestBundle.bundle/Contents/MacOS/TestBundle -------------------------------------------------------------------------------- /src/test/resources/TestBundle.bundle/Contents/Resources/TestWindow.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannah/Java-Objective-C-Bridge/dbd12377aee75acf3afd6f99b4ff92e8faf56058/src/test/resources/TestBundle.bundle/Contents/Resources/TestWindow.nib -------------------------------------------------------------------------------- /src/test/resources/TestBundle.bundle/Contents/Resources/TestWindowController.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannah/Java-Objective-C-Bridge/dbd12377aee75acf3afd6f99b4ff92e8faf56058/src/test/resources/TestBundle.bundle/Contents/Resources/TestWindowController.nib -------------------------------------------------------------------------------- /src/test/resources/TestBundle.bundle/Contents/Resources/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shannah/Java-Objective-C-Bridge/dbd12377aee75acf3afd6f99b4ff92e8faf56058/src/test/resources/TestBundle.bundle/Contents/Resources/en.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /src/test/resources/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /src/test/resources/libjcocoaTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ca.weblite.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | --------------------------------------------------------------------------------