summaryrefslogtreecommitdiffstats
path: root/src/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java')
-rw-r--r--src/java/com/jogamp/common/ExceptionUtils.java96
-rw-r--r--src/java/com/jogamp/common/JogampRuntimeException.java7
-rw-r--r--src/java/com/jogamp/common/jvm/JNILibLoaderBase.java130
-rw-r--r--src/java/com/jogamp/common/net/AssetURLContext.java6
-rw-r--r--src/java/com/jogamp/common/net/Uri.java12
-rw-r--r--src/java/com/jogamp/common/nio/Buffers.java71
-rw-r--r--src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java98
-rw-r--r--src/java/com/jogamp/common/os/DynamicLibraryBundle.java14
-rw-r--r--src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java15
-rw-r--r--src/java/com/jogamp/common/os/NativeLibrary.java162
-rw-r--r--src/java/com/jogamp/common/util/ArrayHashMap.java305
-rw-r--r--src/java/com/jogamp/common/util/ArrayHashSet.java190
-rw-r--r--src/java/com/jogamp/common/util/Bitfield.java208
-rw-r--r--src/java/com/jogamp/common/util/CustomCompress.java167
-rw-r--r--src/java/com/jogamp/common/util/FunctionTask.java81
-rw-r--r--src/java/com/jogamp/common/util/IOUtil.java299
-rw-r--r--src/java/com/jogamp/common/util/IntBitfield.java170
-rw-r--r--src/java/com/jogamp/common/util/InterruptSource.java157
-rw-r--r--src/java/com/jogamp/common/util/InterruptedRuntimeException.java80
-rw-r--r--src/java/com/jogamp/common/util/JarUtil.java20
-rw-r--r--src/java/com/jogamp/common/util/JogampVersion.java24
-rw-r--r--src/java/com/jogamp/common/util/RunnableTask.java98
-rw-r--r--src/java/com/jogamp/common/util/SourcedInterruptedException.java166
-rw-r--r--src/java/com/jogamp/common/util/TaskBase.java43
-rw-r--r--src/java/com/jogamp/common/util/bin/exe-windows-i386.deflbin0 -> 300 bytes
-rw-r--r--src/java/com/jogamp/common/util/bin/exe-windows-i586-268b.binbin268 -> 0 bytes
-rw-r--r--src/java/com/jogamp/common/util/bin/exe-windows-x86_64.deflbin0 -> 345 bytes
-rw-r--r--src/java/com/jogamp/common/util/cache/TempFileCache.java5
-rw-r--r--src/java/com/jogamp/common/util/cache/TempJarCache.java21
-rw-r--r--src/java/com/jogamp/gluegen/ConstantDefinition.java831
-rw-r--r--src/java/com/jogamp/gluegen/DebugEmitter.java2
-rw-r--r--src/java/com/jogamp/gluegen/GlueGen.java11
-rw-r--r--src/java/com/jogamp/gluegen/JavaEmitter.java318
-rw-r--r--src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java2
-rw-r--r--src/java/com/jogamp/gluegen/cgram/TNode.java83
-rw-r--r--src/java/com/jogamp/gluegen/cgram/types/EnumType.java76
-rw-r--r--src/java/jogamp/android/launcher/MainLauncher.java8
-rw-r--r--src/java/jogamp/common/os/PlatformPropsImpl.java6
-rw-r--r--src/java/jogamp/common/util/Int32ArrayBitfield.java207
-rw-r--r--src/java/jogamp/common/util/Int32Bitfield.java163
-rw-r--r--src/java/jogamp/common/util/SyncedBitfield.java96
-rw-r--r--src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java4
-rw-r--r--src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java34
-rw-r--r--src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java4
-rw-r--r--src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java129
45 files changed, 3567 insertions, 1052 deletions
diff --git a/src/java/com/jogamp/common/ExceptionUtils.java b/src/java/com/jogamp/common/ExceptionUtils.java
index c848a99..386f42b 100644
--- a/src/java/com/jogamp/common/ExceptionUtils.java
+++ b/src/java/com/jogamp/common/ExceptionUtils.java
@@ -58,21 +58,97 @@ public class ExceptionUtils {
}
/**
- * Dumps a {@link Throwable} in a decorating message including the current thread name,
+ * Interface allowing {@link Throwable} specializations to provide their custom stack trace presentation.
+ * @since 2.3.2
+ */
+ public static interface CustomStackTrace {
+ /**
+ * Prints this {@link Throwable} as a cause to the output {@link PrintStream} {@code s},
+ * not iterating over all inner causes!
+ * @param s output stream
+ * @param causeStr the cause title
+ * @param causeIdx the cause index over all causes known by caller
+ * @param stackDepth the maximum depth for stack entries, or {@code -1} for all
+ * @since 2.3.2
+ */
+ void printCauseStack(final PrintStream s, final String causeStr, final int causeIdx, final int stackDepth);
+ /**
+ * Custom {@code printStackTrace} method, similar to {@link Throwable#printStackTrace(PrintStream, int, int)}.
+ * @param s output stream
+ * @param causeDepth the maximum depth for causes, or {@code -1} for all
+ * @param stackDepth the maximum depth for stack entries, or {@code -1} for all
+ */
+ void printStackTrace(final PrintStream s, final int causeDepth, final int stackDepth);
+ }
+
+ /**
+ * Prints the given {@link Throwable} cause to the output {@link PrintStream} {@code s}.
+ * @param s output stream
+ * @param causeStr the cause title
+ * @param cause the {@link Throwable} cause for output
+ * @param causeIdx the cause index over all causes known by caller
+ * @param causeDepth the maximum depth for causes, or {@code -1} for all
+ * @param stackDepth the maximum depth for stack entries, or {@code -1} for all
+ * @since 2.3.2
+ */
+ public static int printCause(final PrintStream s, final String causeStr, Throwable cause, final int causeIdx, final int causeDepth, final int stackDepth) {
+ int i=causeIdx;
+ for(; null != cause && ( -1 == causeDepth || i < causeDepth ); cause = cause.getCause()) {
+ if( cause instanceof CustomStackTrace ) {
+ ((CustomStackTrace)cause).printCauseStack(s, causeStr, i, stackDepth);
+ } else {
+ s.println(causeStr+"["+i+"] by "+cause.getClass().getSimpleName()+": "+cause.getMessage()+" on thread "+Thread.currentThread().getName());
+ dumpStack(s, cause.getStackTrace(), 0, stackDepth);
+ }
+ i++;
+ }
+ return i;
+ }
+
+ /**
+ * Prints the given {@link Throwable} to the output {@link PrintStream} {@code s}.
+ * @param s output stream
+ * @param t the {@link Throwable} for output
+ * @param causeDepth the maximum depth for causes, or {@code -1} for all
+ * @param stackDepth the maximum depth for stack entries, or {@code -1} for all
+ * @since 2.3.2
+ */
+ public static void printStackTrace(final PrintStream s, final Throwable t, final int causeDepth, final int stackDepth) {
+ if( t instanceof CustomStackTrace ) {
+ ((CustomStackTrace)t).printStackTrace(s, causeDepth, stackDepth);
+ } else {
+ s.println(t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName());
+ dumpStack(s, t.getStackTrace(), 0, stackDepth);
+ printCause(s, "Caused", t.getCause(), 0, causeDepth, stackDepth);
+ }
+ }
+
+ /**
+ * Dumps a {@link Throwable} to {@link System.err} in a decorating message including the current thread name,
* and its {@link #dumpStack(PrintStream, StackTraceElement[], int, int) stack trace}.
* <p>
* Implementation will iterate through all {@link Throwable#getCause() causes}.
* </p>
+ * @param additionalDescr additional text placed before the {@link Throwable} details.
+ * @param t the {@link Throwable} for output
*/
public static void dumpThrowable(final String additionalDescr, final Throwable t) {
- System.err.println("Caught "+additionalDescr+" "+t.getClass().getSimpleName()+": "+t.getMessage()+" on thread "+Thread.currentThread().getName());
- dumpStack(System.err, t.getStackTrace(), 0, -1);
- int causeDepth = 1;
- for( Throwable cause = t.getCause(); null != cause; cause = cause.getCause() ) {
- System.err.println("Caused["+causeDepth+"] by "+cause.getClass().getSimpleName()+": "+cause.getMessage()+" on thread "+Thread.currentThread().getName());
- dumpStack(System.err, cause.getStackTrace(), 0, -1);
- causeDepth++;
- }
+ dumpThrowable(additionalDescr, t, -1, -1);
+ }
+ /**
+ * Dumps a {@link Throwable} to {@link System.err} in a decorating message including the current thread name,
+ * and its {@link #dumpStack(PrintStream, StackTraceElement[], int, int) stack trace}.
+ * <p>
+ * Implementation will iterate through all {@link Throwable#getCause() causes}.
+ * </p>
+ * @param additionalDescr additional text placed before the {@link Throwable} details.
+ * @param t the {@link Throwable} for output
+ * @param causeDepth the maximum depth for causes, or {@code -1} for all
+ * @param stackDepth the maximum depth for stack entries, or {@code -1} for all
+ * @since 2.3.2
+ */
+ public static void dumpThrowable(final String additionalDescr, final Throwable t, final int causeDepth, final int stackDepth) {
+ System.err.print("Caught "+additionalDescr+" ");
+ printStackTrace(System.err, t, causeDepth, stackDepth);
}
-
}
diff --git a/src/java/com/jogamp/common/JogampRuntimeException.java b/src/java/com/jogamp/common/JogampRuntimeException.java
index d33d498..524bb93 100644
--- a/src/java/com/jogamp/common/JogampRuntimeException.java
+++ b/src/java/com/jogamp/common/JogampRuntimeException.java
@@ -28,9 +28,10 @@
package com.jogamp.common;
-/** A generic exception for Jogamp errors used throughout the binding
- as a substitute for {@link RuntimeException}. */
-
+/**
+ * A generic <i>unchecked exception</i> for Jogamp errors used throughout the binding
+ * as a substitute for {@link RuntimeException}.
+ */
@SuppressWarnings("serial")
public class JogampRuntimeException extends RuntimeException {
/** Constructs a JogampRuntimeException object. */
diff --git a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java
index 9b1865f..3ba8dff 100644
--- a/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java
+++ b/src/java/com/jogamp/common/jvm/JNILibLoaderBase.java
@@ -61,7 +61,18 @@ import jogamp.common.Debug;
import jogamp.common.os.PlatformPropsImpl;
public class JNILibLoaderBase {
- public static final boolean DEBUG = Debug.debug("JNILibLoader");
+ public static final boolean DEBUG;
+ protected static final boolean PERF;
+
+ static {
+ Debug.initSingleton();
+ DEBUG = Debug.debug("JNILibLoader");
+ PERF = DEBUG || PropertyAccess.isPropertyDefined("jogamp.debug.JNILibLoader.Perf", true);
+ }
+
+ private static final Object perfSync = new Object();
+ private static long perfTotal = 0;
+ private static long perfCount = 0;
public interface LoaderAction {
/**
@@ -177,6 +188,7 @@ public class JNILibLoaderBase {
msg.append(")");
System.err.println(msg.toString());
}
+ final long t0 = PERF ? System.currentTimeMillis() : 0; // 'Platform.currentTimeMillis()' not yet available!
boolean ok = false;
@@ -195,28 +207,9 @@ public class JNILibLoaderBase {
if (DEBUG) {
System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: nativeLibraryPath: %s%n", nativeLibraryPath);
}
- final ClassLoader cl = classFromJavaJar.getClassLoader();
- final URL nativeLibraryURI = cl.getResource(nativeLibraryPath);
- if (null != nativeLibraryURI) {
- // We probably have one big-fat jar file, containing java classes
- // and all native platform libraries under 'natives/os.and.arch'!
- final Uri nativeJarURI = JarUtil.getJarFileUri( jarSubUriRoot.getEncoded().concat(jarBasename) );
- try {
- if( TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, nativeLibraryPath) ) {
- ok = true;
- if (DEBUG) {
- System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: fat: %s -> %s%n", jarBasename, nativeJarURI);
- }
- }
- } catch(final Exception e) {
- if(DEBUG) {
- System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage());
- e.printStackTrace();
- }
- }
- }
- if (!ok) {
- // We assume one slim native jar file per 'os.and.arch'!
+ {
+ // Attempt-1 a 'one slim native jar file' per 'os.and.arch' layout
+ // with native platform libraries under 'natives/os.and.arch'!
final Uri nativeJarURI = JarUtil.getJarFileUri( jarSubUriRoot.getEncoded().concat(nativeJarBasename) );
if (DEBUG) {
@@ -224,7 +217,7 @@ public class JNILibLoaderBase {
}
try {
- ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, null /* nativeLibraryPath */);
+ ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, nativeLibraryPath);
} catch(final Exception e) {
if(DEBUG) {
System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage());
@@ -233,40 +226,75 @@ public class JNILibLoaderBase {
}
}
if (!ok) {
- // Attempt to find via ClassLoader and Native-Jar-Tag,
- // assuming one slim native jar file per 'os.and.arch'!
- final String moduleName;
+ final ClassLoader cl = classFromJavaJar.getClassLoader();
{
- final String packageName = classFromJavaJar.getPackage().getName();
- final int idx = packageName.lastIndexOf('.');
- if( 0 <= idx ) {
- moduleName = packageName.substring(idx+1);
- } else {
- moduleName = packageName;
+ // Attempt-2 a 'one big-fat jar file' layout, containing java classes
+ // and all native platform libraries under 'natives/os.and.arch' per platform!
+ final URL nativeLibraryURI = cl.getResource(nativeLibraryPath);
+ if (null != nativeLibraryURI) {
+ final Uri nativeJarURI = JarUtil.getJarFileUri( jarSubUriRoot.getEncoded().concat(jarBasename) );
+ try {
+ if( TempJarCache.addNativeLibs(classFromJavaJar, nativeJarURI, nativeLibraryPath) ) {
+ ok = true;
+ if (DEBUG) {
+ System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: fat: %s -> %s%n", jarBasename, nativeJarURI);
+ }
+ }
+ } catch(final Exception e) {
+ if(DEBUG) {
+ System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage());
+ e.printStackTrace();
+ }
+ }
}
}
- final String os_and_arch_dot = PlatformPropsImpl.os_and_arch.replace('-', '.');
- final String nativeJarTagClassName = nativeJarTagPackage + "." + moduleName + "." + os_and_arch_dot + ".TAG"; // TODO: sync with gluegen-cpptasks-base.xml
- try {
- if(DEBUG) {
- System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: Locating module %s, os.and.arch %s: %s%n",
- moduleName, os_and_arch_dot, nativeJarTagClassName);
- }
- final Uri nativeJarTagClassJarURI = JarUtil.getJarUri(nativeJarTagClassName, cl);
- if (DEBUG) {
- System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: %s -> %s%n", nativeJarTagClassName, nativeJarTagClassJarURI);
+ if (!ok) {
+ // Attempt-3 to find via ClassLoader and Native-Jar-Tag,
+ // assuming one slim native jar file per 'os.and.arch'
+ // and native platform libraries under 'natives/os.and.arch'!
+ final String moduleName;
+ {
+ final String packageName = classFromJavaJar.getPackage().getName();
+ final int idx = packageName.lastIndexOf('.');
+ if( 0 <= idx ) {
+ moduleName = packageName.substring(idx+1);
+ } else {
+ moduleName = packageName;
+ }
}
- ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarTagClassJarURI, null /* nativeLibraryPath */);
- } catch (final Exception e ) {
- if(DEBUG) {
- System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage());
- e.printStackTrace();
+ final String os_and_arch_dot = PlatformPropsImpl.os_and_arch.replace('-', '.');
+ final String nativeJarTagClassName = nativeJarTagPackage + "." + moduleName + "." + os_and_arch_dot + ".TAG"; // TODO: sync with gluegen-cpptasks-base.xml
+ try {
+ if(DEBUG) {
+ System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: Locating module %s, os.and.arch %s: %s%n",
+ moduleName, os_and_arch_dot, nativeJarTagClassName);
+ }
+ final Uri nativeJarTagClassJarURI = JarUtil.getJarUri(nativeJarTagClassName, cl);
+ if (DEBUG) {
+ System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ClassLoader/TAG: %s -> %s%n", nativeJarTagClassName, nativeJarTagClassJarURI);
+ }
+ ok = TempJarCache.addNativeLibs(classFromJavaJar, nativeJarTagClassJarURI, nativeLibraryPath);
+ } catch (final Exception e ) {
+ if(DEBUG) {
+ System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: Caught %s%n", e.getMessage());
+ e.printStackTrace();
+ }
}
}
}
- if (DEBUG) {
- System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl: ok: %b%n", ok);
+ if (DEBUG || PERF) {
+ final long tNow = System.currentTimeMillis() - t0;
+ final long tTotal, tCount;
+ synchronized(perfSync) {
+ tCount = perfCount+1;
+ tTotal = perfTotal + tNow;
+ perfTotal = tTotal;
+ perfCount = tCount;
+ }
+ final double tAvrg = tTotal / (double)tCount;
+ System.err.printf("JNILibLoaderBase: addNativeJarLibsImpl.X: %s / %s -> ok: %b; duration: now %d ms, total %d ms (count %d, avrg %.3f ms)%n",
+ jarBasename, nativeJarBasename, ok, tNow, tTotal, tCount, tAvrg);
}
return ok;
}
@@ -585,7 +613,7 @@ public class JNILibLoaderBase {
if(DEBUG) {
System.err.println("ERROR (retry w/ enumLibPath) - "+ex1.getMessage());
}
- final List<String> possiblePaths = NativeLibrary.enumerateLibraryPaths(libraryName, libraryName, libraryName, true, cl);
+ final List<String> possiblePaths = NativeLibrary.enumerateLibraryPaths(libraryName, libraryName, libraryName, cl);
// Iterate down these and see which one if any we can actually find.
for (final Iterator<String> iter = possiblePaths.iterator(); 0 == mode && iter.hasNext(); ) {
final String path = iter.next();
diff --git a/src/java/com/jogamp/common/net/AssetURLContext.java b/src/java/com/jogamp/common/net/AssetURLContext.java
index af90c01..8462a41 100644
--- a/src/java/com/jogamp/common/net/AssetURLContext.java
+++ b/src/java/com/jogamp/common/net/AssetURLContext.java
@@ -164,7 +164,7 @@ public abstract class AssetURLContext implements PiggybackURLContext {
url = new URL(path);
conn = open(url);
type = null != conn ? 1 : -1;
- } catch(final MalformedURLException e1) { if(DEBUG) { System.err.println("ERR(0): "+e1.getMessage()); } }
+ } catch(final MalformedURLException e1) { if(DEBUG) { System.err.println("FAIL(1): "+e1.getMessage()); } }
if(null == conn && null != cl) {
// lookup via ClassLoader .. cleanup leading '/'
@@ -189,7 +189,7 @@ public abstract class AssetURLContext implements PiggybackURLContext {
conn = open(url);
type = null != conn ? 3 : -1;
}
- } catch (final Throwable e) { if(DEBUG) { System.err.println("ERR(1): "+e.getMessage()); } }
+ } catch (final Throwable e) { if(DEBUG) { System.err.println("FAIL(3): "+e.getMessage()); } }
}
if(DEBUG) {
@@ -209,7 +209,7 @@ public abstract class AssetURLContext implements PiggybackURLContext {
final URLConnection c = url.openConnection();
c.connect(); // redundant
return c;
- } catch (final IOException ioe) { if(DEBUG) { System.err.println("ERR: "+ioe.getMessage()); } }
+ } catch (final IOException ioe) { if(DEBUG) { System.err.println("FAIL(2): "+ioe.getMessage()); } }
return null;
}
diff --git a/src/java/com/jogamp/common/net/Uri.java b/src/java/com/jogamp/common/net/Uri.java
index 6bafba2..bca90bf 100644
--- a/src/java/com/jogamp/common/net/Uri.java
+++ b/src/java/com/jogamp/common/net/Uri.java
@@ -1232,7 +1232,15 @@ public class Uri {
/** Returns true, if this instance is a {@code file} {@code scheme}, otherwise false. */
public final boolean isFileScheme() {
- return FILE_SCHEME.equals( scheme.get() );
+ return null != scheme && FILE_SCHEME.equals( scheme.get() );
+ }
+
+ /**
+ * Returns true, if this instance is a {@code jar} {@code scheme}, otherwise false.
+ * @since 2.3.2
+ */
+ public final boolean isJarScheme() {
+ return null != scheme && JAR_SCHEME.equals( scheme.get() );
}
/**
@@ -1386,7 +1394,7 @@ public class Uri {
if( !emptyString(schemeSpecificPart) ) {
final StringBuilder sb = new StringBuilder();
- if( scheme.equals(JAR_SCHEME) ) {
+ if( isJarScheme() ) {
final int idx = schemeSpecificPart.lastIndexOf(JAR_SCHEME_SEPARATOR);
if (0 > idx) {
throw new URISyntaxException(input.get(), "missing jar separator");
diff --git a/src/java/com/jogamp/common/nio/Buffers.java b/src/java/com/jogamp/common/nio/Buffers.java
index aae2be8..fb23627 100644
--- a/src/java/com/jogamp/common/nio/Buffers.java
+++ b/src/java/com/jogamp/common/nio/Buffers.java
@@ -39,6 +39,7 @@
*/
package com.jogamp.common.nio;
+import java.lang.reflect.Method;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -48,9 +49,14 @@ import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import com.jogamp.common.util.ReflectionUtil;
import com.jogamp.common.util.ValueConv;
+import jogamp.common.Debug;
+
/**
* Utility methods allowing easy {@link java.nio.Buffer} manipulations.
*
@@ -60,6 +66,11 @@ import com.jogamp.common.util.ValueConv;
*/
public class Buffers {
+ static final boolean DEBUG;
+ static {
+ DEBUG = Debug.debug("Buffers");
+ }
+
public static final int SIZEOF_BYTE = 1;
public static final int SIZEOF_SHORT = 2;
public static final int SIZEOF_CHAR = 2;
@@ -1150,4 +1161,64 @@ public class Buffers {
return sb;
}
+ /**
+ * Access to NIO {@link sun.misc.Cleaner}, allowing caller to deterministically clean a given {@link sun.nio.ch.DirectBuffer}.
+ */
+ public static class Cleaner {
+ private static final Method mbbCleaner;
+ private static final Method cClean;
+ private static final boolean hasCleaner;
+ /** OK to be lazy on thread synchronization, just for early out **/
+ private static volatile boolean cleanerError;
+ static {
+ final Method[] _mbbCleaner = { null };
+ final Method[] _cClean = { null };
+ if( AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+ @Override
+ public Boolean run() {
+ try {
+ _mbbCleaner[0] = ReflectionUtil.getMethod("sun.nio.ch.DirectBuffer", "cleaner", null, Buffers.class.getClassLoader());
+ _mbbCleaner[0].setAccessible(true);
+ _cClean[0] = Class.forName("sun.misc.Cleaner").getMethod("clean");
+ _cClean[0].setAccessible(true);
+ return Boolean.TRUE;
+ } catch(final Throwable t) {
+ if( DEBUG ) {
+ System.err.println("Caught "+t.getMessage());
+ t.printStackTrace();
+ }
+ return Boolean.FALSE;
+ } } } ).booleanValue() ) {
+ mbbCleaner = _mbbCleaner[0];
+ cClean = _cClean[0];
+ hasCleaner = null != mbbCleaner && null != cClean;
+ } else {
+ mbbCleaner = null;
+ cClean = null;
+ hasCleaner = false;
+ }
+ cleanerError = !hasCleaner;
+ }
+ /**
+ * If {@code b} is an direct NIO buffer, i.e {@link sun.nio.ch.DirectBuffer},
+ * calls it's {@link sun.misc.Cleaner} instance {@code clean()} method.
+ * @return {@code true} if successful, otherwise {@code false}.
+ */
+ public static boolean clean(final Buffer b) {
+ if( !hasCleaner || cleanerError || !b.isDirect() ) {
+ return false;
+ }
+ try {
+ cClean.invoke(mbbCleaner.invoke(b));
+ return true;
+ } catch(final Throwable t) {
+ cleanerError = true;
+ if( DEBUG ) {
+ System.err.println("Caught "+t.getMessage());
+ t.printStackTrace();
+ }
+ return false;
+ }
+ }
+ }
}
diff --git a/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java b/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java
index f8d5857..6a56d6e 100644
--- a/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java
+++ b/src/java/com/jogamp/common/nio/MappedByteBufferInputStream.java
@@ -33,13 +33,10 @@ import java.io.OutputStream;
import java.io.PrintStream;
import java.io.RandomAccessFile;
import java.lang.ref.WeakReference;
-import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import jogamp.common.Debug;
@@ -163,10 +160,6 @@ public class MappedByteBufferInputStream extends InputStream {
private int refCount;
- private Method mbbCleaner;
- private Method cClean;
- private boolean cleanerInit;
- private boolean hasCleaner;
private CacheMode cmode;
private int sliceIdx;
@@ -191,10 +184,12 @@ public class MappedByteBufferInputStream extends InputStream {
}
}
long fcSz = 0, pos = 0, rem = 0;
- try {
- fcSz = fc.size();
- } catch (final IOException e) {
- e.printStackTrace();
+ if( fc.isOpen() ) {
+ try {
+ fcSz = fc.size();
+ } catch (final IOException e) {
+ e.printStackTrace();
+ }
}
if( 0 < refCount ) {
try {
@@ -229,14 +224,16 @@ public class MappedByteBufferInputStream extends InputStream {
notifyLengthChange( totalSize );
this.refCount = 1;
- this.cleanerInit = false;
- this.hasCleaner = false;
this.cmode = cmode;
this.sliceIdx = currSliceIdx;
this.mark = -1;
currentSlice().position(0);
+
+ if( MappedByteBufferInputStream.DEBUG ) {
+ this.dbgDump("CTOR", System.err);
+ }
}
/**
@@ -330,6 +327,9 @@ public class MappedByteBufferInputStream extends InputStream {
}
}
}
+ if( MappedByteBufferInputStream.DEBUG ) {
+ this.dbgDump("Close", System.err);
+ }
}
final FileChannel.MapMode getMapMode() { return mmode; }
@@ -441,10 +441,9 @@ public class MappedByteBufferInputStream extends InputStream {
}
position2( Math.min(prePosition, newTotalSize) ); // -> clipped position (set currSlice and re-map/-pos buffer)
}
- /* if( DEBUG ) {
- System.err.println("notifyLengthChange.X: "+slices[currSlice]);
- dbgDump("notifyLengthChange.X:", System.err);
- } */
+ if( MappedByteBufferInputStream.DEBUG ) {
+ this.dbgDump("NotifyLengthChange", System.err);
+ }
}
/**
@@ -551,6 +550,21 @@ public class MappedByteBufferInputStream extends InputStream {
}
}
+ /**
+ * Releases the mapped {@link ByteBuffer} slices.
+ * @throws IOException if a buffer slice operation failed.
+ */
+ public final synchronized void flushSlices() throws IOException {
+ if( null != slices ) {
+ for(int i=0; i<sliceCount; i++) {
+ flushSlice(i, synchronous);
+ }
+ }
+ if( MappedByteBufferInputStream.DEBUG ) {
+ this.dbgDump("FlushSlices", System.err);
+ }
+ }
+
synchronized void syncSlice(final ByteBuffer s) throws IOException {
syncSlice(s, synchronous);
}
@@ -630,58 +644,16 @@ public class MappedByteBufferInputStream extends InputStream {
}
}
private synchronized boolean cleanBuffer(final ByteBuffer mbb, final boolean syncBuffer) throws IOException {
- if( !cleanerInit ) {
- initCleaner(mbb);
- }
syncSlice(mbb, syncBuffer);
if( !mbb.isDirect() ) {
return false;
}
- boolean res = false;
- if ( hasCleaner ) {
- try {
- cClean.invoke(mbbCleaner.invoke(mbb));
- res = true;
- } catch(final Throwable t) {
- hasCleaner = false;
- if( DEBUG ) {
- System.err.println("Caught "+t.getMessage());
- t.printStackTrace();
- }
- }
- }
- if( !res && CacheMode.FLUSH_PRE_HARD == cmode ) {
+ if( !Buffers.Cleaner.clean(mbb) && CacheMode.FLUSH_PRE_HARD == cmode ) {
cmode = CacheMode.FLUSH_PRE_SOFT;
+ return false;
+ } else {
+ return true;
}
- return res;
- }
- private synchronized void initCleaner(final ByteBuffer bb) {
- final Method[] _mbbCleaner = { null };
- final Method[] _cClean = { null };
- AccessController.doPrivileged(new PrivilegedAction<Object>() {
- @Override
- public Object run() {
- try {
- _mbbCleaner[0] = bb.getClass().getMethod("cleaner");
- _mbbCleaner[0].setAccessible(true);
- _cClean[0] = Class.forName("sun.misc.Cleaner").getMethod("clean");
- _cClean[0].setAccessible(true);
- } catch(final Throwable t) {
- if( DEBUG ) {
- System.err.println("Caught "+t.getMessage());
- t.printStackTrace();
- }
- }
- return null;
- } } );
- mbbCleaner = _mbbCleaner[0];
- cClean = _cClean[0];
- final boolean res = null != mbbCleaner && null != cClean;
- if( DEBUG ) {
- System.err.println("initCleaner: Has cleaner: "+res+", mbbCleaner "+mbbCleaner+", cClean "+cClean);
- }
- hasCleaner = res;
- cleanerInit = true;
}
/**
diff --git a/src/java/com/jogamp/common/os/DynamicLibraryBundle.java b/src/java/com/jogamp/common/os/DynamicLibraryBundle.java
index 2f51e2e..a3d6198 100644
--- a/src/java/com/jogamp/common/os/DynamicLibraryBundle.java
+++ b/src/java/com/jogamp/common/os/DynamicLibraryBundle.java
@@ -249,9 +249,12 @@ public class DynamicLibraryBundle implements DynamicLookupHelper {
return aptr;
}
- protected static final NativeLibrary loadFirstAvailable(final List<String> libNames, final ClassLoader loader, final boolean global) throws SecurityException {
+ protected static final NativeLibrary loadFirstAvailable(final List<String> libNames,
+ final boolean searchSystemPath,
+ final boolean searchSystemPathFirst,
+ final ClassLoader loader, final boolean global) throws SecurityException {
for (int i=0; i < libNames.size(); i++) {
- final NativeLibrary lib = NativeLibrary.open(libNames.get(i), loader, global);
+ final NativeLibrary lib = NativeLibrary.open(libNames.get(i), searchSystemPath, searchSystemPathFirst, loader, global);
if (lib != null) {
return lib;
}
@@ -269,7 +272,10 @@ public class DynamicLibraryBundle implements DynamicLookupHelper {
for (i=0; i < toolLibNames.size(); i++) {
final List<String> libNames = toolLibNames.get(i);
if( null != libNames && libNames.size() > 0 ) {
- lib = loadFirstAvailable(libNames, cl, info.shallLinkGlobal());
+ lib = loadFirstAvailable(libNames,
+ info.searchToolLibInSystemPath(),
+ info.searchToolLibSystemPathFirst(),
+ cl, info.shallLinkGlobal());
if ( null == lib ) {
if(DEBUG) {
System.err.println("Unable to load any Tool library of: "+libNames);
@@ -361,7 +367,7 @@ public class DynamicLibraryBundle implements DynamicLookupHelper {
final long addr = info.toolGetProcAddress(toolGetProcAddressHandle, funcName);
if(DEBUG_LOOKUP) {
if(0!=addr) {
- System.err.println("Lookup-Tool: <"+funcName+"> 0x"+Long.toHexString(addr));
+ System.err.println("Lookup-Tool: <"+funcName+"> 0x"+Long.toHexString(addr)+", via tool 0x"+Long.toHexString(toolGetProcAddressHandle));
}
}
return addr;
diff --git a/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java b/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java
index 7be5f25..01068b4 100644
--- a/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java
+++ b/src/java/com/jogamp/common/os/DynamicLibraryBundleInfo.java
@@ -37,6 +37,21 @@ public interface DynamicLibraryBundleInfo {
public static final boolean DEBUG = DynamicLibraryBundle.DEBUG;
/**
+ * Returns {@code true} if tool libraries shall be searched in the system path <i>(default)</i>, otherwise {@code false}.
+ * @since 2.4.0
+ */
+ public boolean searchToolLibInSystemPath();
+
+ /**
+ * Returns {@code true} if system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last.
+ * <p>
+ * If {@link #searchToolLibInSystemPath()} is {@code false} the return value is ignored.
+ * </p>
+ * @since 2.4.0
+ */
+ public boolean searchToolLibSystemPathFirst();
+
+ /**
* If a {@link SecurityManager} is installed, user needs link permissions
* for the named libraries.
*
diff --git a/src/java/com/jogamp/common/os/NativeLibrary.java b/src/java/com/jogamp/common/os/NativeLibrary.java
index 747f92d..2ba2581 100644
--- a/src/java/com/jogamp/common/os/NativeLibrary.java
+++ b/src/java/com/jogamp/common/os/NativeLibrary.java
@@ -138,32 +138,47 @@ public final class NativeLibrary implements DynamicLookupHelper {
}
/** Opens the given native library, assuming it has the same base
- name on all platforms, looking first in the system's search
- path, and in the context of the specified ClassLoader, which is
- used to help find the library in the case of e.g. Java Web Start.
- * @throws SecurityException if user is not granted access for the named library.
- */
- public static final NativeLibrary open(final String libName, final ClassLoader loader) throws SecurityException {
- return open(libName, libName, libName, true, loader, true);
- }
-
- /** Opens the given native library, assuming it has the same base
- name on all platforms, looking first in the system's search
- path, and in the context of the specified ClassLoader, which is
- used to help find the library in the case of e.g. Java Web Start.
+ name on all platforms.
+ <p>
+ The {@code searchSystemPath} argument changes the behavior to
+ either use the default system path or not at all.
+ </p>
+ <p>
+ Assuming {@code searchSystemPath} is {@code true},
+ the {@code searchSystemPathFirst} argument changes the behavior to first
+ search the default system path rather than searching it last.
+ </p>
+ * @param libName library name, with or without prefix and suffix
+ * @param searchSystemPath if {@code true} library shall be searched in the system path <i>(default)</i>, otherwise {@code false}.
+ * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last.
+ * if {@code searchSystemPath} is {@code false} this argument is ignored.
+ * @param loader {@link ClassLoader} to locate the library
+ * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process.
+ * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded.
* @throws SecurityException if user is not granted access for the named library.
+ * @since 2.4.0
*/
- public static final NativeLibrary open(final String libName, final ClassLoader loader, final boolean global) throws SecurityException {
- return open(libName, libName, libName, true, loader, global);
+ public static final NativeLibrary open(final String libName,
+ final boolean searchSystemPath,
+ final boolean searchSystemPathFirst,
+ final ClassLoader loader, final boolean global) throws SecurityException {
+ return open(libName, libName, libName, searchSystemPath, searchSystemPathFirst, loader, global);
}
/** Opens the given native library, assuming it has the given base
names (no "lib" prefix or ".dll/.so/.dylib" suffix) on the
Windows, Unix and Mac OS X platforms, respectively, and in the
context of the specified ClassLoader, which is used to help find
- the library in the case of e.g. Java Web Start. The
- searchSystemPathFirst argument changes the behavior to first
+ the library in the case of e.g. Java Web Start.
+ <p>
+ The {@code searchSystemPath} argument changes the behavior to
+ either use the default system path or not at all.
+ </p>
+ <p>
+ Assuming {@code searchSystemPath} is {@code true},
+ the {@code searchSystemPathFirst} argument changes the behavior to first
search the default system path rather than searching it last.
+ </p>
Note that we do not currently handle DSO versioning on Unix.
Experience with JOAL and OpenAL has shown that it is extremely
problematic to rely on a specific .so version (for one thing,
@@ -171,28 +186,27 @@ public final class NativeLibrary implements DynamicLookupHelper {
ending in .so, for example .so.0), and in general if this
dynamic loading facility is used correctly the version number
will be irrelevant.
+ * @param windowsLibName windows library name, with or without prefix and suffix
+ * @param unixLibName unix library name, with or without prefix and suffix
+ * @param macOSXLibName mac-osx library name, with or without prefix and suffix
+ * @param searchSystemPath if {@code true} library shall be searched in the system path <i>(default)</i>, otherwise {@code false}.
+ * @param searchSystemPathFirst if {@code true} system path shall be searched <i>first</i> <i>(default)</i>, rather than searching it last.
+ * if {@code searchSystemPath} is {@code false} this argument is ignored.
+ * @param loader {@link ClassLoader} to locate the library
+ * @param global if {@code true} allows system wide access of the loaded library, otherwise access is restricted to the process.
+ * @return {@link NativeLibrary} instance or {@code null} if library could not be loaded.
* @throws SecurityException if user is not granted access for the named library.
*/
public static final NativeLibrary open(final String windowsLibName,
final String unixLibName,
final String macOSXLibName,
- final boolean searchSystemPathFirst,
- final ClassLoader loader) throws SecurityException {
- return open(windowsLibName, unixLibName, macOSXLibName, searchSystemPathFirst, loader, true);
- }
-
- /**
- * @throws SecurityException if user is not granted access for the named library.
- */
- public static final NativeLibrary open(final String windowsLibName,
- final String unixLibName,
- final String macOSXLibName,
+ final boolean searchSystemPath,
final boolean searchSystemPathFirst,
final ClassLoader loader, final boolean global) throws SecurityException {
final List<String> possiblePaths = enumerateLibraryPaths(windowsLibName,
unixLibName,
macOSXLibName,
- searchSystemPathFirst,
+ searchSystemPath, searchSystemPathFirst,
loader);
Platform.initSingleton(); // loads native gluegen-rt library
@@ -367,12 +381,33 @@ public final class NativeLibrary implements DynamicLookupHelper {
/** Given the base library names (no prefixes/suffixes) for the
various platforms, enumerate the possible locations and names of
- the indicated native library on the system. */
+ the indicated native library on the system not using the system path. */
+ public static final List<String> enumerateLibraryPaths(final String windowsLibName,
+ final String unixLibName,
+ final String macOSXLibName,
+ final ClassLoader loader) {
+ return enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName,
+ false /* searchSystemPath */, false /* searchSystemPathFirst */,
+ loader);
+ }
+ /** Given the base library names (no prefixes/suffixes) for the
+ various platforms, enumerate the possible locations and names of
+ the indicated native library on the system using the system path. */
public static final List<String> enumerateLibraryPaths(final String windowsLibName,
final String unixLibName,
final String macOSXLibName,
final boolean searchSystemPathFirst,
final ClassLoader loader) {
+ return enumerateLibraryPaths(windowsLibName, unixLibName, macOSXLibName,
+ true /* searchSystemPath */, searchSystemPathFirst,
+ loader);
+ }
+ private static final List<String> enumerateLibraryPaths(final String windowsLibName,
+ final String unixLibName,
+ final String macOSXLibName,
+ final boolean searchSystemPath,
+ final boolean searchSystemPathFirst,
+ final ClassLoader loader) {
final List<String> paths = new ArrayList<String>();
final String libName = selectName(windowsLibName, unixLibName, macOSXLibName);
if (libName == null) {
@@ -388,11 +423,18 @@ public final class NativeLibrary implements DynamicLookupHelper {
final String[] baseNames = buildNames(libName);
- if (searchSystemPathFirst) {
- // Add just the library names to use the OS's search algorithm
- for (int i = 0; i < baseNames.length; i++) {
- paths.add(baseNames[i]);
- }
+ if( searchSystemPath && searchSystemPathFirst ) {
+ // Add just the library names to use the OS's search algorithm
+ for (int i = 0; i < baseNames.length; i++) {
+ paths.add(baseNames[i]);
+ }
+ // Add probable Mac OS X-specific paths
+ if ( isOSX ) {
+ // Add historical location
+ addPaths("/Library/Frameworks/" + libName + ".Framework", baseNames, paths);
+ // Add current location
+ addPaths("/System/Library/Frameworks/" + libName + ".Framework", baseNames, paths);
+ }
}
// The idea to ask the ClassLoader to find the library is borrowed
@@ -412,24 +454,25 @@ public final class NativeLibrary implements DynamicLookupHelper {
if(null != usrPath) {
count++;
}
- final String sysPath = System.getProperty("sun.boot.library.path");
- if(null != sysPath) {
- count++;
+ final String sysPath;
+ if( searchSystemPath ) {
+ sysPath = System.getProperty("sun.boot.library.path");
+ if(null != sysPath) {
+ count++;
+ }
+ } else {
+ sysPath = null;
}
final String[] res = new String[count];
int i=0;
- if (searchSystemPathFirst) {
- if(null != sysPath) {
- res[i++] = sysPath;
- }
+ if( null != sysPath && searchSystemPathFirst ) {
+ res[i++] = sysPath;
}
if(null != usrPath) {
res[i++] = usrPath;
}
- if (!searchSystemPathFirst) {
- if(null != sysPath) {
- res[i++] = sysPath;
- }
+ if( null != sysPath && !searchSystemPathFirst ) {
+ res[i++] = sysPath;
}
return res;
}
@@ -453,19 +496,22 @@ public final class NativeLibrary implements DynamicLookupHelper {
});
addPaths(userDir, baseNames, paths);
- if (!searchSystemPathFirst) {
- // Add just the library names to use the OS's search algorithm
- for (int i = 0; i < baseNames.length; i++) {
- paths.add(baseNames[i]);
- }
- }
+ // Add current working directory + natives/os-arch/ + library names
+ // to handle Bug 1145 cc1 using an unpacked fat-jar
+ addPaths(userDir+File.separator+"natives"+File.separator+PlatformPropsImpl.os_and_arch+File.separator, baseNames, paths);
- // Add probable Mac OS X-specific paths
- if ( isOSX ) {
- // Add historical location
- addPaths("/Library/Frameworks/" + libName + ".Framework", baseNames, paths);
- // Add current location
- addPaths("/System/Library/Frameworks/" + libName + ".Framework", baseNames, paths);
+ if( searchSystemPath && !searchSystemPathFirst ) {
+ // Add just the library names to use the OS's search algorithm
+ for (int i = 0; i < baseNames.length; i++) {
+ paths.add(baseNames[i]);
+ }
+ // Add probable Mac OS X-specific paths
+ if ( isOSX ) {
+ // Add historical location
+ addPaths("/Library/Frameworks/" + libName + ".Framework", baseNames, paths);
+ // Add current location
+ addPaths("/System/Library/Frameworks/" + libName + ".Framework", baseNames, paths);
+ }
}
return paths;
diff --git a/src/java/com/jogamp/common/util/ArrayHashMap.java b/src/java/com/jogamp/common/util/ArrayHashMap.java
new file mode 100644
index 0000000..35a484b
--- /dev/null
+++ b/src/java/com/jogamp/common/util/ArrayHashMap.java
@@ -0,0 +1,305 @@
+/**
+ * Copyright 2015 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.common.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * {@link HashMap} implementation backed by an {@link ArrayList} to preserve order of values.
+ *
+ * Implementation properties are:
+ * <ul>
+ * <li> Unique elements utilizing {@link java.lang.Object#hashCode()} for O(1) operations, see below.</li>
+ * <li> Java 1.5 compatible</li>
+ * </ul>
+ *
+ * O(1) operations:
+ * <ul>
+ * <li> put new key-value-pair(s) </li>
+ * <li> test for containment </li>
+ * <li> trying to remove non existent elements </li>
+ * </ul>
+ *
+ * O(n) operations:
+ * <ul>
+ * <li> put existing key-value-pair(s) </li>
+ * <li> removing existing elements</li>
+ * </ul>
+ *
+ * For thread safety, the application shall decorate access to instances via
+ * {@link com.jogamp.common.util.locks.RecursiveLock}.
+ *
+*/
+public class ArrayHashMap<K, V>
+ implements Cloneable, Map<K, V>
+{
+ /**
+ * Default load factor: {@value}
+ */
+ public static final float DEFAULT_LOAD_FACTOR = 0.75f;
+ /**
+ * The default initial capacity: {@value}
+ */
+ public static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ private final HashMap<K,V> map; // key -> object
+ private final ArrayList<V> data; // list of objects
+ private final boolean supportNullValue;
+
+ /**
+ *
+ * @param supportNullValue Use {@code true} for default behavior, i.e. {@code null} can be a valid value.
+ * Use {@code false} if {@code null} is not a valid value,
+ * here {@link #put(Object, Object)} and {@link #remove(Object)} will be optimized.
+ * @param initialCapacity use {@link #DEFAULT_INITIAL_CAPACITY} for default
+ * @param loadFactor use {@link #DEFAULT_LOAD_FACTOR} for default
+ * @see #supportsNullValue()
+ */
+ public ArrayHashMap(final boolean supportNullValue, final int initialCapacity, final float loadFactor) {
+ this.map = new HashMap<K,V>(initialCapacity, loadFactor);
+ this.data = new ArrayList<V>(initialCapacity);
+ this.supportNullValue = supportNullValue;
+ }
+
+ /**
+ * @return a shallow copy of this ArrayHashMap, elements are not copied.
+ */
+ public ArrayHashMap(final ArrayHashMap<K, V> o) {
+ map = new HashMap<K, V>(o.map);
+ data = new ArrayList<V>(o.data);
+ supportNullValue = o.supportNullValue;
+ }
+
+ /**
+ * Returns {@code true} for default behavior, i.e. {@code null} can be a valid value.
+ * <p>
+ * Returns {@code false} if {@code null} is not a valid value,
+ * here {@link #put(Object, Object)} and {@link #remove(Object)} are optimized operations.
+ * </p>
+ * @see #ArrayHashMap(boolean, int, float)
+ */
+ public final boolean supportsNullValue() { return supportNullValue; }
+
+ //
+ // Cloneable
+ //
+
+ /**
+ * Implementation uses {@link #ArrayHashMap(ArrayHashMap)}.
+ * @return a shallow copy of this ArrayHashMap, elements are not copied.
+ */
+ @Override
+ public final Object clone() {
+ return new ArrayHashMap<K, V>(this);
+ }
+
+ /**
+ * Returns this object ordered ArrayList. Use w/ care, it's not a copy.
+ * @see #toArrayList()
+ */
+ public final ArrayList<V> getData() { return data; }
+
+ /**
+ * @return a shallow copy of this ArrayHashMap's ArrayList, elements are not copied.
+ * @see #getData()
+ */
+ public final ArrayList<V> toArrayList() {
+ return new ArrayList<V>(data);
+ }
+
+ /** Returns this object hash map. Use w/ care, it's not a copy. */
+ public final HashMap<K,V> getMap() { return map; }
+
+ @Override
+ public final String toString() { return data.toString(); }
+
+ //
+ // Map
+ //
+
+ @Override
+ public final void clear() {
+ data.clear();
+ map.clear();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return map.keySet();
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * See {@link #getData()} and {@link #toArrayList()}.
+ * </p>
+ * @see #getData()
+ * @see #toArrayList()
+ */
+ @Override
+ public Collection<V> values() {
+ return map.values();
+ }
+
+ @Override
+ public Set<java.util.Map.Entry<K, V>> entrySet() {
+ return map.entrySet();
+ }
+
+ @Override
+ public final V get(final Object key) {
+ return map.get(key);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This is an O(1) operation, in case the key does not exist,
+ * otherwise O(n).
+ * </p>
+ * @throws NullPointerException if {@code value} is {@code null} but {@link #supportsNullValue()} == {@code false}
+ */
+ @Override
+ public final V put(final K key, final V value) throws NullPointerException {
+ final V oldValue;
+ if( supportNullValue ) {
+ // slow path
+ final boolean exists = map.containsKey(key);
+ if(!exists) {
+ // !exists
+ if( null != ( oldValue = map.put(key, value) ) ) {
+ // slips a valid null ..
+ throw new InternalError("Already existing, but checked before: "+key+" -> "+oldValue);
+ }
+ } else {
+ // exists
+ oldValue = map.put(key, value);
+ if( !data.remove(oldValue) ) {
+ throw new InternalError("Already existing, but not in list: "+oldValue);
+ }
+ }
+ } else {
+ checkNullValue(value);
+ // fast path
+ if( null != ( oldValue = map.put(key, value) ) ) {
+ // exists
+ if( !data.remove(oldValue) ) {
+ throw new InternalError("Already existing, but not in list: "+oldValue);
+ }
+ }
+ }
+ if(!data.add(value)) {
+ throw new InternalError("Couldn't add value to list: "+value);
+ }
+ return oldValue;
+ }
+
+ @Override
+ public void putAll(final Map<? extends K, ? extends V> m) {
+ for (final Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
+ final Map.Entry<? extends K, ? extends V> e = i.next();
+ put(e.getKey(), e.getValue());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This is an O(1) operation, in case the key does not exist,
+ * otherwise O(n).
+ * </p>
+ */
+ @Override
+ public final V remove(final Object key) {
+ if( supportNullValue ) {
+ if( map.containsKey(key) ) {
+ // exists
+ final V oldValue = map.remove(key);
+ if ( !data.remove(oldValue) ) {
+ throw new InternalError("Couldn't remove prev mapped pair: "+key+" -> "+oldValue);
+ }
+ return oldValue;
+ }
+ } else {
+ final V oldValue;
+ if ( null != (oldValue = map.remove(key) ) ) {
+ // exists
+ if ( !data.remove(oldValue) ) {
+ throw new InternalError("Couldn't remove prev mapped pair: "+key+" -> "+oldValue);
+ }
+ }
+ return oldValue;
+ }
+ return null;
+ }
+
+ @Override
+ public final boolean containsKey(final Object key) {
+ return map.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(final Object value) {
+ return map.containsValue(value);
+ }
+
+ @Override
+ public final boolean equals(final Object arrayHashMap) {
+ if ( !(arrayHashMap instanceof ArrayHashMap) ) {
+ return false;
+ }
+ return map.equals(((ArrayHashMap<?,?>)arrayHashMap).map);
+ }
+
+ @Override
+ public final int hashCode() {
+ return map.hashCode();
+ }
+
+ @Override
+ public final boolean isEmpty() {
+ return data.isEmpty();
+ }
+
+ @Override
+ public final int size() {
+ return data.size();
+ }
+
+ private static final void checkNullValue(final Object value) throws NullPointerException {
+ if( null == value ) {
+ throw new NullPointerException("Null value not supported");
+ }
+ }
+}
diff --git a/src/java/com/jogamp/common/util/ArrayHashSet.java b/src/java/com/jogamp/common/util/ArrayHashSet.java
index 34e84c4..c0ac2fa 100644
--- a/src/java/com/jogamp/common/util/ArrayHashSet.java
+++ b/src/java/com/jogamp/common/util/ArrayHashSet.java
@@ -68,23 +68,52 @@ import java.util.ListIterator;
public class ArrayHashSet<E>
implements Cloneable, Collection<E>, List<E>
{
+ /**
+ * Default load factor: {@value}
+ */
+ public static final float DEFAULT_LOAD_FACTOR = 0.75f;
+ /**
+ * The default initial capacity: {@value}
+ */
+ public static final int DEFAULT_INITIAL_CAPACITY = 16;
+
private final HashMap<E,E> map; // object -> object
private final ArrayList<E> data; // list of objects
+ private final boolean supportNullValue;
- public ArrayHashSet() {
- map = new HashMap<E,E>();
- data = new ArrayList<E>();
+ /**
+ *
+ * @param supportNullValue Use {@code true} for default behavior, i.e. {@code null} can be a valid value.
+ * Use {@code false} if {@code null} is not a valid value,
+ * here {@link #remove(E)} and {@link #getOrAdd(Object)} will be optimized.
+ * @param initialCapacity use {@link #DEFAULT_INITIAL_CAPACITY} for default
+ * @param loadFactor use {@link #DEFAULT_LOAD_FACTOR} for default
+ * @see #supportsNullValue()
+ */
+ public ArrayHashSet(final boolean supportNullValue, final int initialCapacity, final float loadFactor) {
+ this.map = new HashMap<E,E>(initialCapacity, loadFactor);
+ this.data = new ArrayList<E>(initialCapacity);
+ this.supportNullValue = supportNullValue;
}
- public ArrayHashSet(final int initialCapacity) {
- map = new HashMap<E,E>(initialCapacity);
- data = new ArrayList<E>(initialCapacity);
+ /**
+ * @return a shallow copy of this ArrayHashSet, elements are not copied.
+ */
+ public ArrayHashSet(final ArrayHashSet<E> o) {
+ map = new HashMap<E, E>(o.map);
+ data = new ArrayList<E>(o.data);
+ supportNullValue = o.supportNullValue;
}
- public ArrayHashSet(final int initialCapacity, final float loadFactor) {
- map = new HashMap<E,E>(initialCapacity, loadFactor);
- data = new ArrayList<E>(initialCapacity);
- }
+ /**
+ * Returns {@code true} for default behavior, i.e. {@code null} can be a valid value.
+ * <p>
+ * Returns {@code false} if {@code null} is not a valid value,
+ * here {@link #remove(E)} and {@link #getOrAdd(Object)} are optimized operations.
+ * </p>
+ * @see #ArrayHashSet(boolean, int, float)
+ */
+ public final boolean supportsNullValue() { return supportNullValue; }
//
// Cloneable
@@ -95,12 +124,7 @@ public class ArrayHashSet<E>
*/
@Override
public final Object clone() {
- final ArrayList<E> clonedList = new ArrayList<E>(data);
-
- final ArrayHashSet<E> newObj = new ArrayHashSet<E>();
- newObj.addAll(clonedList);
-
- return newObj;
+ return new ArrayHashSet<E>(this);
}
/** Returns this object ordered ArrayList. Use w/ care, it's not a copy. */
@@ -125,40 +149,66 @@ public class ArrayHashSet<E>
* Add element at the end of this list, if it is not contained yet.
* <br>
* This is an O(1) operation
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @return true if the element was added to this list,
* otherwise false (already contained).
+ * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false}
*/
@Override
- public final boolean add(final E element) {
- final boolean exists = map.containsKey(element);
- if(!exists) {
+ public final boolean add(final E element) throws NullPointerException {
+ if( !supportNullValue ) {
+ checkNull(element);
+ }
+ if( !map.containsKey(element) ) {
+ // !exists
if(null != map.put(element, element)) {
+ // slips a valid null ..
throw new InternalError("Already existing, but checked before: "+element);
}
if(!data.add(element)) {
throw new InternalError("Couldn't add element: "+element);
}
+ return true;
}
- return !exists;
+ return false;
}
/**
* Remove element from this list.
* <br>
- * This is an O(1) operation, in case it does not exist,
+ * This is an O(1) operation, in case the element does not exist,
* otherwise O(n).
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @return true if the element was removed from this list,
* otherwise false (not contained).
+ * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false}
*/
@Override
- public final boolean remove(final Object element) {
- if ( null != map.remove(element) ) {
- if ( ! data.remove(element) ) {
- throw new InternalError("Couldn't remove prev mapped element: "+element);
+ public final boolean remove(final Object element) throws NullPointerException {
+ if( supportNullValue ) {
+ if( map.containsKey(element) ) {
+ // exists
+ map.remove(element);
+ if ( !data.remove(element) ) {
+ throw new InternalError("Couldn't remove prev mapped element: "+element);
+ }
+ return true;
+ }
+ } else {
+ checkNull(element);
+ if ( null != map.remove(element) ) {
+ // exists
+ if ( !data.remove(element) ) {
+ throw new InternalError("Couldn't remove prev mapped element: "+element);
+ }
+ return true;
}
- return true;
}
return false;
}
@@ -167,6 +217,9 @@ public class ArrayHashSet<E>
* Add all elements of given {@link java.util.Collection} at the end of this list.
* <br>
* This is an O(n) operation, over the given Collection size.
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @return true if at least one element was added to this list,
* otherwise false (completely container).
@@ -184,6 +237,9 @@ public class ArrayHashSet<E>
* Test for containment
* <br>
* This is an O(1) operation.
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @return true if the given element is contained by this list using fast hash map,
* otherwise false.
@@ -197,6 +253,9 @@ public class ArrayHashSet<E>
* Test for containment of given {@link java.util.Collection}
* <br>
* This is an O(n) operation, over the given Collection size.
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @return true if the given Collection is completly contained by this list using hash map,
* otherwise false.
@@ -215,6 +274,9 @@ public class ArrayHashSet<E>
* Remove all elements of given {@link java.util.Collection} from this list.
* <br>
* This is an O(n) operation.
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @return true if at least one element of this list was removed,
* otherwise false.
@@ -233,6 +295,9 @@ public class ArrayHashSet<E>
* remove all elements not contained by the given {@link java.util.Collection} c.
* <br>
* This is an O(n) operation.
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @return true if at least one element of this list was removed,
* otherwise false.
@@ -250,6 +315,9 @@ public class ArrayHashSet<E>
/**
* This is an O(n) operation.
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @return true if arrayHashSet is of type ArrayHashSet and all entries are equal
* Performance: arrayHashSet(1)
@@ -264,6 +332,9 @@ public class ArrayHashSet<E>
/**
* This is an O(n) operation over the size of this list.
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @return the hash code of this list as define in {@link java.util.List#hashCode()},
* ie hashing all elements of this list.
@@ -316,30 +387,44 @@ public class ArrayHashSet<E>
* Add element at the given index in this list, if it is not contained yet.
* <br>
* This is an O(1) operation
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @throws IllegalArgumentException if the given element was already contained
+ * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false}
*/
@Override
- public final void add(final int index, final E element) {
+ public final void add(final int index, final E element) throws IllegalArgumentException, NullPointerException {
+ if( !supportNullValue ) {
+ checkNull(element);
+ }
if ( map.containsKey(element) ) {
throw new IllegalArgumentException("Element "+element+" is already contained");
}
if(null != map.put(element, element)) {
+ // slips a valid null ..
throw new InternalError("Already existing, but checked before: "+element);
}
+ // !exists
data.add(index, element);
}
/**
+ * <p>
+ * {@inheritDoc}
+ * </p>
* @throws UnsupportedOperationException
*/
@Override
- public final boolean addAll(final int index, final Collection<? extends E> c) {
+ public final boolean addAll(final int index, final Collection<? extends E> c) throws UnsupportedOperationException {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
- * @throws UnsupportedOperationException
+ * <p>
+ * {@inheritDoc}
+ * </p>
*/
@Override
public final E set(final int index, final E element) {
@@ -354,6 +439,9 @@ public class ArrayHashSet<E>
* Remove element at given index from this list.
* <br>
* This is an O(n) operation.
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @return the removed object
*/
@@ -370,6 +458,9 @@ public class ArrayHashSet<E>
* Since this list is unique, equivalent to {@link #indexOf(java.lang.Object)}.
* <br>
* This is an O(n) operation.
+ * <p>
+ * {@inheritDoc}
+ * </p>
*
* @return index of element, or -1 if not found
*/
@@ -409,34 +500,44 @@ public class ArrayHashSet<E>
* <br>
* This is an O(1) operation.
*
- * @param key hash source to find the identical Object within this list
+ * @param element hash source to find the identical Object within this list
* @return object from this list, identical to the given <code>key</code> hash code,
* or null if not contained
*/
- public final E get(final Object key) {
- return map.get(key);
+ public final E get(final Object element) {
+ return map.get(element);
}
/**
* Identity method allowing to get the identical object, using the internal hash map.<br>
- * If the <code>key</code> is not yet contained, add it.
+ * If the <code>element</code> is not yet contained, add it.
* <br>
* This is an O(1) operation.
*
- * @param key hash source to find the identical Object within this list
+ * @param element hash source to find the identical Object within this list
* @return object from this list, identical to the given <code>key</code> hash code,
* or add the given <code>key</code> and return it.
+ * @throws NullPointerException if {@code element} is {@code null} but {@link #supportsNullValue()} == {@code false}
*/
- public final E getOrAdd(final E key) {
- final E identity = get(key);
- if(null == identity) {
- // object not contained yet, add it
- if(!this.add(key)) {
- throw new InternalError("Key not mapped, but contained in list: "+key);
+ public final E getOrAdd(final E element) throws NullPointerException {
+ if( supportNullValue ) {
+ if( map.containsKey(element) ) {
+ // existent
+ return map.get(element);
+ }
+ } else {
+ checkNull(element);
+ final E identity = map.get(element);
+ if(null != identity) {
+ // existent
+ return identity;
}
- return key;
}
- return identity;
+ // !existent
+ if(!this.add(element)) {
+ throw new InternalError("Element not mapped, but contained in list: "+element);
+ }
+ return element;
}
/**
@@ -455,4 +556,9 @@ public class ArrayHashSet<E>
return data.contains(element);
}
+ private static final void checkNull(final Object element) throws NullPointerException {
+ if( null == element ) {
+ throw new NullPointerException("Null element not supported");
+ }
+ }
}
diff --git a/src/java/com/jogamp/common/util/Bitfield.java b/src/java/com/jogamp/common/util/Bitfield.java
new file mode 100644
index 0000000..4b2b9d5
--- /dev/null
+++ b/src/java/com/jogamp/common/util/Bitfield.java
@@ -0,0 +1,208 @@
+/**
+ * Copyright 2015 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.common.util;
+
+import jogamp.common.util.SyncedBitfield;
+
+/**
+ * Simple bitfield interface for efficient bit storage access in O(1).
+ * @since 2.3.2
+ */
+public interface Bitfield {
+ /** Maximum 32 bit Unsigned Integer Value: {@code 0xffffffff} == {@value}. */
+ public static final int UNSIGNED_INT_MAX_VALUE = 0xffffffff;
+
+ /**
+ * Bit operation utilities (static).
+ */
+ public static class Util {
+ /**
+ * Returns the 32 bit mask of n-bits, i.e. n low order 1’s.
+ * <p>
+ * Implementation handles n == 32.
+ * </p>
+ * @throws IndexOutOfBoundsException if {@code b} is out of bounds, i.e. &gt; 32
+ */
+ public static int getBitMask(final int n) {
+ if( 32 > n ) {
+ return ( 1 << n ) - 1;
+ } else if ( 32 == n ) {
+ return UNSIGNED_INT_MAX_VALUE;
+ } else {
+ throw new IndexOutOfBoundsException("n <= 32 expected, is "+n);
+ }
+ }
+
+ /**
+ * Returns the number of set bits within given 32bit integer in O(1)
+ * using a <i>HAKEM 169 Bit Count</i> inspired implementation:
+ * <pre>
+ * http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html
+ * http://home.pipeline.com/~hbaker1/hakmem/hacks.html#item169
+ * http://tekpool.wordpress.com/category/bit-count/
+ * http://www.hackersdelight.org/
+ * </pre>
+ */
+ public static final int bitCount(int n) {
+ // Note: Original used 'unsigned int',
+ // hence we use the unsigned right-shift '>>>'
+ /**
+ * Original does not work due to lack of 'unsigned' right-shift and modulo,
+ * we need 2-complementary solution, i.e. 'signed'.
+ int c = n;
+ c -= (n >>> 1) & 033333333333;
+ c -= (n >>> 2) & 011111111111;
+ return ( (c + ( c >>> 3 ) ) & 030707070707 ) & 0x3f; // % 63
+ */
+ // Hackers Delight, Figure 5-2, pop1 of pop.c.txt
+ n = n - ((n >>> 1) & 0x55555555);
+ n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
+ n = (n + (n >>> 4)) & 0x0f0f0f0f;
+ n = n + (n >>> 8);
+ n = n + (n >>> 16);
+ return n & 0x3f;
+ }
+ }
+ /**
+ * Simple {@link Bitfield} factory for returning the efficient implementation.
+ */
+ public static class Factory {
+ /**
+ * Creates am efficient {@link Bitfield} instance based on the requested {@code storageBitSize}.
+ * <p>
+ * Implementation returns a plain 32 bit integer field implementation for
+ * {@code storageBitSize} &le; 32 bits or an 32 bit integer array implementation otherwise.
+ * </p>
+ */
+ public static Bitfield create(final int storageBitSize) {
+ if( 32 >= storageBitSize ) {
+ return new jogamp.common.util.Int32Bitfield();
+ } else {
+ return new jogamp.common.util.Int32ArrayBitfield(storageBitSize);
+ }
+ }
+ /**
+ * Creates a synchronized {@link Bitfield} by wrapping the given {@link Bitfield} instance.
+ */
+ public static Bitfield synchronize(final Bitfield impl) {
+ return new SyncedBitfield(impl);
+ }
+ }
+ /**
+ * Returns the storage size in bit units, e.g. 32 bit for implementations using one {@code int} field.
+ */
+ int size();
+
+
+ /**
+ * Set all bits of this bitfield to the given value {@code bit}.
+ */
+ void clearField(final boolean bit);
+
+ /**
+ * Returns {@code length} bits from this storage,
+ * starting with the lowest bit from the storage position {@code lowBitnum}.
+ * @param lowBitnum storage bit position of the lowest bit, restricted to [0..{@link #size()}-{@code length}].
+ * @param length number of bits to read, constrained to [0..32].
+ * @throws IndexOutOfBoundsException if {@code rightBitnum} is out of bounds
+ * @see #put32(int, int, int)
+ */
+ int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException;
+
+ /**
+ * Puts {@code length} bits of given {@code data} into this storage,
+ * starting w/ the lowest bit to the storage position {@code lowBitnum}.
+ * @param lowBitnum storage bit position of the lowest bit, restricted to [0..{@link #size()}-{@code length}].
+ * @param length number of bits to write, constrained to [0..32].
+ * @param data the actual bits to be put into this storage
+ * @throws IndexOutOfBoundsException if {@code rightBitnum} is out of bounds
+ * @see #get32(int, int)
+ */
+ void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException;
+
+ /**
+ * Copies {@code length} bits at position {@code srcLowBitnum} to position {@code dstLowBitnum}
+ * and returning the bits.
+ * <p>
+ * Implementation shall operate as if invoking {@link #get32(int, int)}
+ * and then {@link #put32(int, int, int)} sequentially.
+ * </p>
+ * @param srcLowBitnum source bit number, restricted to [0..{@link #size()}-1].
+ * @param dstLowBitnum destination bit number, restricted to [0..{@link #size()}-1].
+ * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds
+ * @see #get32(int, int)
+ * @see #put32(int, int, int)
+ */
+ int copy32(final int srcLowBitnum, final int dstLowBitnum, final int length) throws IndexOutOfBoundsException;
+
+ /**
+ * Return <code>true</code> if the bit at position <code>bitnum</code> is set, otherwise <code>false</code>.
+ * @param bitnum bit number, restricted to [0..{@link #size()}-1].
+ * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds
+ */
+ boolean get(final int bitnum) throws IndexOutOfBoundsException;
+
+ /**
+ * Set or clear the bit at position <code>bitnum</code> according to <code>bit</code>
+ * and return the previous value.
+ * @param bitnum bit number, restricted to [0..{@link #size()}-1].
+ * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds
+ */
+ boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException;
+
+ /**
+ * Set the bit at position <code>bitnum</code> according to <code>bit</code>.
+ * @param bitnum bit number, restricted to [0..{@link #size()}-1].
+ * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds
+ */
+ void set(final int bitnum) throws IndexOutOfBoundsException;
+
+ /**
+ * Clear the bit at position <code>bitnum</code> according to <code>bit</code>.
+ * @param bitnum bit number, restricted to [0..{@link #size()}-1].
+ * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds
+ */
+ void clear(final int bitnum) throws IndexOutOfBoundsException;
+
+ /**
+ * Copies the bit at position {@code srcBitnum} to position {@code dstBitnum}
+ * and returning <code>true</code> if the bit is set, otherwise <code>false</code>.
+ * @param srcBitnum source bit number, restricted to [0..{@link #size()}-1].
+ * @param dstBitnum destination bit number, restricted to [0..{@link #size()}-1].
+ * @throws IndexOutOfBoundsException if {@code bitnum} is out of bounds
+ */
+ boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException;
+
+ /**
+ * Returns the number of one bits within this bitfield.
+ * <p>
+ * Utilizes {#link {@link Bitfield.Util#bitCount(int)}}.
+ * </p>
+ */
+ int bitCount();
+}
diff --git a/src/java/com/jogamp/common/util/CustomCompress.java b/src/java/com/jogamp/common/util/CustomCompress.java
new file mode 100644
index 0000000..6bca095
--- /dev/null
+++ b/src/java/com/jogamp/common/util/CustomCompress.java
@@ -0,0 +1,167 @@
+/**
+ * Copyright 2015 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package com.jogamp.common.util;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+/**
+ * All in memory inflater / deflator for small chunks using streams
+ * <p>
+ * Stream header of deflated data:
+ * <ul>
+ * <li>4 bytes magic 0xDEF1A7E0 (Big Endian)</li>
+ * <li>4 bytes integer deflated-size (Big Endian)</li>
+ * <li>4 bytes integer inflated-size (Big Endian)</li>
+ * <li>deflated bytes</li>
+ * </ul>
+ * </p>
+ */
+public class CustomCompress {
+ /** Start of stream header for deflated data */
+ public static final int MAGIC = 0xDEF1A7E0;
+
+ /**
+ *
+ * @param in {@link InputStream} at start of stream header, i.e. position {@link #MAGIC}.
+ * @return the inflated bytes from the stream
+ * @throws IOException if an I/O or deflation exception occurs
+ * @throws IllegalArgumentException if {@code inLen} &le; 0 or {@code outLen} &le; 0, as read from header
+ */
+ public static byte[] inflateFromStream(final InputStream in)
+ throws IOException, ArrayIndexOutOfBoundsException, IllegalArgumentException
+ {
+ final int inLen;
+ final int outLen;
+ {
+ final DataInputStream din = new DataInputStream(in);
+ final int _magic = din.readInt();
+ if( _magic != MAGIC ) {
+ throw new IOException("wrong magic: "+Integer.toHexString(_magic)+", expected "+Integer.toHexString(MAGIC));
+ }
+ inLen = din.readInt();
+ outLen = din.readInt();
+ }
+ return inflateFromStream(in, inLen, outLen, new byte[outLen], 0);
+ }
+
+ /**
+ *
+ * @param in {@link InputStream} at start of deflated bytes, i.e. after the stream header.
+ * @param inLen number of deflated bytes in stream {@code in}
+ * @param outLen number of inflated {@code output} bytes at {@code outOff}
+ * @param output sink for deflated bytes
+ * @param outOff offset to {@code output}
+ * @return the inflated bytes from the stream, passing {@code output} for chaining
+ * @throws IOException if an I/O or deflation exception occurs
+ * @throws ArrayIndexOutOfBoundsException if {@code outOff} and {@code outLen} exceeds {@code output}
+ * @throws IllegalArgumentException if {@code inLen} &le; 0 or {@code outLen} &le; 0
+ */
+ public static byte[] inflateFromStream(final InputStream in, final int inLen, final int outLen,
+ final byte[] output, final int outOff)
+ throws IOException, ArrayIndexOutOfBoundsException, IllegalArgumentException
+ {
+ if (inLen <= 0 || outLen <= 0 ) {
+ throw new IllegalArgumentException("Length[input "+inLen+", output "+outLen+"]");
+ }
+ if (outOff < 0 || output.length < outOff + outLen) {
+ throw new ArrayIndexOutOfBoundsException("output.length "+output.length+", offset "+outOff+", length "+outLen);
+ }
+ final byte[] input = new byte[inLen];
+ int numBytes = 0;
+ try {
+ while (true) {
+ final int remBytes = inLen - numBytes;
+ int count;
+ if ( 0 >= remBytes || (count = in.read(input, numBytes, remBytes)) == -1 ) {
+ break;
+ }
+ numBytes += count;
+ }
+ } finally {
+ in.close();
+ }
+ if( inLen != numBytes ) {
+ throw new IOException("Got "+numBytes+" bytes != expected "+inLen);
+ }
+ try {
+ final Inflater inflater = new Inflater();
+ inflater.setInput(input, 0, inLen);
+ final int outSize = inflater.inflate(output, outOff, outLen);
+ inflater.end();
+ if( outLen != outSize ) {
+ throw new IOException("Got inflated "+outSize+" bytes != expected "+outLen);
+ }
+ } catch(final DataFormatException dfe) {
+ throw new IOException(dfe);
+ }
+ return output;
+ }
+
+ /**
+ * @param input raw input bytes
+ * @param inOff offset to {@code input}
+ * @param inLen number of {@code input} bytes at {@code inOff}
+ * @param level compression level 0-9 or {@link Deflater#DEFAULT_COMPRESSION}
+ * @param out sink for deflated bytes
+ * @return number of deflated bytes written, not including the header.
+ * @throws IOException if an I/O or deflation exception occurs
+ * @throws ArrayIndexOutOfBoundsException if {@code inOff} and {@code inLen} exceeds {@code input}
+ * @throws IllegalArgumentException if {@code inLen} &le; 0
+ */
+ public static int deflateToStream(final byte[] input, final int inOff, final int inLen,
+ final int level, final OutputStream out) throws IOException, ArrayIndexOutOfBoundsException, IllegalArgumentException {
+ if (inLen <= 0 ) {
+ throw new IllegalArgumentException("Length[input "+inLen+"]");
+ }
+ if (inOff < 0 || input.length < inOff + inLen) {
+ throw new ArrayIndexOutOfBoundsException("input.length "+input.length+", offset "+inOff+", length "+inLen);
+ }
+ final byte[] output = new byte[inLen];
+ final Deflater deflater = new Deflater(level);
+ deflater.setInput(input, inOff, inLen);
+ deflater.finish();
+ final int outSize = deflater.deflate(output, 0, inLen);
+ deflater.end();
+ {
+ final DataOutputStream dout = new DataOutputStream(out);
+ dout.writeInt(CustomCompress.MAGIC);
+ dout.writeInt(outSize);
+ dout.writeInt(inLen);
+ }
+ out.write(output, 0, outSize);
+ return outSize;
+ }
+
+}
diff --git a/src/java/com/jogamp/common/util/FunctionTask.java b/src/java/com/jogamp/common/util/FunctionTask.java
index 4ac64d3..9eb1ca5 100644
--- a/src/java/com/jogamp/common/util/FunctionTask.java
+++ b/src/java/com/jogamp/common/util/FunctionTask.java
@@ -30,6 +30,8 @@ package com.jogamp.common.util;
import java.io.PrintStream;
+import com.jogamp.common.JogampRuntimeException;
+
/**
* Helper class to provide a Runnable queue implementation with a Runnable wrapper
* which notifies after execution for the <code>invokeAndWait()</code> semantics.
@@ -40,34 +42,67 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> {
protected A[] args;
/**
- * Invokes <code>func</code>.
+ * Invokes <code>func</code> on the current {@link Thread}.
+ * <p>
+ * The result can be retrieved via {@link FunctionTask#getResult()},
+ * using the returned instance.
+ * </p>
+ * @param func the {@link Function} to execute.
+ * @param args the {@link Function} arguments
+ * @return the newly created and invoked {@link FunctionTask}
+ * @since 2.4.0
+ */
+ public static <U,V> FunctionTask<U,V> invokeOnCurrentThread(final Function<U,V> func, final V... args) {
+ final FunctionTask<U,V> rt = new FunctionTask<U,V>( func, null, false, null);
+ rt.args = args;
+ rt.run();
+ return rt;
+ }
+
+ /**
+ * Invokes <code>func</code> on a new {@link InterruptSource.Thread},
+ * see {@link InterruptSource.Thread#Thread(ThreadGroup, Runnable, String)} for details.
+ * <p>
+ * The result can be retrieved via {@link FunctionTask#getResult()},
+ * using the returned instance.
+ * </p>
+ * @param tg the {@link ThreadGroup} for the new thread, maybe <code>null</code>
+ * @param threadName the name for the new thread, maybe <code>null</code>
* @param waitUntilDone if <code>true</code>, waits until <code>func</code> execution is completed, otherwise returns immediately.
* @param func the {@link Function} to execute.
* @param args the {@link Function} arguments
- * @return the {@link Function} return value
+ * @return the newly created and invoked {@link FunctionTask}
+ * @since 2.3.2
*/
- public static <U,V> U invoke(final boolean waitUntilDone, final Function<U,V> func, final V... args) {
- Throwable throwable = null;
- final Object sync = new Object();
- final FunctionTask<U,V> rt = new FunctionTask<U,V>( func, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err );
- final U res;
- synchronized(sync) {
- res = rt.eval(args);
- if( waitUntilDone ) {
- try {
- sync.wait();
- } catch (final InterruptedException ie) {
- throwable = ie;
- }
- if(null==throwable) {
- throwable = rt.getThrowable();
- }
- if(null!=throwable) {
- throw new RuntimeException(throwable);
+ public static <U,V> FunctionTask<U,V> invokeOnNewThread(final ThreadGroup tg, final String threadName,
+ final boolean waitUntilDone, final Function<U,V> func, final V... args) {
+ final FunctionTask<U,V> rt;
+ if( !waitUntilDone ) {
+ rt = new FunctionTask<U,V>( func, null, true, System.err );
+ final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName);
+ rt.args = args;
+ t.start();
+ } else {
+ final Object sync = new Object();
+ rt = new FunctionTask<U,V>( func, sync, true, null );
+ final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName);
+ synchronized(sync) {
+ rt.args = args;
+ t.start();
+ while( rt.isInQueue() ) {
+ try {
+ sync.wait();
+ } catch (final InterruptedException ie) {
+ throw new InterruptedRuntimeException(ie);
+ }
+ final Throwable throwable = rt.getThrowable();
+ if(null!=throwable) {
+ throw new JogampRuntimeException(throwable);
+ }
}
}
}
- return res;
+ return rt;
}
/**
@@ -124,6 +159,8 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> {
*/
@Override
public final void run() {
+ execThread = Thread.currentThread();
+
final A[] args = this.args;
this.args = null;
this.result = null;
@@ -144,6 +181,7 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> {
}
} finally {
tExecuted = System.currentTimeMillis();
+ isExecuted = true;
}
} else {
synchronized (syncObject) {
@@ -161,6 +199,7 @@ public class FunctionTask<R,A> extends TaskBase implements Function<R,A> {
}
} finally {
tExecuted = System.currentTimeMillis();
+ isExecuted = true;
syncObject.notifyAll();
}
}
diff --git a/src/java/com/jogamp/common/util/IOUtil.java b/src/java/com/jogamp/common/util/IOUtil.java
index b9a0a33..342c557 100644
--- a/src/java/com/jogamp/common/util/IOUtil.java
+++ b/src/java/com/jogamp/common/util/IOUtil.java
@@ -40,6 +40,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
+import java.io.SyncFailedException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.net.URISyntaxException;
@@ -52,6 +53,7 @@ import jogamp.common.Debug;
import jogamp.common.os.AndroidUtils;
import jogamp.common.os.PlatformPropsImpl;
+import com.jogamp.common.ExceptionUtils;
import com.jogamp.common.net.AssetURLContext;
import com.jogamp.common.net.Uri;
import com.jogamp.common.nio.Buffers;
@@ -59,7 +61,19 @@ import com.jogamp.common.os.MachineDataInfo;
import com.jogamp.common.os.Platform;
public class IOUtil {
- public static final boolean DEBUG = Debug.debug("IOUtil");
+ public static final boolean DEBUG;
+ private static final boolean DEBUG_EXE;
+ private static final boolean DEBUG_EXE_NOSTREAM;
+ private static final boolean DEBUG_EXE_EXISTING_FILE;
+
+ static {
+ Debug.initSingleton();
+ DEBUG = Debug.debug("IOUtil");
+ DEBUG_EXE = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe", true);
+ DEBUG_EXE_NOSTREAM = PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.NoStream", true);
+ // For security reasons, we have to hardcode this, i.e. disable this manual debug feature!
+ DEBUG_EXE_EXISTING_FILE = false; // PropertyAccess.isPropertyDefined("jogamp.debug.IOUtil.Exe.ExistingFile", true);
+ }
/** Std. temporary directory property key <code>java.io.tmpdir</code>. */
private static final String java_io_tmpdir_propkey = "java.io.tmpdir";
@@ -424,7 +438,7 @@ public class IOUtil {
/***
*
- * RESOURCE LOCATION STUFF
+ * RESOURCE LOCATION HELPER
*
*/
@@ -433,7 +447,10 @@ public class IOUtil {
* to be {@link #resolve(int) resolved} at a later time.
*/
public static class ClassResources {
- /** Class instance used to {@link #resolve(int)} the {@link #resourcePaths}. */
+ /** Optional {@link ClassLoader} used to {@link #resolve(int)} {@link #resourcePaths}. */
+ public final ClassLoader classLoader;
+
+ /** Optional class instance used to {@link #resolve(int)} relative {@link #resourcePaths}. */
public final Class<?> contextCL;
/** Resource paths, see {@link #resolve(int)}. */
@@ -443,67 +460,73 @@ public class IOUtil {
public final int resourceCount() { return resourcePaths.length; }
/**
- * @param contextCL class instance to {@link #resolve(int)} {@link #resourcePaths}.
- * @param resourcePaths array of strings denominating multiple resource paths. None shall be null.
+ * @param resourcePaths multiple relative or absolute resource locations
+ * @param classLoader optional {@link ClassLoader}, see {@link IOUtil#getResource(String, ClassLoader, Class)}
+ * @param relContext optional relative context, see {@link IOUtil#getResource(String, ClassLoader, Class)}
*/
- public ClassResources(final Class<?> contextCL, final String[] resourcePaths) {
+ public ClassResources(final String[] resourcePaths, final ClassLoader classLoader, final Class<?> relContext) {
for(int i=resourcePaths.length-1; i>=0; i--) {
if( null == resourcePaths[i] ) {
throw new IllegalArgumentException("resourcePath["+i+"] is null");
}
}
- this.contextCL = contextCL;
+ this.classLoader = classLoader;
+ this.contextCL = relContext;
this.resourcePaths = resourcePaths;
}
/**
- * Resolving one of the {@link #resourcePaths} indexed by <code>uriIndex</code> using {@link #contextCL} and {@link IOUtil#getResource(Class, String)}.
+ * Resolving one of the {@link #resourcePaths} indexed by <code>uriIndex</code> using
+ * {@link #classLoader}, {@link #contextCL} through {@link IOUtil#getResource(String, ClassLoader, Class)}.
* @throws ArrayIndexOutOfBoundsException if <code>uriIndex</code> is < 0 or >= {@link #resourceCount()}.
*/
public URLConnection resolve(final int uriIndex) throws ArrayIndexOutOfBoundsException {
- return getResource(contextCL, resourcePaths[uriIndex]);
+ return getResource(resourcePaths[uriIndex], classLoader, contextCL);
}
}
/**
* Locating a resource using {@link #getResource(String, ClassLoader)}:
* <ul>
- * <li><i>relative</i>: <code>context</code>'s package name-path plus <code>resourcePath</code> via <code>context</code>'s ClassLoader.
+ * <li><i>relative</i>: <code>relContext</code>'s package name-path plus <code>resourcePath</code> via <code>classLoader</code>.
* This allows locations relative to JAR- and other URLs.
- * The <code>resourcePath</code> may start with <code>../</code> to navigate to parent folder.</li>
- * <li><i>absolute</i>: <code>context</code>'s ClassLoader and the <code>resourcePath</code> as is (filesystem)</li>
+ * The <code>resourcePath</code> may start with <code>../</code> to navigate to parent folder.
+ * This attempt is skipped if {@code relContext} is {@code null}.</li>
+ * <li><i>absolute</i>: <code>resourcePath</code> as is via <code>classLoader</code>.
* </ul>
- *
* <p>
* Returns the resolved and open URLConnection or null if not found.
* </p>
*
+ * @param resourcePath the resource path to locate relative or absolute
+ * @param classLoader the optional {@link ClassLoader}, recommended
+ * @param relContext relative context, i.e. position, of the {@code resourcePath},
+ * to perform the relative lookup, if not {@code null}.
* @see #getResource(String, ClassLoader)
* @see ClassLoader#getResource(String)
* @see ClassLoader#getSystemResource(String)
*/
- public static URLConnection getResource(final Class<?> context, final String resourcePath) {
+ public static URLConnection getResource(final String resourcePath, final ClassLoader classLoader, final Class<?> relContext) {
if(null == resourcePath) {
return null;
}
- final ClassLoader contextCL = (null!=context)?context.getClassLoader():IOUtil.class.getClassLoader();
URLConnection conn = null;
- if(null != context) {
+ if(null != relContext) {
// scoping the path within the class's package
- final String className = context.getName().replace('.', '/');
+ final String className = relContext.getName().replace('.', '/');
final int lastSlash = className.lastIndexOf('/');
if (lastSlash >= 0) {
final String pkgName = className.substring(0, lastSlash + 1);
- conn = getResource(pkgName + resourcePath, contextCL);
+ conn = getResource(pkgName + resourcePath, classLoader);
if(DEBUG) {
- System.err.println("IOUtil: found <"+resourcePath+"> within class package <"+pkgName+"> of given class <"+context.getName()+">: "+(null!=conn));
+ System.err.println("IOUtil: found <"+resourcePath+"> within class package <"+pkgName+"> of given class <"+relContext.getName()+">: "+(null!=conn));
}
}
} else if(DEBUG) {
- System.err.println("IOUtil: null context");
+ System.err.println("IOUtil: null context, skip rel. lookup");
}
if(null == conn) {
- conn = getResource(resourcePath, contextCL);
+ conn = getResource(resourcePath, classLoader);
if(DEBUG) {
System.err.println("IOUtil: found <"+resourcePath+"> by classloader: "+(null!=conn));
}
@@ -534,8 +557,7 @@ public class IOUtil {
return AssetURLContext.createURL(resourcePath, cl).openConnection();
} catch (final IOException ioe) {
if(DEBUG) {
- System.err.println("IOUtil: Caught Exception:");
- ioe.printStackTrace();
+ ExceptionUtils.dumpThrowable("IOUtil", ioe);
}
return null;
}
@@ -544,8 +566,7 @@ public class IOUtil {
return AssetURLContext.resolve(resourcePath, cl);
} catch (final IOException ioe) {
if(DEBUG) {
- System.err.println("IOUtil: Caught Exception:");
- ioe.printStackTrace();
+ ExceptionUtils.dumpThrowable("IOUtil", ioe);
}
}
}
@@ -573,7 +594,7 @@ public class IOUtil {
}
/**
- * @param path assuming a slashified path beginning with "/" as it's root directory, either denotes a file or directory.
+ * @param path assuming a slashified path, either denotes a file or directory, either relative or absolute.
* @return parent of path
* @throws URISyntaxException if path is empty or has no parent directory available
*/
@@ -585,11 +606,11 @@ public class IOUtil {
final int e = path.lastIndexOf("/");
if( e < 0 ) {
- throw new URISyntaxException(path, "path contains no '/'");
+ throw new URISyntaxException(path, "path contains no '/': <"+path+">");
}
if( e == 0 ) {
// path is root directory
- throw new URISyntaxException(path, "path has no parents");
+ throw new URISyntaxException(path, "path has no parents: <"+path+">");
}
if( e < pl - 1 ) {
// path is file, return it's parent directory
@@ -599,23 +620,45 @@ public class IOUtil {
// path is a directory ..
final int p = path.lastIndexOf("/", e-1);
if( p >= j) {
+ // parent itself has '/' - post '!' or no '!' at all
return path.substring(0, p+1);
+ } else {
+ // parent itself has no '/'
+ final String parent = path.substring(j, e);
+ if( parent.equals("..") ) {
+ throw new URISyntaxException(path, "parent is unresolved: <"+path+">");
+ } else {
+ // parent is '!' or empty (relative path)
+ return path.substring(0, j);
+ }
}
- throw new URISyntaxException(path, "parent of path contains no '/'");
}
/**
- * @param path assuming a slashified path beginning with "/" as it's root directory, either denotes a file or directory.
- * @return clean path string where <code>../</code> and <code>./</code> is resolved.
+ * @param path assuming a slashified path, either denoting a file or directory, either relative or absolute.
+ * @return clean path string where {@code ./} and {@code ../} is resolved,
+ * while keeping a starting {@code ../} at the beginning of a relative path.
* @throws URISyntaxException if path is empty or has no parent directory available while resolving <code>../</code>
*/
public static String cleanPathString(String path) throws URISyntaxException {
- int idx;
- while ( ( idx = path.indexOf("../") ) >= 0 ) {
- path = getParentOf(path.substring(0, idx)) + path.substring(idx+3);
+ // Resolve './' before '../' to handle case 'parent/./../a.txt' properly.
+ int idx = path.length() - 1;
+ while ( idx >= 1 && ( idx = path.lastIndexOf("./", idx) ) >= 0 ) {
+ if( 0 < idx && path.charAt(idx-1) == '.' ) {
+ idx-=2; // skip '../' -> idx upfront
+ } else {
+ path = path.substring(0, idx) + path.substring(idx+2);
+ idx--; // idx upfront
+ }
}
- while ( ( idx = path.indexOf("./") ) >= 0 ) {
- path = path.substring(0, idx) + path.substring(idx+2);
+ idx = 0;
+ while ( ( idx = path.indexOf("../", idx) ) >= 0 ) {
+ if( 0 == idx ) {
+ idx += 3; // skip starting '../'
+ } else {
+ path = getParentOf(path.substring(0, idx)) + path.substring(idx+3);
+ idx = 0;
+ }
}
return path;
}
@@ -658,8 +701,7 @@ public class IOUtil {
return c;
} catch (final IOException ioe) {
if(DEBUG) {
- System.err.println("IOUtil: urlExists("+url+") ["+dbgmsg+"] - false - "+ioe.getClass().getSimpleName()+": "+ioe.getMessage());
- ioe.printStackTrace();
+ ExceptionUtils.dumpThrowable("IOUtil: urlExists("+url+") ["+dbgmsg+"] - false -", ioe);
}
}
} else if(DEBUG) {
@@ -697,48 +739,49 @@ public class IOUtil {
return new String[] { scriptFile };
}
}
- private static final byte[] getBytesFromRelFile(final byte[] res, final String fname, final int size) throws IOException {
- final URLConnection con = IOUtil.getResource(IOUtil.class, fname);
+
+ private static final byte[] readCode(final String fname) throws IOException {
+ final URLConnection con = IOUtil.getResource(fname, IOUtil.class.getClassLoader(), IOUtil.class);
final InputStream in = con.getInputStream();
- int numBytes = 0;
+ byte[] output = null;
try {
- while (true) {
- final int remBytes = size - numBytes;
- int count;
- if ( 0 >= remBytes || (count = in.read(res, numBytes, remBytes)) == -1 ) {
- break;
- }
- numBytes += count;
- }
+ output = CustomCompress.inflateFromStream(in);
} finally {
in.close();
}
- if( size != numBytes ) {
- throw new IOException("Got "+numBytes+" bytes != expected "+size);
- }
- return res;
+ return output;
}
- private static final Object exeTestBytesLock = new Object();
- private static WeakReference<byte[]> exeTestBytesRef = null;
+ private static final Object exeTestLock = new Object();
+ private static WeakReference<byte[]> exeTestCodeRef = null;
private static void fillExeTestFile(final File exefile) throws IOException {
if( Platform.OSType.WINDOWS == PlatformPropsImpl.OS_TYPE &&
Platform.CPUFamily.X86 == PlatformPropsImpl.CPU_ARCH.family
) {
- final int codeSize = 268;
- final byte[] code;
- synchronized ( exeTestBytesLock ) {
- byte[] _code;
- if( null == exeTestBytesRef || null == ( _code = exeTestBytesRef.get() ) ) {
- code = getBytesFromRelFile(new byte[512], "bin/exe-windows-i586-268b.bin", codeSize);
- exeTestBytesRef = new WeakReference<byte[]>(code);
+ final byte[] exeTestCode;
+ synchronized ( exeTestLock ) {
+ byte[] _exeTestCode = null;
+ if( null == exeTestCodeRef || null == ( _exeTestCode = exeTestCodeRef.get() ) ) {
+ final String fname;
+ if( Platform.CPUType.X86_64 == PlatformPropsImpl.CPU_ARCH ) {
+ fname = "bin/exe-windows-x86_64.defl";
+ } else {
+ fname = "bin/exe-windows-i386.defl";
+ }
+ exeTestCode = readCode(fname);
+ exeTestCodeRef = new WeakReference<byte[]>(exeTestCode);
} else {
- code = _code;
+ exeTestCode = _exeTestCode;
}
}
- final OutputStream out = new FileOutputStream(exefile);
+ final FileOutputStream out = new FileOutputStream(exefile);
try {
- out.write(code, 0, codeSize);
+ out.write(exeTestCode, 0, exeTestCode.length);
+ try {
+ out.getFD().sync();
+ } catch (final SyncFailedException sfe) {
+ ExceptionUtils.dumpThrowable("", sfe);
+ }
} finally {
out.close();
}
@@ -748,6 +791,11 @@ public class IOUtil {
final FileWriter fout = new FileWriter(exefile);
try {
fout.write(shellCode);
+ try {
+ fout.flush();
+ } catch (final IOException sfe) {
+ ExceptionUtils.dumpThrowable("", sfe);
+ }
} finally {
fout.close();
}
@@ -812,44 +860,52 @@ public class IOUtil {
public static class StreamMonitor implements Runnable {
private final InputStream[] istreams;
+ private final boolean[] eos;
private final PrintStream ostream;
private final String prefix;
public StreamMonitor(final InputStream[] streams, final PrintStream ostream, final String prefix) {
this.istreams = streams;
+ this.eos = new boolean[streams.length];
this.ostream = ostream;
this.prefix = prefix;
- new Thread(this, "StreamMonitor-"+Thread.currentThread().getName()).start();
+ final InterruptSource.Thread t = new InterruptSource.Thread(null, this, "StreamMonitor-"+Thread.currentThread().getName());
+ t.setDaemon(true);
+ t.start();
}
+
@Override
public void run()
{
final byte[] buffer = new byte[4096];
try {
- int numRead;
+ final int streamCount = istreams.length;
+ int eosCount = 0;
do {
- numRead = 0;
for(int i=0; i<istreams.length; i++) {
- final int numReadI = istreams[i].read(buffer);
- if (numReadI > 0) {
- if( null != ostream ) {
- if( null != prefix ) {
- ostream.write(prefix.getBytes());
+ if( !eos[i] ) {
+ final int numReadI = istreams[i].read(buffer);
+ if (numReadI > 0) {
+ if( null != ostream ) {
+ if( null != prefix ) {
+ ostream.write(prefix.getBytes());
+ }
+ ostream.write(buffer, 0, numReadI);
}
- ostream.write(buffer, 0, numReadI);
+ } else {
+ // numReadI == -1
+ eosCount++;
+ eos[i] = true;
}
- numRead += numReadI;
}
}
if( null != ostream ) {
ostream.flush();
}
- } while (numRead >= 0);
- }
- catch (final IOException e) {
- for(int i=0; i<istreams.length; i++) {
- try {
- istreams[i].close();
- } catch (final IOException e2) { }
+ } while ( eosCount < streamCount );
+ } catch (final IOException e) {
+ } finally {
+ if( null != ostream ) {
+ ostream.flush();
}
// Should allow clean exit when process shuts down
}
@@ -871,6 +927,8 @@ public class IOUtil {
public static boolean testDirExec(final File dir)
throws SecurityException
{
+ final boolean debug = DEBUG_EXE || DEBUG;
+
if( !testTempDirExec ) {
if(DEBUG) {
System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Disabled "+_TestTempDirExec);
@@ -878,64 +936,93 @@ public class IOUtil {
return false;
}
if (!testFile(dir, true, true)) {
- if(DEBUG) {
+ if( debug ) {
System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Not writeable dir");
}
return false;
}
if(!getOSHasNoexecFS()) {
- if(DEBUG) {
+ if( debug ) {
System.err.println("IOUtil.testDirExec: <"+dir.getAbsolutePath()+">: Always executable");
}
return true;
}
- final long t0 = DEBUG ? System.currentTimeMillis() : 0;
+ final long t0 = debug ? System.currentTimeMillis() : 0;
final File exeTestFile;
+ final boolean existingExe;
try {
- exeTestFile = File.createTempFile("jogamp_exe_tst", getExeTestFileSuffix(), dir);
+ final File permExeTestFile = DEBUG_EXE_EXISTING_FILE ? new File(dir, "jogamp_exe_tst"+getExeTestFileSuffix()) : null;
+ if( null != permExeTestFile && permExeTestFile.exists() ) {
+ exeTestFile = permExeTestFile;
+ existingExe = true;
+ } else {
+ exeTestFile = File.createTempFile("jogamp_exe_tst", getExeTestFileSuffix(), dir);
+ existingExe = false;
+ }
} catch (final SecurityException se) {
throw se; // fwd Security exception
} catch (final IOException e) {
- if(DEBUG) {
+ if( debug ) {
e.printStackTrace();
}
return false;
}
- final long t1 = DEBUG ? System.currentTimeMillis() : 0;
+ final long t1 = debug ? System.currentTimeMillis() : 0;
+ long t2;
int res = -1;
- if(exeTestFile.setExecutable(true /* exec */, true /* ownerOnly */)) {
+ int exitValue = -1;
+ if( existingExe || exeTestFile.setExecutable(true /* exec */, true /* ownerOnly */) ) {
+ Process pr = null;
try {
- fillExeTestFile(exeTestFile);
-
+ if( !existingExe ) {
+ fillExeTestFile(exeTestFile);
+ }
+ t2 = debug ? System.currentTimeMillis() : 0;
// Using 'Process.exec(String[])' avoids StringTokenizer of 'Process.exec(String)'
// and hence splitting up command by spaces!
- final Process pr = Runtime.getRuntime().exec( getExeTestCommandArgs( exeTestFile.getCanonicalPath() ) );
- /**
- * Disable StreamMonitor, which throttles exec-test performance a lot!
- *
- * if( isStringSet(shellCode) ) {
+ // Note: All no-exec cases throw an IOExceptions at ProcessBuilder.start(), i.e. below exec() call!
+ pr = Runtime.getRuntime().exec( getExeTestCommandArgs( exeTestFile.getCanonicalPath() ), null, null );
+ if( DEBUG_EXE && !DEBUG_EXE_NOSTREAM ) {
new StreamMonitor(new InputStream[] { pr.getInputStream(), pr.getErrorStream() }, System.err, "Exe-Tst: ");
- }
- */
- pr.waitFor() ;
- res = pr.exitValue();
+ }
+ pr.waitFor();
+ exitValue = pr.exitValue(); // Note: Bug 1219 Comment 50: On reporter's machine exit value 1 is being returned
+ res = 0; // file has been executed
} catch (final SecurityException se) {
throw se; // fwd Security exception
} catch (final Throwable t) {
+ t2 = debug ? System.currentTimeMillis() : 0;
res = -2;
- if(DEBUG) {
+ if( debug ) {
System.err.println("IOUtil.testDirExec: <"+exeTestFile.getAbsolutePath()+">: Caught "+t.getClass().getSimpleName()+": "+t.getMessage());
t.printStackTrace();
}
+ } finally {
+ if( null != pr ) {
+ // Bug 1219 Comment 58: Ensure that the launched process gets terminated!
+ // This is Process implementation specific and varies on different platforms,
+ // hence it may be required.
+ try {
+ pr.destroy();
+ } catch (final Throwable t) {
+ ExceptionUtils.dumpThrowable("", t);
+ }
+ }
}
+ } else {
+ t2 = debug ? System.currentTimeMillis() : 0;
}
+
final boolean ok = 0 == res;
- final long t2 = DEBUG ? System.currentTimeMillis() : 0;
- exeTestFile.delete();
- if( DEBUG) {
- System.err.println("IOUtil.testDirExec(): <"+dir.getAbsolutePath()+">: res "+res+" -> "+ok);
- System.err.println("IOUtil.testDirExec(): total "+(t2-t0)+"ms, create "+(t1-t0)+"ms, execute "+(t2-t1)+"ms");
+ if( !DEBUG_EXE && !existingExe ) {
+ exeTestFile.delete();
+ }
+ if( debug ) {
+ final long t3 = System.currentTimeMillis();
+ System.err.println("IOUtil.testDirExec(): test-exe <"+exeTestFile.getAbsolutePath()+">, existingFile "+existingExe+", returned "+exitValue);
+ System.err.println("IOUtil.testDirExec(): abs-path <"+dir.getAbsolutePath()+">: res "+res+" -> "+ok);
+ System.err.println("IOUtil.testDirExec(): total "+(t3-t0)+"ms, create "+(t1-t0)+"ms, fill "+(t2-t1)+"ms, execute "+(t3-t2)+"ms");
}
return ok;
}
diff --git a/src/java/com/jogamp/common/util/IntBitfield.java b/src/java/com/jogamp/common/util/IntBitfield.java
deleted file mode 100644
index 74e37ac..0000000
--- a/src/java/com/jogamp/common/util/IntBitfield.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/**
- * Copyright 2012 JogAmp Community. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are
- * permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of
- * conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list
- * of conditions and the following disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and documentation are those of the
- * authors and should not be interpreted as representing official policies, either expressed
- * or implied, of JogAmp Community.
- */
-package com.jogamp.common.util;
-
-/**
- * Simple bitfield holder class using an int[] storage.
- * <p>
- * IntBitfield allows convenient access of a wide field of transient bits using efficient storage in O(1).
- * </p>
- * <p>
- * It can be used e.g. to map key-codes to pressed-state etc.
- * </p>
- */
-public class IntBitfield {
- /** Unit size in bits, here 32 bits for one int unit. */
- public static final int UNIT_SIZE = 32;
-
- private static final long UNIT_SHIFT_L = 5L;
- private static final int UNIT_SHIFT_I = 5;
-
- private final int[] storage;
- private final long bitsCountL;
- private final int bitsCountI;
-
- /**
- * @param bitCount
- */
- public IntBitfield(final long bitCount) {
- final int units = (int) Math.max(1L, ( bitCount + 7L ) >>> UNIT_SHIFT_L);
- this.storage = new int[units];
- this.bitsCountL = (long)units << UNIT_SHIFT_L ;
- this.bitsCountI = bitsCountL > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)bitsCountL;
- }
-
- /**
- * @param bitCount
- */
- public IntBitfield(final int bitCount) {
- final int units = Math.max(1, ( bitCount + 7 ) >>> UNIT_SHIFT_I);
- this.storage = new int[units];
- this.bitsCountI = units << UNIT_SHIFT_I;
- this.bitsCountL = bitsCountI;
- }
-
- private final void check(final long bitnum) {
- if( 0 > bitnum || bitnum >= bitsCountL ) {
- throw new ArrayIndexOutOfBoundsException("Bitnum should be within [0.."+(bitsCountL-1)+"], but is "+bitnum);
- }
- }
- private final void check(final int bitnum) {
- if( 0 > bitnum || bitnum >= bitsCountI ) {
- throw new ArrayIndexOutOfBoundsException("Bitnum should be within [0.."+(bitsCountI-1)+"], but is "+bitnum);
- }
- }
-
- /** Return the capacity of this bit field, i.e. the number of bits stored int this field. */
- public final long capacity() { return bitsCountL; }
-
- /** Return <code>true</code> if the bit at position <code>bitnum</code> is set, otherwise <code>false</code>. */
- public final boolean get(final long bitnum) {
- check(bitnum);
- final int u = (int) ( bitnum >>> UNIT_SHIFT_L );
- final int b = (int) ( bitnum - ( (long)u << UNIT_SHIFT_L ) );
- return 0 != ( storage[u] & ( 1 << b ) ) ;
- }
-
- /** Return <code>true</code> if the bit at position <code>bitnum</code> is set, otherwise <code>false</code>. */
- public final boolean get(final int bitnum) {
- check(bitnum);
- final int u = bitnum >>> UNIT_SHIFT_I;
- final int b = bitnum - ( u << UNIT_SHIFT_I );
- return 0 != ( storage[u] & ( 1 << b ) ) ;
- }
-
- /**
- * Set or clear the bit at position <code>bitnum</code> according to <code>bit</code>
- * and return the previous value.
- */
- public final boolean put(final long bitnum, final boolean bit) {
- check(bitnum);
- final int u = (int) ( bitnum >>> UNIT_SHIFT_L );
- final int b = (int) ( bitnum - ( (long)u << UNIT_SHIFT_L ) );
- final int m = 1 << b;
- final boolean prev = 0 != ( storage[u] & m ) ;
- if( prev != bit ) {
- if( bit ) {
- storage[u] |= m;
- } else {
- storage[u] &= ~m;
- }
- }
- return prev;
- }
-
- /**
- * Set or clear the bit at position <code>bitnum</code> according to <code>bit</code>
- * and return the previous value.
- */
- public final boolean put(final int bitnum, final boolean bit) {
- check(bitnum);
- final int u = bitnum >>> UNIT_SHIFT_I;
- final int b = bitnum - ( u << UNIT_SHIFT_I );
- final int m = 1 << b;
- final boolean prev = 0 != ( storage[u] & m ) ;
- if( prev != bit ) {
- if( bit ) {
- storage[u] |= m;
- } else {
- storage[u] &= ~m;
- }
- }
- return prev;
- }
- /**
- * Returns the number of set bits within given 32bit integer in O(1)
- * using <i>HAKEM Bit Count</i>:
- * <pre>
- * http://www.inwap.com/pdp10/hbaker/hakmem/hakmem.html
- * http://home.pipeline.com/~hbaker1/hakmem/hacks.html#item169
- * http://tekpool.wordpress.com/category/bit-count/
- * </pre>
- */
- public static final int getBitCount(final int n) {
- // Note: Original used 'unsigned int',
- // hence we use the unsigned right-shift '>>>'
- int c = n;
- c -= (n >>> 1) & 033333333333;
- c -= (n >>> 2) & 011111111111;
- return ( (c + ( c >>> 3 ) ) & 030707070707 ) % 63;
- }
-
- /**
- * Returns the number of set bits within this bitfield.
- * <p>
- * Utilizes {#link {@link #getBitCount(int)}}.
- * </p>
- */
- public long getBitCount() {
- long c = 0;
- for(int i = storage.length-1; i>=0; i--) {
- c += getBitCount(storage[i]);
- }
- return c;
- }
-}
diff --git a/src/java/com/jogamp/common/util/InterruptSource.java b/src/java/com/jogamp/common/util/InterruptSource.java
new file mode 100644
index 0000000..01fcdb5
--- /dev/null
+++ b/src/java/com/jogamp/common/util/InterruptSource.java
@@ -0,0 +1,157 @@
+/**
+ * Copyright 2015 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.common.util;
+
+/**
+ * Interface exposing {@link java.lang.Thread#interrupt()} source,
+ * intended for {@link java.lang.Thread} specializations.
+ * @since 2.3.2
+ */
+public interface InterruptSource {
+ /**
+ * Returns the source of the last {@link #interrupt()} call.
+ * @param clear if true, issues {@link #clearInterruptSource()}
+ */
+ Throwable getInterruptSource(final boolean clear);
+
+ /**
+ * Returns the count of {@link java.lang.Thread#interrupt()} calls.
+ * @param clear if true, issues {@link #clearInterruptSource()}
+ */
+ int getInterruptCounter(final boolean clear);
+
+ /**
+ * Clears source and count of {@link java.lang.Thread#interrupt()} calls, if any.
+ */
+ void clearInterruptSource();
+
+ public static class Util {
+ /**
+ * Casts given {@link java.lang.Thread} to {@link InterruptSource}
+ * if applicable, otherwise returns {@code null}.
+ */
+ public static InterruptSource get(final java.lang.Thread t) {
+ if(t instanceof InterruptSource) {
+ return (InterruptSource)t;
+ } else {
+ return null;
+ }
+ }
+ /**
+ * Casts current {@link java.lang.Thread} to {@link InterruptSource}
+ * if applicable, otherwise returns {@code null}.
+ */
+ public static InterruptSource currentThread() {
+ return get(java.lang.Thread.currentThread());
+ }
+ }
+
+ /**
+ * {@link java.lang.Thread} specialization implementing {@link InterruptSource}
+ * to track {@link java.lang.Thread#interrupt()} calls.
+ * @since 2.3.2
+ */
+ public static class Thread extends java.lang.Thread implements InterruptSource {
+ volatile Throwable interruptSource = null;
+ volatile int interruptCounter = 0;
+ final Object sync = new Object();
+
+ /**
+ * See {@link Thread#Thread(} for details.
+ */
+ public Thread() {
+ super();
+ }
+ /**
+ * See {@link Thread#Thread(ThreadGroup, Runnable)} for details.
+ * @param tg explicit {@link ThreadGroup}, may be {@code null}
+ * @param target explicit {@link Runnable}, may be {@code null}
+ */
+ public Thread(final ThreadGroup tg, final Runnable target) {
+ super(tg, target);
+ }
+ /**
+ * See {@link Thread#Thread(ThreadGroup, Runnable, String)} for details.
+ * @param tg explicit {@link ThreadGroup}, may be {@code null}
+ * @param target explicit {@link Runnable}, may be {@code null}
+ * @param name explicit name of thread, must not be {@code null}
+ */
+ public Thread(final ThreadGroup tg, final Runnable target, final String name) {
+ super(tg, target, name);
+ }
+
+ /**
+ * Depending on whether {@code name} is null, either
+ * {@link #Thread(ThreadGroup, Runnable, String)} or
+ * {@link #Thread(ThreadGroup, Runnable)} is being utilized.
+ * @param tg explicit {@link ThreadGroup}, may be {@code null}
+ * @param target explicit {@link Runnable}, may be {@code null}
+ * @param name explicit name of thread, may be {@code null}
+ */
+ public static Thread create(final ThreadGroup tg, final Runnable target, final String name) {
+ return null != name ? new Thread(tg, target, name) : new Thread(tg, target);
+ }
+
+ @Override
+ public final Throwable getInterruptSource(final boolean clear) {
+ synchronized(sync) {
+ final Throwable r = interruptSource;
+ if( clear ) {
+ clearInterruptSource();
+ }
+ return r;
+ }
+ }
+ @Override
+ public final int getInterruptCounter(final boolean clear) {
+ synchronized(sync) {
+ final int r = interruptCounter;
+ if( clear ) {
+ clearInterruptSource();
+ }
+ return r;
+ }
+ }
+ @Override
+ public final void clearInterruptSource() {
+ synchronized(sync) {
+ interruptCounter = 0;
+ interruptSource = null;
+ }
+ }
+ @Override
+ public final void interrupt() {
+ synchronized(sync) {
+ interruptCounter++;
+ interruptSource = new Throwable(getName()+".interrupt() #"+interruptCounter);
+ }
+ super.interrupt();
+ }
+ }
+}
diff --git a/src/java/com/jogamp/common/util/InterruptedRuntimeException.java b/src/java/com/jogamp/common/util/InterruptedRuntimeException.java
new file mode 100644
index 0000000..af6fea8
--- /dev/null
+++ b/src/java/com/jogamp/common/util/InterruptedRuntimeException.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright 2015 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.common.util;
+
+import com.jogamp.common.JogampRuntimeException;
+
+/**
+ * <i>Unchecked exception</i> propagating an {@link InterruptedException}
+ * where handling of the latter is not desired.
+ * <p>
+ * {@link InterruptedRuntimeException} may be thrown either by waiting for any {@link Runnable}
+ * to be completed, or during its execution.
+ * </p>
+ * <p>
+ * The propagated {@link InterruptedException} may be of type {@link SourcedInterruptedException}.
+ * </p>
+ * <p>
+ * </p>
+ */
+@SuppressWarnings("serial")
+public class InterruptedRuntimeException extends JogampRuntimeException {
+
+ /**
+ * Constructor attempts to {@link SourcedInterruptedException#wrap(InterruptedException) wrap}
+ * the given {@link InterruptedException} {@code cause} into a {@link SourcedInterruptedException}.
+ *
+ * @param message the message of this exception
+ * @param cause the propagated {@link InterruptedException}
+ */
+ public InterruptedRuntimeException(final String message, final InterruptedException cause) {
+ super(message, SourcedInterruptedException.wrap(cause));
+ }
+
+ /**
+ * Constructor attempts to {@link SourcedInterruptedException#wrap(InterruptedException) wrap}
+ * the given {@link InterruptedException} {@code cause} into a {@link SourcedInterruptedException}.
+ *
+ * @param cause the propagated {@link InterruptedException}
+ */
+ public InterruptedRuntimeException(final InterruptedException cause) {
+ super(SourcedInterruptedException.wrap(cause));
+ }
+
+ /**
+ * Returns the propagated {@link InterruptedException}, i.e. the cause of this exception.
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ */
+ @Override
+ public InterruptedException getCause() {
+ return (InterruptedException)super.getCause();
+ }
+}
diff --git a/src/java/com/jogamp/common/util/JarUtil.java b/src/java/com/jogamp/common/util/JarUtil.java
index 745dd12..d6c8fd4 100644
--- a/src/java/com/jogamp/common/util/JarUtil.java
+++ b/src/java/com/jogamp/common/util/JarUtil.java
@@ -163,7 +163,7 @@ public class JarUtil {
}
}
}
- if( !uri.scheme.equals( Uri.JAR_SCHEME ) ) {
+ if( !uri.isJarScheme() ) {
throw new IllegalArgumentException("Uri is not using scheme "+Uri.JAR_SCHEME+": <"+uri+">");
}
if(DEBUG) {
@@ -190,7 +190,7 @@ public class JarUtil {
if(null == classJarUri) {
throw new IllegalArgumentException("Uri is null");
}
- if( !classJarUri.scheme.equals(Uri.JAR_SCHEME) ) {
+ if( !classJarUri.isJarScheme() ) {
throw new IllegalArgumentException("Uri is not using scheme "+Uri.JAR_SCHEME+": <"+classJarUri+">");
}
Uri.Encoded ssp = classJarUri.schemeSpecificPart;
@@ -262,7 +262,7 @@ public class JarUtil {
if(null == classJarUri) {
throw new IllegalArgumentException("Uri is null");
}
- if( !classJarUri.scheme.equals(Uri.JAR_SCHEME) ) {
+ if( !classJarUri.isJarScheme() ) {
throw new IllegalArgumentException("Uri is not a using scheme "+Uri.JAR_SCHEME+": <"+classJarUri+">");
}
final Uri.Encoded uriSSP = classJarUri.schemeSpecificPart;
@@ -413,20 +413,6 @@ public class JarUtil {
}
/**
- * See {@link #getRelativeOf(Class, com.jogamp.common.net.Uri.Encoded, com.jogamp.common.net.Uri.Encoded)}.
- * @param classFromJavaJar URI encoded!
- * @param cutOffInclSubDir URI encoded!
- * @param relResPath URI encoded!
- * @return
- * @throws IllegalArgumentException
- * @throws IOException
- * @throws URISyntaxException
- * @deprecated Use {@link #getRelativeOf(Class, com.jogamp.common.net.Uri.Encoded, com.jogamp.common.net.Uri.Encoded)}.
- */
- public static java.net.URI getRelativeOf(final Class<?> classFromJavaJar, final String cutOffInclSubDir, final String relResPath) throws IllegalArgumentException, IOException, URISyntaxException {
- return getRelativeOf(classFromJavaJar, Uri.Encoded.cast(cutOffInclSubDir), Uri.Encoded.cast(relResPath)).toURI();
- }
- /**
* Locates the {@link JarUtil#getJarFileUri(Uri) Jar file Uri} of a given resource
* relative to a given class's Jar's Uri.
* <pre>
diff --git a/src/java/com/jogamp/common/util/JogampVersion.java b/src/java/com/jogamp/common/util/JogampVersion.java
index e9becc6..e06ce1f 100644
--- a/src/java/com/jogamp/common/util/JogampVersion.java
+++ b/src/java/com/jogamp/common/util/JogampVersion.java
@@ -46,6 +46,9 @@ public class JogampVersion {
/** See {@link #getImplementationCommit()} */
public static final Attributes.Name IMPLEMENTATION_COMMIT = new Attributes.Name("Implementation-Commit");
+ /** For FAT JogAmp jar files */
+ private static final String packageNameFAT = "com.jogamp";
+
private final String packageName;
private final Manifest mf;
private final int hash;
@@ -55,12 +58,27 @@ public class JogampVersion {
private final String androidPackageVersionName;
protected JogampVersion(final String packageName, final Manifest mf) {
- this.packageName = packageName;
- this.mf = ( null != mf ) ? mf : new Manifest();
+ if( null != mf ) {
+ // use provided valid data
+ this.mf = mf;
+ this.packageName = packageName;
+ } else {
+ // try FAT jar file
+ final Manifest fatMF = VersionUtil.getManifest(JogampVersion.class.getClassLoader(), packageNameFAT);
+ if( null != fatMF ) {
+ // use FAT jar file
+ this.mf = fatMF;
+ this.packageName = packageNameFAT;
+ } else {
+ // use faulty data, unresolvable ..
+ this.mf = new Manifest();
+ this.packageName = packageName;
+ }
+ }
this.hash = this.mf.hashCode();
mainAttributes = this.mf.getMainAttributes();
mainAttributeNames = mainAttributes.keySet();
- androidPackageVersionName = AndroidUtils.getPackageInfoVersionName(packageName); // null if !Android
+ androidPackageVersionName = AndroidUtils.getPackageInfoVersionName(this.packageName); // null if !Android
}
@Override
diff --git a/src/java/com/jogamp/common/util/RunnableTask.java b/src/java/com/jogamp/common/util/RunnableTask.java
index 6fb98de..2689de1 100644
--- a/src/java/com/jogamp/common/util/RunnableTask.java
+++ b/src/java/com/jogamp/common/util/RunnableTask.java
@@ -30,6 +30,8 @@ package com.jogamp.common.util;
import java.io.PrintStream;
+import com.jogamp.common.JogampRuntimeException;
+
/**
* Helper class to provide a Runnable queue implementation with a Runnable wrapper
* which notifies after execution for the <code>invokeAndWait()</code> semantics.
@@ -38,70 +40,58 @@ public class RunnableTask extends TaskBase {
protected final Runnable runnable;
/**
- * Invokes <code>runnable</code> on the current thread.
- * @param waitUntilDone if <code>true</code>, waits until <code>runnable</code> execution is completed, otherwise returns immediately.
- * @param runnable the {@link Runnable} to execute.
+ * Invokes <code>runnable</code> on the current {@link Thread}.
+ * @param runnable the {@link Runnable} to execute on the current thread.
+ * The runnable <b>must exit</b>, i.e. not loop forever.
+ * @return the newly created and invoked {@link RunnableTask}
+ * @since 2.4.0
*/
- public static void invoke(final boolean waitUntilDone, final Runnable runnable) {
- Throwable throwable = null;
- final Object sync = new Object();
- final RunnableTask rt = new RunnableTask( runnable, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err );
- synchronized(sync) {
- rt.run();
- if( waitUntilDone ) {
- try {
- sync.wait();
- } catch (final InterruptedException ie) {
- throwable = ie;
- }
- if(null==throwable) {
- throwable = rt.getThrowable();
- }
- if(null!=throwable) {
- throw new RuntimeException(throwable);
- }
- }
- }
+ public static RunnableTask invokeOnCurrentThread(final Runnable runnable) {
+ final RunnableTask rt = new RunnableTask( runnable, null, false, null );
+ rt.run();
+ return rt;
}
/**
- * Invokes <code>runnable</code> on a new thread belonging to the given {@link ThreadGroup}.
+ * Invokes <code>runnable</code> on a new {@link InterruptSource.Thread},
+ * see {@link InterruptSource.Thread#Thread(ThreadGroup, Runnable, String)} for details.
* @param tg the {@link ThreadGroup} for the new thread, maybe <code>null</code>
+ * @param threadName the name for the new thread, maybe <code>null</code>
* @param waitUntilDone if <code>true</code>, waits until <code>runnable</code> execution is completed, otherwise returns immediately.
* @param runnable the {@link Runnable} to execute on the new thread. If <code>waitUntilDone</code> is <code>true</code>,
- * the runnable <b>must exist</b>, i.e. not loop forever.
- * @param threadName the name for the new thread
- * @return the newly created {@link Thread}
+ * the runnable <b>must exit</b>, i.e. not loop forever.
+ * @return the newly created and invoked {@link RunnableTask}
+ * @since 2.3.2
*/
- public static Thread invokeOnNewThread(final ThreadGroup tg, final boolean waitUntilDone, final Runnable runnable, final String threadName) {
- final Thread t = new Thread(tg, threadName) {
- @Override
- public void run() {
- Throwable throwable = null;
- final Object sync = new Object();
- final RunnableTask rt = new RunnableTask( runnable, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err );
- synchronized(sync) {
- rt.run();
- if( waitUntilDone ) {
- try {
- sync.wait();
- } catch (final InterruptedException ie) {
- throwable = ie;
- }
- if(null==throwable) {
- throwable = rt.getThrowable();
- }
- if(null!=throwable) {
- throw new RuntimeException(throwable);
- }
+ public static RunnableTask invokeOnNewThread(final ThreadGroup tg, final String threadName,
+ final boolean waitUntilDone, final Runnable runnable) {
+ final RunnableTask rt;
+ if( !waitUntilDone ) {
+ rt = new RunnableTask( runnable, null, true, System.err );
+ final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName);
+ t.start();
+ } else {
+ final Object sync = new Object();
+ rt = new RunnableTask( runnable, sync, true, null );
+ final InterruptSource.Thread t = InterruptSource.Thread.create(tg, rt, threadName);
+ synchronized(sync) {
+ t.start();
+ while( rt.isInQueue() ) {
+ try {
+ sync.wait();
+ } catch (final InterruptedException ie) {
+ throw new InterruptedRuntimeException(ie);
+ }
+ final Throwable throwable = rt.getThrowable();
+ if(null!=throwable) {
+ throw new JogampRuntimeException(throwable);
}
}
- } };
- t.start();
- return t;
+ }
+ }
+ return rt;
}
-
/**
* Create a RunnableTask object w/ synchronization,
* ie. suitable for <code>invokeAndWait()</code>, i.e. {@link #invoke(boolean, Runnable) invoke(true, runnable)}.
@@ -126,6 +116,8 @@ public class RunnableTask extends TaskBase {
@Override
public final void run() {
+ execThread = Thread.currentThread();
+
runnableException = null;
tStarted = System.currentTimeMillis();
if(null == syncObject) {
@@ -143,6 +135,7 @@ public class RunnableTask extends TaskBase {
}
} finally {
tExecuted = System.currentTimeMillis();
+ isExecuted = true;
}
} else {
synchronized (syncObject) {
@@ -160,6 +153,7 @@ public class RunnableTask extends TaskBase {
}
} finally {
tExecuted = System.currentTimeMillis();
+ isExecuted = true;
syncObject.notifyAll();
}
}
diff --git a/src/java/com/jogamp/common/util/SourcedInterruptedException.java b/src/java/com/jogamp/common/util/SourcedInterruptedException.java
new file mode 100644
index 0000000..530f1e7
--- /dev/null
+++ b/src/java/com/jogamp/common/util/SourcedInterruptedException.java
@@ -0,0 +1,166 @@
+/**
+ * Copyright 2015 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.common.util;
+
+import java.io.PrintStream;
+
+import com.jogamp.common.ExceptionUtils;
+import com.jogamp.common.ExceptionUtils.CustomStackTrace;
+
+/**
+ * {@link InterruptedException}, which may include the source, see {@link #getInterruptSource()}.
+ * <p>
+ * This exception may be created directly where {@link #getCause()} returns {@code null},
+ * or by propagating an existing {@link InterruptedException} as returned by {@link #getCause()}.
+ * </p>
+ * @since 2.3.2
+ */
+@SuppressWarnings("serial")
+public class SourcedInterruptedException extends InterruptedException implements CustomStackTrace {
+ final Throwable interruptSource;
+
+ /**
+ * Wraps the given {@link InterruptedException} into a {@link SourcedInterruptedException}
+ * if it is not yet of the desired type and
+ * if the current thread if a {@link InterruptSource}, i.e. the source is known.
+ * <p>
+ * Otherwise the given {@link InterruptedException} instance is returned.
+ * </p>
+ * <p>
+ * In case method is creating a new wrapping instance,
+ * {@link InterruptSource#clearInterruptSource()} is being issued.
+ * </p>
+ *
+ * @param ie the to be wrapped {@link InterruptedException}
+ */
+ public static InterruptedException wrap(final InterruptedException ie) {
+ return wrap(ie, InterruptSource.Util.currentThread());
+ }
+
+ /**
+ * Wraps the given {@link InterruptedException} into a {@link SourcedInterruptedException}
+ * if it is not yet of the same type and if {@code source} is not {@code null}.
+ * <p>
+ * Otherwise the given {@link InterruptedException} instance is returned.
+ * </p>
+ * <p>
+ * In case method is creating a new wrapping instance,
+ * {@link InterruptSource#clearInterruptSource()} is being issued.
+ * </p>
+ *
+ * @param ie the to be wrapped {@link InterruptedException}
+ * @param source the {@link InterruptSource}
+ */
+ public static InterruptedException wrap(final InterruptedException ie, final InterruptSource source) {
+ if( !(ie instanceof SourcedInterruptedException) && null != source ) {
+ return new SourcedInterruptedException(ie, source.getInterruptSource(true));
+ } else {
+ return ie;
+ }
+ }
+
+ /**
+ * @param message mandatory message of this exception
+ * @param cause optional propagated cause
+ * @param interruptSource optional propagated source of {@link Thread#interrupt()} call
+ */
+ public SourcedInterruptedException(final String message, final InterruptedException cause, final Throwable interruptSource) {
+ super(message);
+ if( null != cause ) {
+ initCause(cause);
+ }
+ this.interruptSource = interruptSource;
+ }
+
+ /**
+ * @param cause mandatory propagated cause
+ * @param interruptSource optional propagated source of {@link Thread#interrupt()} call
+ */
+ public SourcedInterruptedException(final InterruptedException cause, final Throwable interruptSource) {
+ super(cause.getMessage());
+ initCause(cause);
+ this.interruptSource = interruptSource;
+ }
+
+ /**
+ * Returns the source of the {@link Thread#interrupt()} call if known,
+ * otherwise {@code null} is returned.
+ */
+ public final Throwable getInterruptSource() {
+ return interruptSource;
+ }
+
+ /**
+ * Returns the propagated {@link InterruptedException}, i.e. the cause of this exception,
+ * or {@code null} if not applicable.
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ */
+ @Override
+ public InterruptedException getCause() {
+ return (InterruptedException)super.getCause();
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder(256);
+ sb.append(getClass().getSimpleName()).append(": ");
+ if (null != interruptSource) {
+ sb.append("[sourced]");
+ } else {
+ sb.append("[unknown]");
+ }
+ final String m = getLocalizedMessage();
+ if( null != m ) {
+ sb.append(" ").append(m);
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public final void printCauseStack(final PrintStream s, final String causeStr, final int causeIdx, final int stackDepth) {
+ final String s0 = causeStr+"["+causeIdx+"]";
+ s.println(s0+" by "+getClass().getSimpleName()+": "+getMessage()+" on thread "+Thread.currentThread().getName());
+ ExceptionUtils.dumpStack(s, getStackTrace(), 0, stackDepth);
+ if( null != interruptSource ) {
+ ExceptionUtils.printCause(s, s0, interruptSource, 0, 1, stackDepth);
+ }
+ }
+
+ @Override
+ public final void printStackTrace(final PrintStream s, final int causeDepth, final int stackDepth) {
+ s.println(getClass().getSimpleName()+": "+getMessage()+" on thread "+Thread.currentThread().getName());
+ ExceptionUtils.dumpStack(s, getStackTrace(), 0, stackDepth);
+ ExceptionUtils.printCause(s, "Caused", getCause(), 0, causeDepth, stackDepth);
+ if( null != interruptSource ) {
+ ExceptionUtils.printCause(s, "InterruptSource", interruptSource, 0, causeDepth, stackDepth);
+ }
+ }
+}
diff --git a/src/java/com/jogamp/common/util/TaskBase.java b/src/java/com/jogamp/common/util/TaskBase.java
index 59b86c3..64a8313 100644
--- a/src/java/com/jogamp/common/util/TaskBase.java
+++ b/src/java/com/jogamp/common/util/TaskBase.java
@@ -54,17 +54,29 @@ public abstract class TaskBase implements Runnable {
protected Throwable runnableException;
protected long tCreated, tStarted;
protected volatile long tExecuted;
+ protected volatile boolean isExecuted;
protected volatile boolean isFlushed;
+ protected volatile Thread execThread;
+ /**
+ * @param syncObject The synchronization object if caller wait until <code>runnable</code> execution is completed,
+ * or <code>null</code> if waiting is not desired.
+ * @param catchExceptions Influence an occurring exception during <code>runnable</code> execution.
+ * If <code>true</code>, the exception is silenced and can be retrieved via {@link #getThrowable()},
+ * otherwise the exception is thrown.
+ * @param exceptionOut If not <code>null</code>, exceptions are written to this {@link PrintStream}.
+ */
protected TaskBase(final Object syncObject, final boolean catchExceptions, final PrintStream exceptionOut) {
this.syncObject = syncObject;
this.catchExceptions = catchExceptions;
this.exceptionOut = exceptionOut;
this.sourceStack = TRACE_SOURCE ? new Throwable("Creation @") : null;
- tCreated = System.currentTimeMillis();
- tStarted = 0;
- tExecuted = 0;
- isFlushed = false;
+ this.tCreated = System.currentTimeMillis();
+ this.tStarted = 0;
+ this.tExecuted = 0;
+ this.isExecuted = false;
+ this.isFlushed = false;
+ this.execThread = null;
}
protected final String getExceptionOutIntro() {
@@ -77,6 +89,14 @@ public abstract class TaskBase implements Runnable {
}
/**
+ * Returns the execution thread or {@code null} if not yet {@link #run()}.
+ * @since 2.3.2
+ */
+ public final Thread getExecutionThread() {
+ return execThread;
+ }
+
+ /**
* Return the synchronization object if any.
* @see #RunnableTask(Runnable, Object, boolean)
*/
@@ -126,12 +146,12 @@ public abstract class TaskBase implements Runnable {
/**
* @return !{@link #isExecuted()} && !{@link #isFlushed()}
*/
- public final boolean isInQueue() { return 0 != tExecuted && !isFlushed; }
+ public final boolean isInQueue() { return !isExecuted && !isFlushed; }
/**
* @return True if executed, otherwise false;
*/
- public final boolean isExecuted() { return 0 != tExecuted ; }
+ public final boolean isExecuted() { return isExecuted; }
/**
* @return True if flushed, otherwise false;
@@ -159,7 +179,16 @@ public abstract class TaskBase implements Runnable {
@Override
public String toString() {
- return "RunnableTask[executed "+isExecuted()+", tTotal "+getDurationTotal()+" ms, tExec "+getDurationInExec()+" ms, tQueue "+getDurationInQueue()+" ms, attachment "+attachment+", throwable "+getThrowable()+"]";
+ final String etn;
+ final String eth;
+ if( null != execThread ) {
+ etn = execThread.getName();
+ eth = "0x"+Integer.toHexString(execThread.hashCode());
+ } else {
+ etn = "n/a";
+ eth = "n/a";
+ }
+ return "RunnableTask[enqueued "+isInQueue()+"[executed "+isExecuted()+", flushed "+isFlushed()+", thread["+eth+", "+etn+"]], tTotal "+getDurationTotal()+" ms, tExec "+getDurationInExec()+" ms, tQueue "+getDurationInQueue()+" ms, attachment "+attachment+", throwable "+getThrowable()+"]";
}
}
diff --git a/src/java/com/jogamp/common/util/bin/exe-windows-i386.defl b/src/java/com/jogamp/common/util/bin/exe-windows-i386.defl
new file mode 100644
index 0000000..d8f1716
--- /dev/null
+++ b/src/java/com/jogamp/common/util/bin/exe-windows-i386.defl
Binary files differ
diff --git a/src/java/com/jogamp/common/util/bin/exe-windows-i586-268b.bin b/src/java/com/jogamp/common/util/bin/exe-windows-i586-268b.bin
deleted file mode 100644
index b0d5f63..0000000
--- a/src/java/com/jogamp/common/util/bin/exe-windows-i586-268b.bin
+++ /dev/null
Binary files differ
diff --git a/src/java/com/jogamp/common/util/bin/exe-windows-x86_64.defl b/src/java/com/jogamp/common/util/bin/exe-windows-x86_64.defl
new file mode 100644
index 0000000..be0998b
--- /dev/null
+++ b/src/java/com/jogamp/common/util/bin/exe-windows-x86_64.defl
Binary files differ
diff --git a/src/java/com/jogamp/common/util/cache/TempFileCache.java b/src/java/com/jogamp/common/util/cache/TempFileCache.java
index 24f0237..44c7a11 100644
--- a/src/java/com/jogamp/common/util/cache/TempFileCache.java
+++ b/src/java/com/jogamp/common/util/cache/TempFileCache.java
@@ -35,6 +35,7 @@ import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import com.jogamp.common.util.IOUtil;
+import com.jogamp.common.util.InterruptSource;
import jogamp.common.Debug;
@@ -238,7 +239,7 @@ public class TempFileCache {
// Add shutdown hook to cleanup the OutputStream, FileChannel,
// and FileLock for the jlnNNNN.lck and jlnNNNN.lck files.
// We do this so that the locks never get garbage-collected.
- Runtime.getRuntime().addShutdownHook(new Thread() {
+ Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() {
/* @Override */
@Override
public void run() {
@@ -265,7 +266,7 @@ public class TempFileCache {
}
// Start a new Reaper thread to do stuff...
- final Thread reaperThread = new Thread() {
+ final Thread reaperThread = new InterruptSource.Thread() {
/* @Override */
@Override
public void run() {
diff --git a/src/java/com/jogamp/common/util/cache/TempJarCache.java b/src/java/com/jogamp/common/util/cache/TempJarCache.java
index ed69ddc..2ff5140 100644
--- a/src/java/com/jogamp/common/util/cache/TempJarCache.java
+++ b/src/java/com/jogamp/common/util/cache/TempJarCache.java
@@ -273,19 +273,6 @@ public class TempJarCache {
}
/**
- * See {@link #addResources(Class, Uri)}
- * @param certClass
- * @param jarURI
- * @throws IOException
- * @throws SecurityException
- * @throws IllegalArgumentException
- * @throws URISyntaxException
- * @deprecated Use {@link #addResources(Class, Uri)}
- */
- public synchronized static final void addResources(final Class<?> certClass, final java.net.URI jarURI) throws IOException, SecurityException, IllegalArgumentException, URISyntaxException {
- addResources(certClass, Uri.valueOf(jarURI));
- }
- /**
* Adds native resources, if not yet added.
*
* @param certClass if class is certified, the JarFile entries needs to have the same certificate
@@ -421,14 +408,6 @@ public class TempJarCache {
return null;
}
- /**
- * See {@link #getResourceUri(String)}
- * @deprecated Use {@link #getResourceUri(String)}
- */
- public synchronized static final java.net.URI getResource(final String name) throws URISyntaxException {
- return getResourceUri(name).toURI();
- }
-
/** Similar to {@link ClassLoader#getResource(String)}. */
public synchronized static final Uri getResourceUri(final String name) throws URISyntaxException {
checkInitialized();
diff --git a/src/java/com/jogamp/gluegen/ConstantDefinition.java b/src/java/com/jogamp/gluegen/ConstantDefinition.java
index f8f4973..675c6d7 100644
--- a/src/java/com/jogamp/gluegen/ConstantDefinition.java
+++ b/src/java/com/jogamp/gluegen/ConstantDefinition.java
@@ -1,38 +1,35 @@
-/*
- * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
+/**
+ * Copyright 2015 JogAmp Community. All rights reserved.
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
*
- * - Redistribution of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
*
- * - Redistribution in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
*
- * Neither the name of Sun Microsystems, Inc. or the names of
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * This software is provided "AS IS," without a warranty of any kind. ALL
- * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
- * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
- * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
- * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
- * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
- * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
- * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
- * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
- * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
- * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
*/
-
package com.jogamp.gluegen;
+import java.math.BigInteger;
+import java.util.Map;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider;
@@ -40,55 +37,351 @@ import com.jogamp.gluegen.cgram.types.AliasedSymbol.AliasedSymbolImpl;
import com.jogamp.gluegen.cgram.types.TypeComparator.AliasedSemanticSymbol;
import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp;
-/** Represents the definition of a constant which was provided either
- via a #define statement or through an enum definition. */
+/**
+ * Represents a [native] constant expression,
+ * comprises the [native] expression, see {@link #getNativeExpr()}
+ * and the optional {@link CNumber} representation, see {@link #getNumber()}.
+ * <p>
+ * The representation of the equivalent java expression including
+ * the result type is covered by {@link JavaExpr},
+ * which can be computed via {@link #computeJavaExpr(Map)}.
+ * </p>
+ * <p>
+ * This class and its sub-classes define and convert all native expressions
+ * to Java space.
+ * </p>
+ */
public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSemanticSymbol, ASTLocusTagProvider {
+ public static final long UNSIGNED_INT_MAX_VALUE = 0xffffffffL;
+ public static final BigInteger UNSIGNED_LONG_MAX_VALUE = new BigInteger("ffffffffffffffff", 16);
+
+ /**
+ * A Number, either integer, optionally [long, unsigned],
+ * or floating point, optionally [double].
+ */
+ public static class CNumber {
+ /**
+ * {@code true} if number is integer and value stored in {@link #i},
+ * otherwise {@code false} for floating point and value stored in {@link #f}.
+ */
+ public final boolean isInteger;
+ /** {@code true} if number is a {@code long} {@link #isInteger}. */
+ public final boolean isLong;
+ /** {@code true} if number is an {@code unsigned} {@link #isInteger}. */
+ public final boolean isUnsigned;
+ /** The value if {@link #isInteger} */
+ public final long i;
+
+ /** {@code true} if number is a {@code double precision} {@code floating point}, i.e. !{@link #isInteger}. */
+ public final boolean isDouble;
+ /** The value if !{@link #isInteger} */
+ public final double f;
+
+ /** ctor for integer number */
+ public CNumber(final boolean isLong, final boolean isUnsigned, final long value) {
+ this.isInteger = true;
+ this.isLong = isLong;
+ this.isUnsigned = isUnsigned;
+ this.i = value;
+ this.isDouble = false;
+ this.f = 0.0;
+ }
+ /** ctor for floating point number */
+ public CNumber(final boolean isDouble, final double value) {
+ this.isInteger = false;
+ this.isLong = false;
+ this.isUnsigned = false;
+ this.i = 0;
+ this.isDouble = isDouble;
+ this.f = value;
+ }
+ @Override
+ public int hashCode() {
+ return isInteger ? Long.valueOf(i).hashCode() : Double.valueOf(f).hashCode();
+ }
+ @Override
+ public boolean equals(final Object arg) {
+ if (arg == this) {
+ return true;
+ } else if ( !(arg instanceof CNumber) ) {
+ return false;
+ }
+ final CNumber t = (CNumber) arg;
+ return isInteger == t.isInteger &&
+ ( isInteger ? i == t.i : f == t.f );
+ }
+ public final String toJavaString() {
+ if( isInteger ) {
+ if( i >= 0 || isUnsigned ) {
+ if( isLong ) {
+ return "0x"+Long.toHexString(i)+"L";
+ } else {
+ return "0x"+Integer.toHexString((int)i);
+ }
+ } else {
+ if( isLong ) {
+ return String.valueOf(i)+"L";
+ } else {
+ return String.valueOf((int)i);
+ }
+ }
+ } else {
+ return String.valueOf(f) + ( !isDouble ? "f" : "");
+ }
+ }
+ public final String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ if( isInteger ) {
+ if( isUnsigned ) {
+ sb.append("unsigned ");
+ }
+ if( isLong) {
+ sb.append("long: ");
+ } else {
+ sb.append("int: ");
+ }
+ sb.append(i);
+ } else {
+ if( isDouble ) {
+ sb.append("double: ");
+ } else {
+ sb.append("float: ");
+ }
+ sb.append(f);
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+ }
+
+ /**
+ * A valid java expression, including its result type,
+ * usually generated from a native [C] expression,
+ * see {@link JavaExpr#create(ConstantDefinition)}.
+ */
+ public static class JavaExpr {
+ public final String javaExpression;
+ public final CNumber resultType;
+ public final Number resultJavaType;
+ public final String resultJavaTypeName;
+ public JavaExpr(final String javaExpression, final CNumber resultType) {
+ this.javaExpression = javaExpression;
+ this.resultType = resultType;
+ if( resultType.isDouble ) {
+ resultJavaTypeName = "double";
+ resultJavaType = Double.valueOf(resultType.f);
+ } else if( !resultType.isInteger ) {
+ resultJavaTypeName = "float";
+ resultJavaType = Double.valueOf(resultType.f).floatValue();
+ } else if( resultType.isLong ) {
+ resultJavaTypeName = "long";
+ resultJavaType = Long.valueOf(resultType.i);
+ } else /* if( resultType.isInteger ) */ {
+ resultJavaTypeName = "int";
+ resultJavaType = Long.valueOf(resultType.i).intValue();
+ }
+ }
+ /**
+ * Computes a valid {@link JavaExpr java expression} based on the given {@link ConstantDefinition},
+ * which may either be a single {@link CNumber}, see {@link ConstantDefinition#getNumber()},
+ * or represents a native expression, see {@link ConstantDefinition#getExpr()}.
+ */
+ public static JavaExpr compute(final ConstantDefinition constDef,
+ final Map<String, ConstantDefinition.JavaExpr> constMap) {
+ final boolean debug = GlueGen.debug();
+ if( debug ) {
+ System.err.println("ConstJavaExpr.create: "+constDef);
+ }
+ if( constDef.hasNumber() ) {
+ // Already parsed as CNumber completely!
+ if( debug ) {
+ System.err.printf("V %s (isCNumber)%n", constDef);
+ }
+ return new JavaExpr(constDef.getNumber().toJavaString(), constDef.getNumber());
+ }
+ final StringBuilder javaExpr = new StringBuilder();
+ final String nativeExpr = constDef.getNativeExpr();
+
+ // "calculates" the result type of a simple expression
+ // example: (2+3)-(2.0f-3.0) -> Double
+ // example: (1 << 2) -> Integer
+ CNumber resultType = null;
+ final Matcher matcher = patternCPPOperand.matcher(nativeExpr);
+ int preStartIdx = 0;
+ int opEndIdx = 0;
+ while ( matcher.find() ) {
+ final int opStartIdx = matcher.start();
+ if( opStartIdx > preStartIdx ) {
+ final String sValue = nativeExpr.substring(preStartIdx, opStartIdx).trim();
+ if( sValue.length() > 0 ) {
+ if( debug ) {
+ System.err.printf("V %03d-%03d: %s%n", preStartIdx, opStartIdx, sValue);
+ }
+ resultType = processValue(constDef, sValue, constMap, resultType, javaExpr);
+ javaExpr.append(" ");
+ }
+ }
+ opEndIdx = matcher.end();
+ final String op = nativeExpr.substring(opStartIdx, opEndIdx);
+ if( debug ) {
+ System.err.printf("O %03d-%03d: %s%n", opStartIdx, opEndIdx, op);
+ }
+ javaExpr.append(op).append(" ");
+ preStartIdx = opEndIdx;
+ }
+ if( opEndIdx < nativeExpr.length() ) {
+ // tail ..
+ final String sValue = nativeExpr.substring(opEndIdx).trim();
+ if( sValue.length() > 0 ) {
+ if( debug ) {
+ System.err.printf("V %03d %03d-%03d: %s (tail)%n", preStartIdx, opEndIdx, nativeExpr.length(), sValue);
+ }
+ resultType = processValue(constDef, sValue, constMap, resultType, javaExpr);
+ }
+ }
+ final String javaExprS = javaExpr.toString().trim();
+ if( null == resultType ) {
+ throw new GlueGenException("Cannot emit const \""+constDef.getName()+"\": value \""+nativeExpr+
+ "\", parsed \""+javaExprS+"\" does not contain a constant number", constDef.getASTLocusTag());
+ }
+ return new JavaExpr(javaExprS, resultType);
+ }
+ private static CNumber processValue(final ConstantDefinition constDef,
+ final String sValue,
+ final Map<String, ConstantDefinition.JavaExpr> constMap,
+ CNumber resultType,
+ final StringBuilder javaExpr) {
+ final CNumber nValue = getANumber(constDef, sValue);
+ if( null != nValue ) {
+ resultType = evalType(resultType , nValue);
+ javaExpr.append(nValue.toJavaString());
+ } else {
+ // Lookup CNumber type in const-map, to evaluate this result type
+ final JavaExpr cje = constMap.get(sValue);
+ if( null != cje ) {
+ resultType = evalType(resultType , cje.resultType);
+ }
+ javaExpr.append(sValue);
+ }
+ return resultType;
+ }
+ private static CNumber getANumber(final ConstantDefinition constDef, final String value) {
+ try {
+ final CNumber number = decodeANumber(value);
+ if( null != number ) {
+ return number;
+ }
+ } catch( final Throwable _t ) {
+ final String msg = "Cannot emit const \""+constDef.getName()+"\": value \""+value+
+ "\" cannot be assigned to a int, long, float, or double";
+ throw new GlueGenException(msg, constDef.getASTLocusTag(), _t);
+ }
+ return null;
+ }
+ private static CNumber evalType(final CNumber resultType, final CNumber type) {
+ //fast path
+ if( type.isDouble ) {
+ return type;
+ }
+ if( null != resultType ) {
+ if( resultType.isInteger ) {
+ if( resultType.isLong ) {
+ /* resultType is Long */
+ if( !type.isInteger ) {
+ /* resultType: Long -> [ Float || Double ] */
+ return type;
+ }
+ } else if( type.isLong || !type.isInteger ) {
+ /* resultType: Integer -> [ Long || Float || Double ] */
+ return type;
+ }
+ } else if( !resultType.isInteger && !resultType.isDouble ) {
+ if( type.isDouble ) {
+ /* resultType: Float -> Double */
+ return type;
+ }
+ }
+ } else {
+ return type;
+ }
+ return resultType;
+ }
+ }
+
private final boolean relaxedEqSem;
- private final String sValue;
- private final long iValue;
- private final boolean hasIntValue;
+ private final String nativeExpr;
+ private final CNumber number;
private final boolean isEnum;
private final String enumName;
private final ASTLocusTag astLocus;
- /** Covering enums */
+ /**
+ * Constructor for plain const-values, non-enumerates.
+ * @param name unique name of this constant expression
+ * @param nativeExpr original [native] expression
+ * @param number optional {@link CNumber} representing this constant.
+ * If {@code null}, implementation attempts to derive a {@link CNumber}
+ * of the given {@code nativeExpr}.
+ * @param astLocus AST location of the represented constant.
+ */
public ConstantDefinition(final String name,
- final long value,
- final String enumName,
+ final String nativeExpr,
+ final CNumber number,
final ASTLocusTag astLocus) {
- super(name);
- this.relaxedEqSem = TypeConfig.relaxedEqualSemanticsTest();
- this.sValue = String.valueOf(value);
- this.iValue = value;
- this.hasIntValue = true;
- this.isEnum = true;
- this.enumName = enumName;
- this.astLocus = astLocus;
+ this(name, nativeExpr, number, false, null, astLocus);
}
-
- /** Covering defines */
+ /**
+ * Constructor for enumerates
+ * @param name unique name of this constant expression
+ * @param nativeExpr original [native] expression
+ * @param number optional {@link CNumber} representing this constant.
+ * If {@code null}, implementation attempts to derive a {@link CNumber}
+ * of the given {@code nativeExpr}.
+ * @param enumName optional name of the represented enumeration
+ * @param astLocus AST location of the represented constant.
+ */
public ConstantDefinition(final String name,
- final String value,
- final ASTLocusTag astLocus) {
+ final String nativeExpr,
+ final CNumber number,
+ final String enumName, final ASTLocusTag astLocus) {
+ this(name, nativeExpr, number, true, enumName, astLocus);
+ }
+ /**
+ * @param name unique name of this constant expression
+ * @param nativeExpr original [native] expression
+ * @param number optional {@link CNumber} representing this constant.
+ * If {@code null}, implementation attempts to derive a {@link CNumber}
+ * of the given {@code nativeExpr}.
+ * @param isEnum {@code true} if this constant is an enumerate, otherwise {@code false}.
+ * @param enumName optional name of the represented enumeration
+ * @param astLocus AST location of the represented constant.
+ */
+ private ConstantDefinition(final String name,
+ final String nativeExpr,
+ final CNumber number,
+ final boolean isEnum, final String enumName, final ASTLocusTag astLocus) {
super(name);
+ this.nativeExpr = nativeExpr;
this.relaxedEqSem = TypeConfig.relaxedEqualSemanticsTest();
- this.sValue = value;
- {
+ if( null != number ) {
+ this.number = number;
+ } else {
// Attempt to parse define string as number
- long v;
- boolean b;
- try {
- v = Long.decode(value).longValue();
- b = true;
- } catch (final NumberFormatException e) {
- v = 0;
- b = false;
+ final CNumber iNum = decodeIntegerNumber(nativeExpr);
+ if( null != iNum ) {
+ this.number = iNum;
+ } else {
+ final CNumber fNum = decodeDecimalNumber(nativeExpr);
+ if( null != fNum ) {
+ this.number = fNum;
+ } else {
+ this.number = null;
+ }
}
- this.iValue = v;
- this.hasIntValue = b;
}
- this.isEnum = false;
- this.enumName = null;
+ this.isEnum = isEnum;
+ this.enumName = enumName;
this.astLocus = astLocus;
}
@@ -122,8 +415,10 @@ public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSema
public final int hashCodeSemantics() {
// 31 * x == (x << 5) - x
int hash = 31 + ( null != getName() ? getName().hashCode() : 0 );
- hash = ((hash << 5) - hash) + ( null != sValue ? sValue.hashCode() : 0 );
- return ((hash << 5) - hash) + ( null != enumName ? enumName.hashCode() : 0 );
+ hash = ((hash << 5) - hash) + ( isEnum ? 1 : 0 );
+ hash = ((hash << 5) - hash) + ( null != enumName ? enumName.hashCode() : 0 );
+ hash = ((hash << 5) - hash) + ( null != number ? number.hashCode() : 0 );
+ return ((hash << 5) - hash) + ( !relaxedEqSem && null != nativeExpr ? nativeExpr.hashCode() : 0 );
}
@Override
@@ -135,30 +430,49 @@ public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSema
} else {
final ConstantDefinition t = (ConstantDefinition) arg;
if( !equals(getName(), t.getName()) ||
+ isEnum != t.isEnum ||
!equals(enumName, t.enumName) ) {
return false;
}
- if( hasIntValue ) {
- return iValue == t.iValue;
+ if( null != number ) {
+ if( number.isInteger ) {
+ return number.i == t.number.i;
+ } else {
+ return number.f == t.number.f;
+ }
} else {
// define's string value may be semantical equal .. but formatted differently!
- return relaxedEqSem || equals(sValue, t.sValue);
+ return relaxedEqSem || equals(nativeExpr, t.nativeExpr);
}
}
}
- public String getValue() { return sValue; }
- /** Returns null if this definition was not part of an
- enumeration, or if the enum was anonymous. */
+ /** Returns the original [native] expression. */
+ public String getNativeExpr() { return nativeExpr; }
+ /**
+ * Returns the parsed {@link CNumber} of the {@link #getNativeExpr() native expression},
+ * or {@code null} if the latter does not comprise a single number,
+ * i.e. is a complex expression.
+ */
+ public CNumber getNumber() { return number; }
+ /**
+ * Returns {@code true} if this instance represents has a {@link #getNumber() number},
+ * otherwise {@code false}.
+ */
+ public boolean hasNumber() { return null != number; }
+
+ /** Returns {@code null} if this definition was not part of an
+ enumeration, or if the enumeration is anonymous. */
public String getEnumName() { return enumName; }
public boolean isEnum() { return isEnum; }
@Override
public String toString() {
- return "ConstantDefinition [name " + getName()
- + ", value " + sValue + " (isInt " + hasIntValue
- + "), enumName " + enumName + ", isEnum " + isEnum + "]";
+ return "ConstantDefinition [name \"" + getName()
+ + "\", expression \"" + nativeExpr
+ + "\", number "+number
+ + "], enum[is " + isEnum + ", name \"" + enumName + "\"]]";
}
private static boolean equals(final String s1, final String s2) {
@@ -172,6 +486,18 @@ public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSema
return s1.equals(s2);
}
+ /**
+ * Computes the {@link JavaExpr java expression} based on this instance,
+ * see {@link JavaExpr#create(ConstantDefinition)}.
+ */
+ public final JavaExpr computeJavaExpr(final Map<String, ConstantDefinition.JavaExpr> constMap) {
+ return JavaExpr.compute(this, constMap);
+ }
+
+ //
+ // Static utility functions for type detection
+ //
+
public static boolean isConstantExpression(final String value) {
if( null != value && value.length() > 0 ) {
// Single numeric value
@@ -222,32 +548,260 @@ public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSema
return identifier;
}
+ /**
+ * Returns either {@link #decodeIntegerNumber(String)},
+ * {@link #decodeDecimalNumber(String)} or {@code null}.
+ * @param v
+ */
+ public static CNumber decodeANumber(final String v) {
+ final CNumber iNumber = ConstantDefinition.decodeIntegerNumber(v);
+ if( null != iNumber ) {
+ return iNumber;
+ }
+ return ConstantDefinition.decodeDecimalNumber(v);
+ }
+
+ /**
+ * If the given string {@link #isIntegerNumber(String)},
+ * return the decoded integer value, represented as a {@code ANumber},
+ * otherwise returns {@code null}.
+ * <p>
+ * Method strips off sign prefix {@code +}
+ * and integer modifier suffixes {@code [uUlL]}
+ * before utilizing {@link Long#decode(String)}.
+ * </p>
+ * @param v
+ */
+ public static CNumber decodeIntegerNumber(final String v) {
+ if( null == v || !isIntegerNumber(v) ) {
+ return null;
+ }
+ String s0 = v.trim();
+ if( 0 == s0.length() ) {
+ return null;
+ }
+ if (s0.startsWith("+")) {
+ s0 = s0.substring(1, s0.length()).trim();
+ if( 0 == s0.length() ) {
+ return null;
+ }
+ }
+ final boolean neg;
+ if (s0.startsWith("-")) {
+ s0 = s0.substring(1, s0.length()).trim();
+ if( 0 == s0.length() ) {
+ return null;
+ }
+ neg = true;
+ } else {
+ neg = false;
+ }
+
+ // Test last two chars for [lL] and [uU] modifiers!
+ boolean isUnsigned = false;
+ boolean isLong = false;
+ final int j = s0.length() - 2;
+ for(int i = s0.length() - 1; i >= 0 && i >= j; i--) {
+ final char lastChar = s0.charAt(s0.length()-1);
+ if( lastChar == 'u' || lastChar == 'U' ) {
+ s0 = s0.substring(0, s0.length()-1);
+ isUnsigned = true;
+ } else if( lastChar == 'l' || lastChar == 'L' ) {
+ s0 = s0.substring(0, s0.length()-1);
+ isLong = true;
+ } else {
+ // early out, no modifier match!
+ break;
+ }
+ }
+ if( 0 == s0.length() ) {
+ return null;
+ }
+ final long res;
+ if( isLong && isUnsigned ) {
+ res = decodeULong(s0, neg);
+ } else {
+ if( neg ) {
+ s0 = "-" + s0;
+ }
+ res = Long.decode(s0).longValue();
+ }
+ final boolean isLong2 = isLong ||
+ ( !isUnsigned && ( Integer.MIN_VALUE > res || res > Integer.MAX_VALUE ) ) ||
+ ( isUnsigned && res > UNSIGNED_INT_MAX_VALUE );
+ return new CNumber(isLong2, isUnsigned, res);
+ }
+ private static long decodeULong(final String v, final boolean neg) throws NumberFormatException {
+ final int radix;
+ final int idx;
+ if (v.startsWith("0x") || v.startsWith("0X")) {
+ idx = 2;
+ radix = 16;
+ } else if (v.startsWith("#")) {
+ idx = 1;
+ radix = 16;
+ } else if (v.startsWith("0") && v.length() > 1) {
+ idx = 1;
+ radix = 8;
+ } else {
+ idx = 0;
+ radix = 10;
+ }
+ final String s0 = ( neg ? "-" : "" ) + v.substring(idx);
+ final BigInteger res = new BigInteger(s0, radix);
+ if( res.compareTo(UNSIGNED_LONG_MAX_VALUE) > 0 ) {
+ throw new NumberFormatException("Value \""+v+"\" is > UNSIGNED_LONG_MAX");
+ }
+ return res.longValue();
+ }
+
+ /**
+ * If the given string {@link #isDecimalNumber(String)},
+ * return the decoded floating-point value, represented as a {@code ANumber} object,
+ * otherwise returns {@code null}.
+ * <p>
+ * Method utilizes {@link Double#valueOf(String)}.
+ * </p>
+ * @param v
+ * @param isDouble return value for {@code double} flag
+ */
+ public static CNumber decodeDecimalNumber(final String v) {
+ if( null == v || !isDecimalNumber(v) ) {
+ return null;
+ }
+ final String s0 = v.trim();
+ if( 0 == s0.length() ) {
+ return null;
+ }
+ boolean _isDouble = false;
+ final char lastChar = s0.charAt(s0.length()-1);
+ if( lastChar == 'd' || lastChar == 'D' ) {
+ _isDouble = true;
+ }
+ final double res = Double.valueOf(s0).doubleValue();
+ final double ares = Math.abs(res);
+ return new CNumber(_isDouble || Float.MIN_VALUE > ares || ares > Float.MAX_VALUE, res);
+ }
+
+ /**
+ * Matches {@link #isHexNumber(String)} or {@link #isDecimalOrIntNumber(String)}.
+ */
public static boolean isNumber(final String s) {
if( isHexNumber(s) ) {
return true;
} else {
- return isDecimalNumber(s);
+ return isDecimalOrIntNumber(s);
+ }
+ }
+
+ /**
+ * Matches {@link #isHexNumber(String)} or {@link #patternIntegerNumber}.
+ */
+ public static boolean isIntegerNumber(final String s) {
+ if( isHexNumber(s) ) {
+ return true;
+ } else {
+ return patternIntegerNumber.matcher(s).matches();
}
}
+
+ /**
+ * Matches {@link #patternHexNumber}.
+ */
public static boolean isHexNumber(final String s) {
return patternHexNumber.matcher(s).matches();
}
- public static Pattern patternHexNumber = Pattern.compile("0[xX][0-9a-fA-F]+[lLfFuU]?");
+
+ /**
+ * Matches pattern for <code>floating point</code> number,
+ * compatible and described in {@link Double#valueOf(String)}.
+ */
+ public static boolean isDecimalNumber(final String s) {
+ return patternDecimalNumber.matcher(s).matches();
+ }
+
+ /**
+ * Complete pattern for <code>floating point</code> <i>and</i> <code>integer</code> number,
+ * covering {@link #patternDecimalNumber} <i>and</i> {@link #patternIntegerNumber}.
+ */
+ public static boolean isDecimalOrIntNumber(final String s) {
+ return patternDecimalOrIntNumber.matcher(s).matches();
+ }
+
+ /**
+ * Matches pattern for valid CPP operands, see {@link #patternCPPOperand}.
+ */
+ public static boolean isCPPOperand(final String s) {
+ return patternCPPOperand.matcher(s).matches();
+ }
+
+ /**
+ * Complete pattern for <code>hexadecimal</code> number,
+ * including an optional sign {@code [+-]} and optional suffixes {@code [uUlL]}.
+ */
+ public static Pattern patternHexNumber;
/**
* Complete pattern for <code>floating point</code> number,
* compatible and described in {@link Double#valueOf(String)}.
*/
- public static Pattern patternDecimalNumber;
- private static String fpRegex;
+ public final static Pattern patternDecimalNumber;
+
+ /**
+ * Complete pattern for <code>floating point</code> <i>and</i> <code>integer</code> number,
+ * covering {@link #patternDecimalNumber} <i>and</i> {@link #patternIntegerNumber}.
+ */
+ public final static Pattern patternDecimalOrIntNumber;
+
+ /**
+ * Complete pattern for <code>integer</code> number,
+ * including an optional sign {@code [+-]} and optional suffixes {@code [uUlL]}.
+ */
+ public final static Pattern patternIntegerNumber;
+
+ /**
+ * One of: {@code +} {@code -} {@code *} {@code /} {@code |} {@code &} {@code (} {@code )} {@code <<} {@code >>}
+ * <p>
+ * Expression excludes {@link #patternDecimalOrIntNumber}.
+ * </p>
+ */
+ public static Pattern patternCPPOperand;
+
static {
+ final String WhiteSpace = "[\\x00-\\x20]*";
final String Digits = "(\\p{Digit}+)";
final String HexDigits = "(\\p{XDigit}+)";
+ final String IntTypeSuffix =
+ "(" +
+ "[uU]|" +
+ "([uU][lL])|" +
+ "[lL]|" +
+ "([lL][uU])" +
+ ")";
+
+ final String hexRegex =
+ WhiteSpace + // Optional leading "whitespace"
+ "[+-]?" + // Optional sign character
+ // HexDigits IntTypeSuffix_opt
+ "0[xX]" + HexDigits + IntTypeSuffix + "?" +
+ WhiteSpace // Optional trailing "whitespace"
+ ;
+ patternHexNumber = Pattern.compile(hexRegex);
+
+ final String intRegex =
+ WhiteSpace + // Optional leading "whitespace"
+ "[+-]?" + // Optional sign character
+ // Digits IntTypeSuffix_opt
+ Digits + IntTypeSuffix + "?" +
+ WhiteSpace // Optional trailing "whitespace"
+ ;
+ patternIntegerNumber = Pattern.compile(intRegex);
+
// an exponent is 'e' or 'E' followed by an optionally
// signed decimal integer.
final String Exp = "[eE][+-]?"+Digits;
- fpRegex =
- ("[\\x00-\\x20]*"+ // Optional leading "whitespace"
+ final String fpRegex =
+ WhiteSpace + // Optional leading "whitespace"
"[+-]?" + // Optional sign character
"("+
"NaN|" + // "NaN" string
@@ -289,23 +843,112 @@ public class ConstantDefinition extends AliasedSymbolImpl implements AliasedSema
"[fFdD]?"+
")"+
")" +
- "[\\x00-\\x20]*"// Optional trailing "whitespace"
- );
+ WhiteSpace // Optional trailing "whitespace"
+ ;
patternDecimalNumber = Pattern.compile(fpRegex);
- }
- public static boolean isDecimalNumber(final String s) {
- return patternDecimalNumber.matcher(s).matches();
- }
- public static boolean isCPPOperand(final String s) {
- return patternCPPOperand.matcher(s).matches();
- }
- /**
- * One of: {@code +} {@code -} {@code *} {@code /} {@code |} {@code &} {@code (} {@code )} {@code <<} {@code >>}
- * <p>
- * Expression excludes {@link #patternDecimalNumber}.
- * </p>
- */
- public static Pattern patternCPPOperand = Pattern.compile("(?!"+fpRegex+")[\\+\\-\\*\\/\\|\\&\\(\\)]|(\\<\\<)|(\\>\\>)");
+ final String fpOrIntRegex =
+ WhiteSpace + // Optional leading "whitespace"
+ "[+-]?" + // Optional sign character
+ "("+
+ "NaN|" + // "NaN" string
+ "Infinity|" + // "Infinity" string
+
+ // Matching integers w/ IntTypeSuffix,
+ // which are otherwise not matched by the below floating point matcher!
+ // Digits IntTypeSuffix
+ "(" + Digits + IntTypeSuffix +")|" +
+
+ // A decimal floating-point string representing a finite positive
+ // number without a leading sign has at most five basic pieces:
+ // Digits . Digits ExponentPart FloatTypeSuffix
+ //
+ // Since this method allows integer-only strings as input
+ // in addition to strings of floating-point literals, the
+ // two sub-patterns below are simplifications of the grammar
+ // productions from the Java Language Specification, 2nd
+ // edition, section 3.10.2.
+
+ "("+
+ "("+
+ // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
+ "(" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" +
+ // . Digits ExponentPart_opt FloatTypeSuffix_opt
+ "(\\.(" + Digits + ")(" + Exp + ")?)|" +
+
+ // Hexadecimal w/ binary exponent
+ "(" +
+ "(" +
+ // Hexadecimal strings
+ // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
+ "(0[xX]" + HexDigits + "(\\.)?)|" +
+
+ // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
+ "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +
+ ")" +
+
+ // binary exponent
+ "[pP][+-]?" + Digits +
+ ")" +
+ ")" +
+ "[fFdD]?"+
+ ")"+
+ ")" +
+ WhiteSpace // Optional trailing "whitespace"
+ ;
+ patternDecimalOrIntNumber = Pattern.compile(fpOrIntRegex);
+
+ final String fpOrIntRegex2 =
+ WhiteSpace + // Optional leading "whitespace"
+ // "[+-]?" + // Optional sign character
+ "("+
+ "NaN|" + // "NaN" string
+ "Infinity|" + // "Infinity" string
+
+ // Matching integers w/ IntTypeSuffix,
+ // which are otherwise not matched by the below floating point matcher!
+ // Digits IntTypeSuffix
+ "(" + Digits + IntTypeSuffix +")|" +
+
+ // A decimal floating-point string representing a finite positive
+ // number without a leading sign has at most five basic pieces:
+ // Digits . Digits ExponentPart FloatTypeSuffix
+ //
+ // Since this method allows integer-only strings as input
+ // in addition to strings of floating-point literals, the
+ // two sub-patterns below are simplifications of the grammar
+ // productions from the Java Language Specification, 2nd
+ // edition, section 3.10.2.
+
+ "("+
+ "("+
+ // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
+ "(" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" +
+
+ // . Digits ExponentPart_opt FloatTypeSuffix_opt
+ "(\\.(" + Digits + ")(" + Exp + ")?)|" +
+
+ // Hexadecimal w/ binary exponent
+ "(" +
+ "(" +
+ // Hexadecimal strings
+ // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
+ "(0[xX]" + HexDigits + "(\\.)?)|" +
+
+ // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
+ "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +
+ ")" +
+
+ // binary exponent
+ "[pP][+-]?" + Digits +
+ ")" +
+ ")" +
+ "[fFdD]?"+
+ ")"+
+ ")" +
+ WhiteSpace // Optional trailing "whitespace"
+ ;
+ patternCPPOperand = Pattern.compile("(?!"+fpOrIntRegex2+")[\\+\\-\\*\\/\\|\\&\\(\\)]|(\\<\\<)|(\\>\\>)");
+ }
}
diff --git a/src/java/com/jogamp/gluegen/DebugEmitter.java b/src/java/com/jogamp/gluegen/DebugEmitter.java
index 582a1d7..046c2b6 100644
--- a/src/java/com/jogamp/gluegen/DebugEmitter.java
+++ b/src/java/com/jogamp/gluegen/DebugEmitter.java
@@ -74,7 +74,7 @@ public class DebugEmitter implements GlueEmitter {
@Override
public void emitDefine(final ConstantDefinition def, final String optionalComment) {
final String name = def.getName();
- final String value = def.getValue();
+ final String value = def.getNativeExpr();
System.out.println("#define " + name + " " + value +
(optionalComment != null ? ("// " + optionalComment) : ""));
}
diff --git a/src/java/com/jogamp/gluegen/GlueGen.java b/src/java/com/jogamp/gluegen/GlueGen.java
index d0ce9ed..6dee6f0 100644
--- a/src/java/com/jogamp/gluegen/GlueGen.java
+++ b/src/java/com/jogamp/gluegen/GlueGen.java
@@ -217,14 +217,17 @@ public class GlueGen implements GlueEmitterControls {
}
// iterate over all values in the enumeration
for (int i = 0; i < enumeration.getNumEnumerates(); ++i) {
- final String enumElementName = enumeration.getEnumName(i);
- allConstants.add(new ConstantDefinition(enumElementName, enumeration.getEnumValue(i),
- enumName, enumeration.getASTLocusTag()));
+ final EnumType.Enumerator enumerate = enumeration.getEnum(i);
+ final ConstantDefinition def =
+ new ConstantDefinition(enumerate.getName(), enumerate.getExpr(),
+ enumerate.getNumber(),
+ enumName, enumeration.getASTLocusTag());
+ allConstants.add(def);
}
}
for (final Object elem : lexer.getDefines()) {
final Define def = (Define) elem;
- allConstants.add(new ConstantDefinition(def.getName(), def.getValue(), def.getASTLocusTag()));
+ allConstants.add(new ConstantDefinition(def.getName(), def.getValue(), null, def.getASTLocusTag()));
}
allConstants.addAll(preprocessor.getConstantDefinitions());
diff --git a/src/java/com/jogamp/gluegen/JavaEmitter.java b/src/java/com/jogamp/gluegen/JavaEmitter.java
index 15dc401..02e56a4 100644
--- a/src/java/com/jogamp/gluegen/JavaEmitter.java
+++ b/src/java/com/jogamp/gluegen/JavaEmitter.java
@@ -40,24 +40,56 @@
package com.jogamp.gluegen;
+import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PACKAGE_PRIVATE;
+import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PRIVATE;
+import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PROTECTED;
+import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PUBLIC;
+import static com.jogamp.gluegen.JavaEmitter.MethodAccess.PUBLIC_ABSTRACT;
+import static java.util.logging.Level.FINE;
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.Buffer;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import jogamp.common.os.MachineDataInfoRuntime;
+
import com.jogamp.common.nio.Buffers;
import com.jogamp.common.os.DynamicLookupHelper;
import com.jogamp.common.os.MachineDataInfo;
-
-import java.io.*;
-import java.util.*;
-import java.text.MessageFormat;
-
+import com.jogamp.common.util.ArrayHashMap;
import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider;
import com.jogamp.gluegen.Logging.LoggerIf;
-import com.jogamp.gluegen.cgram.types.*;
+import com.jogamp.gluegen.cgram.types.AliasedSymbol;
+import com.jogamp.gluegen.cgram.types.ArrayType;
+import com.jogamp.gluegen.cgram.types.CVAttributes;
+import com.jogamp.gluegen.cgram.types.CompoundType;
+import com.jogamp.gluegen.cgram.types.Field;
+import com.jogamp.gluegen.cgram.types.FunctionSymbol;
+import com.jogamp.gluegen.cgram.types.FunctionType;
+import com.jogamp.gluegen.cgram.types.IntType;
+import com.jogamp.gluegen.cgram.types.PointerType;
+import com.jogamp.gluegen.cgram.types.SizeThunk;
+import com.jogamp.gluegen.cgram.types.StructLayout;
+import com.jogamp.gluegen.cgram.types.Type;
import com.jogamp.gluegen.cgram.types.TypeComparator.AliasedSemanticSymbol;
-
-import java.nio.Buffer;
-
-import jogamp.common.os.MachineDataInfoRuntime;
-import static java.util.logging.Level.*;
-import static com.jogamp.gluegen.JavaEmitter.MethodAccess.*;
+import com.jogamp.gluegen.cgram.types.TypeDictionary;
// PROBLEMS:
// - what if something returns 'const int *'? Could we
@@ -134,9 +166,12 @@ public class JavaEmitter implements GlueEmitter {
return functions;
}
- private <T extends AliasedSemanticSymbol> List<T> filterSymbolsInt(final List<T> inList, final List<T> outList) {
+ private <T extends AliasedSemanticSymbol> List<T> filterSymbolsInt(final List<T> inList,
+ final boolean preserveOrder,
+ final List<T> outList) {
final JavaConfiguration cfg = getConfig();
- final HashMap<String, T> symMap = new HashMap<String, T>(100);
+ final ArrayHashMap<String, T> symMap =
+ new ArrayHashMap<String, T>(false, 100, ArrayHashMap.DEFAULT_LOAD_FACTOR);
for (final T sym : inList) {
final String origName = sym.getName();
final String newName = cfg.getJavaSymbolRename(origName);
@@ -195,21 +230,23 @@ public class JavaEmitter implements GlueEmitter {
}
}
}
- outList.addAll(symMap.values());
- // sort constants to make them easier to find in native code
- Collections.sort(outList, new Comparator<T>() {
- @Override
- public int compare(final T o1, final T o2) {
- return o1.getName().compareTo(o2.getName());
- }
- });
+ outList.addAll(symMap.getData());
+ if( !preserveOrder ) {
+ // sort constants to make them easier to find in native code
+ Collections.sort(outList, new Comparator<T>() {
+ @Override
+ public int compare(final T o1, final T o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
+ }
return outList;
}
@Override
public void filterSymbols(final List<ConstantDefinition> inConstList, final List<FunctionSymbol> inFuncList) {
- constants = filterSymbolsInt(inConstList, new ArrayList<ConstantDefinition>(100));
- functions = filterSymbolsInt(inFuncList, new ArrayList<FunctionSymbol>(100));
+ constants = filterSymbolsInt(inConstList, true, new ArrayList<ConstantDefinition>(100));
+ functions = filterSymbolsInt(inFuncList, true, new ArrayList<FunctionSymbol>(100));
}
}
@@ -253,171 +290,6 @@ public class JavaEmitter implements GlueEmitter {
}
}
- protected static int getJavaRadix(final String name, final String value) {
- // FIXME: need to handle when type specifier is in last char (e.g.,
- // "1.0d or 2759L", because parseXXX() methods don't allow the type
- // specifier character in the string.
- //
- //char lastChar = value.charAt(value.length()-1);
-
- try {
- // see if it's a long or int
- int radix;
- String parseValue;
- // FIXME: are you allowed to specify hex/octal constants with
- // negation, e.g. "-0xFF" or "-056"? If so, need to modify the
- // following "if(..)" checks and parseValue computation
- if (value.startsWith("0x") || value.startsWith("0X")) {
- radix = 16;
- parseValue = value.substring(2);
- }
- else if (value.startsWith("0") && value.length() > 1) {
- // TODO: is "0" the prefix in C to indicate octal???
- radix = 8;
- parseValue = value.substring(1);
- }
- else {
- radix = 10;
- parseValue = value;
- }
- //System.err.println("parsing " + value + " as long w/ radix " + radix);
- Long.parseLong(parseValue, radix);
- return radix;
- } catch (final NumberFormatException e) {
- try {
- // see if it's a double or float
- Double.parseDouble(value);
- return 10;
- } catch (final NumberFormatException e2) {
- throw new RuntimeException(
- "Cannot emit define \""+name+"\": value \""+value+
- "\" cannot be assigned to a int, long, float, or double", e2);
- }
- }
- }
-
- protected static Object getJavaValue(final String name, final String value) {
-
- // "calculates" the result type of a simple expression
- // example: (2+3)-(2.0f-3.0) -> Double
- // example: (1 << 2) -> Integer
- final Scanner scanner = new Scanner(value).useDelimiter(ConstantDefinition.patternCPPOperand);
-
- Object resultType = null;
-
- while (scanner.hasNext()) {
-
- final String t = scanner.next().trim();
-
- if(0<t.length()) {
- final Object type = getJavaValue2(name, t);
-
- //fast path
- if(type instanceof Double)
- return type;
-
- if(resultType != null) {
-
- if(resultType instanceof Integer) {
- if(type instanceof Long || type instanceof Float || type instanceof Double)
- resultType = type;
- }else if(resultType instanceof Long) {
- if(type instanceof Float || type instanceof Double)
- resultType = type;
- }else if(resultType instanceof Float) {
- if(type instanceof Float)
- resultType = type;
- }
- }else{
- resultType = type;
- }
-
- //fast path
- if(resultType instanceof Double)
- return type;
- }
- }
-
- return resultType;
- }
-
- private static Object getJavaValue2(final String name, final String value) {
- // FIXME: need to handle when type specifier is in last char (e.g.,
- // "1.0d or 2759L", because parseXXX() methods don't allow the type
- // specifier character in the string.
- //
- final char lastChar = value.charAt(value.length()-1);
-
- try {
- // see if it's a long or int
- int radix;
- String parseValue;
- // FIXME: are you allowed to specify hex/octal constants with
- // negation, e.g. "-0xFF" or "-056"? If so, need to modify the
- // following "if(..)" checks and parseValue computation
- if (value.startsWith("0x") || value.startsWith("0X")) {
- radix = 16;
- parseValue = value.substring(2);
- } else if (value.startsWith("0") && value.length() > 1) {
- // TODO: is "0" the prefix in C to indicate octal???
- radix = 8;
- parseValue = value.substring(1);
- } else {
- radix = 10;
- parseValue = value;
- }
- if(lastChar == 'u' || lastChar == 'U') {
- parseValue = parseValue.substring(0, parseValue.length()-1);
- }
-
- //System.err.println("parsing " + value + " as long w/ radix " + radix);
- final long longVal = Long.parseLong(parseValue, radix);
- // if constant is small enough, store it as an int instead of a long
- if (longVal > Integer.MIN_VALUE && longVal < Integer.MAX_VALUE) {
- return (int)longVal;
- }
- return longVal;
-
- } catch (final NumberFormatException e) {
- try {
- // see if it's a double or float
- final double dVal = Double.parseDouble(value);
- final double absVal = Math.abs(dVal);
- // if constant is small enough, store it as a float instead of a double
- if (absVal < Float.MIN_VALUE || absVal > Float.MAX_VALUE || lastChar == 'd' || lastChar == 'D' ) {
- return new Double(dVal);
- }
- return new Float((float) dVal);
- } catch (final NumberFormatException e2) {
- throw new RuntimeException(
- "Cannot emit define \""+name+"\": value \""+value+
- "\" cannot be assigned to a int, long, float, or double", e2);
- }
- }
- }
-
-
- protected static String getJavaType(final String name, final String value) {
- final Object oval = getJavaValue(name, value);
- return getJavaType(name, oval);
- }
-
- protected static String getJavaType(final String name, final Object oval) {
- if(oval instanceof Integer) {
- return "int";
- } else if(oval instanceof Long) {
- return "long";
- } else if(oval instanceof Float) {
- return "float";
- } else if(oval instanceof Double) {
- return "double";
- }
-
- throw new RuntimeException(
- "Cannot emit define (2) \""+name+"\": value \""+oval+
- "\" cannot be assigned to a int, long, float, or double");
- }
-
/** Mangle a class, package or function name for JNI usage, i.e. replace all '.' w/ '_' */
protected static String jniMangle(final String name) {
return name.replaceAll("_", "_1").replace('.', '_');
@@ -427,6 +299,9 @@ public class JavaEmitter implements GlueEmitter {
return "Java_"+jniMangle(javaPackageName)+"_"+jniMangle(javaClassName);
}
+ private final Map<String, ConstantDefinition.JavaExpr> constMap =
+ new HashMap<String, ConstantDefinition.JavaExpr>();
+
@Override
public void emitDefine(final ConstantDefinition def, final String optionalComment) throws Exception {
if ( ( cfg.allStatic() || cfg.emitInterface() ) && !cfg.structsOnly() ) {
@@ -439,24 +314,22 @@ public class JavaEmitter implements GlueEmitter {
// currently only emits only numeric defines -- if it handled #define'd
// objects it would make a bigger difference.
- final String name = def.getName();
- String value = def.getValue();
-
if ( !cfg.shouldIgnoreInInterface(def) ) {
- final String type = getJavaType(name, value);
+ final ConstantDefinition.JavaExpr constExpr = def.computeJavaExpr(constMap);
+ constMap.put(def.getName(), constExpr);
+ javaWriter().print(" /** ");
if (optionalComment != null && optionalComment.length() != 0) {
- javaWriter().println(" /** " + optionalComment + " */");
+ javaWriter().print(optionalComment);
+ javaWriter().print(" - ");
}
- String suffix = "";
- final char lastChar = value.charAt(value.length()-1);
- if( lastChar != ')' ) {
- if (type.equals("float") && lastChar != 'f' && lastChar != 'F' ) {
- suffix = "f";
- }else if( lastChar == 'u' || lastChar == 'U' ) {
- value = value.substring(0, value.length()-1);
- }
+ javaWriter().print("CType: ");
+ if( constExpr.resultType.isUnsigned ) {
+ javaWriter().print("unsigned ");
}
- javaWriter().println(" public static final " + type + " " + name + " = " + value + suffix + ";");
+ javaWriter().print(constExpr.resultJavaTypeName);
+ javaWriter().println(" */");
+ javaWriter().println(" public static final " + constExpr.resultJavaTypeName +
+ " " + def.getName() + " = " + constExpr.javaExpression + ";");
}
}
}
@@ -2627,8 +2500,9 @@ public class JavaEmitter implements GlueEmitter {
if (getConfig().emitImpl()) {
cWriter.println("#include <assert.h>");
+ cWriter.println("#include <stddef.h>");
cWriter.println();
- cWriter.println("static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, jlong capacity); /* forward decl. */");
+ cWriter.println("static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, size_t capacity); /* forward decl. */");
cWriter.println();
}
for (final String code : cfg.customCCode()) {
@@ -2641,6 +2515,7 @@ public class JavaEmitter implements GlueEmitter {
"static const char * clazzNameBuffers = \"com/jogamp/common/nio/Buffers\";\n"+
"static const char * clazzNameBuffersStaticNewCstrName = \"newDirectByteBuffer\";\n"+
"static const char * clazzNameBuffersStaticNewCstrSignature = \"(I)Ljava/nio/ByteBuffer;\";\n"+
+ "static const char * sFatalError = \"FatalError:\";\n"+
"static jclass clazzBuffers = NULL;\n"+
"static jmethodID cstrBuffersNew = NULL;\n"+
"static jboolean _initClazzAccessDone = JNI_FALSE;\n"+
@@ -2652,13 +2527,13 @@ public class JavaEmitter implements GlueEmitter {
"\n"+
" c = (*env)->FindClass(env, clazzNameBuffers);\n"+
" if(NULL==c) {\n"+
- " fprintf(stderr, \"FatalError: Can't find %s\\n\", clazzNameBuffers);\n"+
+ " fprintf(stderr, \"%s Can't find %s\\n\", sFatalError, clazzNameBuffers);\n"+
" (*env)->FatalError(env, clazzNameBuffers);\n"+
" return JNI_FALSE;\n"+
" }\n"+
" clazzBuffers = (jclass)(*env)->NewGlobalRef(env, c);\n"+
" if(NULL==clazzBuffers) {\n"+
- " fprintf(stderr, \"FatalError: Can't use %s\\n\", clazzNameBuffers);\n"+
+ " fprintf(stderr, \"%s Can't use %s\\n\", sFatalError, clazzNameBuffers);\n"+
" (*env)->FatalError(env, clazzNameBuffers);\n"+
" return JNI_FALSE;\n"+
" }\n"+
@@ -2666,7 +2541,7 @@ public class JavaEmitter implements GlueEmitter {
" cstrBuffersNew = (*env)->GetStaticMethodID(env, clazzBuffers,\n"+
" clazzNameBuffersStaticNewCstrName, clazzNameBuffersStaticNewCstrSignature);\n"+
" if(NULL==cstrBuffersNew) {\n"+
- " fprintf(stderr, \"FatalError: can't create %s.%s %s\\n\",\n"+
+ " fprintf(stderr, \"%s can't create %s.%s %s\\n\", sFatalError,\n"+
" clazzNameBuffers,\n"+
" clazzNameBuffersStaticNewCstrName, clazzNameBuffersStaticNewCstrSignature);\n"+
" (*env)->FatalError(env, clazzNameBuffersStaticNewCstrName);\n"+
@@ -2676,18 +2551,35 @@ public class JavaEmitter implements GlueEmitter {
" return JNI_TRUE;\n"+
"}\n"+
"\n"+
- "static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, jlong capacity) {\n"+
+ "#define JINT_MAX_VALUE ((size_t)0x7fffffffU)\n"+
+ "static const char * sNewBufferImplNotCalled = \"initializeImpl() not called\";\n"+
+ "static const char * sNewBufferMAX_INT = \"capacity > MAX_INT\";\n"+
+ "static const char * sNewBufferNULL = \"New direct ByteBuffer is NULL\";\n"+
+ "\n"+
+ "static jobject JVMUtil_NewDirectByteBufferCopy(JNIEnv *env, void * source_address, size_t capacity) {\n"+
" jobject jbyteBuffer;\n"+
" void * byteBufferPtr;\n"+
"\n"+
" if( JNI_FALSE == _initClazzAccessDone ) {\n"+
- " fprintf(stderr, \"FatalError: initializeImpl() not called\\n\");\n"+
- " (*env)->FatalError(env, \"initializeImpl() not called\");\n"+
+ " fprintf(stderr, \"%s %s\\n\", sFatalError, sNewBufferImplNotCalled);\n"+
+ " (*env)->FatalError(env, sNewBufferImplNotCalled);\n"+
" return NULL;\n"+
" }\n"+
- " jbyteBuffer = (*env)->CallStaticObjectMethod(env, clazzBuffers, cstrBuffersNew, capacity);\n"+
- " byteBufferPtr = (*env)->GetDirectBufferAddress(env, jbyteBuffer);\n"+
- " memcpy(byteBufferPtr, source_address, capacity);\n"+
+ " if( JINT_MAX_VALUE < capacity ) {\n"+
+ " fprintf(stderr, \"%s %s: %lu\\n\", sFatalError, sNewBufferMAX_INT, (unsigned long)capacity);\n"+
+ " (*env)->FatalError(env, sNewBufferMAX_INT);\n"+
+ " return NULL;\n"+
+ " }\n"+
+ " jbyteBuffer = (*env)->CallStaticObjectMethod(env, clazzBuffers, cstrBuffersNew, (jint)capacity);\n"+
+ " if( NULL == jbyteBuffer ) {\n"+
+ " fprintf(stderr, \"%s %s: size %lu\\n\", sFatalError, sNewBufferNULL, (unsigned long)capacity);\n"+
+ " (*env)->FatalError(env, sNewBufferNULL);\n"+
+ " return NULL;\n"+
+ " }\n"+
+ " if( 0 < capacity ) {\n"+
+ " byteBufferPtr = (*env)->GetDirectBufferAddress(env, jbyteBuffer);\n"+
+ " memcpy(byteBufferPtr, source_address, capacity);\n"+
+ " }\n"+
" return jbyteBuffer;\n"+
"}\n"+
"\n";
diff --git a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java
index c145ff5..d3fca14 100644
--- a/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java
+++ b/src/java/com/jogamp/gluegen/JavaMethodBindingEmitter.java
@@ -894,7 +894,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter {
writer.print(" valid values are: <code>");
for (int j = 0; j < enumType.getNumEnumerates(); ++j) {
if (j>0) writer.print(", ");
- writer.print(enumType.getEnumName(j));
+ writer.print(enumType.getEnum(j).getName());
}
writer.println("</code>");
} else if (javaType.isNIOBuffer()) {
diff --git a/src/java/com/jogamp/gluegen/cgram/TNode.java b/src/java/com/jogamp/gluegen/cgram/TNode.java
index 70dc2c4..5a36945 100644
--- a/src/java/com/jogamp/gluegen/cgram/TNode.java
+++ b/src/java/com/jogamp/gluegen/cgram/TNode.java
@@ -10,6 +10,7 @@ import java.util.Enumeration;
import com.jogamp.gluegen.ASTLocusTag;
import com.jogamp.gluegen.ASTLocusTag.ASTLocusTagProvider;
+import com.jogamp.gluegen.GlueGen;
/**
Class TNode is an implementation of the AST interface
@@ -179,15 +180,87 @@ public void initialize(final AST tr) {
text = text_;
}
- /** Returns the text for this node and all children */
- public String getAllChildrenText() {
+ static class DebugASTVisitor {
+ protected int level;
+ private String tabs(final StringBuilder sb) {
+ sb.setLength(0);
+ for (int i = 0; i < level; i++) {
+ sb.append(" ");
+ }
+ return sb.toString();
+ }
+ DebugASTVisitor(final int level) {
+ this.level = level;
+ }
+ void visit(final AST node) {
+ final StringBuilder sb = new StringBuilder();
+ AST node2;
+ for (node2 = node; node2 != null; node2 = node2.getNextSibling()) {
+ if (node2.getText() == null) {
+ System.err.printf("%03d: %snil [%d]%n", level, tabs(sb), node2.getType());
+ } else {
+ System.err.printf("%03d: %s%s [%d]%n", level, tabs(sb), node2.getText(), node2.getType());
+ }
+ if (node2.getFirstChild() != null) {
+ level++;
+ visit(node2.getFirstChild());
+ level--;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the text for this node, its children and siblings.
+ * <p>
+ * Implementation converts the AST LISP notation to serialized form.
+ * </p>
+ */
+ public String getAllChildrenText(final String name) {
+ if( GlueGen.debug() ) {
+ System.err.println("TNode.XXX: "+name);
+ new DebugASTVisitor(1).visit(getFirstChild());
+ }
final StringBuilder buf = new StringBuilder();
- buf.append(getText());
- for (TNode node = (TNode) getFirstChild(); node != null; node = (TNode) node.getNextSibling()) {
- buf.append(node.getText());
+ final TNode down = (TNode) this.getFirstChild();
+ if( null == down ) {
+ buf.append(this.getText());
+ } else {
+ getAllChildrenText(buf, this, down);
}
return buf.toString();
}
+ private static void getAllChildrenText(final StringBuilder buf,
+ final TNode upNode, TNode thisNode) {
+ boolean first = true;
+ while( null != thisNode ) {
+ final boolean isClosing = HeaderParserTokenTypes.RPAREN == thisNode.getType();
+ final boolean isGroupStart = HeaderParserTokenTypes.NExpressionGroup == thisNode.getType();
+
+ final TNode nextNode = (TNode) thisNode.getNextSibling();
+ final TNode downNode = (TNode) thisNode.getFirstChild();
+ if( !isClosing &&
+ ( null == downNode && null == nextNode || // unary
+ !first // binary
+ )
+ ) {
+ buf.append(" ").append(upNode.getText());
+ }
+ if( null != downNode ) {
+ if( !isGroupStart ) {
+ buf.append(" (");
+ }
+ getAllChildrenText(buf, thisNode, downNode);
+ if( !isGroupStart ) {
+ buf.append(" )");
+ }
+ } else if( !isClosing ) {
+ buf.append(" ").append(thisNode.getText());
+ }
+ thisNode = nextNode;
+ first = false;
+ }
+ }
/** return the last child of this node, or null if there is none */
public TNode getLastChild() {
diff --git a/src/java/com/jogamp/gluegen/cgram/types/EnumType.java b/src/java/com/jogamp/gluegen/cgram/types/EnumType.java
index f49c2ec..7c2fa73 100644
--- a/src/java/com/jogamp/gluegen/cgram/types/EnumType.java
+++ b/src/java/com/jogamp/gluegen/cgram/types/EnumType.java
@@ -43,6 +43,9 @@ import java.util.ArrayList;
import java.util.NoSuchElementException;
import com.jogamp.gluegen.ASTLocusTag;
+import com.jogamp.gluegen.ConstantDefinition;
+import com.jogamp.gluegen.ConstantDefinition.CNumber;
+import com.jogamp.gluegen.GlueGenException;
import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp;
@@ -50,40 +53,49 @@ import com.jogamp.gluegen.cgram.types.TypeComparator.SemanticEqualityOp;
they have a set of named values. */
public class EnumType extends IntType implements Cloneable {
- private static class Enum implements TypeComparator.SemanticEqualityOp {
- final String name;
- final long value;
+ public static class Enumerator implements TypeComparator.SemanticEqualityOp {
+ private final String name;
+ private final String expr;
+ private final CNumber number;
- Enum(final String name, final long value) {
+ public Enumerator(final String name, final long value) {
this.name = name;
- this.value = value;
+ this.number = new CNumber(false, false, value);
+ this.expr = this.number.toJavaString();
}
-
- String getName() {
- return name;
+ public Enumerator(final String name, final CNumber number) {
+ this.name = name;
+ this.number = number;
+ this.expr = this.number.toJavaString();
}
-
- long getValue() {
- return value;
+ public Enumerator(final String name, final String value) {
+ this.name = name;
+ this.expr = value;
+ this.number = ConstantDefinition.decodeIntegerNumber(value);
}
+ public String getName() { return name; }
+ public String getExpr() { return expr; }
+ public CNumber getNumber() { return number; }
+ public boolean hasNumber() { return null != number; }
+
@Override
public int hashCode() {
// 31 * x == (x << 5) - x
final int hash = name.hashCode();
- return ((hash << 5) - hash) + (int)(value ^ (value >>> 32));
+ return ((hash << 5) - hash) + expr.hashCode();
}
@Override
public boolean equals(final Object arg) {
if (arg == this) {
return true;
- } else if ( !(arg instanceof Enum) ) {
+ } else if ( !(arg instanceof Enumerator) ) {
return false;
}
- final Enum t = (Enum) arg;
+ final Enumerator t = (Enumerator) arg;
return name.equals(t.name) &&
- value == t.value;
+ expr.equals(t.expr);
}
@Override
@@ -96,11 +108,12 @@ public class EnumType extends IntType implements Cloneable {
return equals(arg);
}
- public String toString() { return name+" = "+value; }
+ @Override
+ public String toString() { return "["+name+" = ["+expr+", "+number+"]"; }
}
private final IntType underlyingType;
- private ArrayList<Enum> enums;
+ private ArrayList<Enumerator> enums;
public EnumType(final String name) {
super(name, SizeThunk.LONG, false, CVAttributes.CONST);
@@ -116,7 +129,7 @@ public class EnumType extends IntType implements Cloneable {
super(o, cvAttributes, astLocus);
underlyingType = o.underlyingType;
if(null != o.enums) {
- enums = new ArrayList<Enum>(o.enums);
+ enums = new ArrayList<Enumerator>(o.enums);
}
}
@@ -164,11 +177,11 @@ public class EnumType extends IntType implements Cloneable {
public Type getUnderlyingType() { return this.underlyingType; }
- public void addEnum(final String name, final long val) {
+ public void addEnum(final String name, final Enumerator newEnum) {
if (enums == null) {
- enums = new ArrayList<Enum>();
+ enums = new ArrayList<Enumerator>();
}
- enums.add(new Enum(name, val));
+ enums.add(newEnum);
clearCache();
}
@@ -177,22 +190,17 @@ public class EnumType extends IntType implements Cloneable {
return enums.size();
}
- /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) name */
- public String getEnumName(final int i) {
- return (enums.get(i)).getName();
- }
-
- /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) value */
- public long getEnumValue(final int i) {
- return (enums.get(i)).getValue();
+ /** Fetch <i>i</i>th (0..getNumEnumerates() - 1) {@link Enumerator} */
+ public Enumerator getEnum(final int i) {
+ return enums.get(i);
}
- /** Fetch the value of the enumerate with the given name. */
- public long getEnumValue(final String name) {
+ /** Fetch the enumerate with the given name. */
+ public Enumerator getEnum(final String name) {
for (int i = 0; i < enums.size(); ++i) {
- final Enum n = (enums.get(i));
+ final Enumerator n = (enums.get(i));
if (n.getName().equals(name)) {
- return n.getValue();
+ return n;
}
}
throw new NoSuchElementException(
@@ -215,7 +223,7 @@ public class EnumType extends IntType implements Cloneable {
*/
public boolean removeEnumerate(final String name) {
for (int i = 0; i < enums.size(); ++i) {
- final Enum e = enums.get(i);
+ final Enumerator e = enums.get(i);
if (e.getName().equals(name)) {
enums.remove(e);
clearCache();
diff --git a/src/java/jogamp/android/launcher/MainLauncher.java b/src/java/jogamp/android/launcher/MainLauncher.java
index 0dc6b4a..e0eff7d 100644
--- a/src/java/jogamp/android/launcher/MainLauncher.java
+++ b/src/java/jogamp/android/launcher/MainLauncher.java
@@ -33,6 +33,8 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
+import com.jogamp.common.util.InterruptSource;
+
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
@@ -117,17 +119,17 @@ public class MainLauncher extends Activity {
public void onResume() {
Log.d(TAG, "onResume - S - "+Thread.currentThread().getName());
super.onResume();
- final Thread mainThread = new Thread("Main") {
+ final Thread mainThread = new InterruptSource.Thread(null, null, "Main") {
public void run() {
try {
- Log.d(TAG, "onResume - main.0 - "+Thread.currentThread().getName());
+ Log.d(TAG, "onResume - main.0 - "+java.lang.Thread.currentThread().getName());
mainClazzMain.invoke(null, new Object[] { mainClassArgs } );
} catch (final InvocationTargetException ite) {
ite.getTargetException().printStackTrace();
} catch (final Throwable t) {
t.printStackTrace();
}
- Log.d(TAG, "onResume - main.X -> finish() - "+Thread.currentThread().getName());
+ Log.d(TAG, "onResume - main.X -> finish() - "+java.lang.Thread.currentThread().getName());
finish();
} };
mainThread.start();
diff --git a/src/java/jogamp/common/os/PlatformPropsImpl.java b/src/java/jogamp/common/os/PlatformPropsImpl.java
index 0d0063c..097a013 100644
--- a/src/java/jogamp/common/os/PlatformPropsImpl.java
+++ b/src/java/jogamp/common/os/PlatformPropsImpl.java
@@ -63,6 +63,10 @@ public abstract class PlatformPropsImpl {
public static final VersionNumber Version16;
/** Version 1.7. As a JVM version, it enables certain JVM 1.7 features. */
public static final VersionNumber Version17;
+ /** Version 1.8. As a JVM version, it enables certain JVM 1.8 features. */
+ public static final VersionNumber Version18;
+ /** Version 1.9. As a JVM version, it enables certain JVM 1.9 features. */
+ public static final VersionNumber Version19;
public static final String OS;
public static final String OS_lower;
@@ -101,6 +105,8 @@ public abstract class PlatformPropsImpl {
static {
Version16 = new VersionNumber(1, 6, 0);
Version17 = new VersionNumber(1, 7, 0);
+ Version18 = new VersionNumber(1, 8, 0);
+ Version19 = new VersionNumber(1, 9, 0);
// We don't seem to need an AccessController.doPrivileged() block
// here as these system properties are visible even to unsigned Applets.
diff --git a/src/java/jogamp/common/util/Int32ArrayBitfield.java b/src/java/jogamp/common/util/Int32ArrayBitfield.java
new file mode 100644
index 0000000..5bc95eb
--- /dev/null
+++ b/src/java/jogamp/common/util/Int32ArrayBitfield.java
@@ -0,0 +1,207 @@
+/**
+ * Copyright 2015 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.common.util;
+
+import com.jogamp.common.util.Bitfield;
+
+/**
+ * Simple bitfield interface for efficient storage access in O(1).
+ * <p>
+ * Implementation uses a 32bit integer array for storage.
+ * </p>
+ */
+public class Int32ArrayBitfield implements Bitfield {
+ private static final int UNIT_SHIFT = 5;
+ private final int[] storage;
+ private final int bitSize;
+
+ /**
+ * @param storageBitSize
+ */
+ public Int32ArrayBitfield(final int storageBitSize) {
+ final int units = Math.max(1, ( storageBitSize + 31 ) >>> UNIT_SHIFT);
+ this.storage = new int[units]; // initialized w/ default '0'
+ this.bitSize = units << UNIT_SHIFT;
+ }
+
+ @Override
+ public int size() {
+ return bitSize;
+ }
+
+ @Override
+ public final void clearField(final boolean bit) {
+ final int v;
+ if( bit ) {
+ v = Bitfield.UNSIGNED_INT_MAX_VALUE;
+ } else {
+ v = 0;
+ }
+ for(int i=storage.length-1; i>=0; i--) {
+ storage[i] = v;
+ }
+ }
+
+ private static final void check(final int size, final int bitnum) throws IndexOutOfBoundsException {
+ if( 0 > bitnum || bitnum >= size ) {
+ throw new IndexOutOfBoundsException("Bitnum should be within [0.."+(size-1)+"], but is "+bitnum);
+ }
+ }
+
+ @Override
+ public final int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException {
+ if( 0 > length || length > 32 ) {
+ throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length);
+ }
+ check(bitSize-length+1, lowBitnum);
+ final int u = lowBitnum >>> UNIT_SHIFT;
+ final int left = 32 - ( lowBitnum - ( u << UNIT_SHIFT ) ); // remaining bits of first chunk storage
+ if( 32 == left ) {
+ // fast path
+ final int m = Util.getBitMask(length); // mask of chunk
+ return m & storage[u];
+ } else {
+ // slow path
+ final int l = Math.min(length, left); // length of first chunk < 32
+ final int m = ( 1 << l ) - 1; // mask of first chunk
+ final int d = m & ( storage[u] >>> lowBitnum );
+ final int l2 = length - l; // length of last chunk < 32
+ if( l2 > 0 ) {
+ final int m2 = ( 1 << l2 ) - 1; // mask of last chunk
+ return d | ( ( m2 & storage[u+1] ) << l );
+ } else {
+ return d;
+ }
+ }
+ }
+ @Override
+ public final void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException {
+ if( 0 > length || length > 32 ) {
+ throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length);
+ }
+ check(bitSize-length+1, lowBitnum);
+ final int u = lowBitnum >>> UNIT_SHIFT;
+ final int left = 32 - ( lowBitnum - ( u << UNIT_SHIFT ) ); // remaining bits of first chunk storage
+ if( 32 == left ) {
+ // fast path
+ final int m = Util.getBitMask(length); // mask of chunk
+ storage[u] = ( ( ~m ) & storage[u] ) // keep non-written storage bits
+ | ( m & data ); // overwrite storage w/ used data bits
+ } else {
+ // slow path
+ final int l = Math.min(length, left); // length of first chunk < 32
+ final int m = ( 1 << l ) - 1; // mask of first chunk
+ storage[u] = ( ( ~( m << lowBitnum ) ) & storage[u] ) // keep non-written storage bits
+ | ( ( m & data ) << lowBitnum ); // overwrite storage w/ used data bits
+ final int l2 = length - l; // length of last chunk < 32
+ if( l2 > 0 ) {
+ final int m2 = ( 1 << l2 ) - 1; // mask of last chunk
+ storage[u+1] = ( ( ~m2 ) & storage[u+1] ) // keep non-written storage bits
+ | ( m2 & ( data >>> l ) ); // overwrite storage w/ used data bits
+ }
+ }
+ }
+ @Override
+ public final int copy32(final int srcBitnum, final int dstBitnum, final int length) throws IndexOutOfBoundsException {
+ final int data = get32(srcBitnum, length);
+ put32(dstBitnum, length, data);
+ return data;
+ }
+
+ @Override
+ public final boolean get(final int bitnum) throws IndexOutOfBoundsException {
+ check(bitSize, bitnum);
+ final int u = bitnum >>> UNIT_SHIFT;
+ final int b = bitnum - ( u << UNIT_SHIFT );
+ return 0 != ( storage[u] & ( 1 << b ) ) ;
+ }
+
+ @Override
+ public final boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException {
+ check(bitSize, bitnum);
+ final int u = bitnum >>> UNIT_SHIFT;
+ final int b = bitnum - ( u << UNIT_SHIFT );
+ final int m = 1 << b;
+ final boolean prev = 0 != ( storage[u] & m ) ;
+ if( prev != bit ) {
+ if( bit ) {
+ storage[u] |= m;
+ } else {
+ storage[u] &= ~m;
+ }
+ }
+ return prev;
+ }
+ @Override
+ public final void set(final int bitnum) throws IndexOutOfBoundsException {
+ check(bitSize, bitnum);
+ final int u = bitnum >>> UNIT_SHIFT;
+ final int b = bitnum - ( u << UNIT_SHIFT );
+ final int m = 1 << b;
+ storage[u] |= m;
+ }
+ @Override
+ public final void clear(final int bitnum) throws IndexOutOfBoundsException {
+ check(bitSize, bitnum);
+ final int u = bitnum >>> UNIT_SHIFT;
+ final int b = bitnum - ( u << UNIT_SHIFT );
+ final int m = 1 << b;
+ storage[u] &= ~m;
+ }
+ @Override
+ public final boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException {
+ check(bitSize, srcBitnum);
+ check(bitSize, dstBitnum);
+ final boolean bit;
+ // get
+ {
+ final int u = srcBitnum >>> UNIT_SHIFT;
+ final int b = srcBitnum - ( u << UNIT_SHIFT );
+ bit = 0 != ( storage[u] & ( 1 << b ) ) ;
+ }
+ // put
+ final int u = dstBitnum >>> UNIT_SHIFT;
+ final int b = dstBitnum - ( u << UNIT_SHIFT );
+ final int m = 1 << b;
+ if( bit ) {
+ storage[u] |= m;
+ } else {
+ storage[u] &= ~m;
+ }
+ return bit;
+ }
+
+ @Override
+ public int bitCount() {
+ int c = 0;
+ for(int i = storage.length-1; i>=0; i--) {
+ c += Bitfield.Util.bitCount(storage[i]);
+ }
+ return c;
+ }
+}
diff --git a/src/java/jogamp/common/util/Int32Bitfield.java b/src/java/jogamp/common/util/Int32Bitfield.java
new file mode 100644
index 0000000..7b55a59
--- /dev/null
+++ b/src/java/jogamp/common/util/Int32Bitfield.java
@@ -0,0 +1,163 @@
+/**
+ * Copyright 2015 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.common.util;
+
+import com.jogamp.common.util.Bitfield;
+
+/**
+ * Simple bitfield interface for efficient storage access in O(1).
+ * <p>
+ * Implementation uses one 32bit integer field for storage.
+ * </p>
+ */
+public class Int32Bitfield implements Bitfield {
+ /** Unit size in bits, here 32 bits for one int unit. */
+ private static final int UNIT_SIZE = 32;
+ private int storage;
+
+ public Int32Bitfield() {
+ this.storage = 0;
+ }
+
+ @Override
+ public int size() {
+ return UNIT_SIZE;
+ }
+
+ @Override
+ public final void clearField(final boolean bit) {
+ if( bit ) {
+ storage = Bitfield.UNSIGNED_INT_MAX_VALUE;
+ } else {
+ storage = 0;
+ }
+ }
+
+ private static final void check(final int size, final int bitnum) throws IndexOutOfBoundsException {
+ if( 0 > bitnum || bitnum >= size ) {
+ throw new IndexOutOfBoundsException("Bitnum should be within [0.."+(size-1)+"], but is "+bitnum);
+ }
+ }
+
+ @Override
+ public final int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException {
+ if( 0 > length || length > 32 ) {
+ throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length);
+ }
+ check(UNIT_SIZE-length+1, lowBitnum);
+ final int left = 32 - lowBitnum; // remaining bits of first chunk
+ if( 32 == left ) {
+ // fast path
+ final int m = Util.getBitMask(length); // mask of chunk
+ return m & storage;
+ } else {
+ // slow path
+ final int l = Math.min(length, left); // length of first chunk < 32
+ final int m = ( 1 << l ) - 1; // mask of first chunk
+ return m & ( storage >>> lowBitnum );
+ }
+ }
+ @Override
+ public final void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException {
+ if( 0 > length || length > 32 ) {
+ throw new IndexOutOfBoundsException("length should be within [0..32], but is "+length);
+ }
+ check(UNIT_SIZE-length+1, lowBitnum);
+ final int left = 32 - lowBitnum; // remaining bits of first chunk storage
+ if( 32 == left ) {
+ // fast path
+ final int m = Util.getBitMask(length); // mask of chunk
+ storage = ( ( ~m ) & storage ) // keep non-written storage bits
+ | ( m & data ); // overwrite storage w/ used data bits
+ } else {
+ // slow path
+ final int l = Math.min(length, left); // length of first chunk < 32
+ final int m = ( 1 << l ) - 1; // mask of first chunk
+ storage = ( ( ~( m << lowBitnum ) ) & storage ) // keep non-written storage bits
+ | ( ( m & data ) << lowBitnum ); // overwrite storage w/ used data bits
+ }
+ }
+ @Override
+ public final int copy32(final int srcBitnum, final int dstBitnum, final int length) throws IndexOutOfBoundsException {
+ final int data = get32(srcBitnum, length);
+ put32(dstBitnum, length, data);
+ return data;
+ }
+
+ @Override
+ public final boolean get(final int bitnum) throws IndexOutOfBoundsException {
+ check(UNIT_SIZE, bitnum);
+ return 0 != ( storage & ( 1 << bitnum ) ) ;
+ }
+ @Override
+ public final boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException {
+ check(UNIT_SIZE, bitnum);
+ final int m = 1 << bitnum;
+ final boolean prev = 0 != ( storage & m ) ;
+ if( prev != bit ) {
+ if( bit ) {
+ storage |= m;
+ } else {
+ storage &= ~m;
+ }
+ }
+ return prev;
+ }
+ @Override
+ public final void set(final int bitnum) throws IndexOutOfBoundsException {
+ check(UNIT_SIZE, bitnum);
+ final int m = 1 << bitnum;
+ storage |= m;
+ }
+ @Override
+ public final void clear (final int bitnum) throws IndexOutOfBoundsException {
+ check(UNIT_SIZE, bitnum);
+ final int m = 1 << bitnum;
+ storage &= ~m;
+ }
+ @Override
+ public final boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException {
+ check(UNIT_SIZE, srcBitnum);
+ check(UNIT_SIZE, dstBitnum);
+ // get
+ final boolean bit = 0 != ( storage & ( 1 << srcBitnum ) ) ;
+ // put
+ final int m = 1 << dstBitnum;
+ if( bit ) {
+ storage |= m;
+ } else {
+ storage &= ~m;
+ }
+ return bit;
+ }
+
+ @Override
+ public int bitCount() {
+ return Bitfield.Util.bitCount(storage);
+ }
+}
diff --git a/src/java/jogamp/common/util/SyncedBitfield.java b/src/java/jogamp/common/util/SyncedBitfield.java
new file mode 100644
index 0000000..49c27b0
--- /dev/null
+++ b/src/java/jogamp/common/util/SyncedBitfield.java
@@ -0,0 +1,96 @@
+/**
+ * Copyright 2015 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+package jogamp.common.util;
+
+import com.jogamp.common.util.Bitfield;
+
+/**
+ * Simple synchronized {@link Bitfield} by wrapping an existing {@link Bitfield}.
+ */
+public class SyncedBitfield implements Bitfield {
+ private final Bitfield impl;
+
+ public SyncedBitfield(final Bitfield impl) {
+ this.impl = impl;
+ }
+
+ @Override
+ public final synchronized int size() {
+ return impl.size();
+ }
+
+ @Override
+ public final synchronized void clearField(final boolean bit) {
+ impl.clearField(bit);
+ }
+
+ @Override
+ public final synchronized int get32(final int lowBitnum, final int length) throws IndexOutOfBoundsException {
+ return impl.get32(lowBitnum, length);
+ }
+
+ @Override
+ public final synchronized void put32(final int lowBitnum, final int length, final int data) throws IndexOutOfBoundsException {
+ impl.put32(lowBitnum, length, data);
+ }
+
+ @Override
+ public final synchronized int copy32(final int srcLowBitnum, final int dstLowBitnum, final int length) throws IndexOutOfBoundsException {
+ return impl.copy32(srcLowBitnum, dstLowBitnum, length);
+ }
+
+ @Override
+ public final synchronized boolean get(final int bitnum) throws IndexOutOfBoundsException {
+ return impl.get(bitnum);
+ }
+
+ @Override
+ public final synchronized boolean put(final int bitnum, final boolean bit) throws IndexOutOfBoundsException {
+ return impl.put(bitnum, bit);
+ }
+
+ @Override
+ public final synchronized void set(final int bitnum) throws IndexOutOfBoundsException {
+ impl.set(bitnum);
+ }
+
+ @Override
+ public final synchronized void clear(final int bitnum) throws IndexOutOfBoundsException {
+ impl.clear(bitnum);
+ }
+
+ @Override
+ public final synchronized boolean copy(final int srcBitnum, final int dstBitnum) throws IndexOutOfBoundsException {
+ return impl.copy(srcBitnum, dstBitnum);
+ }
+
+ @Override
+ public final synchronized int bitCount() {
+ return impl.bitCount();
+ }
+} \ No newline at end of file
diff --git a/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java b/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java
index c930dff..1286924 100644
--- a/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java
+++ b/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java
@@ -32,6 +32,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.AbstractOwnableSynchronizer;
+import com.jogamp.common.util.SourcedInterruptedException;
import com.jogamp.common.util.locks.RecursiveLock;
/**
@@ -197,7 +198,7 @@ public class RecursiveLockImpl01CompleteFair implements RecursiveLock {
} catch (final InterruptedException e) {
if( !wCur.signaledByUnlock ) {
sync.queue.remove(wCur); // O(n)
- throw e; // propagate interruption not send by unlock
+ throw SourcedInterruptedException.wrap(e); // propagate interruption not send by unlock
} else if( cur != sync.getOwner() ) {
// Issued by unlock, but still locked by other thread
//
@@ -215,6 +216,7 @@ public class RecursiveLockImpl01CompleteFair implements RecursiveLock {
} // else: Issued by unlock, owning lock .. expected!
}
} while ( cur != sync.getOwner() && 0 < timeout ) ;
+ Thread.interrupted(); // clear slipped interrupt
if( 0 >= timeout && cur != sync.getOwner() ) {
// timed out
diff --git a/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java b/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java
index 77f73d8..fc5f739 100644
--- a/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java
+++ b/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java
@@ -42,6 +42,7 @@ public class RecursiveThreadGroupLockImpl01Unfairish
threadNum = 0;
threads = null;
holdCountAdditionOwner = 0;
+ waitingOrigOwner = null;
}
@Override
public final void incrHoldCount(final Thread t) {
@@ -64,6 +65,12 @@ public class RecursiveThreadGroupLockImpl01Unfairish
public final boolean isOriginalOwner(final Thread t) {
return super.isOwner(t);
}
+ public final void setWaitingOrigOwner(final Thread origOwner) {
+ waitingOrigOwner = origOwner;
+ }
+ public final Thread getWaitingOrigOwner() {
+ return waitingOrigOwner;
+ }
@Override
public final boolean isOwner(final Thread t) {
if(getExclusiveOwnerThread()==t) {
@@ -133,6 +140,7 @@ public class RecursiveThreadGroupLockImpl01Unfairish
private int holdCountAdditionOwner;
private Thread[] threads;
private int threadNum;
+ private Thread waitingOrigOwner;
}
public RecursiveThreadGroupLockImpl01Unfairish() {
@@ -157,10 +165,10 @@ public class RecursiveThreadGroupLockImpl01Unfairish
final Thread cur = Thread.currentThread();
final ThreadGroupSync tgSync = (ThreadGroupSync)sync;
if(!tgSync.isOriginalOwner(cur)) {
- throw new IllegalArgumentException("Current thread is not the original owner: orig-owner: "+tgSync.getOwner()+", current "+cur);
+ throw new IllegalArgumentException("Current thread is not the original owner: orig-owner: "+tgSync.getOwner()+", current "+cur+": "+toString());
}
if(tgSync.isOriginalOwner(t)) {
- throw new IllegalArgumentException("Passed thread is original owner: "+t);
+ throw new IllegalArgumentException("Passed thread is original owner: "+t+", "+toString());
}
tgSync.addOwner(t);
}
@@ -179,19 +187,25 @@ public class RecursiveThreadGroupLockImpl01Unfairish
// original locking owner thread
if( tgSync.getHoldCount() - tgSync.getAdditionalOwnerHoldCount() == 1 ) {
// release orig. lock
- while ( tgSync.getAdditionalOwnerHoldCount() > 0 ) {
- try {
- sync.wait();
- } catch (final InterruptedException e) {
- // regular wake up!
+ tgSync.setWaitingOrigOwner(cur);
+ try {
+ while ( tgSync.getAdditionalOwnerHoldCount() > 0 ) {
+ try {
+ sync.wait();
+ } catch (final InterruptedException e) {
+ // regular wake up!
+ }
}
+ } finally {
+ tgSync.setWaitingOrigOwner(null);
+ Thread.interrupted(); // clear slipped interrupt
}
tgSync.removeAllOwners();
}
} else if( tgSync.getAdditionalOwnerHoldCount() == 1 ) {
- // last additional owner thread wakes up original owner
- final Thread originalOwner = tgSync.getOwner();
- if(originalOwner.getState() == Thread.State.WAITING) {
+ // last additional owner thread wakes up original owner if waiting in unlock(..)
+ final Thread originalOwner = tgSync.getWaitingOrigOwner();
+ if( null != originalOwner ) {
originalOwner.interrupt();
}
}
diff --git a/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java b/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java
index 44a5d28..9fe7966 100644
--- a/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java
+++ b/src/java/jogamp/common/util/locks/SingletonInstanceFileLock.java
@@ -32,6 +32,8 @@ import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
+
+import com.jogamp.common.util.InterruptSource;
import com.jogamp.common.util.locks.SingletonInstance;
public class SingletonInstanceFileLock extends SingletonInstance {
@@ -76,7 +78,7 @@ public class SingletonInstanceFileLock extends SingletonInstance {
private void setupFileCleanup() {
file.deleteOnExit();
- Runtime.getRuntime().addShutdownHook(new Thread() {
+ Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() {
@Override
public void run() {
if(isLocked()) {
diff --git a/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java b/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java
index b1b42c3..6219b5c 100644
--- a/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java
+++ b/src/java/jogamp/common/util/locks/SingletonInstanceServerSocket.java
@@ -33,10 +33,16 @@ import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
+
+import com.jogamp.common.ExceptionUtils;
+import com.jogamp.common.util.InterruptSource;
+import com.jogamp.common.util.InterruptedRuntimeException;
+import com.jogamp.common.util.SourcedInterruptedException;
import com.jogamp.common.util.locks.SingletonInstance;
public class SingletonInstanceServerSocket extends SingletonInstance {
+ private static int serverInstanceCount = 0;
private final Server singletonServer;
private final String fullName;
@@ -71,7 +77,7 @@ public class SingletonInstanceServerSocket extends SingletonInstance {
fullName = ilh.toString()+":"+portNumber;
singletonServer = new Server(ilh, portNumber);
- Runtime.getRuntime().addShutdownHook(new Thread() {
+ Runtime.getRuntime().addShutdownHook(new InterruptSource.Thread() {
@Override
public void run() {
singletonServer.kill();
@@ -139,38 +145,58 @@ public class SingletonInstanceServerSocket extends SingletonInstance {
public final boolean start() {
if(alive) return true;
+ final String sname;
+ synchronized (Server.class) {
+ serverInstanceCount++;
+ sname = "SingletonServerSocket"+serverInstanceCount+"-"+fullName;
+ }
synchronized (syncOnStartStop) {
- serverThread = new Thread(this);
+ shallQuit = false;
+ serverThread = new InterruptSource.Thread(null, this, sname);
serverThread.setDaemon(true); // be a daemon, don't keep the JVM running
serverThread.start();
try {
- syncOnStartStop.wait();
+ while( !alive && !shallQuit ) {
+ syncOnStartStop.wait();
+ }
} catch (final InterruptedException ie) {
- ie.printStackTrace();
+ final InterruptedException ie2 = SourcedInterruptedException.wrap(ie);
+ shutdown(false);
+ throw new InterruptedRuntimeException(ie2);
}
}
final boolean ok = isBound();
if(!ok) {
- shutdown();
+ shutdown(true);
}
return ok;
}
public final boolean shutdown() {
+ return shutdown(true);
+ }
+ private final boolean shutdown(final boolean wait) {
if(!alive) return true;
- synchronized (syncOnStartStop) {
- shallQuit = true;
- connect();
- try {
- syncOnStartStop.wait();
- } catch (final InterruptedException ie) {
- ie.printStackTrace();
+ try {
+ synchronized (syncOnStartStop) {
+ shallQuit = true;
+ connect();
+ if( wait ) {
+ try {
+ while( alive ) {
+ syncOnStartStop.wait();
+ }
+ } catch (final InterruptedException ie) {
+ throw new InterruptedRuntimeException(ie);
+ }
+ }
+ }
+ } finally {
+ if(alive) {
+ System.err.println(infoPrefix()+" EEE "+getName()+" - Unable to remove lock: ServerThread still alive ?");
+ kill();
}
- }
- if(alive) {
- System.err.println(infoPrefix()+" EEE "+getName()+" - Unable to remove lock: ServerThread still alive ?");
- kill();
}
return true;
}
@@ -185,7 +211,8 @@ public class SingletonInstanceServerSocket extends SingletonInstance {
System.err.println(infoPrefix()+" XXX "+getName()+" - Kill @ JVM Shutdown");
}
alive = false;
- if(null != serverThread) {
+ shallQuit = false;
+ if(null != serverThread && serverThread.isAlive() ) {
try {
serverThread.stop();
} catch(final Throwable t) { }
@@ -214,47 +241,49 @@ public class SingletonInstanceServerSocket extends SingletonInstance {
@Override
public void run() {
- {
- final Thread currentThread = Thread.currentThread();
- currentThread.setName(currentThread.getName() + " - SISock: "+getName());
- if(DEBUG) {
- System.err.println(currentThread.getName()+" - started");
- }
+ if(DEBUG) {
+ System.err.println(infoPrefix()+" III - Start");
}
- alive = false;
- synchronized (syncOnStartStop) {
- try {
- serverSocket = new ServerSocket(portNumber, 1, localInetAddress);
- serverSocket.setReuseAddress(true); // reuse same port w/ subsequent instance, i.e. overcome TO state when JVM crashed
- alive = true;
- } catch (final IOException e) {
- System.err.println(infoPrefix()+" III - Unable to install ServerSocket: "+e.getMessage());
- shallQuit = true;
- } finally {
- syncOnStartStop.notifyAll();
+ try {
+ synchronized (syncOnStartStop) {
+ try {
+ serverSocket = new ServerSocket(portNumber, 1, localInetAddress);
+ serverSocket.setReuseAddress(true); // reuse same port w/ subsequent instance, i.e. overcome TO state when JVM crashed
+ alive = true;
+ } catch (final IOException e) {
+ System.err.println(infoPrefix()+" III - Unable to install ServerSocket: "+e.getMessage());
+ shallQuit = true;
+ } finally {
+ syncOnStartStop.notifyAll();
+ }
}
- }
- while (!shallQuit) {
- try {
- final Socket clientSocket = serverSocket.accept();
- clientSocket.close();
- } catch (final IOException ioe) {
- System.err.println(infoPrefix()+" EEE - Exception during accept: " + ioe.getMessage());
+ while (!shallQuit) {
+ try {
+ final Socket clientSocket = serverSocket.accept();
+ clientSocket.close();
+ } catch (final IOException ioe) {
+ System.err.println(infoPrefix()+" EEE - Exception during accept: " + ioe.getMessage());
+ }
}
- }
-
- synchronized (syncOnStartStop) {
- try {
+ } catch(final ThreadDeath td) {
+ if( DEBUG ) {
+ ExceptionUtils.dumpThrowable("", td);
+ }
+ } finally {
+ synchronized (syncOnStartStop) {
+ if(DEBUG) {
+ System.err.println(infoPrefix()+" III - Stopping: alive "+alive+", shallQuit "+shallQuit+", hasSocket "+(null!=serverSocket));
+ }
if(null != serverSocket) {
- serverSocket.close();
+ try {
+ serverSocket.close();
+ } catch (final IOException e) {
+ System.err.println(infoPrefix()+" EEE - Exception during close: " + e.getMessage());
+ }
}
- } catch (final IOException e) {
- System.err.println(infoPrefix()+" EEE - Exception during close: " + e.getMessage());
- } finally {
serverSocket = null;
alive = false;
- shallQuit = false;
syncOnStartStop.notifyAll();
}
}