diff --git a/build.gradle b/build.gradle index c667daa..98ded3f 100755 --- a/build.gradle +++ b/build.gradle @@ -1,166 +1,166 @@ -apply plugin: 'java' -apply plugin: 'groovy' -apply plugin: 'cpp' -apply plugin: 'idea' -apply plugin: 'application' -apply plugin: 'maven' - -repositories { - mavenCentral() -} - -dependencies { - groovy 'org.codehaus.groovy:groovy:1.8.7' - testCompile 'org.spockframework:spock-core:0.6-groovy-1.8' -} - -group = 'net.rubygrapefruit' -version = '0.1' - -mainClassName = 'net.rubygrapefruit.platform.Main' -def nativeHeadersDir = file("$buildDir/nativeHeaders") -sourceCompatibility = 1.5 -targetCompatibility = 1.5 - -configurations.compile.extendsFrom = [] - -cpp { - sourceSets { - main - } -} - -libraries { - if (org.gradle.internal.os.OperatingSystem.current().macOsX) { - universal.spec { - baseName = 'native-platform-osx-universal' - includes(['/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/']) - args("-lcurses", "-arch", "x86_64", "-arch", "i386", "-o", outputFile) - } - } else if (org.gradle.internal.os.OperatingSystem.current().windows) { - main.spec { - baseName = 'native-platform-win32' - includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) - includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/win32"]) - args("/DWIN32") - } - } else if (org.gradle.internal.os.OperatingSystem.current().linux) { - all { - spec { - includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) - includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/linux"]) - args("-lcurses") - } - } - if (System.getProperty('os.arch') == 'i386' || project.hasProperty('multiarch')) { - i386.spec { - baseName = 'native-platform-linux-i386' - args("-m32") - } - } - if (System.getProperty('os.arch') == 'amd64' || project.hasProperty('multiarch')) { - amd64.spec { - baseName = 'native-platform-linux-amd64' - args("-m64") - } - } - } else { - baseName = "native-platform-solaris" - main.spec { - includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) - includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/solaris"]) - args("-DSOLARIS", "-lcurses") - } - } - all { - spec { - includes([nativeHeadersDir]) - } - sourceSets << cpp.sourceSets.main - } -} - -task nativeHeaders { - def outputFile = file("$nativeHeadersDir/native.h") - inputs.files sourceSets.main.output - outputs.file outputFile - doLast { - outputFile.parentFile.mkdirs() - exec { - executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah') - args '-o', outputFile - args '-classpath', sourceSets.main.output.classesDir - args 'net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions' - args 'net.rubygrapefruit.platform.internal.jni.PosixFileFunctions' - args 'net.rubygrapefruit.platform.internal.jni.PosixFileSystemFunctions' - args 'net.rubygrapefruit.platform.internal.jni.PosixProcessFunctions' - args 'net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions' - args 'net.rubygrapefruit.platform.internal.jni.TerminfoFunctions' - args 'net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions' - } - } -} - -def deployer = null -uploadArchives { - repositories { - deployer = mavenDeployer { - repository(url: uri("$buildDir/repo")) - } - } -} - -libraries.all { lib -> - def nativeJar = task("nativeJar${lib.name.capitalize()}", type: Jar) { - from lib.spec.task - baseName = lib.spec.baseName - } - startScripts.classpath += nativeJar.outputs.files - applicationDistribution.from(nativeJar) { - into 'lib' - } - lib.spec.task.dependsOn nativeHeaders - test.dependsOn lib.spec.task - artifacts { - archives nativeJar - } - def jniPom = deployer.addFilter(lib.name) { artifact, file -> - return file == nativeJar.archivePath - } - jniPom.groupId = project.group - jniPom.artifactId = lib.spec.baseName - jniPom.version = project.version - jniPom.scopeMappings.mappings.clear() -} - -javadoc { - exclude '**/internal/**' -} - -applicationDistribution.from(javadoc) { - into 'javadoc' -} - -artifacts { - archives distZip -} - -def mainPom = deployer.addFilter('main') { artifact, file -> - return file == distZip.archivePath || file == jar.archivePath -} -mainPom.groupId = project.group -mainPom.artifactId = jar.baseName -mainPom.version = project.version -mainPom.scopeMappings.mappings.clear() -mainPom.withXml { provider -> - def node = provider.asNode() - def deps = node.appendNode('dependencies') - def dep = deps.appendNode('dependency') - dep.appendNode('groupId', project.group) - dep.appendNode('artifactId', 'native-platform-osx-universal') - dep.appendNode('version', project.version) -} - -task wrapper(type: Wrapper) { - gradleVersion = "1.3-20120907220018+0000" -} +apply plugin: 'java' +apply plugin: 'groovy' +apply plugin: 'cpp' +apply plugin: 'idea' +apply plugin: 'application' +apply plugin: 'maven' + +repositories { + mavenCentral() +} + +dependencies { + groovy 'org.codehaus.groovy:groovy:1.8.7' + testCompile 'org.spockframework:spock-core:0.6-groovy-1.8' +} + +group = 'net.rubygrapefruit' +version = '0.1' + +mainClassName = 'net.rubygrapefruit.platform.Main' +def nativeHeadersDir = file("$buildDir/nativeHeaders") +sourceCompatibility = 1.5 +targetCompatibility = 1.5 + +configurations.compile.extendsFrom = [] + +cpp { + sourceSets { + main + } +} + +libraries { + if (org.gradle.internal.os.OperatingSystem.current().macOsX) { + universal.spec { + baseName = 'native-platform-osx-universal' + includes(['/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/']) + args("-lcurses", "-arch", "x86_64", "-arch", "i386", "-o", outputFile) + } + } else if (org.gradle.internal.os.OperatingSystem.current().windows) { + i386.spec { + baseName = 'native-platform-windows-i386' + includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) + includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/win32"]) + args("/DWIN32") + } + } else if (org.gradle.internal.os.OperatingSystem.current().linux) { + all { + spec { + includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) + includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/linux"]) + args("-lcurses") + } + } + if (System.getProperty('os.arch') == 'i386' || project.hasProperty('multiarch')) { + i386.spec { + baseName = 'native-platform-linux-i386' + args("-m32") + } + } + if (System.getProperty('os.arch') == 'amd64' || project.hasProperty('multiarch')) { + amd64.spec { + baseName = 'native-platform-linux-amd64' + args("-m64") + } + } + } else { + baseName = "native-platform-solaris" + main.spec { + includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) + includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/solaris"]) + args("-DSOLARIS", "-lcurses") + } + } + all { + spec { + includes([nativeHeadersDir]) + } + sourceSets << cpp.sourceSets.main + } +} + +task nativeHeaders { + def outputFile = file("$nativeHeadersDir/native.h") + inputs.files sourceSets.main.output + outputs.file outputFile + doLast { + outputFile.parentFile.mkdirs() + exec { + executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah') + args '-o', outputFile + args '-classpath', sourceSets.main.output.classesDir + args 'net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions' + args 'net.rubygrapefruit.platform.internal.jni.PosixFileFunctions' + args 'net.rubygrapefruit.platform.internal.jni.PosixFileSystemFunctions' + args 'net.rubygrapefruit.platform.internal.jni.PosixProcessFunctions' + args 'net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions' + args 'net.rubygrapefruit.platform.internal.jni.TerminfoFunctions' + args 'net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions' + } + } +} + +def deployer = null +uploadArchives { + repositories { + deployer = mavenDeployer { + repository(url: uri("$buildDir/repo")) + } + } +} + +libraries.all { lib -> + def nativeJar = task("nativeJar${lib.name.capitalize()}", type: Jar) { + from lib.spec.task + baseName = lib.spec.baseName + } + startScripts.classpath += nativeJar.outputs.files + applicationDistribution.from(nativeJar) { + into 'lib' + } + lib.spec.task.dependsOn nativeHeaders + test.dependsOn lib.spec.task + artifacts { + archives nativeJar + } + def jniPom = deployer.addFilter(lib.name) { artifact, file -> + return file == nativeJar.archivePath + } + jniPom.groupId = project.group + jniPom.artifactId = lib.spec.baseName + jniPom.version = project.version + jniPom.scopeMappings.mappings.clear() +} + +javadoc { + exclude '**/internal/**' +} + +applicationDistribution.from(javadoc) { + into 'javadoc' +} + +artifacts { + archives distZip +} + +def mainPom = deployer.addFilter('main') { artifact, file -> + return file == distZip.archivePath || file == jar.archivePath +} +mainPom.groupId = project.group +mainPom.artifactId = jar.baseName +mainPom.version = project.version +mainPom.scopeMappings.mappings.clear() +mainPom.withXml { provider -> + def node = provider.asNode() + def deps = node.appendNode('dependencies') + def dep = deps.appendNode('dependency') + dep.appendNode('groupId', project.group) + dep.appendNode('artifactId', 'native-platform-osx-universal') + dep.appendNode('version', project.version) +} + +task wrapper(type: Wrapper) { + gradleVersion = "1.3-20120907220018+0000" +} diff --git a/readme.md b/readme.md index 3550a06..0a1e3a6 100755 --- a/readme.md +++ b/readme.md @@ -1,149 +1,147 @@ - -# Native-platform: Java bindings for various native APIs - -A collection of cross-platform Java APIs for various native APIs. Supports OS X, Linux, Solaris and Windows. - -These APIs support Java 5 and later. Some of these APIs overlap with APIs available in later Java versions. - -## Available bindings - -### Generic - -* Get PID of current process. -* Get kernel name and version. -* Get machine architecture. - -### Terminal and console - -These bindings work for both the UNIX terminal and the Windows console: - -* Determine if stdout/stderr are attached to a terminal. -* Query the terminal size. -* Switch between bold and normal mode on the terminal. -* Change foreground color on the terminal. -* Move terminal cursor up, down, left, right, start of line. -* Clear to end of line. - -### File systems - -* Get and set UNIX file mode. -* Create and read symbolic links. -* List the available file systems on the machine -* Query file system mount point. -* Query file system type. -* Query file system device name. -* Query whether a file system is local or remote. - -## Supported platforms - -Currently ported to OS X, Linux, Solaris and Windows. Tested on: - -* OS X 10.7.4, 10.8 (x86_64), 10.6.7 (i386) -* Ubunutu 12.04 (amd64), 8.04.4 (i386, amd64) -* Solaris 11 (x86) -* Windows 7 (amd64) - -## Using - -Include `native-platform.jar` and `native-platform-jni.jar` in your classpath. - - import net.rubygrapefruit.platform.Native; - import net.rubygrapefruit.platform.Terminals; - import net.rubygrapefruit.platform.Terminal; - import static net.rubygrapefruit.platform.Terminals.Output.*; - - Terminals terminals = Native.get(Terminals.class); - - // check if terminal - terminals.isTerminal(Stdout); - - // use terminal - Terminal stdout = terminals.getTerminal(Stdout); - stdout.bold(); - System.out.println("bold text"); - - -## Building - -You will need to use the Gradle wrapper. Just run `gradlew` in the root directory. - -### Ubuntu - -The g++ compiler is required to build the native library. You will need the `g++` package for this. Usually this is already installed. - -You need to install the `libncurses5-dev` package to pick up the ncurses header files. Also worth installing the `ncurses-doc` package too. - -#### 64-bit machines with multi-arch support - -Where multi-arch support is available (e.g. recent Ubuntu releases), you can build the i386 and amd64 versions of the library on the -same machine. - -You need to install the `gcc-multilib` and `g++-multilib` packages to pick up i386 support. - -You need to install the `lib32ncurses5-dev` package to pick up the ncurses i386 version. - -To build, include `-Pmultiarch` on the command-line. - -### Windows - -You need to install Visual studio, and build from a Visual studio command prompt. - -### OS X - -The g++ compiler is required to build the native library. You will need to install the XCode tools for this. - -### Solaris - -For Solaris 11, you need to install the `development/gcc-45` and `system/header` packages. - -## Running - -Run `gradle install` to install into `build/install/native-platform`. Or `gradle distZip` to create an application distribtion -in `build/distributions/native-platform.zip`. - -You can run `$INSTALL_DIR/bin/native-platform` to run the test application. - -## Testing - -* Test on IBM JVM. -* Test on Java 5, 6, 7. -* Test on Windows 7, Windows XP - -## TODO - -### Fixes - -* Windows: build 32 bit and 64 bit libraries. -* Windows: fail for unsupported architecture. -* Linux: detect remote filesystems. -* Solaris: fix unicode file name handling. -* Solaris: fail for unsupported architecture. -* Solaris: build 32 bit and 64 bit libraries. -* Freebsd: finish port. -* Freebsd: fail for unsupported architecture. -* Freebsd: build 32 bit and 64 bit libraries. - -### Improvements - -* Cache class, method and field lookups (in particular for String conversions). -* Determine C charset once at startup -* Change readLink() implementation so that it does not need to NULL terminate the encoded content -* Implement java_to_char_str() -* Use iconv() to convert from C char string to UTF-16 when converting from C char string to Java String. -* Support for cygwin terminal -* Use TERM=xtermc instead of TERM=xterm on Solaris. -* Add diagnostics for terminal. -* Split out separate native library for terminal handling. -* Version each native interface separately. -* String names for errno values. -* Split into multiple projects. -* Convert to c. -* Make native library extraction multi-process safe. -* Initial release. -* Use fully decomposed form for unicode file names on hfs+ filesystems. -* Handle string encoding for file system details. -* Handle string encoding for system info. - -### Ideas - -* Expose platform-specific HTTP proxy configuration. Query registry on windows to determine IE settings. + +# Native-platform: Java bindings for various native APIs + +A collection of cross-platform Java APIs for various native APIs. Supports OS X, Linux, Solaris and Windows. + +These APIs support Java 5 and later. Some of these APIs overlap with APIs available in later Java versions. + +## Available bindings + +### Generic + +* Get PID of current process. +* Get kernel name and version. +* Get machine architecture. + +### Terminal and console + +These bindings work for both the UNIX terminal and the Windows console: + +* Determine if stdout/stderr are attached to a terminal. +* Query the terminal size. +* Switch between bold and normal mode on the terminal. +* Change foreground color on the terminal. +* Move terminal cursor up, down, left, right, start of line. +* Clear to end of line. + +### File systems + +* Get and set UNIX file mode. +* Create and read symbolic links. +* List the available file systems on the machine +* Query file system mount point. +* Query file system type. +* Query file system device name. +* Query whether a file system is local or remote. + +## Supported platforms + +Currently ported to OS X, Linux, Solaris and Windows. Tested on: + +* OS X 10.7.4, 10.8 (x86_64), 10.6.7 (i386) +* Ubunutu 12.04 (amd64), 8.04.4 (i386, amd64) +* Solaris 11 (x86) +* Windows 7 (amd64) + +## Using + +Include `native-platform.jar` and `native-platform-jni.jar` in your classpath. + + import net.rubygrapefruit.platform.Native; + import net.rubygrapefruit.platform.Terminals; + import net.rubygrapefruit.platform.Terminal; + import static net.rubygrapefruit.platform.Terminals.Output.*; + + Terminals terminals = Native.get(Terminals.class); + + // check if terminal + terminals.isTerminal(Stdout); + + // use terminal + Terminal stdout = terminals.getTerminal(Stdout); + stdout.bold(); + System.out.println("bold text"); + + +## Building + +You will need to use the Gradle wrapper. Just run `gradlew` in the root directory. + +### Ubuntu + +The g++ compiler is required to build the native library. You will need the `g++` package for this. Usually this is already installed. + +You need to install the `libncurses5-dev` package to pick up the ncurses header files. Also worth installing the `ncurses-doc` package too. + +#### 64-bit machines with multi-arch support + +Where multi-arch support is available (e.g. recent Ubuntu releases), you can build the i386 and amd64 versions of the library on the +same machine. + +You need to install the `gcc-multilib` and `g++-multilib` packages to pick up i386 support. + +You need to install the `lib32ncurses5-dev` package to pick up the ncurses i386 version. + +To build, include `-Pmultiarch` on the command-line. + +### Windows + +You need to install Visual studio, and build from a Visual studio command prompt. + +### OS X + +The g++ compiler is required to build the native library. You will need to install the XCode tools for this. + +### Solaris + +For Solaris 11, you need to install the `development/gcc-45` and `system/header` packages. + +## Running + +Run `gradle install` to install into `build/install/native-platform`. Or `gradle distZip` to create an application distribtion +in `build/distributions/native-platform.zip`. + +You can run `$INSTALL_DIR/bin/native-platform` to run the test application. + +## Testing + +* Test on IBM JVM. +* Test on Java 5, 6, 7. +* Test on Windows 7, Windows XP + +## TODO + +### Fixes + +* Windows: build 32 bit and 64 bit libraries. +* Windows: fail for unsupported architecture. +* Linux: detect remote filesystems. +* Solaris: fix unicode file name handling. +* Solaris: fail for unsupported architecture. +* Solaris: build 32 bit and 64 bit libraries. +* Freebsd: finish port. +* Freebsd: fail for unsupported architecture. +* Freebsd: build 32 bit and 64 bit libraries. + +### Improvements + +* Cache class, method and field lookups (in particular for String conversions). +* Determine C charset once at startup +* Change readLink() implementation so that it does not need to NULL terminate the encoded content +* Don't use NewStringUTF() anywhere +* Use iconv() to convert from C char string to UTF-16 when converting from C char string to Java String. +* Support for cygwin terminal +* Use TERM=xtermc instead of TERM=xterm on Solaris. +* Add diagnostics for terminal. +* Split out separate native library for terminal handling. +* Version each native interface separately. +* String names for errno values. +* Split into multiple projects. +* Convert to c. +* Make native library extraction multi-process safe. +* Initial release. +* Use fully decomposed form for unicode file names on hfs+ filesystems. + +### Ideas + +* Expose platform-specific HTTP proxy configuration. Query registry on windows to determine IE settings. diff --git a/src/main/cpp/win.cpp b/src/main/cpp/win.cpp index 2d53619..a5bd1f2 100755 --- a/src/main/cpp/win.cpp +++ b/src/main/cpp/win.cpp @@ -1,240 +1,266 @@ -#ifdef WIN32 - -#include "native.h" -#include "generic.h" -#include - -/* - * Marks the given result as failed, using the current value of GetLastError() - */ -void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) { - mark_failed_with_code(env, message, GetLastError(), result); -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getPlatform(JNIEnv *env, jclass target) { -} - -/* - * Process functions - */ - -JNIEXPORT jint JNICALL -Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getPid(JNIEnv *env, jclass target) { - return GetCurrentProcessId(); -} - -/* - * Console functions - */ - -HANDLE getHandle(JNIEnv *env, int output, jobject result) { - HANDLE handle = output == 1 ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE); - if (handle == INVALID_HANDLE_VALUE) { - mark_failed_with_errno(env, "could not get console handle", result); - return NULL; - } - return handle; -} - -JNIEXPORT jboolean JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_isConsole(JNIEnv *env, jclass target, jint output, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - HANDLE handle = getHandle(env, output, result); - if (handle == NULL) { - return JNI_FALSE; - } - if (!GetConsoleScreenBufferInfo(handle, &console_info)) { - if (GetLastError() == ERROR_INVALID_HANDLE) { - return JNI_FALSE; - } - mark_failed_with_errno(env, "could not get console buffer", result); - return JNI_FALSE; - } - return JNI_TRUE; -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_getConsoleSize(JNIEnv *env, jclass target, jint output, jobject dimension, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - HANDLE handle = getHandle(env, output, result); - if (handle == NULL) { - mark_failed_with_message(env, "not a console", result); - return; - } - if (!GetConsoleScreenBufferInfo(handle, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - - jclass dimensionClass = env->GetObjectClass(dimension); - jfieldID widthField = env->GetFieldID(dimensionClass, "cols", "I"); - env->SetIntField(dimension, widthField, console_info.srWindow.Right - console_info.srWindow.Left + 1); - jfieldID heightField = env->GetFieldID(dimensionClass, "rows", "I"); - env->SetIntField(dimension, heightField, console_info.srWindow.Bottom - console_info.srWindow.Top + 1); -} - -HANDLE current_console = NULL; -WORD original_attributes = 0; -WORD current_attributes = 0; - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_initConsole(JNIEnv *env, jclass target, jint output, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - HANDLE handle = getHandle(env, output, result); - if (handle == NULL) { - mark_failed_with_message(env, "not a terminal", result); - return; - } - if (!GetConsoleScreenBufferInfo(handle, &console_info)) { - if (GetLastError() == ERROR_INVALID_HANDLE) { - mark_failed_with_message(env, "not a console", result); - } else { - mark_failed_with_errno(env, "could not get console buffer", result); - } - return; - } - current_console = handle; - original_attributes = console_info.wAttributes; - current_attributes = original_attributes; - Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(env, target, result); -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_bold(JNIEnv *env, jclass target, jobject result) { - current_attributes |= FOREGROUND_INTENSITY; - if (!SetConsoleTextAttribute(current_console, current_attributes)) { - mark_failed_with_errno(env, "could not set text attributes", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(JNIEnv *env, jclass target, jobject result) { - current_attributes &= ~FOREGROUND_INTENSITY; - SetConsoleTextAttribute(current_console, current_attributes); - if (!SetConsoleTextAttribute(current_console, current_attributes)) { - mark_failed_with_errno(env, "could not set text attributes", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_reset(JNIEnv *env, jclass target, jobject result) { - SetConsoleTextAttribute(current_console, original_attributes); - if (!SetConsoleTextAttribute(current_console, current_attributes)) { - mark_failed_with_errno(env, "could not set text attributes", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_foreground(JNIEnv *env, jclass target, jint color, jobject result) { - current_attributes &= ~ (FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN); - switch (color) { - case 0: - break; - case 1: - current_attributes |= FOREGROUND_RED; - break; - case 2: - current_attributes |= FOREGROUND_GREEN; - break; - case 3: - current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN; - break; - case 4: - current_attributes |= FOREGROUND_BLUE; - break; - case 5: - current_attributes |= FOREGROUND_RED|FOREGROUND_BLUE; - break; - case 6: - current_attributes |= FOREGROUND_GREEN|FOREGROUND_BLUE; - break; - default: - current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; - break; - } - - SetConsoleTextAttribute(current_console, current_attributes); - if (!SetConsoleTextAttribute(current_console, current_attributes)) { - mark_failed_with_errno(env, "could not set text attributes", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_left(JNIEnv *env, jclass target, jint count, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - console_info.dwCursorPosition.X -= count; - if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { - mark_failed_with_errno(env, "could not set cursor position", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_right(JNIEnv *env, jclass target, jint count, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - console_info.dwCursorPosition.X += count; - if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { - mark_failed_with_errno(env, "could not set cursor position", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_up(JNIEnv *env, jclass target, jint count, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - console_info.dwCursorPosition.Y -= count; - if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { - mark_failed_with_errno(env, "could not set cursor position", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_down(JNIEnv *env, jclass target, jint count, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - console_info.dwCursorPosition.Y += count; - if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { - mark_failed_with_errno(env, "could not set cursor position", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_startLine(JNIEnv *env, jclass target, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - console_info.dwCursorPosition.X = 0; - if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { - mark_failed_with_errno(env, "could not set cursor position", result); - } -} - -JNIEXPORT void JNICALL -Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_clearToEndOfLine(JNIEnv *env, jclass target, jobject result) { - CONSOLE_SCREEN_BUFFER_INFO console_info; - if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { - mark_failed_with_errno(env, "could not get console buffer", result); - return; - } - for (int i = console_info.dwCursorPosition.X; i < console_info.dwSize.X; i++) { - WriteConsole(current_console, " ", 1, NULL, NULL); - } -} - -#endif +#ifdef WIN32 + +#include "native.h" +#include "generic.h" +#include + +/* + * Marks the given result as failed, using the current value of GetLastError() + */ +void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) { + mark_failed_with_code(env, message, GetLastError(), result); +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getSystemInfo(JNIEnv *env, jclass target, jobject info, jobject result) { + jclass infoClass = env->GetObjectClass(info); + + OSVERSIONINFOEX versionInfo; + versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if (GetVersionEx((OSVERSIONINFO*)&versionInfo) == 0) { + mark_failed_with_errno(env, "could not get version info", result); + return; + } + + SYSTEM_INFO systemInfo; + GetNativeSystemInfo(&systemInfo); + jstring arch = NULL; + if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { + arch = env->NewStringUTF("amd64"); + } else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) { + arch = env->NewStringUTF("x86"); + } else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { + arch = env->NewStringUTF("ia64"); + } else { + arch = env->NewStringUTF("unknown"); + } + + jmethodID method = env->GetMethodID(infoClass, "windows", "(IIIZLjava/lang/String;)V"); + env->CallVoidMethod(info, method, versionInfo.dwMajorVersion, versionInfo.dwMinorVersion, + versionInfo.dwBuildNumber, versionInfo.wProductType == VER_NT_WORKSTATION, + arch); +} + +/* + * Process functions + */ + +JNIEXPORT jint JNICALL +Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getPid(JNIEnv *env, jclass target) { + return GetCurrentProcessId(); +} + +/* + * Console functions + */ + +HANDLE getHandle(JNIEnv *env, int output, jobject result) { + HANDLE handle = output == 1 ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE); + if (handle == INVALID_HANDLE_VALUE) { + mark_failed_with_errno(env, "could not get console handle", result); + return NULL; + } + return handle; +} + +JNIEXPORT jboolean JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_isConsole(JNIEnv *env, jclass target, jint output, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + HANDLE handle = getHandle(env, output, result); + if (handle == NULL) { + return JNI_FALSE; + } + if (!GetConsoleScreenBufferInfo(handle, &console_info)) { + if (GetLastError() == ERROR_INVALID_HANDLE) { + return JNI_FALSE; + } + mark_failed_with_errno(env, "could not get console buffer", result); + return JNI_FALSE; + } + return JNI_TRUE; +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_getConsoleSize(JNIEnv *env, jclass target, jint output, jobject dimension, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + HANDLE handle = getHandle(env, output, result); + if (handle == NULL) { + mark_failed_with_message(env, "not a console", result); + return; + } + if (!GetConsoleScreenBufferInfo(handle, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + + jclass dimensionClass = env->GetObjectClass(dimension); + jfieldID widthField = env->GetFieldID(dimensionClass, "cols", "I"); + env->SetIntField(dimension, widthField, console_info.srWindow.Right - console_info.srWindow.Left + 1); + jfieldID heightField = env->GetFieldID(dimensionClass, "rows", "I"); + env->SetIntField(dimension, heightField, console_info.srWindow.Bottom - console_info.srWindow.Top + 1); +} + +HANDLE current_console = NULL; +WORD original_attributes = 0; +WORD current_attributes = 0; + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_initConsole(JNIEnv *env, jclass target, jint output, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + HANDLE handle = getHandle(env, output, result); + if (handle == NULL) { + mark_failed_with_message(env, "not a terminal", result); + return; + } + if (!GetConsoleScreenBufferInfo(handle, &console_info)) { + if (GetLastError() == ERROR_INVALID_HANDLE) { + mark_failed_with_message(env, "not a console", result); + } else { + mark_failed_with_errno(env, "could not get console buffer", result); + } + return; + } + current_console = handle; + original_attributes = console_info.wAttributes; + current_attributes = original_attributes; + Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(env, target, result); +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_bold(JNIEnv *env, jclass target, jobject result) { + current_attributes |= FOREGROUND_INTENSITY; + if (!SetConsoleTextAttribute(current_console, current_attributes)) { + mark_failed_with_errno(env, "could not set text attributes", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(JNIEnv *env, jclass target, jobject result) { + current_attributes &= ~FOREGROUND_INTENSITY; + SetConsoleTextAttribute(current_console, current_attributes); + if (!SetConsoleTextAttribute(current_console, current_attributes)) { + mark_failed_with_errno(env, "could not set text attributes", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_reset(JNIEnv *env, jclass target, jobject result) { + SetConsoleTextAttribute(current_console, original_attributes); + if (!SetConsoleTextAttribute(current_console, current_attributes)) { + mark_failed_with_errno(env, "could not set text attributes", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_foreground(JNIEnv *env, jclass target, jint color, jobject result) { + current_attributes &= ~ (FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN); + switch (color) { + case 0: + break; + case 1: + current_attributes |= FOREGROUND_RED; + break; + case 2: + current_attributes |= FOREGROUND_GREEN; + break; + case 3: + current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN; + break; + case 4: + current_attributes |= FOREGROUND_BLUE; + break; + case 5: + current_attributes |= FOREGROUND_RED|FOREGROUND_BLUE; + break; + case 6: + current_attributes |= FOREGROUND_GREEN|FOREGROUND_BLUE; + break; + default: + current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; + break; + } + + SetConsoleTextAttribute(current_console, current_attributes); + if (!SetConsoleTextAttribute(current_console, current_attributes)) { + mark_failed_with_errno(env, "could not set text attributes", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_left(JNIEnv *env, jclass target, jint count, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + console_info.dwCursorPosition.X -= count; + if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { + mark_failed_with_errno(env, "could not set cursor position", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_right(JNIEnv *env, jclass target, jint count, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + console_info.dwCursorPosition.X += count; + if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { + mark_failed_with_errno(env, "could not set cursor position", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_up(JNIEnv *env, jclass target, jint count, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + console_info.dwCursorPosition.Y -= count; + if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { + mark_failed_with_errno(env, "could not set cursor position", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_down(JNIEnv *env, jclass target, jint count, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + console_info.dwCursorPosition.Y += count; + if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { + mark_failed_with_errno(env, "could not set cursor position", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_startLine(JNIEnv *env, jclass target, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + console_info.dwCursorPosition.X = 0; + if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { + mark_failed_with_errno(env, "could not set cursor position", result); + } +} + +JNIEXPORT void JNICALL +Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_clearToEndOfLine(JNIEnv *env, jclass target, jobject result) { + CONSOLE_SCREEN_BUFFER_INFO console_info; + if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { + mark_failed_with_errno(env, "could not get console buffer", result); + return; + } + for (int i = console_info.dwCursorPosition.X; i < console_info.dwSize.X; i++) { + WriteConsole(current_console, " ", 1, NULL, NULL); + } +} + +#endif diff --git a/src/main/java/net/rubygrapefruit/platform/internal/MutableSystemInfo.java b/src/main/java/net/rubygrapefruit/platform/internal/MutableSystemInfo.java old mode 100644 new mode 100755 index 85a227d..032b26b --- a/src/main/java/net/rubygrapefruit/platform/internal/MutableSystemInfo.java +++ b/src/main/java/net/rubygrapefruit/platform/internal/MutableSystemInfo.java @@ -1,21 +1,53 @@ -package net.rubygrapefruit.platform.internal; - -import net.rubygrapefruit.platform.SystemInfo; - -public class MutableSystemInfo implements SystemInfo { - public String osName; - public String osVersion; - public String machineArchitecture; - - public String getKernelName() { - return osName; - } - - public String getKernelVersion() { - return osVersion; - } - - public String getMachineArchitecture() { - return machineArchitecture; - } -} +package net.rubygrapefruit.platform.internal; + +import net.rubygrapefruit.platform.SystemInfo; + +public class MutableSystemInfo implements SystemInfo { + public String osName; + public String osVersion; + public String machineArchitecture; + + public String getKernelName() { + return osName; + } + + public String getKernelVersion() { + return osVersion; + } + + public String getMachineArchitecture() { + return machineArchitecture; + } + + void windows(int major, int minor, int build, boolean workstation, String arch) { + osName = toWindowsVersionName(major, minor, workstation); + osVersion = String.format("%s.%s (build %s)", major, minor, build); + machineArchitecture = arch; + } + + private String toWindowsVersionName(int major, int minor, boolean workstation) { + switch (major) { + case 5: + switch (minor) { + case 0: + return "Windows 2000"; + case 1: + return "Windows XP"; + case 2: + return "Windows Server 2003"; + } + break; + case 6: + switch (minor) { + case 0: + return workstation ? "Windows Vista" : "Windows Server 2008"; + case 1: + return workstation ? "Windows 7" : "Windows Server 2008 R2"; + case 2: + return workstation ? "Windows 8" : "Windows Server 2012"; + } + break; + } + return "Windows (unknown version)"; + } +} diff --git a/src/main/java/net/rubygrapefruit/platform/internal/Platform.java b/src/main/java/net/rubygrapefruit/platform/internal/Platform.java index cfda151..82dec44 100755 --- a/src/main/java/net/rubygrapefruit/platform/internal/Platform.java +++ b/src/main/java/net/rubygrapefruit/platform/internal/Platform.java @@ -1,145 +1,152 @@ -package net.rubygrapefruit.platform.internal; - -import net.rubygrapefruit.platform.*; -import net.rubygrapefruit.platform.Process; - -public abstract class Platform { - private static Platform platform; - - public static Platform current() { - synchronized (Platform.class) { - if (platform == null) { - String osName = getOperatingSystem().toLowerCase(); - String arch = getArchitecture(); - if (osName.contains("windows")) { - platform = new Windows(); - } else if (osName.contains("linux")) { - platform = new Linux(); - } else if (osName.contains("os x") && (arch.equals("i386") || arch.equals("x86_64"))) { - platform = new OsX(); - } else if (osName.contains("sunos")) { - platform = new Solaris(); - } else { - platform = new Unsupported(); - } - } - return platform; - } - } - - public boolean isWindows() { - return false; - } - - public T get(Class type) { - throw new NativeIntegrationUnavailableException(String.format("Native integration %s is not supported on this operating system (%s %s)", - type.getSimpleName(), getOperatingSystem(), getArchitecture())); - } - - public abstract String getLibraryName() throws NativeIntegrationUnavailableException; - - private static class Windows extends Platform { - @Override - public boolean isWindows() { - return true; - } - - @Override - public String getLibraryName() { - return "native-platform-win32.dll"; - } - - @Override - public T get(Class type) { - if (type.equals(net.rubygrapefruit.platform.Process.class)) { - return type.cast(new DefaultProcess()); - } - if (type.equals(Terminals.class)) { - return type.cast(new WindowsTerminals()); - } - return super.get(type); - } - } - - private static abstract class Posix extends Platform { - @Override - public T get(Class type) { - if (type.equals(PosixFile.class)) { - return type.cast(new DefaultPosixFile()); - } - if (type.equals(Process.class)) { - return type.cast(new DefaultProcess()); - } - if (type.equals(Terminals.class)) { - return type.cast(new TerminfoTerminals()); - } - if (type.equals(SystemInfo.class)) { - return type.cast(new DefaultSystemInfo()); - } - return super.get(type); - } - } - - private abstract static class Unix extends Posix { - } - - private static class Linux extends Unix { - @Override - public T get(Class type) { - if (type.equals(FileSystems.class)) { - return type.cast(new PosixFileSystems()); - } - return super.get(type); - } - - @Override - public String getLibraryName() { - if (getArchitecture().equals("amd64")) { - return "libnative-platform-linux-amd64.so"; - } - if (getArchitecture().equals("i386") || getArchitecture().equals("x86")) { - return "libnative-platform-linux-i386.so"; - } - throw new NativeIntegrationUnavailableException(String.format( - "Native integration is not available for this architecture (%s) on Linux.", getArchitecture())); - } - } - - private static String getArchitecture() { - return System.getProperty("os.arch"); - } - - private static class Solaris extends Unix { - @Override - public String getLibraryName() { - return "libnative-platform-solaris.so"; - } - } - - private static class OsX extends Posix { - @Override - public T get(Class type) { - if (type.equals(FileSystems.class)) { - return type.cast(new PosixFileSystems()); - } - return super.get(type); - } - - @Override - public String getLibraryName() { - return "libnative-platform-osx-universal.dylib"; - } - } - - private static class Unsupported extends Platform { - public String getLibraryName() { - throw new NativeIntegrationUnavailableException(String.format( - "Native integration is not available for this operating system (%s %s)", getOperatingSystem(), - getArchitecture())); - } - } - - private static String getOperatingSystem() { - return System.getProperty("os.name"); - } -} +package net.rubygrapefruit.platform.internal; + +import net.rubygrapefruit.platform.*; +import net.rubygrapefruit.platform.Process; + +public abstract class Platform { + private static Platform platform; + + public static Platform current() { + synchronized (Platform.class) { + if (platform == null) { + String osName = getOperatingSystem().toLowerCase(); + String arch = getArchitecture(); + if (osName.contains("windows")) { + platform = new Windows(); + } else if (osName.contains("linux")) { + platform = new Linux(); + } else if (osName.contains("os x") && (arch.equals("i386") || arch.equals("x86_64"))) { + platform = new OsX(); + } else if (osName.contains("sunos")) { + platform = new Solaris(); + } else { + platform = new Unsupported(); + } + } + return platform; + } + } + + public boolean isWindows() { + return false; + } + + public T get(Class type) { + throw new NativeIntegrationUnavailableException(String.format("Native integration %s is not supported on this operating system (%s %s)", + type.getSimpleName(), getOperatingSystem(), getArchitecture())); + } + + public abstract String getLibraryName() throws NativeIntegrationUnavailableException; + + private static class Windows extends Platform { + @Override + public boolean isWindows() { + return true; + } + + @Override + public String getLibraryName() { + if (getArchitecture().equals("x86")) { + return "native-platform-windows-i386.dll"; + } + throw new NativeIntegrationUnavailableException(String.format( + "Native integration is not available for this architecture (%s) on Windows.", getArchitecture())); + } + + @Override + public T get(Class type) { + if (type.equals(net.rubygrapefruit.platform.Process.class)) { + return type.cast(new DefaultProcess()); + } + if (type.equals(Terminals.class)) { + return type.cast(new WindowsTerminals()); + } + if (type.equals(SystemInfo.class)) { + return type.cast(new DefaultSystemInfo()); + } + return super.get(type); + } + } + + private static abstract class Posix extends Platform { + @Override + public T get(Class type) { + if (type.equals(PosixFile.class)) { + return type.cast(new DefaultPosixFile()); + } + if (type.equals(Process.class)) { + return type.cast(new DefaultProcess()); + } + if (type.equals(Terminals.class)) { + return type.cast(new TerminfoTerminals()); + } + if (type.equals(SystemInfo.class)) { + return type.cast(new DefaultSystemInfo()); + } + return super.get(type); + } + } + + private abstract static class Unix extends Posix { + } + + private static class Linux extends Unix { + @Override + public T get(Class type) { + if (type.equals(FileSystems.class)) { + return type.cast(new PosixFileSystems()); + } + return super.get(type); + } + + @Override + public String getLibraryName() { + if (getArchitecture().equals("amd64")) { + return "libnative-platform-linux-amd64.so"; + } + if (getArchitecture().equals("i386") || getArchitecture().equals("x86")) { + return "libnative-platform-linux-i386.so"; + } + throw new NativeIntegrationUnavailableException(String.format( + "Native integration is not available for this architecture (%s) on Linux.", getArchitecture())); + } + } + + private static String getArchitecture() { + return System.getProperty("os.arch"); + } + + private static class Solaris extends Unix { + @Override + public String getLibraryName() { + return "libnative-platform-solaris.so"; + } + } + + private static class OsX extends Posix { + @Override + public T get(Class type) { + if (type.equals(FileSystems.class)) { + return type.cast(new PosixFileSystems()); + } + return super.get(type); + } + + @Override + public String getLibraryName() { + return "libnative-platform-osx-universal.dylib"; + } + } + + private static class Unsupported extends Platform { + public String getLibraryName() { + throw new NativeIntegrationUnavailableException(String.format( + "Native integration is not available for this operating system (%s %s)", getOperatingSystem(), + getArchitecture())); + } + } + + private static String getOperatingSystem() { + return System.getProperty("os.name"); + } +}