--- a/jdk/make/launcher/Launcher-jdk.accessibility.gmk Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/make/launcher/Launcher-jdk.accessibility.gmk Thu Dec 24 10:34:31 2015 -0800
@@ -73,8 +73,9 @@
$$(eval $$(call SetupNativeCompilation, BUILD_JACCESSINSPECTOR$1, \
SRC := $(TOPDIR)/jaccessinspector $(TOPDIR)/common \
$(TOPDIR)/toolscommon $(TOPDIR)/include/bridge, \
- CFLAGS := $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 /EHsc, \
- LDFLAGS := $$(LDFLAGS_JDKEXE) /STACK:655360 Advapi32.lib User32.lib, \
+ CFLAGS := $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 -EHsc, \
+ LDFLAGS := $$(LDFLAGS_JDKEXE) -stack:655360, \
+ LIBS := advapi32.lib user32.lib, \
OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/jdk.accessibility/jaccessinspector$1, \
OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/modules_cmds/jdk.accessibility, \
PROGRAM := jaccessinspector$1, \
@@ -100,8 +101,9 @@
$$(eval $$(call SetupNativeCompilation,BUILD_JACCESSWALKER$1, \
SRC := $(TOPDIR)/jaccesswalker $(TOPDIR)/common \
$(TOPDIR)/toolscommon $(TOPDIR)/include/bridge, \
- CFLAGS :== $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 /EHsc, \
- LDFLAGS := $$(LDFLAGS_JDKEXE) /STACK:655360 Advapi32.lib Comctl32.lib Gdi32.lib User32.lib, \
+ CFLAGS := $$(CFLAGS_JDKEXE) $(TOOLS_CFLAGS) -DACCESSBRIDGE_ARCH_$2 -EHsc, \
+ LDFLAGS := $$(LDFLAGS_JDKEXE) -stack:655360, \
+ LIBS := advapi32.lib comctl32.lib gdi32.lib user32.lib, \
OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/jdk.accessibility/jaccesswalker$1, \
OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/modules_cmds/jdk.accessibility, \
PROGRAM := jaccesswalker$1, \
--- a/jdk/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -29,7 +29,6 @@
import java.nio.channels.*;
import java.nio.channels.spi.*;
import java.util.*;
-import sun.misc.*;
/**
* An implementation of Selector for Linux 2.6+ kernels that uses
@@ -50,7 +49,7 @@
private Map<Integer,SelectionKeyImpl> fdToKey;
// True if this Selector has been closed
- private volatile boolean closed = false;
+ private volatile boolean closed;
// Lock for interrupt triggering and clearing
private final Object interruptLock = new Object();
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java Thu Dec 24 10:34:31 2015 -0800
@@ -93,7 +93,7 @@
// Instance of this provider, so we don't have to call the provider list
// to find ourselves or run the risk of not being in the list.
- private static volatile SunJCE instance = null;
+ private static volatile SunJCE instance;
// lazy initialize SecureRandom to avoid potential recursion if Sun
// provider has not been installed yet
--- a/jdk/src/java.base/share/classes/java/io/File.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/io/File.java Thu Dec 24 10:34:31 2015 -0800
@@ -420,11 +420,11 @@
String scheme = uri.getScheme();
if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
throw new IllegalArgumentException("URI scheme is not \"file\"");
- if (uri.getAuthority() != null)
+ if (uri.getRawAuthority() != null)
throw new IllegalArgumentException("URI has an authority component");
- if (uri.getFragment() != null)
+ if (uri.getRawFragment() != null)
throw new IllegalArgumentException("URI has a fragment component");
- if (uri.getQuery() != null)
+ if (uri.getRawQuery() != null)
throw new IllegalArgumentException("URI has a query component");
String p = uri.getPath();
if (p.equals(""))
--- a/jdk/src/java.base/share/classes/java/io/PipedInputStream.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/io/PipedInputStream.java Thu Dec 24 10:34:31 2015 -0800
@@ -48,9 +48,9 @@
* @since 1.0
*/
public class PipedInputStream extends InputStream {
- boolean closedByWriter = false;
- volatile boolean closedByReader = false;
- boolean connected = false;
+ boolean closedByWriter;
+ volatile boolean closedByReader;
+ boolean connected;
/* REMIND: identification of the read and write sides needs to be
more sophisticated. Either using thread groups (but what about
--- a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java Thu Dec 24 10:34:31 2015 -0800
@@ -25,7 +25,7 @@
package java.lang;
-import sun.misc.FloatingDecimal;
+import jdk.internal.math.FloatingDecimal;
import java.util.Arrays;
import java.util.Spliterator;
import java.util.stream.IntStream;
--- a/jdk/src/java.base/share/classes/java/lang/Class.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/Class.java Thu Dec 24 10:34:31 2015 -0800
@@ -2518,7 +2518,7 @@
// Incremented by the VM on each call to JVM TI RedefineClasses()
// that redefines this class or a superclass.
- private transient volatile int classRedefinedCount = 0;
+ private transient volatile int classRedefinedCount;
// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
@@ -3331,7 +3331,8 @@
* uncloned, cached, and shared by all callers.
*/
T[] getEnumConstantsShared() {
- if (enumConstants == null) {
+ T[] constants = enumConstants;
+ if (constants == null) {
if (!isEnum()) return null;
try {
final Method values = getMethod("values");
@@ -3344,16 +3345,16 @@
});
@SuppressWarnings("unchecked")
T[] temporaryConstants = (T[])values.invoke(null);
- enumConstants = temporaryConstants;
+ enumConstants = constants = temporaryConstants;
}
// These can happen when users concoct enum-like classes
// that don't comply with the enum spec.
catch (InvocationTargetException | NoSuchMethodException |
IllegalAccessException ex) { return null; }
}
- return enumConstants;
+ return constants;
}
- private transient volatile T[] enumConstants = null;
+ private transient volatile T[] enumConstants;
/**
* Returns a map from simple name to enum constant. This package-private
@@ -3363,19 +3364,21 @@
* created lazily on first use. Typically it won't ever get created.
*/
Map<String, T> enumConstantDirectory() {
- if (enumConstantDirectory == null) {
+ Map<String, T> directory = enumConstantDirectory;
+ if (directory == null) {
T[] universe = getEnumConstantsShared();
if (universe == null)
throw new IllegalArgumentException(
getName() + " is not an enum type");
- Map<String, T> m = new HashMap<>(2 * universe.length);
- for (T constant : universe)
- m.put(((Enum<?>)constant).name(), constant);
- enumConstantDirectory = m;
+ directory = new HashMap<>(2 * universe.length);
+ for (T constant : universe) {
+ directory.put(((Enum<?>)constant).name(), constant);
+ }
+ enumConstantDirectory = directory;
}
- return enumConstantDirectory;
+ return directory;
}
- private transient volatile Map<String, T> enumConstantDirectory = null;
+ private transient volatile Map<String, T> enumConstantDirectory;
/**
* Casts an object to the class or interface represented
--- a/jdk/src/java.base/share/classes/java/lang/ClassLoader.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/ClassLoader.java Thu Dec 24 10:34:31 2015 -0800
@@ -45,11 +45,11 @@
import java.util.Set;
import java.util.Stack;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Vector;
import java.util.Hashtable;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
-import sun.misc.CompoundEnumeration;
import sun.misc.Resource;
import sun.misc.URLClassPath;
import sun.reflect.CallerSensitive;
@@ -2206,3 +2206,36 @@
return sys;
}
}
+
+/*
+ * A utility class that will enumerate over an array of enumerations.
+ */
+final class CompoundEnumeration<E> implements Enumeration<E> {
+ private final Enumeration<E>[] enums;
+ private int index;
+
+ public CompoundEnumeration(Enumeration<E>[] enums) {
+ this.enums = enums;
+ }
+
+ private boolean next() {
+ while (index < enums.length) {
+ if (enums[index] != null && enums[index].hasMoreElements()) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public boolean hasMoreElements() {
+ return next();
+ }
+
+ public E nextElement() {
+ if (!next()) {
+ throw new NoSuchElementException();
+ }
+ return enums[index].nextElement();
+ }
+}
--- a/jdk/src/java.base/share/classes/java/lang/Double.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/Double.java Thu Dec 24 10:34:31 2015 -0800
@@ -25,8 +25,8 @@
package java.lang;
-import sun.misc.FloatingDecimal;
-import sun.misc.DoubleConsts;
+import jdk.internal.math.FloatingDecimal;
+import jdk.internal.math.DoubleConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
/**
--- a/jdk/src/java.base/share/classes/java/lang/Float.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/Float.java Thu Dec 24 10:34:31 2015 -0800
@@ -25,9 +25,9 @@
package java.lang;
-import sun.misc.FloatingDecimal;
-import sun.misc.FloatConsts;
-import sun.misc.DoubleConsts;
+import jdk.internal.math.FloatingDecimal;
+import jdk.internal.math.FloatConsts;
+import jdk.internal.math.DoubleConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
/**
--- a/jdk/src/java.base/share/classes/java/lang/Math.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/Math.java Thu Dec 24 10:34:31 2015 -0800
@@ -26,8 +26,8 @@
package java.lang;
import java.util.Random;
-import sun.misc.FloatConsts;
-import sun.misc.DoubleConsts;
+import jdk.internal.math.FloatConsts;
+import jdk.internal.math.DoubleConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
/**
--- a/jdk/src/java.base/share/classes/java/lang/StrictMath.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/StrictMath.java Thu Dec 24 10:34:31 2015 -0800
@@ -26,7 +26,7 @@
package java.lang;
import java.util.Random;
-import sun.misc.DoubleConsts;
+import jdk.internal.math.DoubleConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
/**
--- a/jdk/src/java.base/share/classes/java/lang/System.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/System.java Thu Dec 24 10:34:31 2015 -0800
@@ -132,7 +132,7 @@
/* The security manager for the system.
*/
- private static volatile SecurityManager security = null;
+ private static volatile SecurityManager security;
/**
* Reassigns the "standard" input stream.
@@ -206,7 +206,7 @@
setErr0(err);
}
- private static volatile Console cons = null;
+ private static volatile Console cons;
/**
* Returns the unique {@link java.io.Console Console} object associated
* with the current Java virtual machine, if any.
@@ -216,12 +216,13 @@
* @since 1.6
*/
public static Console console() {
- if (cons == null) {
+ Console c = cons;
+ if (c == null) {
synchronized (System.class) {
- cons = SharedSecrets.getJavaIOAccess().console();
+ cons = c = SharedSecrets.getJavaIOAccess().console();
}
}
- return cons;
+ return c;
}
/**
--- a/jdk/src/java.base/share/classes/java/lang/Thread.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/Thread.java Thu Dec 24 10:34:31 2015 -0800
@@ -207,12 +207,10 @@
/* For generating thread ID */
private static long threadSeqNumber;
- /* Java thread status for tools,
- * initialized to indicate thread 'not yet started'
+ /*
+ * Java thread status for tools, default indicates thread 'not yet started'
*/
-
- private volatile int threadStatus = 0;
-
+ private volatile int threadStatus;
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
--- a/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/ref/ReferenceQueue.java Thu Dec 24 10:34:31 2015 -0800
@@ -53,7 +53,7 @@
private static class Lock { };
private Lock lock = new Lock();
- private volatile Reference<? extends T> head = null;
+ private volatile Reference<? extends T> head;
private long queueLength = 0;
boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
--- a/jdk/src/java.base/share/classes/java/lang/reflect/Parameter.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/Parameter.java Thu Dec 24 10:34:31 2015 -0800
@@ -205,7 +205,7 @@
return tmp;
}
- private transient volatile Type parameterTypeCache = null;
+ private transient volatile Type parameterTypeCache;
/**
* Returns a {@code Class} object that identifies the
@@ -237,7 +237,7 @@
return executable.getAnnotatedParameterTypes()[index];
}
- private transient volatile Class<?> parameterClassCache = null;
+ private transient volatile Class<?> parameterClassCache;
/**
* Returns {@code true} if this parameter is implicitly declared
--- a/jdk/src/java.base/share/classes/java/math/BigInteger.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/math/BigInteger.java Thu Dec 24 10:34:31 2015 -0800
@@ -38,8 +38,8 @@
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
-import sun.misc.DoubleConsts;
-import sun.misc.FloatConsts;
+import jdk.internal.math.DoubleConsts;
+import jdk.internal.math.FloatConsts;
import jdk.internal.HotSpotIntrinsicCandidate;
/**
--- a/jdk/src/java.base/share/classes/java/net/URI.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/net/URI.java Thu Dec 24 10:34:31 2015 -0800
@@ -33,7 +33,6 @@
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.CharacterCodingException;
@@ -490,17 +489,17 @@
private transient String path; // null ==> opaque
private transient String query;
- // The remaining fields may be computed on demand
-
- private transient volatile String schemeSpecificPart;
- private transient volatile int hash; // Zero ==> undefined
-
- private transient volatile String decodedUserInfo = null;
- private transient volatile String decodedAuthority = null;
- private transient volatile String decodedPath = null;
- private transient volatile String decodedQuery = null;
- private transient volatile String decodedFragment = null;
- private transient volatile String decodedSchemeSpecificPart = null;
+ // The remaining fields may be computed on demand, which is safe even in
+ // the face of multiple threads racing to initialize them
+ private transient String schemeSpecificPart;
+ private transient int hash; // Zero ==> undefined
+
+ private transient String decodedUserInfo;
+ private transient String decodedAuthority;
+ private transient String decodedPath;
+ private transient String decodedQuery;
+ private transient String decodedFragment;
+ private transient String decodedSchemeSpecificPart;
/**
* The string form of this URI.
@@ -911,8 +910,7 @@
// either more fields or a more-obscure representation.
if ((host != null) || (authority == null))
return this;
- defineString();
- new Parser(string).parse(true);
+ new Parser(toString()).parse(true);
return this;
}
@@ -1144,8 +1142,17 @@
* (never {@code null})
*/
public String getRawSchemeSpecificPart() {
- defineSchemeSpecificPart();
- return schemeSpecificPart;
+ String part = schemeSpecificPart;
+ if (part != null) {
+ return part;
+ }
+ StringBuilder sb = new StringBuilder();
+ appendSchemeSpecificPart(sb, null, getAuthority(), getUserInfo(),
+ host, port, getPath(), getQuery());
+ if (sb.length() == 0) {
+ return null;
+ }
+ return schemeSpecificPart = sb.toString();
}
/**
@@ -1160,9 +1167,11 @@
* (never {@code null})
*/
public String getSchemeSpecificPart() {
- if (decodedSchemeSpecificPart == null)
- decodedSchemeSpecificPart = decode(getRawSchemeSpecificPart());
- return decodedSchemeSpecificPart;
+ String part = decodedSchemeSpecificPart;
+ if (part == null) {
+ decodedSchemeSpecificPart = part = decode(getRawSchemeSpecificPart());
+ }
+ return part;
}
/**
@@ -1193,9 +1202,11 @@
* or {@code null} if the authority is undefined
*/
public String getAuthority() {
- if (decodedAuthority == null)
- decodedAuthority = decode(authority);
- return decodedAuthority;
+ String auth = decodedAuthority;
+ if ((auth == null) && (authority != null)) {
+ decodedAuthority = auth = decode(authority);
+ }
+ return auth;
}
/**
@@ -1223,9 +1234,11 @@
* or {@code null} if the user information is undefined
*/
public String getUserInfo() {
- if ((decodedUserInfo == null) && (userInfo != null))
- decodedUserInfo = decode(userInfo);
- return decodedUserInfo;
+ String user = decodedUserInfo;
+ if ((user == null) && (userInfo != null)) {
+ decodedUserInfo = user = decode(userInfo);
+ }
+ return user;
}
/**
@@ -1307,9 +1320,11 @@
* or {@code null} if the path is undefined
*/
public String getPath() {
- if ((decodedPath == null) && (path != null))
- decodedPath = decode(path);
- return decodedPath;
+ String decoded = decodedPath;
+ if ((decoded == null) && (path != null)) {
+ decodedPath = decoded = decode(path);
+ }
+ return decoded;
}
/**
@@ -1336,9 +1351,11 @@
* or {@code null} if the query is undefined
*/
public String getQuery() {
- if ((decodedQuery == null) && (query != null))
- decodedQuery = decode(query, false);
- return decodedQuery;
+ String decoded = decodedQuery;
+ if ((decoded == null) && (query != null)) {
+ decodedQuery = decoded = decode(query, false);
+ }
+ return decoded;
}
/**
@@ -1365,9 +1382,11 @@
* or {@code null} if the fragment is undefined
*/
public String getFragment() {
- if ((decodedFragment == null) && (fragment != null))
- decodedFragment = decode(fragment, false);
- return decodedFragment;
+ String decoded = decodedFragment;
+ if ((decoded == null) && (fragment != null)) {
+ decodedFragment = decoded = decode(fragment, false);
+ }
+ return decoded;
}
@@ -1453,24 +1472,27 @@
* @return A hash-code value for this URI
*/
public int hashCode() {
- if (hash != 0)
- return hash;
- int h = hashIgnoringCase(0, scheme);
- h = hash(h, fragment);
- if (isOpaque()) {
- h = hash(h, schemeSpecificPart);
- } else {
- h = hash(h, path);
- h = hash(h, query);
- if (host != null) {
- h = hash(h, userInfo);
- h = hashIgnoringCase(h, host);
- h += 1949 * port;
+ int h = hash;
+ if (h == 0) {
+ h = hashIgnoringCase(0, scheme);
+ h = hash(h, fragment);
+ if (isOpaque()) {
+ h = hash(h, schemeSpecificPart);
} else {
- h = hash(h, authority);
+ h = hash(h, path);
+ h = hash(h, query);
+ if (host != null) {
+ h = hash(h, userInfo);
+ h = hashIgnoringCase(h, host);
+ h += 1949 * port;
+ } else {
+ h = hash(h, authority);
+ }
+ }
+ if (h != 0) {
+ hash = h;
}
}
- hash = h;
return h;
}
@@ -1600,8 +1622,59 @@
* @return The string form of this URI
*/
public String toString() {
- defineString();
- return string;
+ String s = string;
+ if (s == null) {
+ s = defineString();
+ }
+ return s;
+ }
+
+ private String defineString() {
+ String s = string;
+ if (s != null) {
+ return s;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ if (scheme != null) {
+ sb.append(scheme);
+ sb.append(':');
+ }
+ if (isOpaque()) {
+ sb.append(schemeSpecificPart);
+ } else {
+ if (host != null) {
+ sb.append("//");
+ if (userInfo != null) {
+ sb.append(userInfo);
+ sb.append('@');
+ }
+ boolean needBrackets = ((host.indexOf(':') >= 0)
+ && !host.startsWith("[")
+ && !host.endsWith("]"));
+ if (needBrackets) sb.append('[');
+ sb.append(host);
+ if (needBrackets) sb.append(']');
+ if (port != -1) {
+ sb.append(':');
+ sb.append(port);
+ }
+ } else if (authority != null) {
+ sb.append("//");
+ sb.append(authority);
+ }
+ if (path != null)
+ sb.append(path);
+ if (query != null) {
+ sb.append('?');
+ sb.append(query);
+ }
+ }
+ if (fragment != null) {
+ sb.append('#');
+ sb.append(fragment);
+ }
+ return string = sb.toString();
}
/**
@@ -1618,8 +1691,7 @@
* charset
*/
public String toASCIIString() {
- defineString();
- return encode(string);
+ return encode(toString());
}
@@ -1825,7 +1897,7 @@
}
}
- private void appendAuthority(StringBuffer sb,
+ private void appendAuthority(StringBuilder sb,
String authority,
String userInfo,
String host,
@@ -1875,7 +1947,7 @@
}
}
- private void appendSchemeSpecificPart(StringBuffer sb,
+ private void appendSchemeSpecificPart(StringBuilder sb,
String opaquePart,
String authority,
String userInfo,
@@ -1916,7 +1988,7 @@
}
}
- private void appendFragment(StringBuffer sb, String fragment) {
+ private void appendFragment(StringBuilder sb, String fragment) {
if (fragment != null) {
sb.append('#');
sb.append(quote(fragment, L_URIC, H_URIC));
@@ -1933,7 +2005,7 @@
String query,
String fragment)
{
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
if (scheme != null) {
sb.append(scheme);
sb.append(':');
@@ -1945,61 +2017,6 @@
return sb.toString();
}
- private void defineSchemeSpecificPart() {
- if (schemeSpecificPart != null) return;
- StringBuffer sb = new StringBuffer();
- appendSchemeSpecificPart(sb, null, getAuthority(), getUserInfo(),
- host, port, getPath(), getQuery());
- if (sb.length() == 0) return;
- schemeSpecificPart = sb.toString();
- }
-
- private void defineString() {
- if (string != null) return;
-
- StringBuilder sb = new StringBuilder();
- if (scheme != null) {
- sb.append(scheme);
- sb.append(':');
- }
- if (isOpaque()) {
- sb.append(schemeSpecificPart);
- } else {
- if (host != null) {
- sb.append("//");
- if (userInfo != null) {
- sb.append(userInfo);
- sb.append('@');
- }
- boolean needBrackets = ((host.indexOf(':') >= 0)
- && !host.startsWith("[")
- && !host.endsWith("]"));
- if (needBrackets) sb.append('[');
- sb.append(host);
- if (needBrackets) sb.append(']');
- if (port != -1) {
- sb.append(':');
- sb.append(port);
- }
- } else if (authority != null) {
- sb.append("//");
- sb.append(authority);
- }
- if (path != null)
- sb.append(path);
- if (query != null) {
- sb.append('?');
- sb.append(query);
- }
- }
- if (fragment != null) {
- sb.append('#');
- sb.append(fragment);
- }
- string = sb.toString();
- }
-
-
// -- Normalization, resolution, and relativization --
// RFC2396 5.2 (6)
@@ -2650,13 +2667,13 @@
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
- private static void appendEscape(StringBuffer sb, byte b) {
+ private static void appendEscape(StringBuilder sb, byte b) {
sb.append('%');
sb.append(hexDigits[(b >> 4) & 0x0f]);
sb.append(hexDigits[(b >> 0) & 0x0f]);
}
- private static void appendEncoded(StringBuffer sb, char c) {
+ private static void appendEncoded(StringBuilder sb, char c) {
ByteBuffer bb = null;
try {
bb = ThreadLocalCoders.encoderFor("UTF-8")
@@ -2677,15 +2694,14 @@
// by the given mask pair
//
private static String quote(String s, long lowMask, long highMask) {
- int n = s.length();
- StringBuffer sb = null;
+ StringBuilder sb = null;
boolean allowNonASCII = ((lowMask & L_ESCAPED) != 0);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c < '\u0080') {
if (!match(c, lowMask, highMask)) {
if (sb == null) {
- sb = new StringBuffer();
+ sb = new StringBuilder();
sb.append(s, 0, i);
}
appendEscape(sb, (byte)c);
@@ -2697,7 +2713,7 @@
&& (Character.isSpaceChar(c)
|| Character.isISOControl(c))) {
if (sb == null) {
- sb = new StringBuffer();
+ sb = new StringBuilder();
sb.append(s, 0, i);
}
appendEncoded(sb, c);
@@ -2734,7 +2750,7 @@
assert false;
}
- StringBuffer sb = new StringBuffer();
+ StringBuilder sb = new StringBuilder();
while (bb.hasRemaining()) {
int b = bb.get() & 0xff;
if (b >= 0x80)
@@ -2865,12 +2881,6 @@
fail("Expected " + expected, p);
}
- private void failExpecting(String expected, String prior, int p)
- throws URISyntaxException
- {
- fail("Expected " + expected + " following " + prior, p);
- }
-
// -- Simple access to the input string --
--- a/jdk/src/java.base/share/classes/java/net/URL.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/net/URL.java Thu Dec 24 10:34:31 2015 -0800
@@ -310,7 +310,8 @@
* @param host the name of the host.
* @param port the port number on the host.
* @param file the file on the host
- * @exception MalformedURLException if an unknown protocol is specified.
+ * @exception MalformedURLException if an unknown protocol or the port
+ * is a negative number other than -1
* @see java.lang.System#getProperty(java.lang.String)
* @see java.net.URL#setURLStreamHandlerFactory(
* java.net.URLStreamHandlerFactory)
@@ -329,9 +330,9 @@
* name, {@code host} name, and {@code file} name. The
* default port for the specified protocol is used.
* <p>
- * This method is equivalent to calling the four-argument
- * constructor with the arguments being {@code protocol},
- * {@code host}, {@code -1}, and {@code file}.
+ * This constructor is equivalent to the four-argument
+ * constructor with the only difference of using the
+ * default port for the specified protocol.
*
* No validation of the inputs is performed by this constructor.
*
@@ -372,7 +373,8 @@
* @param port the port number on the host.
* @param file the file on the host
* @param handler the stream handler for the URL.
- * @exception MalformedURLException if an unknown protocol is specified.
+ * @exception MalformedURLException if an unknown protocol or the port
+ is a negative number other than -1
* @exception SecurityException
* if a security manager exists and its
* {@code checkPermission} method doesn't allow
@@ -446,7 +448,9 @@
*
* @param spec the {@code String} to parse as a URL.
* @exception MalformedURLException if no protocol is specified, or an
- * unknown protocol is found, or {@code spec} is {@code null}.
+ * unknown protocol is found, or {@code spec} is {@code null},
+ * or the parsed URL fails to comply with the specific syntax
+ * of the associated protocol.
* @see java.net.URL#URL(java.net.URL, java.lang.String)
*/
public URL(String spec) throws MalformedURLException {
@@ -493,7 +497,9 @@
* @param context the context in which to parse the specification.
* @param spec the {@code String} to parse as a URL.
* @exception MalformedURLException if no protocol is specified, or an
- * unknown protocol is found, or {@code spec} is {@code null}.
+ * unknown protocol is found, or {@code spec} is {@code null},
+ * or the parsed URL fails to comply with the specific syntax
+ * of the associated protocol.
* @see java.net.URL#URL(java.lang.String, java.lang.String,
* int, java.lang.String)
* @see java.net.URLStreamHandler
@@ -513,7 +519,9 @@
* @param spec the {@code String} to parse as a URL.
* @param handler the stream handler for the URL.
* @exception MalformedURLException if no protocol is specified, or an
- * unknown protocol is found, or {@code spec} is {@code null}.
+ * unknown protocol is found, or {@code spec} is {@code null},
+ * or the parsed URL fails to comply with the specific syntax
+ * of the associated protocol.
* @exception SecurityException
* if a security manager exists and its
* {@code checkPermission} method doesn't allow
--- a/jdk/src/java.base/share/classes/java/nio/Bits.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/nio/Bits.java Thu Dec 24 10:34:31 2015 -0800
@@ -25,9 +25,7 @@
package java.nio;
-import java.security.AccessController;
import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.LongAdder;
import jdk.internal.misc.JavaNioAccess;
import jdk.internal.misc.JavaLangRefAccess;
@@ -603,7 +601,8 @@
private static final AtomicLong reservedMemory = new AtomicLong();
private static final AtomicLong totalCapacity = new AtomicLong();
private static final AtomicLong count = new AtomicLong();
- private static volatile boolean memoryLimitSet = false;
+ private static volatile boolean memoryLimitSet;
+
// max. number of sleeps during try-reserving with exponentially
// increasing delay before throwing OutOfMemoryError:
// 1, 2, 4, 8, 16, 32, 64, 128, 256 (total 511 ms ~ 0.5 s)
--- a/jdk/src/java.base/share/classes/java/nio/channels/SelectionKey.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/nio/channels/SelectionKey.java Thu Dec 24 10:34:31 2015 -0800
@@ -26,8 +26,6 @@
package java.nio.channels;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
-import java.io.IOException;
-
/**
* A token representing the registration of a {@link SelectableChannel} with a
@@ -363,7 +361,7 @@
// -- Attachments --
- private volatile Object attachment = null;
+ private volatile Object attachment;
private static final AtomicReferenceFieldUpdater<SelectionKey,Object>
attachmentUpdater = AtomicReferenceFieldUpdater.newUpdater(
--- a/jdk/src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/nio/channels/spi/AbstractInterruptibleChannel.java Thu Dec 24 10:34:31 2015 -0800
@@ -29,11 +29,7 @@
package java.nio.channels.spi;
import java.io.IOException;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
import java.nio.channels.*;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import jdk.internal.misc.SharedSecrets;
import sun.nio.ch.Interruptible;
@@ -90,7 +86,7 @@
{
private final Object closeLock = new Object();
- private volatile boolean open = true;
+ private volatile boolean closed;
/**
* Initializes a new instance of this class.
@@ -110,9 +106,9 @@
*/
public final void close() throws IOException {
synchronized (closeLock) {
- if (!open)
+ if (closed)
return;
- open = false;
+ closed = true;
implCloseChannel();
}
}
@@ -136,7 +132,7 @@
protected abstract void implCloseChannel() throws IOException;
public final boolean isOpen() {
- return open;
+ return !closed;
}
@@ -158,9 +154,9 @@
interruptor = new Interruptible() {
public void interrupt(Thread target) {
synchronized (closeLock) {
- if (!open)
+ if (closed)
return;
- open = false;
+ closed = true;
interrupted = target;
try {
AbstractInterruptibleChannel.this.implCloseChannel();
@@ -202,7 +198,7 @@
this.interrupted = null;
throw new ClosedByInterruptException();
}
- if (!completed && !open)
+ if (!completed && closed)
throw new AsynchronousCloseException();
}
--- a/jdk/src/java.base/share/classes/java/nio/charset/Charset.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/nio/charset/Charset.java Thu Dec 24 10:34:31 2015 -0800
@@ -276,7 +276,7 @@
/* -- Static methods -- */
- private static volatile String bugLevel = null;
+ private static volatile String bugLevel;
static boolean atBugLevel(String bl) { // package-private
String level = bugLevel;
@@ -324,8 +324,8 @@
// Cache of the most-recently-returned charsets,
// along with the names that were used to find them
//
- private static volatile Object[] cache1 = null; // "Level 1" cache
- private static volatile Object[] cache2 = null; // "Level 2" cache
+ private static volatile Object[] cache1; // "Level 1" cache
+ private static volatile Object[] cache2; // "Level 2" cache
private static void cache(String charsetName, Charset cs) {
cache2 = cache1;
--- a/jdk/src/java.base/share/classes/java/security/SecureRandom.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/security/SecureRandom.java Thu Dec 24 10:34:31 2015 -0800
@@ -124,7 +124,7 @@
private String algorithm;
// Seed Generator
- private static volatile SecureRandom seedGenerator = null;
+ private static volatile SecureRandom seedGenerator;
/**
* Constructs a secure random number generator (RNG) implementing the
@@ -522,10 +522,12 @@
* @see #setSeed
*/
public static byte[] getSeed(int numBytes) {
- if (seedGenerator == null) {
- seedGenerator = new SecureRandom();
+ SecureRandom seedGen = seedGenerator;
+ if (seedGen == null) {
+ seedGen = new SecureRandom();
+ seedGenerator = seedGen;
}
- return seedGenerator.generateSeed(numBytes);
+ return seedGen.generateSeed(numBytes);
}
/**
--- a/jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/text/DateFormatSymbols.java Thu Dec 24 10:34:31 2015 -0800
@@ -630,7 +630,9 @@
hashCode = 11 * hashCode + Arrays.hashCode(ampms);
hashCode = 11 * hashCode + Arrays.deepHashCode(getZoneStringsWrapper());
hashCode = 11 * hashCode + Objects.hashCode(localPatternChars);
- cachedHashCode = hashCode;
+ if (hashCode != 0) {
+ cachedHashCode = hashCode;
+ }
}
return hashCode;
@@ -670,12 +672,12 @@
private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> cachedInstances
= new ConcurrentHashMap<>(3);
- private transient int lastZoneIndex = 0;
+ private transient int lastZoneIndex;
/**
* Cached hash code
*/
- transient volatile int cachedHashCode = 0;
+ transient volatile int cachedHashCode;
private void initializeData(Locale desiredLocale) {
locale = desiredLocale;
--- a/jdk/src/java.base/share/classes/java/text/DecimalFormatSymbols.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/text/DecimalFormatSymbols.java Thu Dec 24 10:34:31 2015 -0800
@@ -42,14 +42,8 @@
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.text.spi.DecimalFormatSymbolsProvider;
-import java.util.ArrayList;
import java.util.Currency;
-import java.util.List;
import java.util.Locale;
-import java.util.MissingResourceException;
-import java.util.ResourceBundle;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import sun.util.locale.provider.LocaleProviderAdapter;
import sun.util.locale.provider.LocaleServiceProviderPool;
import sun.util.locale.provider.ResourceBundleBasedAdapter;
@@ -875,7 +869,7 @@
// currency; only the ISO code is serialized.
private transient Currency currency;
- private transient volatile boolean currencyInitialized = false;
+ private transient volatile boolean currencyInitialized;
// Proclaim JDK 1.1 FCS compatibility
static final long serialVersionUID = 5772796243397350300L;
--- a/jdk/src/java.base/share/classes/java/text/DigitList.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/text/DigitList.java Thu Dec 24 10:34:31 2015 -0800
@@ -41,7 +41,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
-import sun.misc.FloatingDecimal;
+import jdk.internal.math.FloatingDecimal;
/**
* Digit List. Private to DecimalFormat.
--- a/jdk/src/java.base/share/classes/java/time/LocalDate.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/time/LocalDate.java Thu Dec 24 10:34:31 2015 -0800
@@ -147,6 +147,10 @@
* This could be used by an application as a "far future" date.
*/
public static final LocalDate MAX = LocalDate.of(Year.MAX_VALUE, 12, 31);
+ /**
+ * The epoch year {@code LocalDate}, '1970-01-01'.
+ */
+ public static final LocalDate EPOCH = LocalDate.of(1970, 1, 1);
/**
* Serialization version.
@@ -1864,6 +1868,29 @@
return total - DAYS_0000_TO_1970;
}
+ /**
+ * Converts this {@code LocalDate} to the number of seconds since the epoch
+ * of 1970-01-01T00:00:00Z.
+ * <p>
+ * This combines this local date with the specified time and
+ * offset to calculate the epoch-second value, which is the
+ * number of elapsed seconds from 1970-01-01T00:00:00Z.
+ * Instants on the time-line after the epoch are positive, earlier
+ * are negative.
+ *
+ * @param time the local time, not null
+ * @param offset the zone offset, not null
+ * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative
+ * @since 9
+ */
+ public long toEpochSecond(LocalTime time, ZoneOffset offset) {
+ Objects.requireNonNull(time, "time");
+ Objects.requireNonNull(offset, "offset");
+ long secs = toEpochDay() * SECONDS_PER_DAY + time.toSecondOfDay();
+ secs -= offset.getTotalSeconds();
+ return secs;
+ }
+
//-----------------------------------------------------------------------
/**
* Compares this date to another date.
--- a/jdk/src/java.base/share/classes/java/time/LocalTime.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/time/LocalTime.java Thu Dec 24 10:34:31 2015 -0800
@@ -1490,6 +1490,30 @@
return total;
}
+ /**
+ * Converts this {@code LocalTime} to the number of seconds since the epoch
+ * of 1970-01-01T00:00:00Z.
+ * <p>
+ * This combines this local time with the specified date and
+ * offset to calculate the epoch-second value, which is the
+ * number of elapsed seconds from 1970-01-01T00:00:00Z.
+ * Instants on the time-line after the epoch are positive, earlier
+ * are negative.
+ *
+ * @param date the local date, not null
+ * @param offset the zone offset, not null
+ * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative
+ * @since 9
+ */
+ public long toEpochSecond(LocalDate date, ZoneOffset offset) {
+ Objects.requireNonNull(date, "date");
+ Objects.requireNonNull(offset, "offset");
+ long epochDay = date.toEpochDay();
+ long secs = epochDay * 86400 + toSecondOfDay();
+ secs -= offset.getTotalSeconds();
+ return secs;
+ }
+
//-----------------------------------------------------------------------
/**
* Compares this time to another time.
--- a/jdk/src/java.base/share/classes/java/time/OffsetTime.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/time/OffsetTime.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -1232,6 +1232,28 @@
return nod - offsetNanos;
}
+ /**
+ * Converts this {@code OffsetTime} to the number of seconds since the epoch
+ * of 1970-01-01T00:00:00Z.
+ * <p>
+ * This combines this offset time with the specified date to calculate the
+ * epoch-second value, which is the number of elapsed seconds from
+ * 1970-01-01T00:00:00Z.
+ * Instants on the time-line after the epoch are positive, earlier
+ * are negative.
+ *
+ * @param date the localdate, not null
+ * @return the number of seconds since the epoch of 1970-01-01T00:00:00Z, may be negative
+ * @since 9
+ */
+ public long toEpochSecond(LocalDate date) {
+ Objects.requireNonNull(date, "date");
+ long epochDay = date.toEpochDay();
+ long secs = epochDay * 86400 + time.toSecondOfDay();
+ secs -= offset.getTotalSeconds();
+ return secs;
+ }
+
//-----------------------------------------------------------------------
/**
* Compares this {@code OffsetTime} to another time.
--- a/jdk/src/java.base/share/classes/java/util/Formatter.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/util/Formatter.java Thu Dec 24 10:34:31 2015 -0800
@@ -60,8 +60,8 @@
import java.time.temporal.TemporalQueries;
import java.time.temporal.UnsupportedTemporalTypeException;
-import sun.misc.DoubleConsts;
-import sun.misc.FormattedFloatingDecimal;
+import jdk.internal.math.DoubleConsts;
+import jdk.internal.math.FormattedFloatingDecimal;
/**
* An interpreter for printf-style format strings. This class provides support
--- a/jdk/src/java.base/share/classes/java/util/Locale.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/util/Locale.java Thu Dec 24 10:34:31 2015 -0800
@@ -62,7 +62,6 @@
import sun.util.locale.provider.LocaleProviderAdapter;
import sun.util.locale.provider.LocaleResources;
import sun.util.locale.provider.LocaleServiceProviderPool;
-import sun.util.locale.provider.ResourceBundleBasedAdapter;
/**
* A <code>Locale</code> object represents a specific geographical, political,
@@ -2016,11 +2015,11 @@
/**
* Calculated hashcode
*/
- private transient volatile int hashCodeValue = 0;
+ private transient volatile int hashCodeValue;
private static volatile Locale defaultLocale = initDefault();
- private static volatile Locale defaultDisplayLocale = null;
- private static volatile Locale defaultFormatLocale = null;
+ private static volatile Locale defaultDisplayLocale;
+ private static volatile Locale defaultFormatLocale;
private transient volatile String languageTag;
@@ -2207,9 +2206,9 @@
baseLocale.getRegion(), baseLocale.getVariant(), localeExtensions);
}
- private static volatile String[] isoLanguages = null;
+ private static volatile String[] isoLanguages;
- private static volatile String[] isoCountries = null;
+ private static volatile String[] isoCountries;
private static String convertOldISOCodes(String language) {
// we accept both the old and the new ISO codes for the languages whose ISO
@@ -2851,7 +2850,7 @@
private final String range;
private final double weight;
- private volatile int hash = 0;
+ private volatile int hash;
/**
* Constructs a {@code LanguageRange} using the given {@code range}.
@@ -3108,14 +3107,17 @@
*/
@Override
public int hashCode() {
- if (hash == 0) {
- int result = 17;
- result = 37*result + range.hashCode();
+ int h = hash;
+ if (h == 0) {
+ h = 17;
+ h = 37*h + range.hashCode();
long bitsWeight = Double.doubleToLongBits(weight);
- result = 37*result + (int)(bitsWeight ^ (bitsWeight >>> 32));
- hash = result;
+ h = 37*h + (int)(bitsWeight ^ (bitsWeight >>> 32));
+ if (h != 0) {
+ hash = h;
+ }
}
- return hash;
+ return h;
}
/**
--- a/jdk/src/java.base/share/classes/java/util/regex/Pattern.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/util/regex/Pattern.java Thu Dec 24 10:34:31 2015 -0800
@@ -950,7 +950,7 @@
* Boolean indicating this Pattern is compiled; this is necessary in order
* to lazily compile deserialized Patterns.
*/
- private transient volatile boolean compiled = false;
+ private transient volatile boolean compiled;
/**
* The normalized pattern string.
@@ -1332,7 +1332,6 @@
localCount = 0;
// if length > 0, the Pattern is lazily compiled
- compiled = false;
if (pattern.length() == 0) {
root = new Start(lastAccept);
matchRoot = lastAccept;
@@ -1377,7 +1376,6 @@
* equivalences of the characters.
*/
private void normalize() {
- boolean inCharClass = false;
int lastCodePoint = -1;
// Convert pattern into normalized form
@@ -1551,7 +1549,6 @@
// offset maintains the index in code units.
loop: for(int x=0, offset=0; x<nCodePoints; x++, offset+=len) {
len = countChars(input, offset, 1);
- boolean skip = false;
for(int y=x-1; y>=0; y--) {
if (combClass[y] == combClass[x]) {
continue loop;
@@ -1566,8 +1563,7 @@
temp[index++] = prefix + sre;
}
String[] result = new String[index];
- for (int x=0; x<index; x++)
- result[x] = temp[x];
+ System.arraycopy(temp, 0, result, 0, index);
return result;
}
@@ -1742,9 +1738,11 @@
}
Map<String, Integer> namedGroups() {
- if (namedGroups == null)
- namedGroups = new HashMap<>(2);
- return namedGroups;
+ Map<String, Integer> groups = namedGroups;
+ if (groups == null) {
+ namedGroups = groups = new HashMap<>(2);
+ }
+ return groups;
}
/**
--- a/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipFile.java Thu Dec 24 10:34:31 2015 -0800
@@ -72,7 +72,7 @@
class ZipFile implements ZipConstants, Closeable {
private final String name; // zip file name
- private volatile boolean closeRequested = false;
+ private volatile boolean closeRequested;
private Source zsrc;
private ZipCoder zc;
@@ -366,7 +366,7 @@
}
private class ZipFileInflaterInputStream extends InflaterInputStream {
- private volatile boolean closeRequested = false;
+ private volatile boolean closeRequested;
private boolean eof = false;
private final ZipFileInputStream zfin;
@@ -653,7 +653,7 @@
* (possibly compressed) zip file entry.
*/
private class ZipFileInputStream extends InputStream {
- private volatile boolean closeRequested = false;
+ private volatile boolean closeRequested;
private long pos; // current position within entry data
protected long rem; // number of remaining bytes within entry
protected long size; // uncompressed size of this entry
--- a/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java Thu Dec 24 10:34:31 2015 -0800
@@ -326,20 +326,22 @@
}
// Do not expose this outside of this package.
- private static volatile LoggerFinder provider = null;
+ private static volatile LoggerFinder provider;
private static LoggerFinder accessLoggerFinder() {
- if (provider == null) {
+ LoggerFinder prov = provider;
+ if (prov == null) {
// no need to lock: it doesn't matter if we call
// getLoggerFinder() twice - since LoggerFinder already caches
// the result.
// This is just an optimization to avoid the cost of calling
// doPrivileged every time.
final SecurityManager sm = System.getSecurityManager();
- provider = sm == null ? LoggerFinder.getLoggerFinder() :
+ prov = sm == null ? LoggerFinder.getLoggerFinder() :
AccessController.doPrivileged(
(PrivilegedAction<LoggerFinder>)LoggerFinder::getLoggerFinder);
+ provider = prov;
}
- return provider;
+ return prov;
}
// Avoid using lambda here as lazy loggers could be created early
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/math/DoubleConsts.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.math;
+
+/**
+ * This class contains additional constants documenting limits of the
+ * <code>double</code> type.
+ *
+ * @author Joseph D. Darcy
+ */
+
+public class DoubleConsts {
+ /**
+ * Don't let anyone instantiate this class.
+ */
+ private DoubleConsts() {}
+
+ public static final double POSITIVE_INFINITY = java.lang.Double.POSITIVE_INFINITY;
+ public static final double NEGATIVE_INFINITY = java.lang.Double.NEGATIVE_INFINITY;
+ public static final double NaN = java.lang.Double.NaN;
+ public static final double MAX_VALUE = java.lang.Double.MAX_VALUE;
+ public static final double MIN_VALUE = java.lang.Double.MIN_VALUE;
+
+ /**
+ * A constant holding the smallest positive normal value of type
+ * <code>double</code>, 2<sup>-1022</sup>. It is equal to the
+ * value returned by
+ * <code>Double.longBitsToDouble(0x0010000000000000L)</code>.
+ *
+ * @since 1.5
+ */
+ public static final double MIN_NORMAL = 2.2250738585072014E-308;
+
+
+ /**
+ * The number of logical bits in the significand of a
+ * <code>double</code> number, including the implicit bit.
+ */
+ public static final int SIGNIFICAND_WIDTH = 53;
+
+ /**
+ * Maximum exponent a finite <code>double</code> number may have.
+ * It is equal to the value returned by
+ * <code>Math.ilogb(Double.MAX_VALUE)</code>.
+ */
+ public static final int MAX_EXPONENT = 1023;
+
+ /**
+ * Minimum exponent a normalized <code>double</code> number may
+ * have. It is equal to the value returned by
+ * <code>Math.ilogb(Double.MIN_NORMAL)</code>.
+ */
+ public static final int MIN_EXPONENT = -1022;
+
+ /**
+ * The exponent the smallest positive <code>double</code>
+ * subnormal value would have if it could be normalized..
+ */
+ public static final int MIN_SUB_EXPONENT = MIN_EXPONENT -
+ (SIGNIFICAND_WIDTH - 1);
+
+ /**
+ * Bias used in representing a <code>double</code> exponent.
+ */
+ public static final int EXP_BIAS = 1023;
+
+ /**
+ * Bit mask to isolate the sign bit of a <code>double</code>.
+ */
+ public static final long SIGN_BIT_MASK = 0x8000000000000000L;
+
+ /**
+ * Bit mask to isolate the exponent field of a
+ * <code>double</code>.
+ */
+ public static final long EXP_BIT_MASK = 0x7FF0000000000000L;
+
+ /**
+ * Bit mask to isolate the significand field of a
+ * <code>double</code>.
+ */
+ public static final long SIGNIF_BIT_MASK = 0x000FFFFFFFFFFFFFL;
+
+ static {
+ // verify bit masks cover all bit positions and that the bit
+ // masks are non-overlapping
+ assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0L) &&
+ (((SIGN_BIT_MASK & EXP_BIT_MASK) == 0L) &&
+ ((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0L) &&
+ ((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0L)));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,1508 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.math;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+//@ model import org.jmlspecs.models.JMLMath;
+
+/**
+ * A simple big integer package specifically for floating point base conversion.
+ */
+public /*@ spec_bigint_math @*/ class FDBigInteger {
+
+ //
+ // This class contains many comments that start with "/*@" mark.
+ // They are behavourial specification in
+ // the Java Modelling Language (JML):
+ // http://www.eecs.ucf.edu/~leavens/JML//index.shtml
+ //
+
+ /*@
+ @ public pure model static \bigint UNSIGNED(int v) {
+ @ return v >= 0 ? v : v + (((\bigint)1) << 32);
+ @ }
+ @
+ @ public pure model static \bigint UNSIGNED(long v) {
+ @ return v >= 0 ? v : v + (((\bigint)1) << 64);
+ @ }
+ @
+ @ public pure model static \bigint AP(int[] data, int len) {
+ @ return (\sum int i; 0 <= 0 && i < len; UNSIGNED(data[i]) << (i*32));
+ @ }
+ @
+ @ public pure model static \bigint pow52(int p5, int p2) {
+ @ ghost \bigint v = 1;
+ @ for (int i = 0; i < p5; i++) v *= 5;
+ @ return v << p2;
+ @ }
+ @
+ @ public pure model static \bigint pow10(int p10) {
+ @ return pow52(p10, p10);
+ @ }
+ @*/
+
+ static final int[] SMALL_5_POW = {
+ 1,
+ 5,
+ 5 * 5,
+ 5 * 5 * 5,
+ 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
+ };
+
+ static final long[] LONG_5_POW = {
+ 1L,
+ 5L,
+ 5L * 5,
+ 5L * 5 * 5,
+ 5L * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
+ };
+
+ // Maximum size of cache of powers of 5 as FDBigIntegers.
+ private static final int MAX_FIVE_POW = 340;
+
+ // Cache of big powers of 5 as FDBigIntegers.
+ private static final FDBigInteger POW_5_CACHE[];
+
+ // Initialize FDBigInteger cache of powers of 5.
+ static {
+ POW_5_CACHE = new FDBigInteger[MAX_FIVE_POW];
+ int i = 0;
+ while (i < SMALL_5_POW.length) {
+ FDBigInteger pow5 = new FDBigInteger(new int[]{SMALL_5_POW[i]}, 0);
+ pow5.makeImmutable();
+ POW_5_CACHE[i] = pow5;
+ i++;
+ }
+ FDBigInteger prev = POW_5_CACHE[i - 1];
+ while (i < MAX_FIVE_POW) {
+ POW_5_CACHE[i] = prev = prev.mult(5);
+ prev.makeImmutable();
+ i++;
+ }
+ }
+
+ // Zero as an FDBigInteger.
+ public static final FDBigInteger ZERO = new FDBigInteger(new int[0], 0);
+
+ // Ensure ZERO is immutable.
+ static {
+ ZERO.makeImmutable();
+ }
+
+ // Constant for casting an int to a long via bitwise AND.
+ private static final long LONG_MASK = 0xffffffffL;
+
+ //@ spec_public non_null;
+ private int data[]; // value: data[0] is least significant
+ //@ spec_public;
+ private int offset; // number of least significant zero padding ints
+ //@ spec_public;
+ private int nWords; // data[nWords-1]!=0, all values above are zero
+ // if nWords==0 -> this FDBigInteger is zero
+ //@ spec_public;
+ private boolean isImmutable = false;
+
+ /*@
+ @ public invariant 0 <= nWords && nWords <= data.length && offset >= 0;
+ @ public invariant nWords == 0 ==> offset == 0;
+ @ public invariant nWords > 0 ==> data[nWords - 1] != 0;
+ @ public invariant (\forall int i; nWords <= i && i < data.length; data[i] == 0);
+ @ public pure model \bigint value() {
+ @ return AP(data, nWords) << (offset*32);
+ @ }
+ @*/
+
+ /**
+ * Constructs an <code>FDBigInteger</code> from data and padding. The
+ * <code>data</code> parameter has the least significant <code>int</code> at
+ * the zeroth index. The <code>offset</code> parameter gives the number of
+ * zero <code>int</code>s to be inferred below the least significant element
+ * of <code>data</code>.
+ *
+ * @param data An array containing all non-zero <code>int</code>s of the value.
+ * @param offset An offset indicating the number of zero <code>int</code>s to pad
+ * below the least significant element of <code>data</code>.
+ */
+ /*@
+ @ requires data != null && offset >= 0;
+ @ ensures this.value() == \old(AP(data, data.length) << (offset*32));
+ @ ensures this.data == \old(data);
+ @*/
+ private FDBigInteger(int[] data, int offset) {
+ this.data = data;
+ this.offset = offset;
+ this.nWords = data.length;
+ trimLeadingZeros();
+ }
+
+ /**
+ * Constructs an <code>FDBigInteger</code> from a starting value and some
+ * decimal digits.
+ *
+ * @param lValue The starting value.
+ * @param digits The decimal digits.
+ * @param kDigits The initial index into <code>digits</code>.
+ * @param nDigits The final index into <code>digits</code>.
+ */
+ /*@
+ @ requires digits != null;
+ @ requires 0 <= kDigits && kDigits <= nDigits && nDigits <= digits.length;
+ @ requires (\forall int i; 0 <= i && i < nDigits; '0' <= digits[i] && digits[i] <= '9');
+ @ ensures this.value() == \old(lValue * pow10(nDigits - kDigits) + (\sum int i; kDigits <= i && i < nDigits; (digits[i] - '0') * pow10(nDigits - i - 1)));
+ @*/
+ public FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits) {
+ int n = Math.max((nDigits + 8) / 9, 2); // estimate size needed.
+ data = new int[n]; // allocate enough space
+ data[0] = (int) lValue; // starting value
+ data[1] = (int) (lValue >>> 32);
+ offset = 0;
+ nWords = 2;
+ int i = kDigits;
+ int limit = nDigits - 5; // slurp digits 5 at a time.
+ int v;
+ while (i < limit) {
+ int ilim = i + 5;
+ v = (int) digits[i++] - (int) '0';
+ while (i < ilim) {
+ v = 10 * v + (int) digits[i++] - (int) '0';
+ }
+ multAddMe(100000, v); // ... where 100000 is 10^5.
+ }
+ int factor = 1;
+ v = 0;
+ while (i < nDigits) {
+ v = 10 * v + (int) digits[i++] - (int) '0';
+ factor *= 10;
+ }
+ if (factor != 1) {
+ multAddMe(factor, v);
+ }
+ trimLeadingZeros();
+ }
+
+ /**
+ * Returns an <code>FDBigInteger</code> with the numerical value
+ * <code>5<sup>p5</sup> * 2<sup>p2</sup></code>.
+ *
+ * @param p5 The exponent of the power-of-five factor.
+ * @param p2 The exponent of the power-of-two factor.
+ * @return <code>5<sup>p5</sup> * 2<sup>p2</sup></code>
+ */
+ /*@
+ @ requires p5 >= 0 && p2 >= 0;
+ @ assignable \nothing;
+ @ ensures \result.value() == \old(pow52(p5, p2));
+ @*/
+ public static FDBigInteger valueOfPow52(int p5, int p2) {
+ if (p5 != 0) {
+ if (p2 == 0) {
+ return big5pow(p5);
+ } else if (p5 < SMALL_5_POW.length) {
+ int pow5 = SMALL_5_POW[p5];
+ int wordcount = p2 >> 5;
+ int bitcount = p2 & 0x1f;
+ if (bitcount == 0) {
+ return new FDBigInteger(new int[]{pow5}, wordcount);
+ } else {
+ return new FDBigInteger(new int[]{
+ pow5 << bitcount,
+ pow5 >>> (32 - bitcount)
+ }, wordcount);
+ }
+ } else {
+ return big5pow(p5).leftShift(p2);
+ }
+ } else {
+ return valueOfPow2(p2);
+ }
+ }
+
+ /**
+ * Returns an <code>FDBigInteger</code> with the numerical value
+ * <code>value * 5<sup>p5</sup> * 2<sup>p2</sup></code>.
+ *
+ * @param value The constant factor.
+ * @param p5 The exponent of the power-of-five factor.
+ * @param p2 The exponent of the power-of-two factor.
+ * @return <code>value * 5<sup>p5</sup> * 2<sup>p2</sup></code>
+ */
+ /*@
+ @ requires p5 >= 0 && p2 >= 0;
+ @ assignable \nothing;
+ @ ensures \result.value() == \old(UNSIGNED(value) * pow52(p5, p2));
+ @*/
+ public static FDBigInteger valueOfMulPow52(long value, int p5, int p2) {
+ assert p5 >= 0 : p5;
+ assert p2 >= 0 : p2;
+ int v0 = (int) value;
+ int v1 = (int) (value >>> 32);
+ int wordcount = p2 >> 5;
+ int bitcount = p2 & 0x1f;
+ if (p5 != 0) {
+ if (p5 < SMALL_5_POW.length) {
+ long pow5 = SMALL_5_POW[p5] & LONG_MASK;
+ long carry = (v0 & LONG_MASK) * pow5;
+ v0 = (int) carry;
+ carry >>>= 32;
+ carry = (v1 & LONG_MASK) * pow5 + carry;
+ v1 = (int) carry;
+ int v2 = (int) (carry >>> 32);
+ if (bitcount == 0) {
+ return new FDBigInteger(new int[]{v0, v1, v2}, wordcount);
+ } else {
+ return new FDBigInteger(new int[]{
+ v0 << bitcount,
+ (v1 << bitcount) | (v0 >>> (32 - bitcount)),
+ (v2 << bitcount) | (v1 >>> (32 - bitcount)),
+ v2 >>> (32 - bitcount)
+ }, wordcount);
+ }
+ } else {
+ FDBigInteger pow5 = big5pow(p5);
+ int[] r;
+ if (v1 == 0) {
+ r = new int[pow5.nWords + 1 + ((p2 != 0) ? 1 : 0)];
+ mult(pow5.data, pow5.nWords, v0, r);
+ } else {
+ r = new int[pow5.nWords + 2 + ((p2 != 0) ? 1 : 0)];
+ mult(pow5.data, pow5.nWords, v0, v1, r);
+ }
+ return (new FDBigInteger(r, pow5.offset)).leftShift(p2);
+ }
+ } else if (p2 != 0) {
+ if (bitcount == 0) {
+ return new FDBigInteger(new int[]{v0, v1}, wordcount);
+ } else {
+ return new FDBigInteger(new int[]{
+ v0 << bitcount,
+ (v1 << bitcount) | (v0 >>> (32 - bitcount)),
+ v1 >>> (32 - bitcount)
+ }, wordcount);
+ }
+ }
+ return new FDBigInteger(new int[]{v0, v1}, 0);
+ }
+
+ /**
+ * Returns an <code>FDBigInteger</code> with the numerical value
+ * <code>2<sup>p2</sup></code>.
+ *
+ * @param p2 The exponent of 2.
+ * @return <code>2<sup>p2</sup></code>
+ */
+ /*@
+ @ requires p2 >= 0;
+ @ assignable \nothing;
+ @ ensures \result.value() == pow52(0, p2);
+ @*/
+ private static FDBigInteger valueOfPow2(int p2) {
+ int wordcount = p2 >> 5;
+ int bitcount = p2 & 0x1f;
+ return new FDBigInteger(new int[]{1 << bitcount}, wordcount);
+ }
+
+ /**
+ * Removes all leading zeros from this <code>FDBigInteger</code> adjusting
+ * the offset and number of non-zero leading words accordingly.
+ */
+ /*@
+ @ requires data != null;
+ @ requires 0 <= nWords && nWords <= data.length && offset >= 0;
+ @ requires nWords == 0 ==> offset == 0;
+ @ ensures nWords == 0 ==> offset == 0;
+ @ ensures nWords > 0 ==> data[nWords - 1] != 0;
+ @*/
+ private /*@ helper @*/ void trimLeadingZeros() {
+ int i = nWords;
+ if (i > 0 && (data[--i] == 0)) {
+ //for (; i > 0 && data[i - 1] == 0; i--) ;
+ while(i > 0 && data[i - 1] == 0) {
+ i--;
+ }
+ this.nWords = i;
+ if (i == 0) { // all words are zero
+ this.offset = 0;
+ }
+ }
+ }
+
+ /**
+ * Retrieves the normalization bias of the <code>FDBigIntger</code>. The
+ * normalization bias is a left shift such that after it the highest word
+ * of the value will have the 4 highest bits equal to zero:
+ * {@code (highestWord & 0xf0000000) == 0}, but the next bit should be 1
+ * {@code (highestWord & 0x08000000) != 0}.
+ *
+ * @return The normalization bias.
+ */
+ /*@
+ @ requires this.value() > 0;
+ @*/
+ public /*@ pure @*/ int getNormalizationBias() {
+ if (nWords == 0) {
+ throw new IllegalArgumentException("Zero value cannot be normalized");
+ }
+ int zeros = Integer.numberOfLeadingZeros(data[nWords - 1]);
+ return (zeros < 4) ? 28 + zeros : zeros - 4;
+ }
+
+ // TODO: Why is anticount param needed if it is always 32 - bitcount?
+ /**
+ * Left shifts the contents of one int array into another.
+ *
+ * @param src The source array.
+ * @param idx The initial index of the source array.
+ * @param result The destination array.
+ * @param bitcount The left shift.
+ * @param anticount The left anti-shift, e.g., <code>32-bitcount</code>.
+ * @param prev The prior source value.
+ */
+ /*@
+ @ requires 0 < bitcount && bitcount < 32 && anticount == 32 - bitcount;
+ @ requires src.length >= idx && result.length > idx;
+ @ assignable result[*];
+ @ ensures AP(result, \old(idx + 1)) == \old((AP(src, idx) + UNSIGNED(prev) << (idx*32)) << bitcount);
+ @*/
+ private static void leftShift(int[] src, int idx, int result[], int bitcount, int anticount, int prev){
+ for (; idx > 0; idx--) {
+ int v = (prev << bitcount);
+ prev = src[idx - 1];
+ v |= (prev >>> anticount);
+ result[idx] = v;
+ }
+ int v = prev << bitcount;
+ result[0] = v;
+ }
+
+ /**
+ * Shifts this <code>FDBigInteger</code> to the left. The shift is performed
+ * in-place unless the <code>FDBigInteger</code> is immutable in which case
+ * a new instance of <code>FDBigInteger</code> is returned.
+ *
+ * @param shift The number of bits to shift left.
+ * @return The shifted <code>FDBigInteger</code>.
+ */
+ /*@
+ @ requires this.value() == 0 || shift == 0;
+ @ assignable \nothing;
+ @ ensures \result == this;
+ @
+ @ also
+ @
+ @ requires this.value() > 0 && shift > 0 && this.isImmutable;
+ @ assignable \nothing;
+ @ ensures \result.value() == \old(this.value() << shift);
+ @
+ @ also
+ @
+ @ requires this.value() > 0 && shift > 0 && this.isImmutable;
+ @ assignable \nothing;
+ @ ensures \result == this;
+ @ ensures \result.value() == \old(this.value() << shift);
+ @*/
+ public FDBigInteger leftShift(int shift) {
+ if (shift == 0 || nWords == 0) {
+ return this;
+ }
+ int wordcount = shift >> 5;
+ int bitcount = shift & 0x1f;
+ if (this.isImmutable) {
+ if (bitcount == 0) {
+ return new FDBigInteger(Arrays.copyOf(data, nWords), offset + wordcount);
+ } else {
+ int anticount = 32 - bitcount;
+ int idx = nWords - 1;
+ int prev = data[idx];
+ int hi = prev >>> anticount;
+ int[] result;
+ if (hi != 0) {
+ result = new int[nWords + 1];
+ result[nWords] = hi;
+ } else {
+ result = new int[nWords];
+ }
+ leftShift(data,idx,result,bitcount,anticount,prev);
+ return new FDBigInteger(result, offset + wordcount);
+ }
+ } else {
+ if (bitcount != 0) {
+ int anticount = 32 - bitcount;
+ if ((data[0] << bitcount) == 0) {
+ int idx = 0;
+ int prev = data[idx];
+ for (; idx < nWords - 1; idx++) {
+ int v = (prev >>> anticount);
+ prev = data[idx + 1];
+ v |= (prev << bitcount);
+ data[idx] = v;
+ }
+ int v = prev >>> anticount;
+ data[idx] = v;
+ if(v==0) {
+ nWords--;
+ }
+ offset++;
+ } else {
+ int idx = nWords - 1;
+ int prev = data[idx];
+ int hi = prev >>> anticount;
+ int[] result = data;
+ int[] src = data;
+ if (hi != 0) {
+ if(nWords == data.length) {
+ data = result = new int[nWords + 1];
+ }
+ result[nWords++] = hi;
+ }
+ leftShift(src,idx,result,bitcount,anticount,prev);
+ }
+ }
+ offset += wordcount;
+ return this;
+ }
+ }
+
+ /**
+ * Returns the number of <code>int</code>s this <code>FDBigInteger</code> represents.
+ *
+ * @return Number of <code>int</code>s required to represent this <code>FDBigInteger</code>.
+ */
+ /*@
+ @ requires this.value() == 0;
+ @ ensures \result == 0;
+ @
+ @ also
+ @
+ @ requires this.value() > 0;
+ @ ensures ((\bigint)1) << (\result - 1) <= this.value() && this.value() <= ((\bigint)1) << \result;
+ @*/
+ private /*@ pure @*/ int size() {
+ return nWords + offset;
+ }
+
+
+ /**
+ * Computes
+ * <pre>
+ * q = (int)( this / S )
+ * this = 10 * ( this mod S )
+ * Return q.
+ * </pre>
+ * This is the iteration step of digit development for output.
+ * We assume that S has been normalized, as above, and that
+ * "this" has been left-shifted accordingly.
+ * Also assumed, of course, is that the result, q, can be expressed
+ * as an integer, {@code 0 <= q < 10}.
+ *
+ * @param S The divisor of this <code>FDBigInteger</code>.
+ * @return <code>q = (int)(this / S)</code>.
+ */
+ /*@
+ @ requires !this.isImmutable;
+ @ requires this.size() <= S.size();
+ @ requires this.data.length + this.offset >= S.size();
+ @ requires S.value() >= ((\bigint)1) << (S.size()*32 - 4);
+ @ assignable this.nWords, this.offset, this.data, this.data[*];
+ @ ensures \result == \old(this.value() / S.value());
+ @ ensures this.value() == \old(10 * (this.value() % S.value()));
+ @*/
+ public int quoRemIteration(FDBigInteger S) throws IllegalArgumentException {
+ assert !this.isImmutable : "cannot modify immutable value";
+ // ensure that this and S have the same number of
+ // digits. If S is properly normalized and q < 10 then
+ // this must be so.
+ int thSize = this.size();
+ int sSize = S.size();
+ if (thSize < sSize) {
+ // this value is significantly less than S, result of division is zero.
+ // just mult this by 10.
+ int p = multAndCarryBy10(this.data, this.nWords, this.data);
+ if(p!=0) {
+ this.data[nWords++] = p;
+ } else {
+ trimLeadingZeros();
+ }
+ return 0;
+ } else if (thSize > sSize) {
+ throw new IllegalArgumentException("disparate values");
+ }
+ // estimate q the obvious way. We will usually be
+ // right. If not, then we're only off by a little and
+ // will re-add.
+ long q = (this.data[this.nWords - 1] & LONG_MASK) / (S.data[S.nWords - 1] & LONG_MASK);
+ long diff = multDiffMe(q, S);
+ if (diff != 0L) {
+ //@ assert q != 0;
+ //@ assert this.offset == \old(Math.min(this.offset, S.offset));
+ //@ assert this.offset <= S.offset;
+
+ // q is too big.
+ // add S back in until this turns +. This should
+ // not be very many times!
+ long sum = 0L;
+ int tStart = S.offset - this.offset;
+ //@ assert tStart >= 0;
+ int[] sd = S.data;
+ int[] td = this.data;
+ while (sum == 0L) {
+ for (int sIndex = 0, tIndex = tStart; tIndex < this.nWords; sIndex++, tIndex++) {
+ sum += (td[tIndex] & LONG_MASK) + (sd[sIndex] & LONG_MASK);
+ td[tIndex] = (int) sum;
+ sum >>>= 32; // Signed or unsigned, answer is 0 or 1
+ }
+ //
+ // Originally the following line read
+ // "if ( sum !=0 && sum != -1 )"
+ // but that would be wrong, because of the
+ // treatment of the two values as entirely unsigned,
+ // it would be impossible for a carry-out to be interpreted
+ // as -1 -- it would have to be a single-bit carry-out, or +1.
+ //
+ assert sum == 0 || sum == 1 : sum; // carry out of division correction
+ q -= 1;
+ }
+ }
+ // finally, we can multiply this by 10.
+ // it cannot overflow, right, as the high-order word has
+ // at least 4 high-order zeros!
+ int p = multAndCarryBy10(this.data, this.nWords, this.data);
+ assert p == 0 : p; // Carry out of *10
+ trimLeadingZeros();
+ return (int) q;
+ }
+
+ /**
+ * Multiplies this <code>FDBigInteger</code> by 10. The operation will be
+ * performed in place unless the <code>FDBigInteger</code> is immutable in
+ * which case a new <code>FDBigInteger</code> will be returned.
+ *
+ * @return The <code>FDBigInteger</code> multiplied by 10.
+ */
+ /*@
+ @ requires this.value() == 0;
+ @ assignable \nothing;
+ @ ensures \result == this;
+ @
+ @ also
+ @
+ @ requires this.value() > 0 && this.isImmutable;
+ @ assignable \nothing;
+ @ ensures \result.value() == \old(this.value() * 10);
+ @
+ @ also
+ @
+ @ requires this.value() > 0 && !this.isImmutable;
+ @ assignable this.nWords, this.data, this.data[*];
+ @ ensures \result == this;
+ @ ensures \result.value() == \old(this.value() * 10);
+ @*/
+ public FDBigInteger multBy10() {
+ if (nWords == 0) {
+ return this;
+ }
+ if (isImmutable) {
+ int[] res = new int[nWords + 1];
+ res[nWords] = multAndCarryBy10(data, nWords, res);
+ return new FDBigInteger(res, offset);
+ } else {
+ int p = multAndCarryBy10(this.data, this.nWords, this.data);
+ if (p != 0) {
+ if (nWords == data.length) {
+ if (data[0] == 0) {
+ System.arraycopy(data, 1, data, 0, --nWords);
+ offset++;
+ } else {
+ data = Arrays.copyOf(data, data.length + 1);
+ }
+ }
+ data[nWords++] = p;
+ } else {
+ trimLeadingZeros();
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Multiplies this <code>FDBigInteger</code> by
+ * <code>5<sup>p5</sup> * 2<sup>p2</sup></code>. The operation will be
+ * performed in place if possible, otherwise a new <code>FDBigInteger</code>
+ * will be returned.
+ *
+ * @param p5 The exponent of the power-of-five factor.
+ * @param p2 The exponent of the power-of-two factor.
+ * @return The multiplication result.
+ */
+ /*@
+ @ requires this.value() == 0 || p5 == 0 && p2 == 0;
+ @ assignable \nothing;
+ @ ensures \result == this;
+ @
+ @ also
+ @
+ @ requires this.value() > 0 && (p5 > 0 && p2 >= 0 || p5 == 0 && p2 > 0 && this.isImmutable);
+ @ assignable \nothing;
+ @ ensures \result.value() == \old(this.value() * pow52(p5, p2));
+ @
+ @ also
+ @
+ @ requires this.value() > 0 && p5 == 0 && p2 > 0 && !this.isImmutable;
+ @ assignable this.nWords, this.data, this.data[*];
+ @ ensures \result == this;
+ @ ensures \result.value() == \old(this.value() * pow52(p5, p2));
+ @*/
+ public FDBigInteger multByPow52(int p5, int p2) {
+ if (this.nWords == 0) {
+ return this;
+ }
+ FDBigInteger res = this;
+ if (p5 != 0) {
+ int[] r;
+ int extraSize = (p2 != 0) ? 1 : 0;
+ if (p5 < SMALL_5_POW.length) {
+ r = new int[this.nWords + 1 + extraSize];
+ mult(this.data, this.nWords, SMALL_5_POW[p5], r);
+ res = new FDBigInteger(r, this.offset);
+ } else {
+ FDBigInteger pow5 = big5pow(p5);
+ r = new int[this.nWords + pow5.size() + extraSize];
+ mult(this.data, this.nWords, pow5.data, pow5.nWords, r);
+ res = new FDBigInteger(r, this.offset + pow5.offset);
+ }
+ }
+ return res.leftShift(p2);
+ }
+
+ /**
+ * Multiplies two big integers represented as int arrays.
+ *
+ * @param s1 The first array factor.
+ * @param s1Len The number of elements of <code>s1</code> to use.
+ * @param s2 The second array factor.
+ * @param s2Len The number of elements of <code>s2</code> to use.
+ * @param dst The product array.
+ */
+ /*@
+ @ requires s1 != dst && s2 != dst;
+ @ requires s1.length >= s1Len && s2.length >= s2Len && dst.length >= s1Len + s2Len;
+ @ assignable dst[0 .. s1Len + s2Len - 1];
+ @ ensures AP(dst, s1Len + s2Len) == \old(AP(s1, s1Len) * AP(s2, s2Len));
+ @*/
+ private static void mult(int[] s1, int s1Len, int[] s2, int s2Len, int[] dst) {
+ for (int i = 0; i < s1Len; i++) {
+ long v = s1[i] & LONG_MASK;
+ long p = 0L;
+ for (int j = 0; j < s2Len; j++) {
+ p += (dst[i + j] & LONG_MASK) + v * (s2[j] & LONG_MASK);
+ dst[i + j] = (int) p;
+ p >>>= 32;
+ }
+ dst[i + s2Len] = (int) p;
+ }
+ }
+
+ /**
+ * Subtracts the supplied <code>FDBigInteger</code> subtrahend from this
+ * <code>FDBigInteger</code>. Assert that the result is positive.
+ * If the subtrahend is immutable, store the result in this(minuend).
+ * If this(minuend) is immutable a new <code>FDBigInteger</code> is created.
+ *
+ * @param subtrahend The <code>FDBigInteger</code> to be subtracted.
+ * @return This <code>FDBigInteger</code> less the subtrahend.
+ */
+ /*@
+ @ requires this.isImmutable;
+ @ requires this.value() >= subtrahend.value();
+ @ assignable \nothing;
+ @ ensures \result.value() == \old(this.value() - subtrahend.value());
+ @
+ @ also
+ @
+ @ requires !subtrahend.isImmutable;
+ @ requires this.value() >= subtrahend.value();
+ @ assignable this.nWords, this.offset, this.data, this.data[*];
+ @ ensures \result == this;
+ @ ensures \result.value() == \old(this.value() - subtrahend.value());
+ @*/
+ public FDBigInteger leftInplaceSub(FDBigInteger subtrahend) {
+ assert this.size() >= subtrahend.size() : "result should be positive";
+ FDBigInteger minuend;
+ if (this.isImmutable) {
+ minuend = new FDBigInteger(this.data.clone(), this.offset);
+ } else {
+ minuend = this;
+ }
+ int offsetDiff = subtrahend.offset - minuend.offset;
+ int[] sData = subtrahend.data;
+ int[] mData = minuend.data;
+ int subLen = subtrahend.nWords;
+ int minLen = minuend.nWords;
+ if (offsetDiff < 0) {
+ // need to expand minuend
+ int rLen = minLen - offsetDiff;
+ if (rLen < mData.length) {
+ System.arraycopy(mData, 0, mData, -offsetDiff, minLen);
+ Arrays.fill(mData, 0, -offsetDiff, 0);
+ } else {
+ int[] r = new int[rLen];
+ System.arraycopy(mData, 0, r, -offsetDiff, minLen);
+ minuend.data = mData = r;
+ }
+ minuend.offset = subtrahend.offset;
+ minuend.nWords = minLen = rLen;
+ offsetDiff = 0;
+ }
+ long borrow = 0L;
+ int mIndex = offsetDiff;
+ for (int sIndex = 0; sIndex < subLen && mIndex < minLen; sIndex++, mIndex++) {
+ long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow;
+ mData[mIndex] = (int) diff;
+ borrow = diff >> 32; // signed shift
+ }
+ for (; borrow != 0 && mIndex < minLen; mIndex++) {
+ long diff = (mData[mIndex] & LONG_MASK) + borrow;
+ mData[mIndex] = (int) diff;
+ borrow = diff >> 32; // signed shift
+ }
+ assert borrow == 0L : borrow; // borrow out of subtract,
+ // result should be positive
+ minuend.trimLeadingZeros();
+ return minuend;
+ }
+
+ /**
+ * Subtracts the supplied <code>FDBigInteger</code> subtrahend from this
+ * <code>FDBigInteger</code>. Assert that the result is positive.
+ * If the this(minuend) is immutable, store the result in subtrahend.
+ * If subtrahend is immutable a new <code>FDBigInteger</code> is created.
+ *
+ * @param subtrahend The <code>FDBigInteger</code> to be subtracted.
+ * @return This <code>FDBigInteger</code> less the subtrahend.
+ */
+ /*@
+ @ requires subtrahend.isImmutable;
+ @ requires this.value() >= subtrahend.value();
+ @ assignable \nothing;
+ @ ensures \result.value() == \old(this.value() - subtrahend.value());
+ @
+ @ also
+ @
+ @ requires !subtrahend.isImmutable;
+ @ requires this.value() >= subtrahend.value();
+ @ assignable subtrahend.nWords, subtrahend.offset, subtrahend.data, subtrahend.data[*];
+ @ ensures \result == subtrahend;
+ @ ensures \result.value() == \old(this.value() - subtrahend.value());
+ @*/
+ public FDBigInteger rightInplaceSub(FDBigInteger subtrahend) {
+ assert this.size() >= subtrahend.size() : "result should be positive";
+ FDBigInteger minuend = this;
+ if (subtrahend.isImmutable) {
+ subtrahend = new FDBigInteger(subtrahend.data.clone(), subtrahend.offset);
+ }
+ int offsetDiff = minuend.offset - subtrahend.offset;
+ int[] sData = subtrahend.data;
+ int[] mData = minuend.data;
+ int subLen = subtrahend.nWords;
+ int minLen = minuend.nWords;
+ if (offsetDiff < 0) {
+ int rLen = minLen;
+ if (rLen < sData.length) {
+ System.arraycopy(sData, 0, sData, -offsetDiff, subLen);
+ Arrays.fill(sData, 0, -offsetDiff, 0);
+ } else {
+ int[] r = new int[rLen];
+ System.arraycopy(sData, 0, r, -offsetDiff, subLen);
+ subtrahend.data = sData = r;
+ }
+ subtrahend.offset = minuend.offset;
+ subLen -= offsetDiff;
+ offsetDiff = 0;
+ } else {
+ int rLen = minLen + offsetDiff;
+ if (rLen >= sData.length) {
+ subtrahend.data = sData = Arrays.copyOf(sData, rLen);
+ }
+ }
+ //@ assert minuend == this && minuend.value() == \old(this.value());
+ //@ assert mData == minuend.data && minLen == minuend.nWords;
+ //@ assert subtrahend.offset + subtrahend.data.length >= minuend.size();
+ //@ assert sData == subtrahend.data;
+ //@ assert AP(subtrahend.data, subtrahend.data.length) << subtrahend.offset == \old(subtrahend.value());
+ //@ assert subtrahend.offset == Math.min(\old(this.offset), minuend.offset);
+ //@ assert offsetDiff == minuend.offset - subtrahend.offset;
+ //@ assert 0 <= offsetDiff && offsetDiff + minLen <= sData.length;
+ int sIndex = 0;
+ long borrow = 0L;
+ for (; sIndex < offsetDiff; sIndex++) {
+ long diff = 0L - (sData[sIndex] & LONG_MASK) + borrow;
+ sData[sIndex] = (int) diff;
+ borrow = diff >> 32; // signed shift
+ }
+ //@ assert sIndex == offsetDiff;
+ for (int mIndex = 0; mIndex < minLen; sIndex++, mIndex++) {
+ //@ assert sIndex == offsetDiff + mIndex;
+ long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow;
+ sData[sIndex] = (int) diff;
+ borrow = diff >> 32; // signed shift
+ }
+ assert borrow == 0L : borrow; // borrow out of subtract,
+ // result should be positive
+ subtrahend.nWords = sIndex;
+ subtrahend.trimLeadingZeros();
+ return subtrahend;
+
+ }
+
+ /**
+ * Determines whether all elements of an array are zero for all indices less
+ * than a given index.
+ *
+ * @param a The array to be examined.
+ * @param from The index strictly below which elements are to be examined.
+ * @return Zero if all elements in range are zero, 1 otherwise.
+ */
+ /*@
+ @ requires 0 <= from && from <= a.length;
+ @ ensures \result == (AP(a, from) == 0 ? 0 : 1);
+ @*/
+ private /*@ pure @*/ static int checkZeroTail(int[] a, int from) {
+ while (from > 0) {
+ if (a[--from] != 0) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Compares the parameter with this <code>FDBigInteger</code>. Returns an
+ * integer accordingly as:
+ * <pre>{@code
+ * > 0: this > other
+ * 0: this == other
+ * < 0: this < other
+ * }</pre>
+ *
+ * @param other The <code>FDBigInteger</code> to compare.
+ * @return A negative value, zero, or a positive value according to the
+ * result of the comparison.
+ */
+ /*@
+ @ ensures \result == (this.value() < other.value() ? -1 : this.value() > other.value() ? +1 : 0);
+ @*/
+ public /*@ pure @*/ int cmp(FDBigInteger other) {
+ int aSize = nWords + offset;
+ int bSize = other.nWords + other.offset;
+ if (aSize > bSize) {
+ return 1;
+ } else if (aSize < bSize) {
+ return -1;
+ }
+ int aLen = nWords;
+ int bLen = other.nWords;
+ while (aLen > 0 && bLen > 0) {
+ int a = data[--aLen];
+ int b = other.data[--bLen];
+ if (a != b) {
+ return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
+ }
+ }
+ if (aLen > 0) {
+ return checkZeroTail(data, aLen);
+ }
+ if (bLen > 0) {
+ return -checkZeroTail(other.data, bLen);
+ }
+ return 0;
+ }
+
+ /**
+ * Compares this <code>FDBigInteger</code> with
+ * <code>5<sup>p5</sup> * 2<sup>p2</sup></code>.
+ * Returns an integer accordingly as:
+ * <pre>{@code
+ * > 0: this > other
+ * 0: this == other
+ * < 0: this < other
+ * }</pre>
+ * @param p5 The exponent of the power-of-five factor.
+ * @param p2 The exponent of the power-of-two factor.
+ * @return A negative value, zero, or a positive value according to the
+ * result of the comparison.
+ */
+ /*@
+ @ requires p5 >= 0 && p2 >= 0;
+ @ ensures \result == (this.value() < pow52(p5, p2) ? -1 : this.value() > pow52(p5, p2) ? +1 : 0);
+ @*/
+ public /*@ pure @*/ int cmpPow52(int p5, int p2) {
+ if (p5 == 0) {
+ int wordcount = p2 >> 5;
+ int bitcount = p2 & 0x1f;
+ int size = this.nWords + this.offset;
+ if (size > wordcount + 1) {
+ return 1;
+ } else if (size < wordcount + 1) {
+ return -1;
+ }
+ int a = this.data[this.nWords -1];
+ int b = 1 << bitcount;
+ if (a != b) {
+ return ( (a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
+ }
+ return checkZeroTail(this.data, this.nWords - 1);
+ }
+ return this.cmp(big5pow(p5).leftShift(p2));
+ }
+
+ /**
+ * Compares this <code>FDBigInteger</code> with <code>x + y</code>. Returns a
+ * value according to the comparison as:
+ * <pre>{@code
+ * -1: this < x + y
+ * 0: this == x + y
+ * 1: this > x + y
+ * }</pre>
+ * @param x The first addend of the sum to compare.
+ * @param y The second addend of the sum to compare.
+ * @return -1, 0, or 1 according to the result of the comparison.
+ */
+ /*@
+ @ ensures \result == (this.value() < x.value() + y.value() ? -1 : this.value() > x.value() + y.value() ? +1 : 0);
+ @*/
+ public /*@ pure @*/ int addAndCmp(FDBigInteger x, FDBigInteger y) {
+ FDBigInteger big;
+ FDBigInteger small;
+ int xSize = x.size();
+ int ySize = y.size();
+ int bSize;
+ int sSize;
+ if (xSize >= ySize) {
+ big = x;
+ small = y;
+ bSize = xSize;
+ sSize = ySize;
+ } else {
+ big = y;
+ small = x;
+ bSize = ySize;
+ sSize = xSize;
+ }
+ int thSize = this.size();
+ if (bSize == 0) {
+ return thSize == 0 ? 0 : 1;
+ }
+ if (sSize == 0) {
+ return this.cmp(big);
+ }
+ if (bSize > thSize) {
+ return -1;
+ }
+ if (bSize + 1 < thSize) {
+ return 1;
+ }
+ long top = (big.data[big.nWords - 1] & LONG_MASK);
+ if (sSize == bSize) {
+ top += (small.data[small.nWords - 1] & LONG_MASK);
+ }
+ if ((top >>> 32) == 0) {
+ if (((top + 1) >>> 32) == 0) {
+ // good case - no carry extension
+ if (bSize < thSize) {
+ return 1;
+ }
+ // here sum.nWords == this.nWords
+ long v = (this.data[this.nWords - 1] & LONG_MASK);
+ if (v < top) {
+ return -1;
+ }
+ if (v > top + 1) {
+ return 1;
+ }
+ }
+ } else { // (top>>>32)!=0 guaranteed carry extension
+ if (bSize + 1 > thSize) {
+ return -1;
+ }
+ // here sum.nWords == this.nWords
+ top >>>= 32;
+ long v = (this.data[this.nWords - 1] & LONG_MASK);
+ if (v < top) {
+ return -1;
+ }
+ if (v > top + 1) {
+ return 1;
+ }
+ }
+ return this.cmp(big.add(small));
+ }
+
+ /**
+ * Makes this <code>FDBigInteger</code> immutable.
+ */
+ /*@
+ @ assignable this.isImmutable;
+ @ ensures this.isImmutable;
+ @*/
+ public void makeImmutable() {
+ this.isImmutable = true;
+ }
+
+ /**
+ * Multiplies this <code>FDBigInteger</code> by an integer.
+ *
+ * @param i The factor by which to multiply this <code>FDBigInteger</code>.
+ * @return This <code>FDBigInteger</code> multiplied by an integer.
+ */
+ /*@
+ @ requires this.value() == 0;
+ @ assignable \nothing;
+ @ ensures \result == this;
+ @
+ @ also
+ @
+ @ requires this.value() != 0;
+ @ assignable \nothing;
+ @ ensures \result.value() == \old(this.value() * UNSIGNED(i));
+ @*/
+ private FDBigInteger mult(int i) {
+ if (this.nWords == 0) {
+ return this;
+ }
+ int[] r = new int[nWords + 1];
+ mult(data, nWords, i, r);
+ return new FDBigInteger(r, offset);
+ }
+
+ /**
+ * Multiplies this <code>FDBigInteger</code> by another <code>FDBigInteger</code>.
+ *
+ * @param other The <code>FDBigInteger</code> factor by which to multiply.
+ * @return The product of this and the parameter <code>FDBigInteger</code>s.
+ */
+ /*@
+ @ requires this.value() == 0;
+ @ assignable \nothing;
+ @ ensures \result == this;
+ @
+ @ also
+ @
+ @ requires this.value() != 0 && other.value() == 0;
+ @ assignable \nothing;
+ @ ensures \result == other;
+ @
+ @ also
+ @
+ @ requires this.value() != 0 && other.value() != 0;
+ @ assignable \nothing;
+ @ ensures \result.value() == \old(this.value() * other.value());
+ @*/
+ private FDBigInteger mult(FDBigInteger other) {
+ if (this.nWords == 0) {
+ return this;
+ }
+ if (this.size() == 1) {
+ return other.mult(data[0]);
+ }
+ if (other.nWords == 0) {
+ return other;
+ }
+ if (other.size() == 1) {
+ return this.mult(other.data[0]);
+ }
+ int[] r = new int[nWords + other.nWords];
+ mult(this.data, this.nWords, other.data, other.nWords, r);
+ return new FDBigInteger(r, this.offset + other.offset);
+ }
+
+ /**
+ * Adds another <code>FDBigInteger</code> to this <code>FDBigInteger</code>.
+ *
+ * @param other The <code>FDBigInteger</code> to add.
+ * @return The sum of the <code>FDBigInteger</code>s.
+ */
+ /*@
+ @ assignable \nothing;
+ @ ensures \result.value() == \old(this.value() + other.value());
+ @*/
+ private FDBigInteger add(FDBigInteger other) {
+ FDBigInteger big, small;
+ int bigLen, smallLen;
+ int tSize = this.size();
+ int oSize = other.size();
+ if (tSize >= oSize) {
+ big = this;
+ bigLen = tSize;
+ small = other;
+ smallLen = oSize;
+ } else {
+ big = other;
+ bigLen = oSize;
+ small = this;
+ smallLen = tSize;
+ }
+ int[] r = new int[bigLen + 1];
+ int i = 0;
+ long carry = 0L;
+ for (; i < smallLen; i++) {
+ carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) )
+ + ((i < small.offset ? 0L : (small.data[i - small.offset] & LONG_MASK)));
+ r[i] = (int) carry;
+ carry >>= 32; // signed shift.
+ }
+ for (; i < bigLen; i++) {
+ carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) );
+ r[i] = (int) carry;
+ carry >>= 32; // signed shift.
+ }
+ r[bigLen] = (int) carry;
+ return new FDBigInteger(r, 0);
+ }
+
+
+ /**
+ * Multiplies a <code>FDBigInteger</code> by an int and adds another int. The
+ * result is computed in place. This method is intended only to be invoked
+ * from
+ * <code>
+ * FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits)
+ * </code>.
+ *
+ * @param iv The factor by which to multiply this <code>FDBigInteger</code>.
+ * @param addend The value to add to the product of this
+ * <code>FDBigInteger</code> and <code>iv</code>.
+ */
+ /*@
+ @ requires this.value()*UNSIGNED(iv) + UNSIGNED(addend) < ((\bigint)1) << ((this.data.length + this.offset)*32);
+ @ assignable this.data[*];
+ @ ensures this.value() == \old(this.value()*UNSIGNED(iv) + UNSIGNED(addend));
+ @*/
+ private /*@ helper @*/ void multAddMe(int iv, int addend) {
+ long v = iv & LONG_MASK;
+ // unroll 0th iteration, doing addition.
+ long p = v * (data[0] & LONG_MASK) + (addend & LONG_MASK);
+ data[0] = (int) p;
+ p >>>= 32;
+ for (int i = 1; i < nWords; i++) {
+ p += v * (data[i] & LONG_MASK);
+ data[i] = (int) p;
+ p >>>= 32;
+ }
+ if (p != 0L) {
+ data[nWords++] = (int) p; // will fail noisily if illegal!
+ }
+ }
+
+ //
+ // original doc:
+ //
+ // do this -=q*S
+ // returns borrow
+ //
+ /**
+ * Multiplies the parameters and subtracts them from this
+ * <code>FDBigInteger</code>.
+ *
+ * @param q The integer parameter.
+ * @param S The <code>FDBigInteger</code> parameter.
+ * @return <code>this - q*S</code>.
+ */
+ /*@
+ @ ensures nWords == 0 ==> offset == 0;
+ @ ensures nWords > 0 ==> data[nWords - 1] != 0;
+ @*/
+ /*@
+ @ requires 0 < q && q <= (1L << 31);
+ @ requires data != null;
+ @ requires 0 <= nWords && nWords <= data.length && offset >= 0;
+ @ requires !this.isImmutable;
+ @ requires this.size() == S.size();
+ @ requires this != S;
+ @ assignable this.nWords, this.offset, this.data, this.data[*];
+ @ ensures -q <= \result && \result <= 0;
+ @ ensures this.size() == \old(this.size());
+ @ ensures this.value() + (\result << (this.size()*32)) == \old(this.value() - q*S.value());
+ @ ensures this.offset == \old(Math.min(this.offset, S.offset));
+ @ ensures \old(this.offset <= S.offset) ==> this.nWords == \old(this.nWords);
+ @ ensures \old(this.offset <= S.offset) ==> this.offset == \old(this.offset);
+ @ ensures \old(this.offset <= S.offset) ==> this.data == \old(this.data);
+ @
+ @ also
+ @
+ @ requires q == 0;
+ @ assignable \nothing;
+ @ ensures \result == 0;
+ @*/
+ private /*@ helper @*/ long multDiffMe(long q, FDBigInteger S) {
+ long diff = 0L;
+ if (q != 0) {
+ int deltaSize = S.offset - this.offset;
+ if (deltaSize >= 0) {
+ int[] sd = S.data;
+ int[] td = this.data;
+ for (int sIndex = 0, tIndex = deltaSize; sIndex < S.nWords; sIndex++, tIndex++) {
+ diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK);
+ td[tIndex] = (int) diff;
+ diff >>= 32; // N.B. SIGNED shift.
+ }
+ } else {
+ deltaSize = -deltaSize;
+ int[] rd = new int[nWords + deltaSize];
+ int sIndex = 0;
+ int rIndex = 0;
+ int[] sd = S.data;
+ for (; rIndex < deltaSize && sIndex < S.nWords; sIndex++, rIndex++) {
+ diff -= q * (sd[sIndex] & LONG_MASK);
+ rd[rIndex] = (int) diff;
+ diff >>= 32; // N.B. SIGNED shift.
+ }
+ int tIndex = 0;
+ int[] td = this.data;
+ for (; sIndex < S.nWords; sIndex++, tIndex++, rIndex++) {
+ diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK);
+ rd[rIndex] = (int) diff;
+ diff >>= 32; // N.B. SIGNED shift.
+ }
+ this.nWords += deltaSize;
+ this.offset -= deltaSize;
+ this.data = rd;
+ }
+ }
+ return diff;
+ }
+
+
+ /**
+ * Multiplies by 10 a big integer represented as an array. The final carry
+ * is returned.
+ *
+ * @param src The array representation of the big integer.
+ * @param srcLen The number of elements of <code>src</code> to use.
+ * @param dst The product array.
+ * @return The final carry of the multiplication.
+ */
+ /*@
+ @ requires src.length >= srcLen && dst.length >= srcLen;
+ @ assignable dst[0 .. srcLen - 1];
+ @ ensures 0 <= \result && \result < 10;
+ @ ensures AP(dst, srcLen) + (\result << (srcLen*32)) == \old(AP(src, srcLen) * 10);
+ @*/
+ private static int multAndCarryBy10(int[] src, int srcLen, int[] dst) {
+ long carry = 0;
+ for (int i = 0; i < srcLen; i++) {
+ long product = (src[i] & LONG_MASK) * 10L + carry;
+ dst[i] = (int) product;
+ carry = product >>> 32;
+ }
+ return (int) carry;
+ }
+
+ /**
+ * Multiplies by a constant value a big integer represented as an array.
+ * The constant factor is an <code>int</code>.
+ *
+ * @param src The array representation of the big integer.
+ * @param srcLen The number of elements of <code>src</code> to use.
+ * @param value The constant factor by which to multiply.
+ * @param dst The product array.
+ */
+ /*@
+ @ requires src.length >= srcLen && dst.length >= srcLen + 1;
+ @ assignable dst[0 .. srcLen];
+ @ ensures AP(dst, srcLen + 1) == \old(AP(src, srcLen) * UNSIGNED(value));
+ @*/
+ private static void mult(int[] src, int srcLen, int value, int[] dst) {
+ long val = value & LONG_MASK;
+ long carry = 0;
+ for (int i = 0; i < srcLen; i++) {
+ long product = (src[i] & LONG_MASK) * val + carry;
+ dst[i] = (int) product;
+ carry = product >>> 32;
+ }
+ dst[srcLen] = (int) carry;
+ }
+
+ /**
+ * Multiplies by a constant value a big integer represented as an array.
+ * The constant factor is a long represent as two <code>int</code>s.
+ *
+ * @param src The array representation of the big integer.
+ * @param srcLen The number of elements of <code>src</code> to use.
+ * @param v0 The lower 32 bits of the long factor.
+ * @param v1 The upper 32 bits of the long factor.
+ * @param dst The product array.
+ */
+ /*@
+ @ requires src != dst;
+ @ requires src.length >= srcLen && dst.length >= srcLen + 2;
+ @ assignable dst[0 .. srcLen + 1];
+ @ ensures AP(dst, srcLen + 2) == \old(AP(src, srcLen) * (UNSIGNED(v0) + (UNSIGNED(v1) << 32)));
+ @*/
+ private static void mult(int[] src, int srcLen, int v0, int v1, int[] dst) {
+ long v = v0 & LONG_MASK;
+ long carry = 0;
+ for (int j = 0; j < srcLen; j++) {
+ long product = v * (src[j] & LONG_MASK) + carry;
+ dst[j] = (int) product;
+ carry = product >>> 32;
+ }
+ dst[srcLen] = (int) carry;
+ v = v1 & LONG_MASK;
+ carry = 0;
+ for (int j = 0; j < srcLen; j++) {
+ long product = (dst[j + 1] & LONG_MASK) + v * (src[j] & LONG_MASK) + carry;
+ dst[j + 1] = (int) product;
+ carry = product >>> 32;
+ }
+ dst[srcLen + 1] = (int) carry;
+ }
+
+ // Fails assertion for negative exponent.
+ /**
+ * Computes <code>5</code> raised to a given power.
+ *
+ * @param p The exponent of 5.
+ * @return <code>5<sup>p</sup></code>.
+ */
+ private static FDBigInteger big5pow(int p) {
+ assert p >= 0 : p; // negative power of 5
+ if (p < MAX_FIVE_POW) {
+ return POW_5_CACHE[p];
+ }
+ return big5powRec(p);
+ }
+
+ // slow path
+ /**
+ * Computes <code>5</code> raised to a given power.
+ *
+ * @param p The exponent of 5.
+ * @return <code>5<sup>p</sup></code>.
+ */
+ private static FDBigInteger big5powRec(int p) {
+ if (p < MAX_FIVE_POW) {
+ return POW_5_CACHE[p];
+ }
+ // construct the value.
+ // recursively.
+ int q, r;
+ // in order to compute 5^p,
+ // compute its square root, 5^(p/2) and square.
+ // or, let q = p / 2, r = p -q, then
+ // 5^p = 5^(q+r) = 5^q * 5^r
+ q = p >> 1;
+ r = p - q;
+ FDBigInteger bigq = big5powRec(q);
+ if (r < SMALL_5_POW.length) {
+ return bigq.mult(SMALL_5_POW[r]);
+ } else {
+ return bigq.mult(big5powRec(r));
+ }
+ }
+
+ // for debugging ...
+ /**
+ * Converts this <code>FDBigInteger</code> to a hexadecimal string.
+ *
+ * @return The hexadecimal string representation.
+ */
+ public String toHexString(){
+ if(nWords ==0) {
+ return "0";
+ }
+ StringBuilder sb = new StringBuilder((nWords +offset)*8);
+ for(int i= nWords -1; i>=0; i--) {
+ String subStr = Integer.toHexString(data[i]);
+ for(int j = subStr.length(); j<8; j++) {
+ sb.append('0');
+ }
+ sb.append(subStr);
+ }
+ for(int i=offset; i>0; i--) {
+ sb.append("00000000");
+ }
+ return sb.toString();
+ }
+
+ // for debugging ...
+ /**
+ * Converts this <code>FDBigInteger</code> to a <code>BigInteger</code>.
+ *
+ * @return The <code>BigInteger</code> representation.
+ */
+ public BigInteger toBigInteger() {
+ byte[] magnitude = new byte[nWords * 4 + 1];
+ for (int i = 0; i < nWords; i++) {
+ int w = data[i];
+ magnitude[magnitude.length - 4 * i - 1] = (byte) w;
+ magnitude[magnitude.length - 4 * i - 2] = (byte) (w >> 8);
+ magnitude[magnitude.length - 4 * i - 3] = (byte) (w >> 16);
+ magnitude[magnitude.length - 4 * i - 4] = (byte) (w >> 24);
+ }
+ return new BigInteger(magnitude).shiftLeft(offset * 32);
+ }
+
+ // for debugging ...
+ /**
+ * Converts this <code>FDBigInteger</code> to a string.
+ *
+ * @return The string representation.
+ */
+ @Override
+ public String toString(){
+ return toBigInteger().toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/math/FloatConsts.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.math;
+
+/**
+ * This class contains additional constants documenting limits of the
+ * <code>float</code> type.
+ *
+ * @author Joseph D. Darcy
+ */
+
+public class FloatConsts {
+ /**
+ * Don't let anyone instantiate this class.
+ */
+ private FloatConsts() {}
+
+ public static final float POSITIVE_INFINITY = java.lang.Float.POSITIVE_INFINITY;
+ public static final float NEGATIVE_INFINITY = java.lang.Float.NEGATIVE_INFINITY;
+ public static final float NaN = java.lang.Float.NaN;
+ public static final float MAX_VALUE = java.lang.Float.MAX_VALUE;
+ public static final float MIN_VALUE = java.lang.Float.MIN_VALUE;
+
+ /**
+ * A constant holding the smallest positive normal value of type
+ * <code>float</code>, 2<sup>-126</sup>. It is equal to the value
+ * returned by <code>Float.intBitsToFloat(0x00800000)</code>.
+ */
+ public static final float MIN_NORMAL = 1.17549435E-38f;
+
+ /**
+ * The number of logical bits in the significand of a
+ * <code>float</code> number, including the implicit bit.
+ */
+ public static final int SIGNIFICAND_WIDTH = 24;
+
+ /**
+ * Maximum exponent a finite <code>float</code> number may have.
+ * It is equal to the value returned by
+ * <code>Math.ilogb(Float.MAX_VALUE)</code>.
+ */
+ public static final int MAX_EXPONENT = 127;
+
+ /**
+ * Minimum exponent a normalized <code>float</code> number may
+ * have. It is equal to the value returned by
+ * <code>Math.ilogb(Float.MIN_NORMAL)</code>.
+ */
+ public static final int MIN_EXPONENT = -126;
+
+ /**
+ * The exponent the smallest positive <code>float</code> subnormal
+ * value would have if it could be normalized.
+ */
+ public static final int MIN_SUB_EXPONENT = MIN_EXPONENT -
+ (SIGNIFICAND_WIDTH - 1);
+
+ /**
+ * Bias used in representing a <code>float</code> exponent.
+ */
+ public static final int EXP_BIAS = 127;
+
+ /**
+ * Bit mask to isolate the sign bit of a <code>float</code>.
+ */
+ public static final int SIGN_BIT_MASK = 0x80000000;
+
+ /**
+ * Bit mask to isolate the exponent field of a
+ * <code>float</code>.
+ */
+ public static final int EXP_BIT_MASK = 0x7F800000;
+
+ /**
+ * Bit mask to isolate the significand field of a
+ * <code>float</code>.
+ */
+ public static final int SIGNIF_BIT_MASK = 0x007FFFFF;
+
+ static {
+ // verify bit masks cover all bit positions and that the bit
+ // masks are non-overlapping
+ assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0) &&
+ (((SIGN_BIT_MASK & EXP_BIT_MASK) == 0) &&
+ ((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0) &&
+ ((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0)));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,2552 @@
+/*
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.math;
+
+import java.util.Arrays;
+import java.util.regex.*;
+
+/**
+ * A class for converting between ASCII and decimal representations of a single
+ * or double precision floating point number. Most conversions are provided via
+ * static convenience methods, although a <code>BinaryToASCIIConverter</code>
+ * instance may be obtained and reused.
+ */
+public class FloatingDecimal{
+ //
+ // Constants of the implementation;
+ // most are IEEE-754 related.
+ // (There are more really boring constants at the end.)
+ //
+ static final int EXP_SHIFT = DoubleConsts.SIGNIFICAND_WIDTH - 1;
+ static final long FRACT_HOB = ( 1L<<EXP_SHIFT ); // assumed High-Order bit
+ static final long EXP_ONE = ((long)DoubleConsts.EXP_BIAS)<<EXP_SHIFT; // exponent of 1.0
+ static final int MAX_SMALL_BIN_EXP = 62;
+ static final int MIN_SMALL_BIN_EXP = -( 63 / 3 );
+ static final int MAX_DECIMAL_DIGITS = 15;
+ static final int MAX_DECIMAL_EXPONENT = 308;
+ static final int MIN_DECIMAL_EXPONENT = -324;
+ static final int BIG_DECIMAL_EXPONENT = 324; // i.e. abs(MIN_DECIMAL_EXPONENT)
+ static final int MAX_NDIGITS = 1100;
+
+ static final int SINGLE_EXP_SHIFT = FloatConsts.SIGNIFICAND_WIDTH - 1;
+ static final int SINGLE_FRACT_HOB = 1<<SINGLE_EXP_SHIFT;
+ static final int SINGLE_MAX_DECIMAL_DIGITS = 7;
+ static final int SINGLE_MAX_DECIMAL_EXPONENT = 38;
+ static final int SINGLE_MIN_DECIMAL_EXPONENT = -45;
+ static final int SINGLE_MAX_NDIGITS = 200;
+
+ static final int INT_DECIMAL_DIGITS = 9;
+
+ /**
+ * Converts a double precision floating point value to a <code>String</code>.
+ *
+ * @param d The double precision value.
+ * @return The value converted to a <code>String</code>.
+ */
+ public static String toJavaFormatString(double d) {
+ return getBinaryToASCIIConverter(d).toJavaFormatString();
+ }
+
+ /**
+ * Converts a single precision floating point value to a <code>String</code>.
+ *
+ * @param f The single precision value.
+ * @return The value converted to a <code>String</code>.
+ */
+ public static String toJavaFormatString(float f) {
+ return getBinaryToASCIIConverter(f).toJavaFormatString();
+ }
+
+ /**
+ * Appends a double precision floating point value to an <code>Appendable</code>.
+ * @param d The double precision value.
+ * @param buf The <code>Appendable</code> with the value appended.
+ */
+ public static void appendTo(double d, Appendable buf) {
+ getBinaryToASCIIConverter(d).appendTo(buf);
+ }
+
+ /**
+ * Appends a single precision floating point value to an <code>Appendable</code>.
+ * @param f The single precision value.
+ * @param buf The <code>Appendable</code> with the value appended.
+ */
+ public static void appendTo(float f, Appendable buf) {
+ getBinaryToASCIIConverter(f).appendTo(buf);
+ }
+
+ /**
+ * Converts a <code>String</code> to a double precision floating point value.
+ *
+ * @param s The <code>String</code> to convert.
+ * @return The double precision value.
+ * @throws NumberFormatException If the <code>String</code> does not
+ * represent a properly formatted double precision value.
+ */
+ public static double parseDouble(String s) throws NumberFormatException {
+ return readJavaFormatString(s).doubleValue();
+ }
+
+ /**
+ * Converts a <code>String</code> to a single precision floating point value.
+ *
+ * @param s The <code>String</code> to convert.
+ * @return The single precision value.
+ * @throws NumberFormatException If the <code>String</code> does not
+ * represent a properly formatted single precision value.
+ */
+ public static float parseFloat(String s) throws NumberFormatException {
+ return readJavaFormatString(s).floatValue();
+ }
+
+ /**
+ * A converter which can process single or double precision floating point
+ * values into an ASCII <code>String</code> representation.
+ */
+ public interface BinaryToASCIIConverter {
+ /**
+ * Converts a floating point value into an ASCII <code>String</code>.
+ * @return The value converted to a <code>String</code>.
+ */
+ public String toJavaFormatString();
+
+ /**
+ * Appends a floating point value to an <code>Appendable</code>.
+ * @param buf The <code>Appendable</code> to receive the value.
+ */
+ public void appendTo(Appendable buf);
+
+ /**
+ * Retrieves the decimal exponent most closely corresponding to this value.
+ * @return The decimal exponent.
+ */
+ public int getDecimalExponent();
+
+ /**
+ * Retrieves the value as an array of digits.
+ * @param digits The digit array.
+ * @return The number of valid digits copied into the array.
+ */
+ public int getDigits(char[] digits);
+
+ /**
+ * Indicates the sign of the value.
+ * @return {@code value < 0.0}.
+ */
+ public boolean isNegative();
+
+ /**
+ * Indicates whether the value is either infinite or not a number.
+ *
+ * @return <code>true</code> if and only if the value is <code>NaN</code>
+ * or infinite.
+ */
+ public boolean isExceptional();
+
+ /**
+ * Indicates whether the value was rounded up during the binary to ASCII
+ * conversion.
+ *
+ * @return <code>true</code> if and only if the value was rounded up.
+ */
+ public boolean digitsRoundedUp();
+
+ /**
+ * Indicates whether the binary to ASCII conversion was exact.
+ *
+ * @return <code>true</code> if any only if the conversion was exact.
+ */
+ public boolean decimalDigitsExact();
+ }
+
+ /**
+ * A <code>BinaryToASCIIConverter</code> which represents <code>NaN</code>
+ * and infinite values.
+ */
+ private static class ExceptionalBinaryToASCIIBuffer implements BinaryToASCIIConverter {
+ private final String image;
+ private boolean isNegative;
+
+ public ExceptionalBinaryToASCIIBuffer(String image, boolean isNegative) {
+ this.image = image;
+ this.isNegative = isNegative;
+ }
+
+ @Override
+ public String toJavaFormatString() {
+ return image;
+ }
+
+ @Override
+ public void appendTo(Appendable buf) {
+ if (buf instanceof StringBuilder) {
+ ((StringBuilder) buf).append(image);
+ } else if (buf instanceof StringBuffer) {
+ ((StringBuffer) buf).append(image);
+ } else {
+ assert false;
+ }
+ }
+
+ @Override
+ public int getDecimalExponent() {
+ throw new IllegalArgumentException("Exceptional value does not have an exponent");
+ }
+
+ @Override
+ public int getDigits(char[] digits) {
+ throw new IllegalArgumentException("Exceptional value does not have digits");
+ }
+
+ @Override
+ public boolean isNegative() {
+ return isNegative;
+ }
+
+ @Override
+ public boolean isExceptional() {
+ return true;
+ }
+
+ @Override
+ public boolean digitsRoundedUp() {
+ throw new IllegalArgumentException("Exceptional value is not rounded");
+ }
+
+ @Override
+ public boolean decimalDigitsExact() {
+ throw new IllegalArgumentException("Exceptional value is not exact");
+ }
+ }
+
+ private static final String INFINITY_REP = "Infinity";
+ private static final int INFINITY_LENGTH = INFINITY_REP.length();
+ private static final String NAN_REP = "NaN";
+ private static final int NAN_LENGTH = NAN_REP.length();
+
+ private static final BinaryToASCIIConverter B2AC_POSITIVE_INFINITY = new ExceptionalBinaryToASCIIBuffer(INFINITY_REP, false);
+ private static final BinaryToASCIIConverter B2AC_NEGATIVE_INFINITY = new ExceptionalBinaryToASCIIBuffer("-" + INFINITY_REP, true);
+ private static final BinaryToASCIIConverter B2AC_NOT_A_NUMBER = new ExceptionalBinaryToASCIIBuffer(NAN_REP, false);
+ private static final BinaryToASCIIConverter B2AC_POSITIVE_ZERO = new BinaryToASCIIBuffer(false, new char[]{'0'});
+ private static final BinaryToASCIIConverter B2AC_NEGATIVE_ZERO = new BinaryToASCIIBuffer(true, new char[]{'0'});
+
+ /**
+ * A buffered implementation of <code>BinaryToASCIIConverter</code>.
+ */
+ static class BinaryToASCIIBuffer implements BinaryToASCIIConverter {
+ private boolean isNegative;
+ private int decExponent;
+ private int firstDigitIndex;
+ private int nDigits;
+ private final char[] digits;
+ private final char[] buffer = new char[26];
+
+ //
+ // The fields below provide additional information about the result of
+ // the binary to decimal digits conversion done in dtoa() and roundup()
+ // methods. They are changed if needed by those two methods.
+ //
+
+ // True if the dtoa() binary to decimal conversion was exact.
+ private boolean exactDecimalConversion = false;
+
+ // True if the result of the binary to decimal conversion was rounded-up
+ // at the end of the conversion process, i.e. roundUp() method was called.
+ private boolean decimalDigitsRoundedUp = false;
+
+ /**
+ * Default constructor; used for non-zero values,
+ * <code>BinaryToASCIIBuffer</code> may be thread-local and reused
+ */
+ BinaryToASCIIBuffer(){
+ this.digits = new char[20];
+ }
+
+ /**
+ * Creates a specialized value (positive and negative zeros).
+ */
+ BinaryToASCIIBuffer(boolean isNegative, char[] digits){
+ this.isNegative = isNegative;
+ this.decExponent = 0;
+ this.digits = digits;
+ this.firstDigitIndex = 0;
+ this.nDigits = digits.length;
+ }
+
+ @Override
+ public String toJavaFormatString() {
+ int len = getChars(buffer);
+ return new String(buffer, 0, len);
+ }
+
+ @Override
+ public void appendTo(Appendable buf) {
+ int len = getChars(buffer);
+ if (buf instanceof StringBuilder) {
+ ((StringBuilder) buf).append(buffer, 0, len);
+ } else if (buf instanceof StringBuffer) {
+ ((StringBuffer) buf).append(buffer, 0, len);
+ } else {
+ assert false;
+ }
+ }
+
+ @Override
+ public int getDecimalExponent() {
+ return decExponent;
+ }
+
+ @Override
+ public int getDigits(char[] digits) {
+ System.arraycopy(this.digits,firstDigitIndex,digits,0,this.nDigits);
+ return this.nDigits;
+ }
+
+ @Override
+ public boolean isNegative() {
+ return isNegative;
+ }
+
+ @Override
+ public boolean isExceptional() {
+ return false;
+ }
+
+ @Override
+ public boolean digitsRoundedUp() {
+ return decimalDigitsRoundedUp;
+ }
+
+ @Override
+ public boolean decimalDigitsExact() {
+ return exactDecimalConversion;
+ }
+
+ private void setSign(boolean isNegative) {
+ this.isNegative = isNegative;
+ }
+
+ /**
+ * This is the easy subcase --
+ * all the significant bits, after scaling, are held in lvalue.
+ * negSign and decExponent tell us what processing and scaling
+ * has already been done. Exceptional cases have already been
+ * stripped out.
+ * In particular:
+ * lvalue is a finite number (not Inf, nor NaN)
+ * lvalue > 0L (not zero, nor negative).
+ *
+ * The only reason that we develop the digits here, rather than
+ * calling on Long.toString() is that we can do it a little faster,
+ * and besides want to treat trailing 0s specially. If Long.toString
+ * changes, we should re-evaluate this strategy!
+ */
+ private void developLongDigits( int decExponent, long lvalue, int insignificantDigits ){
+ if ( insignificantDigits != 0 ){
+ // Discard non-significant low-order bits, while rounding,
+ // up to insignificant value.
+ long pow10 = FDBigInteger.LONG_5_POW[insignificantDigits] << insignificantDigits; // 10^i == 5^i * 2^i;
+ long residue = lvalue % pow10;
+ lvalue /= pow10;
+ decExponent += insignificantDigits;
+ if ( residue >= (pow10>>1) ){
+ // round up based on the low-order bits we're discarding
+ lvalue++;
+ }
+ }
+ int digitno = digits.length -1;
+ int c;
+ if ( lvalue <= Integer.MAX_VALUE ){
+ assert lvalue > 0L : lvalue; // lvalue <= 0
+ // even easier subcase!
+ // can do int arithmetic rather than long!
+ int ivalue = (int)lvalue;
+ c = ivalue%10;
+ ivalue /= 10;
+ while ( c == 0 ){
+ decExponent++;
+ c = ivalue%10;
+ ivalue /= 10;
+ }
+ while ( ivalue != 0){
+ digits[digitno--] = (char)(c+'0');
+ decExponent++;
+ c = ivalue%10;
+ ivalue /= 10;
+ }
+ digits[digitno] = (char)(c+'0');
+ } else {
+ // same algorithm as above (same bugs, too )
+ // but using long arithmetic.
+ c = (int)(lvalue%10L);
+ lvalue /= 10L;
+ while ( c == 0 ){
+ decExponent++;
+ c = (int)(lvalue%10L);
+ lvalue /= 10L;
+ }
+ while ( lvalue != 0L ){
+ digits[digitno--] = (char)(c+'0');
+ decExponent++;
+ c = (int)(lvalue%10L);
+ lvalue /= 10;
+ }
+ digits[digitno] = (char)(c+'0');
+ }
+ this.decExponent = decExponent+1;
+ this.firstDigitIndex = digitno;
+ this.nDigits = this.digits.length - digitno;
+ }
+
+ private void dtoa( int binExp, long fractBits, int nSignificantBits, boolean isCompatibleFormat)
+ {
+ assert fractBits > 0 ; // fractBits here can't be zero or negative
+ assert (fractBits & FRACT_HOB)!=0 ; // Hi-order bit should be set
+ // Examine number. Determine if it is an easy case,
+ // which we can do pretty trivially using float/long conversion,
+ // or whether we must do real work.
+ final int tailZeros = Long.numberOfTrailingZeros(fractBits);
+
+ // number of significant bits of fractBits;
+ final int nFractBits = EXP_SHIFT+1-tailZeros;
+
+ // reset flags to default values as dtoa() does not always set these
+ // flags and a prior call to dtoa() might have set them to incorrect
+ // values with respect to the current state.
+ decimalDigitsRoundedUp = false;
+ exactDecimalConversion = false;
+
+ // number of significant bits to the right of the point.
+ int nTinyBits = Math.max( 0, nFractBits - binExp - 1 );
+ if ( binExp <= MAX_SMALL_BIN_EXP && binExp >= MIN_SMALL_BIN_EXP ){
+ // Look more closely at the number to decide if,
+ // with scaling by 10^nTinyBits, the result will fit in
+ // a long.
+ if ( (nTinyBits < FDBigInteger.LONG_5_POW.length) && ((nFractBits + N_5_BITS[nTinyBits]) < 64 ) ){
+ //
+ // We can do this:
+ // take the fraction bits, which are normalized.
+ // (a) nTinyBits == 0: Shift left or right appropriately
+ // to align the binary point at the extreme right, i.e.
+ // where a long int point is expected to be. The integer
+ // result is easily converted to a string.
+ // (b) nTinyBits > 0: Shift right by EXP_SHIFT-nFractBits,
+ // which effectively converts to long and scales by
+ // 2^nTinyBits. Then multiply by 5^nTinyBits to
+ // complete the scaling. We know this won't overflow
+ // because we just counted the number of bits necessary
+ // in the result. The integer you get from this can
+ // then be converted to a string pretty easily.
+ //
+ if ( nTinyBits == 0 ) {
+ int insignificant;
+ if ( binExp > nSignificantBits ){
+ insignificant = insignificantDigitsForPow2(binExp-nSignificantBits-1);
+ } else {
+ insignificant = 0;
+ }
+ if ( binExp >= EXP_SHIFT ){
+ fractBits <<= (binExp-EXP_SHIFT);
+ } else {
+ fractBits >>>= (EXP_SHIFT-binExp) ;
+ }
+ developLongDigits( 0, fractBits, insignificant );
+ return;
+ }
+ //
+ // The following causes excess digits to be printed
+ // out in the single-float case. Our manipulation of
+ // halfULP here is apparently not correct. If we
+ // better understand how this works, perhaps we can
+ // use this special case again. But for the time being,
+ // we do not.
+ // else {
+ // fractBits >>>= EXP_SHIFT+1-nFractBits;
+ // fractBits//= long5pow[ nTinyBits ];
+ // halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits);
+ // developLongDigits( -nTinyBits, fractBits, insignificantDigits(halfULP) );
+ // return;
+ // }
+ //
+ }
+ }
+ //
+ // This is the hard case. We are going to compute large positive
+ // integers B and S and integer decExp, s.t.
+ // d = ( B / S )// 10^decExp
+ // 1 <= B / S < 10
+ // Obvious choices are:
+ // decExp = floor( log10(d) )
+ // B = d// 2^nTinyBits// 10^max( 0, -decExp )
+ // S = 10^max( 0, decExp)// 2^nTinyBits
+ // (noting that nTinyBits has already been forced to non-negative)
+ // I am also going to compute a large positive integer
+ // M = (1/2^nSignificantBits)// 2^nTinyBits// 10^max( 0, -decExp )
+ // i.e. M is (1/2) of the ULP of d, scaled like B.
+ // When we iterate through dividing B/S and picking off the
+ // quotient bits, we will know when to stop when the remainder
+ // is <= M.
+ //
+ // We keep track of powers of 2 and powers of 5.
+ //
+ int decExp = estimateDecExp(fractBits,binExp);
+ int B2, B5; // powers of 2 and powers of 5, respectively, in B
+ int S2, S5; // powers of 2 and powers of 5, respectively, in S
+ int M2, M5; // powers of 2 and powers of 5, respectively, in M
+
+ B5 = Math.max( 0, -decExp );
+ B2 = B5 + nTinyBits + binExp;
+
+ S5 = Math.max( 0, decExp );
+ S2 = S5 + nTinyBits;
+
+ M5 = B5;
+ M2 = B2 - nSignificantBits;
+
+ //
+ // the long integer fractBits contains the (nFractBits) interesting
+ // bits from the mantissa of d ( hidden 1 added if necessary) followed
+ // by (EXP_SHIFT+1-nFractBits) zeros. In the interest of compactness,
+ // I will shift out those zeros before turning fractBits into a
+ // FDBigInteger. The resulting whole number will be
+ // d * 2^(nFractBits-1-binExp).
+ //
+ fractBits >>>= tailZeros;
+ B2 -= nFractBits-1;
+ int common2factor = Math.min( B2, S2 );
+ B2 -= common2factor;
+ S2 -= common2factor;
+ M2 -= common2factor;
+
+ //
+ // HACK!! For exact powers of two, the next smallest number
+ // is only half as far away as we think (because the meaning of
+ // ULP changes at power-of-two bounds) for this reason, we
+ // hack M2. Hope this works.
+ //
+ if ( nFractBits == 1 ) {
+ M2 -= 1;
+ }
+
+ if ( M2 < 0 ){
+ // oops.
+ // since we cannot scale M down far enough,
+ // we must scale the other values up.
+ B2 -= M2;
+ S2 -= M2;
+ M2 = 0;
+ }
+ //
+ // Construct, Scale, iterate.
+ // Some day, we'll write a stopping test that takes
+ // account of the asymmetry of the spacing of floating-point
+ // numbers below perfect powers of 2
+ // 26 Sept 96 is not that day.
+ // So we use a symmetric test.
+ //
+ int ndigit = 0;
+ boolean low, high;
+ long lowDigitDifference;
+ int q;
+
+ //
+ // Detect the special cases where all the numbers we are about
+ // to compute will fit in int or long integers.
+ // In these cases, we will avoid doing FDBigInteger arithmetic.
+ // We use the same algorithms, except that we "normalize"
+ // our FDBigIntegers before iterating. This is to make division easier,
+ // as it makes our fist guess (quotient of high-order words)
+ // more accurate!
+ //
+ // Some day, we'll write a stopping test that takes
+ // account of the asymmetry of the spacing of floating-point
+ // numbers below perfect powers of 2
+ // 26 Sept 96 is not that day.
+ // So we use a symmetric test.
+ //
+ // binary digits needed to represent B, approx.
+ int Bbits = nFractBits + B2 + (( B5 < N_5_BITS.length )? N_5_BITS[B5] : ( B5*3 ));
+
+ // binary digits needed to represent 10*S, approx.
+ int tenSbits = S2+1 + (( (S5+1) < N_5_BITS.length )? N_5_BITS[(S5+1)] : ( (S5+1)*3 ));
+ if ( Bbits < 64 && tenSbits < 64){
+ if ( Bbits < 32 && tenSbits < 32){
+ // wa-hoo! They're all ints!
+ int b = ((int)fractBits * FDBigInteger.SMALL_5_POW[B5] ) << B2;
+ int s = FDBigInteger.SMALL_5_POW[S5] << S2;
+ int m = FDBigInteger.SMALL_5_POW[M5] << M2;
+ int tens = s * 10;
+ //
+ // Unroll the first iteration. If our decExp estimate
+ // was too high, our first quotient will be zero. In this
+ // case, we discard it and decrement decExp.
+ //
+ ndigit = 0;
+ q = b / s;
+ b = 10 * ( b % s );
+ m *= 10;
+ low = (b < m );
+ high = (b+m > tens );
+ assert q < 10 : q; // excessively large digit
+ if ( (q == 0) && ! high ){
+ // oops. Usually ignore leading zero.
+ decExp--;
+ } else {
+ digits[ndigit++] = (char)('0' + q);
+ }
+ //
+ // HACK! Java spec sez that we always have at least
+ // one digit after the . in either F- or E-form output.
+ // Thus we will need more than one digit if we're using
+ // E-form
+ //
+ if ( !isCompatibleFormat ||decExp < -3 || decExp >= 8 ){
+ high = low = false;
+ }
+ while( ! low && ! high ){
+ q = b / s;
+ b = 10 * ( b % s );
+ m *= 10;
+ assert q < 10 : q; // excessively large digit
+ if ( m > 0L ){
+ low = (b < m );
+ high = (b+m > tens );
+ } else {
+ // hack -- m might overflow!
+ // in this case, it is certainly > b,
+ // which won't
+ // and b+m > tens, too, since that has overflowed
+ // either!
+ low = true;
+ high = true;
+ }
+ digits[ndigit++] = (char)('0' + q);
+ }
+ lowDigitDifference = (b<<1) - tens;
+ exactDecimalConversion = (b == 0);
+ } else {
+ // still good! they're all longs!
+ long b = (fractBits * FDBigInteger.LONG_5_POW[B5] ) << B2;
+ long s = FDBigInteger.LONG_5_POW[S5] << S2;
+ long m = FDBigInteger.LONG_5_POW[M5] << M2;
+ long tens = s * 10L;
+ //
+ // Unroll the first iteration. If our decExp estimate
+ // was too high, our first quotient will be zero. In this
+ // case, we discard it and decrement decExp.
+ //
+ ndigit = 0;
+ q = (int) ( b / s );
+ b = 10L * ( b % s );
+ m *= 10L;
+ low = (b < m );
+ high = (b+m > tens );
+ assert q < 10 : q; // excessively large digit
+ if ( (q == 0) && ! high ){
+ // oops. Usually ignore leading zero.
+ decExp--;
+ } else {
+ digits[ndigit++] = (char)('0' + q);
+ }
+ //
+ // HACK! Java spec sez that we always have at least
+ // one digit after the . in either F- or E-form output.
+ // Thus we will need more than one digit if we're using
+ // E-form
+ //
+ if ( !isCompatibleFormat || decExp < -3 || decExp >= 8 ){
+ high = low = false;
+ }
+ while( ! low && ! high ){
+ q = (int) ( b / s );
+ b = 10 * ( b % s );
+ m *= 10;
+ assert q < 10 : q; // excessively large digit
+ if ( m > 0L ){
+ low = (b < m );
+ high = (b+m > tens );
+ } else {
+ // hack -- m might overflow!
+ // in this case, it is certainly > b,
+ // which won't
+ // and b+m > tens, too, since that has overflowed
+ // either!
+ low = true;
+ high = true;
+ }
+ digits[ndigit++] = (char)('0' + q);
+ }
+ lowDigitDifference = (b<<1) - tens;
+ exactDecimalConversion = (b == 0);
+ }
+ } else {
+ //
+ // We really must do FDBigInteger arithmetic.
+ // Fist, construct our FDBigInteger initial values.
+ //
+ FDBigInteger Sval = FDBigInteger.valueOfPow52(S5, S2);
+ int shiftBias = Sval.getNormalizationBias();
+ Sval = Sval.leftShift(shiftBias); // normalize so that division works better
+
+ FDBigInteger Bval = FDBigInteger.valueOfMulPow52(fractBits, B5, B2 + shiftBias);
+ FDBigInteger Mval = FDBigInteger.valueOfPow52(M5 + 1, M2 + shiftBias + 1);
+
+ FDBigInteger tenSval = FDBigInteger.valueOfPow52(S5 + 1, S2 + shiftBias + 1); //Sval.mult( 10 );
+ //
+ // Unroll the first iteration. If our decExp estimate
+ // was too high, our first quotient will be zero. In this
+ // case, we discard it and decrement decExp.
+ //
+ ndigit = 0;
+ q = Bval.quoRemIteration( Sval );
+ low = (Bval.cmp( Mval ) < 0);
+ high = tenSval.addAndCmp(Bval,Mval)<=0;
+
+ assert q < 10 : q; // excessively large digit
+ if ( (q == 0) && ! high ){
+ // oops. Usually ignore leading zero.
+ decExp--;
+ } else {
+ digits[ndigit++] = (char)('0' + q);
+ }
+ //
+ // HACK! Java spec sez that we always have at least
+ // one digit after the . in either F- or E-form output.
+ // Thus we will need more than one digit if we're using
+ // E-form
+ //
+ if (!isCompatibleFormat || decExp < -3 || decExp >= 8 ){
+ high = low = false;
+ }
+ while( ! low && ! high ){
+ q = Bval.quoRemIteration( Sval );
+ assert q < 10 : q; // excessively large digit
+ Mval = Mval.multBy10(); //Mval = Mval.mult( 10 );
+ low = (Bval.cmp( Mval ) < 0);
+ high = tenSval.addAndCmp(Bval,Mval)<=0;
+ digits[ndigit++] = (char)('0' + q);
+ }
+ if ( high && low ){
+ Bval = Bval.leftShift(1);
+ lowDigitDifference = Bval.cmp(tenSval);
+ } else {
+ lowDigitDifference = 0L; // this here only for flow analysis!
+ }
+ exactDecimalConversion = (Bval.cmp( FDBigInteger.ZERO ) == 0);
+ }
+ this.decExponent = decExp+1;
+ this.firstDigitIndex = 0;
+ this.nDigits = ndigit;
+ //
+ // Last digit gets rounded based on stopping condition.
+ //
+ if ( high ){
+ if ( low ){
+ if ( lowDigitDifference == 0L ){
+ // it's a tie!
+ // choose based on which digits we like.
+ if ( (digits[firstDigitIndex+nDigits-1]&1) != 0 ) {
+ roundup();
+ }
+ } else if ( lowDigitDifference > 0 ){
+ roundup();
+ }
+ } else {
+ roundup();
+ }
+ }
+ }
+
+ // add one to the least significant digit.
+ // in the unlikely event there is a carry out, deal with it.
+ // assert that this will only happen where there
+ // is only one digit, e.g. (float)1e-44 seems to do it.
+ //
+ private void roundup() {
+ int i = (firstDigitIndex + nDigits - 1);
+ int q = digits[i];
+ if (q == '9') {
+ while (q == '9' && i > firstDigitIndex) {
+ digits[i] = '0';
+ q = digits[--i];
+ }
+ if (q == '9') {
+ // carryout! High-order 1, rest 0s, larger exp.
+ decExponent += 1;
+ digits[firstDigitIndex] = '1';
+ return;
+ }
+ // else fall through.
+ }
+ digits[i] = (char) (q + 1);
+ decimalDigitsRoundedUp = true;
+ }
+
+ /**
+ * Estimate decimal exponent. (If it is small-ish,
+ * we could double-check.)
+ *
+ * First, scale the mantissa bits such that 1 <= d2 < 2.
+ * We are then going to estimate
+ * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5)
+ * and so we can estimate
+ * log10(d) ~=~ log10(d2) + binExp * log10(2)
+ * take the floor and call it decExp.
+ */
+ static int estimateDecExp(long fractBits, int binExp) {
+ double d2 = Double.longBitsToDouble( EXP_ONE | ( fractBits & DoubleConsts.SIGNIF_BIT_MASK ) );
+ double d = (d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981;
+ long dBits = Double.doubleToRawLongBits(d); //can't be NaN here so use raw
+ int exponent = (int)((dBits & DoubleConsts.EXP_BIT_MASK) >> EXP_SHIFT) - DoubleConsts.EXP_BIAS;
+ boolean isNegative = (dBits & DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign
+ if(exponent>=0 && exponent<52) { // hot path
+ long mask = DoubleConsts.SIGNIF_BIT_MASK >> exponent;
+ int r = (int)(( (dBits&DoubleConsts.SIGNIF_BIT_MASK) | FRACT_HOB )>>(EXP_SHIFT-exponent));
+ return isNegative ? (((mask & dBits) == 0L ) ? -r : -r-1 ) : r;
+ } else if (exponent < 0) {
+ return (((dBits&~DoubleConsts.SIGN_BIT_MASK) == 0) ? 0 :
+ ( (isNegative) ? -1 : 0) );
+ } else { //if (exponent >= 52)
+ return (int)d;
+ }
+ }
+
+ private static int insignificantDigits(int insignificant) {
+ int i;
+ for ( i = 0; insignificant >= 10L; i++ ) {
+ insignificant /= 10L;
+ }
+ return i;
+ }
+
+ /**
+ * Calculates
+ * <pre>
+ * insignificantDigitsForPow2(v) == insignificantDigits(1L<<v)
+ * </pre>
+ */
+ private static int insignificantDigitsForPow2(int p2) {
+ if(p2>1 && p2 < insignificantDigitsNumber.length) {
+ return insignificantDigitsNumber[p2];
+ }
+ return 0;
+ }
+
+ /**
+ * If insignificant==(1L << ixd)
+ * i = insignificantDigitsNumber[idx] is the same as:
+ * int i;
+ * for ( i = 0; insignificant >= 10L; i++ )
+ * insignificant /= 10L;
+ */
+ private static int[] insignificantDigitsNumber = {
+ 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3,
+ 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
+ 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11,
+ 12, 12, 12, 12, 13, 13, 13, 14, 14, 14,
+ 15, 15, 15, 15, 16, 16, 16, 17, 17, 17,
+ 18, 18, 18, 19
+ };
+
+ // approximately ceil( log2( long5pow[i] ) )
+ private static final int[] N_5_BITS = {
+ 0,
+ 3,
+ 5,
+ 7,
+ 10,
+ 12,
+ 14,
+ 17,
+ 19,
+ 21,
+ 24,
+ 26,
+ 28,
+ 31,
+ 33,
+ 35,
+ 38,
+ 40,
+ 42,
+ 45,
+ 47,
+ 49,
+ 52,
+ 54,
+ 56,
+ 59,
+ 61,
+ };
+
+ private int getChars(char[] result) {
+ assert nDigits <= 19 : nDigits; // generous bound on size of nDigits
+ int i = 0;
+ if (isNegative) {
+ result[0] = '-';
+ i = 1;
+ }
+ if (decExponent > 0 && decExponent < 8) {
+ // print digits.digits.
+ int charLength = Math.min(nDigits, decExponent);
+ System.arraycopy(digits, firstDigitIndex, result, i, charLength);
+ i += charLength;
+ if (charLength < decExponent) {
+ charLength = decExponent - charLength;
+ Arrays.fill(result,i,i+charLength,'0');
+ i += charLength;
+ result[i++] = '.';
+ result[i++] = '0';
+ } else {
+ result[i++] = '.';
+ if (charLength < nDigits) {
+ int t = nDigits - charLength;
+ System.arraycopy(digits, firstDigitIndex+charLength, result, i, t);
+ i += t;
+ } else {
+ result[i++] = '0';
+ }
+ }
+ } else if (decExponent <= 0 && decExponent > -3) {
+ result[i++] = '0';
+ result[i++] = '.';
+ if (decExponent != 0) {
+ Arrays.fill(result, i, i-decExponent, '0');
+ i -= decExponent;
+ }
+ System.arraycopy(digits, firstDigitIndex, result, i, nDigits);
+ i += nDigits;
+ } else {
+ result[i++] = digits[firstDigitIndex];
+ result[i++] = '.';
+ if (nDigits > 1) {
+ System.arraycopy(digits, firstDigitIndex+1, result, i, nDigits - 1);
+ i += nDigits - 1;
+ } else {
+ result[i++] = '0';
+ }
+ result[i++] = 'E';
+ int e;
+ if (decExponent <= 0) {
+ result[i++] = '-';
+ e = -decExponent + 1;
+ } else {
+ e = decExponent - 1;
+ }
+ // decExponent has 1, 2, or 3, digits
+ if (e <= 9) {
+ result[i++] = (char) (e + '0');
+ } else if (e <= 99) {
+ result[i++] = (char) (e / 10 + '0');
+ result[i++] = (char) (e % 10 + '0');
+ } else {
+ result[i++] = (char) (e / 100 + '0');
+ e %= 100;
+ result[i++] = (char) (e / 10 + '0');
+ result[i++] = (char) (e % 10 + '0');
+ }
+ }
+ return i;
+ }
+
+ }
+
+ private static final ThreadLocal<BinaryToASCIIBuffer> threadLocalBinaryToASCIIBuffer =
+ new ThreadLocal<BinaryToASCIIBuffer>() {
+ @Override
+ protected BinaryToASCIIBuffer initialValue() {
+ return new BinaryToASCIIBuffer();
+ }
+ };
+
+ private static BinaryToASCIIBuffer getBinaryToASCIIBuffer() {
+ return threadLocalBinaryToASCIIBuffer.get();
+ }
+
+ /**
+ * A converter which can process an ASCII <code>String</code> representation
+ * of a single or double precision floating point value into a
+ * <code>float</code> or a <code>double</code>.
+ */
+ interface ASCIIToBinaryConverter {
+
+ double doubleValue();
+
+ float floatValue();
+
+ }
+
+ /**
+ * A <code>ASCIIToBinaryConverter</code> container for a <code>double</code>.
+ */
+ static class PreparedASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
+ private final double doubleVal;
+ private final float floatVal;
+
+ public PreparedASCIIToBinaryBuffer(double doubleVal, float floatVal) {
+ this.doubleVal = doubleVal;
+ this.floatVal = floatVal;
+ }
+
+ @Override
+ public double doubleValue() {
+ return doubleVal;
+ }
+
+ @Override
+ public float floatValue() {
+ return floatVal;
+ }
+ }
+
+ static final ASCIIToBinaryConverter A2BC_POSITIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
+ static final ASCIIToBinaryConverter A2BC_NEGATIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
+ static final ASCIIToBinaryConverter A2BC_NOT_A_NUMBER = new PreparedASCIIToBinaryBuffer(Double.NaN, Float.NaN);
+ static final ASCIIToBinaryConverter A2BC_POSITIVE_ZERO = new PreparedASCIIToBinaryBuffer(0.0d, 0.0f);
+ static final ASCIIToBinaryConverter A2BC_NEGATIVE_ZERO = new PreparedASCIIToBinaryBuffer(-0.0d, -0.0f);
+
+ /**
+ * A buffered implementation of <code>ASCIIToBinaryConverter</code>.
+ */
+ static class ASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
+ boolean isNegative;
+ int decExponent;
+ char digits[];
+ int nDigits;
+
+ ASCIIToBinaryBuffer( boolean negSign, int decExponent, char[] digits, int n)
+ {
+ this.isNegative = negSign;
+ this.decExponent = decExponent;
+ this.digits = digits;
+ this.nDigits = n;
+ }
+
+ /**
+ * Takes a FloatingDecimal, which we presumably just scanned in,
+ * and finds out what its value is, as a double.
+ *
+ * AS A SIDE EFFECT, SET roundDir TO INDICATE PREFERRED
+ * ROUNDING DIRECTION in case the result is really destined
+ * for a single-precision float.
+ */
+ @Override
+ public double doubleValue() {
+ int kDigits = Math.min(nDigits, MAX_DECIMAL_DIGITS + 1);
+ //
+ // convert the lead kDigits to a long integer.
+ //
+ // (special performance hack: start to do it using int)
+ int iValue = (int) digits[0] - (int) '0';
+ int iDigits = Math.min(kDigits, INT_DECIMAL_DIGITS);
+ for (int i = 1; i < iDigits; i++) {
+ iValue = iValue * 10 + (int) digits[i] - (int) '0';
+ }
+ long lValue = (long) iValue;
+ for (int i = iDigits; i < kDigits; i++) {
+ lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
+ }
+ double dValue = (double) lValue;
+ int exp = decExponent - kDigits;
+ //
+ // lValue now contains a long integer with the value of
+ // the first kDigits digits of the number.
+ // dValue contains the (double) of the same.
+ //
+
+ if (nDigits <= MAX_DECIMAL_DIGITS) {
+ //
+ // possibly an easy case.
+ // We know that the digits can be represented
+ // exactly. And if the exponent isn't too outrageous,
+ // the whole thing can be done with one operation,
+ // thus one rounding error.
+ // Note that all our constructors trim all leading and
+ // trailing zeros, so simple values (including zero)
+ // will always end up here
+ //
+ if (exp == 0 || dValue == 0.0) {
+ return (isNegative) ? -dValue : dValue; // small floating integer
+ }
+ else if (exp >= 0) {
+ if (exp <= MAX_SMALL_TEN) {
+ //
+ // Can get the answer with one operation,
+ // thus one roundoff.
+ //
+ double rValue = dValue * SMALL_10_POW[exp];
+ return (isNegative) ? -rValue : rValue;
+ }
+ int slop = MAX_DECIMAL_DIGITS - kDigits;
+ if (exp <= MAX_SMALL_TEN + slop) {
+ //
+ // We can multiply dValue by 10^(slop)
+ // and it is still "small" and exact.
+ // Then we can multiply by 10^(exp-slop)
+ // with one rounding.
+ //
+ dValue *= SMALL_10_POW[slop];
+ double rValue = dValue * SMALL_10_POW[exp - slop];
+ return (isNegative) ? -rValue : rValue;
+ }
+ //
+ // Else we have a hard case with a positive exp.
+ //
+ } else {
+ if (exp >= -MAX_SMALL_TEN) {
+ //
+ // Can get the answer in one division.
+ //
+ double rValue = dValue / SMALL_10_POW[-exp];
+ return (isNegative) ? -rValue : rValue;
+ }
+ //
+ // Else we have a hard case with a negative exp.
+ //
+ }
+ }
+
+ //
+ // Harder cases:
+ // The sum of digits plus exponent is greater than
+ // what we think we can do with one error.
+ //
+ // Start by approximating the right answer by,
+ // naively, scaling by powers of 10.
+ //
+ if (exp > 0) {
+ if (decExponent > MAX_DECIMAL_EXPONENT + 1) {
+ //
+ // Lets face it. This is going to be
+ // Infinity. Cut to the chase.
+ //
+ return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+ }
+ if ((exp & 15) != 0) {
+ dValue *= SMALL_10_POW[exp & 15];
+ }
+ if ((exp >>= 4) != 0) {
+ int j;
+ for (j = 0; exp > 1; j++, exp >>= 1) {
+ if ((exp & 1) != 0) {
+ dValue *= BIG_10_POW[j];
+ }
+ }
+ //
+ // The reason for the weird exp > 1 condition
+ // in the above loop was so that the last multiply
+ // would get unrolled. We handle it here.
+ // It could overflow.
+ //
+ double t = dValue * BIG_10_POW[j];
+ if (Double.isInfinite(t)) {
+ //
+ // It did overflow.
+ // Look more closely at the result.
+ // If the exponent is just one too large,
+ // then use the maximum finite as our estimate
+ // value. Else call the result infinity
+ // and punt it.
+ // ( I presume this could happen because
+ // rounding forces the result here to be
+ // an ULP or two larger than
+ // Double.MAX_VALUE ).
+ //
+ t = dValue / 2.0;
+ t *= BIG_10_POW[j];
+ if (Double.isInfinite(t)) {
+ return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+ }
+ t = Double.MAX_VALUE;
+ }
+ dValue = t;
+ }
+ } else if (exp < 0) {
+ exp = -exp;
+ if (decExponent < MIN_DECIMAL_EXPONENT - 1) {
+ //
+ // Lets face it. This is going to be
+ // zero. Cut to the chase.
+ //
+ return (isNegative) ? -0.0 : 0.0;
+ }
+ if ((exp & 15) != 0) {
+ dValue /= SMALL_10_POW[exp & 15];
+ }
+ if ((exp >>= 4) != 0) {
+ int j;
+ for (j = 0; exp > 1; j++, exp >>= 1) {
+ if ((exp & 1) != 0) {
+ dValue *= TINY_10_POW[j];
+ }
+ }
+ //
+ // The reason for the weird exp > 1 condition
+ // in the above loop was so that the last multiply
+ // would get unrolled. We handle it here.
+ // It could underflow.
+ //
+ double t = dValue * TINY_10_POW[j];
+ if (t == 0.0) {
+ //
+ // It did underflow.
+ // Look more closely at the result.
+ // If the exponent is just one too small,
+ // then use the minimum finite as our estimate
+ // value. Else call the result 0.0
+ // and punt it.
+ // ( I presume this could happen because
+ // rounding forces the result here to be
+ // an ULP or two less than
+ // Double.MIN_VALUE ).
+ //
+ t = dValue * 2.0;
+ t *= TINY_10_POW[j];
+ if (t == 0.0) {
+ return (isNegative) ? -0.0 : 0.0;
+ }
+ t = Double.MIN_VALUE;
+ }
+ dValue = t;
+ }
+ }
+
+ //
+ // dValue is now approximately the result.
+ // The hard part is adjusting it, by comparison
+ // with FDBigInteger arithmetic.
+ // Formulate the EXACT big-number result as
+ // bigD0 * 10^exp
+ //
+ if (nDigits > MAX_NDIGITS) {
+ nDigits = MAX_NDIGITS + 1;
+ digits[MAX_NDIGITS] = '1';
+ }
+ FDBigInteger bigD0 = new FDBigInteger(lValue, digits, kDigits, nDigits);
+ exp = decExponent - nDigits;
+
+ long ieeeBits = Double.doubleToRawLongBits(dValue); // IEEE-754 bits of double candidate
+ final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
+ final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
+ bigD0 = bigD0.multByPow52(D5, 0);
+ bigD0.makeImmutable(); // prevent bigD0 modification inside correctionLoop
+ FDBigInteger bigD = null;
+ int prevD2 = 0;
+
+ correctionLoop:
+ while (true) {
+ // here ieeeBits can't be NaN, Infinity or zero
+ int binexp = (int) (ieeeBits >>> EXP_SHIFT);
+ long bigBbits = ieeeBits & DoubleConsts.SIGNIF_BIT_MASK;
+ if (binexp > 0) {
+ bigBbits |= FRACT_HOB;
+ } else { // Normalize denormalized numbers.
+ assert bigBbits != 0L : bigBbits; // doubleToBigInt(0.0)
+ int leadingZeros = Long.numberOfLeadingZeros(bigBbits);
+ int shift = leadingZeros - (63 - EXP_SHIFT);
+ bigBbits <<= shift;
+ binexp = 1 - shift;
+ }
+ binexp -= DoubleConsts.EXP_BIAS;
+ int lowOrderZeros = Long.numberOfTrailingZeros(bigBbits);
+ bigBbits >>>= lowOrderZeros;
+ final int bigIntExp = binexp - EXP_SHIFT + lowOrderZeros;
+ final int bigIntNBits = EXP_SHIFT + 1 - lowOrderZeros;
+
+ //
+ // Scale bigD, bigB appropriately for
+ // big-integer operations.
+ // Naively, we multiply by powers of ten
+ // and powers of two. What we actually do
+ // is keep track of the powers of 5 and
+ // powers of 2 we would use, then factor out
+ // common divisors before doing the work.
+ //
+ int B2 = B5; // powers of 2 in bigB
+ int D2 = D5; // powers of 2 in bigD
+ int Ulp2; // powers of 2 in halfUlp.
+ if (bigIntExp >= 0) {
+ B2 += bigIntExp;
+ } else {
+ D2 -= bigIntExp;
+ }
+ Ulp2 = B2;
+ // shift bigB and bigD left by a number s. t.
+ // halfUlp is still an integer.
+ int hulpbias;
+ if (binexp <= -DoubleConsts.EXP_BIAS) {
+ // This is going to be a denormalized number
+ // (if not actually zero).
+ // half an ULP is at 2^-(DoubleConsts.EXP_BIAS+EXP_SHIFT+1)
+ hulpbias = binexp + lowOrderZeros + DoubleConsts.EXP_BIAS;
+ } else {
+ hulpbias = 1 + lowOrderZeros;
+ }
+ B2 += hulpbias;
+ D2 += hulpbias;
+ // if there are common factors of 2, we might just as well
+ // factor them out, as they add nothing useful.
+ int common2 = Math.min(B2, Math.min(D2, Ulp2));
+ B2 -= common2;
+ D2 -= common2;
+ Ulp2 -= common2;
+ // do multiplications by powers of 5 and 2
+ FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
+ if (bigD == null || prevD2 != D2) {
+ bigD = bigD0.leftShift(D2);
+ prevD2 = D2;
+ }
+ //
+ // to recap:
+ // bigB is the scaled-big-int version of our floating-point
+ // candidate.
+ // bigD is the scaled-big-int version of the exact value
+ // as we understand it.
+ // halfUlp is 1/2 an ulp of bigB, except for special cases
+ // of exact powers of 2
+ //
+ // the plan is to compare bigB with bigD, and if the difference
+ // is less than halfUlp, then we're satisfied. Otherwise,
+ // use the ratio of difference to halfUlp to calculate a fudge
+ // factor to add to the floating value, then go 'round again.
+ //
+ FDBigInteger diff;
+ int cmpResult;
+ boolean overvalue;
+ if ((cmpResult = bigB.cmp(bigD)) > 0) {
+ overvalue = true; // our candidate is too big.
+ diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
+ if ((bigIntNBits == 1) && (bigIntExp > -DoubleConsts.EXP_BIAS + 1)) {
+ // candidate is a normalized exact power of 2 and
+ // is too big (larger than Double.MIN_NORMAL). We will be subtracting.
+ // For our purposes, ulp is the ulp of the
+ // next smaller range.
+ Ulp2 -= 1;
+ if (Ulp2 < 0) {
+ // rats. Cannot de-scale ulp this far.
+ // must scale diff in other direction.
+ Ulp2 = 0;
+ diff = diff.leftShift(1);
+ }
+ }
+ } else if (cmpResult < 0) {
+ overvalue = false; // our candidate is too small.
+ diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
+ } else {
+ // the candidate is exactly right!
+ // this happens with surprising frequency
+ break correctionLoop;
+ }
+ cmpResult = diff.cmpPow52(B5, Ulp2);
+ if ((cmpResult) < 0) {
+ // difference is small.
+ // this is close enough
+ break correctionLoop;
+ } else if (cmpResult == 0) {
+ // difference is exactly half an ULP
+ // round to some other value maybe, then finish
+ if ((ieeeBits & 1) != 0) { // half ties to even
+ ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
+ }
+ break correctionLoop;
+ } else {
+ // difference is non-trivial.
+ // could scale addend by ratio of difference to
+ // halfUlp here, if we bothered to compute that difference.
+ // Most of the time ( I hope ) it is about 1 anyway.
+ ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
+ if (ieeeBits == 0 || ieeeBits == DoubleConsts.EXP_BIT_MASK) { // 0.0 or Double.POSITIVE_INFINITY
+ break correctionLoop; // oops. Fell off end of range.
+ }
+ continue; // try again.
+ }
+
+ }
+ if (isNegative) {
+ ieeeBits |= DoubleConsts.SIGN_BIT_MASK;
+ }
+ return Double.longBitsToDouble(ieeeBits);
+ }
+
+ /**
+ * Takes a FloatingDecimal, which we presumably just scanned in,
+ * and finds out what its value is, as a float.
+ * This is distinct from doubleValue() to avoid the extremely
+ * unlikely case of a double rounding error, wherein the conversion
+ * to double has one rounding error, and the conversion of that double
+ * to a float has another rounding error, IN THE WRONG DIRECTION,
+ * ( because of the preference to a zero low-order bit ).
+ */
+ @Override
+ public float floatValue() {
+ int kDigits = Math.min(nDigits, SINGLE_MAX_DECIMAL_DIGITS + 1);
+ //
+ // convert the lead kDigits to an integer.
+ //
+ int iValue = (int) digits[0] - (int) '0';
+ for (int i = 1; i < kDigits; i++) {
+ iValue = iValue * 10 + (int) digits[i] - (int) '0';
+ }
+ float fValue = (float) iValue;
+ int exp = decExponent - kDigits;
+ //
+ // iValue now contains an integer with the value of
+ // the first kDigits digits of the number.
+ // fValue contains the (float) of the same.
+ //
+
+ if (nDigits <= SINGLE_MAX_DECIMAL_DIGITS) {
+ //
+ // possibly an easy case.
+ // We know that the digits can be represented
+ // exactly. And if the exponent isn't too outrageous,
+ // the whole thing can be done with one operation,
+ // thus one rounding error.
+ // Note that all our constructors trim all leading and
+ // trailing zeros, so simple values (including zero)
+ // will always end up here.
+ //
+ if (exp == 0 || fValue == 0.0f) {
+ return (isNegative) ? -fValue : fValue; // small floating integer
+ } else if (exp >= 0) {
+ if (exp <= SINGLE_MAX_SMALL_TEN) {
+ //
+ // Can get the answer with one operation,
+ // thus one roundoff.
+ //
+ fValue *= SINGLE_SMALL_10_POW[exp];
+ return (isNegative) ? -fValue : fValue;
+ }
+ int slop = SINGLE_MAX_DECIMAL_DIGITS - kDigits;
+ if (exp <= SINGLE_MAX_SMALL_TEN + slop) {
+ //
+ // We can multiply fValue by 10^(slop)
+ // and it is still "small" and exact.
+ // Then we can multiply by 10^(exp-slop)
+ // with one rounding.
+ //
+ fValue *= SINGLE_SMALL_10_POW[slop];
+ fValue *= SINGLE_SMALL_10_POW[exp - slop];
+ return (isNegative) ? -fValue : fValue;
+ }
+ //
+ // Else we have a hard case with a positive exp.
+ //
+ } else {
+ if (exp >= -SINGLE_MAX_SMALL_TEN) {
+ //
+ // Can get the answer in one division.
+ //
+ fValue /= SINGLE_SMALL_10_POW[-exp];
+ return (isNegative) ? -fValue : fValue;
+ }
+ //
+ // Else we have a hard case with a negative exp.
+ //
+ }
+ } else if ((decExponent >= nDigits) && (nDigits + decExponent <= MAX_DECIMAL_DIGITS)) {
+ //
+ // In double-precision, this is an exact floating integer.
+ // So we can compute to double, then shorten to float
+ // with one round, and get the right answer.
+ //
+ // First, finish accumulating digits.
+ // Then convert that integer to a double, multiply
+ // by the appropriate power of ten, and convert to float.
+ //
+ long lValue = (long) iValue;
+ for (int i = kDigits; i < nDigits; i++) {
+ lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
+ }
+ double dValue = (double) lValue;
+ exp = decExponent - nDigits;
+ dValue *= SMALL_10_POW[exp];
+ fValue = (float) dValue;
+ return (isNegative) ? -fValue : fValue;
+
+ }
+ //
+ // Harder cases:
+ // The sum of digits plus exponent is greater than
+ // what we think we can do with one error.
+ //
+ // Start by approximating the right answer by,
+ // naively, scaling by powers of 10.
+ // Scaling uses doubles to avoid overflow/underflow.
+ //
+ double dValue = fValue;
+ if (exp > 0) {
+ if (decExponent > SINGLE_MAX_DECIMAL_EXPONENT + 1) {
+ //
+ // Lets face it. This is going to be
+ // Infinity. Cut to the chase.
+ //
+ return (isNegative) ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
+ }
+ if ((exp & 15) != 0) {
+ dValue *= SMALL_10_POW[exp & 15];
+ }
+ if ((exp >>= 4) != 0) {
+ int j;
+ for (j = 0; exp > 0; j++, exp >>= 1) {
+ if ((exp & 1) != 0) {
+ dValue *= BIG_10_POW[j];
+ }
+ }
+ }
+ } else if (exp < 0) {
+ exp = -exp;
+ if (decExponent < SINGLE_MIN_DECIMAL_EXPONENT - 1) {
+ //
+ // Lets face it. This is going to be
+ // zero. Cut to the chase.
+ //
+ return (isNegative) ? -0.0f : 0.0f;
+ }
+ if ((exp & 15) != 0) {
+ dValue /= SMALL_10_POW[exp & 15];
+ }
+ if ((exp >>= 4) != 0) {
+ int j;
+ for (j = 0; exp > 0; j++, exp >>= 1) {
+ if ((exp & 1) != 0) {
+ dValue *= TINY_10_POW[j];
+ }
+ }
+ }
+ }
+ fValue = Math.max(Float.MIN_VALUE, Math.min(Float.MAX_VALUE, (float) dValue));
+
+ //
+ // fValue is now approximately the result.
+ // The hard part is adjusting it, by comparison
+ // with FDBigInteger arithmetic.
+ // Formulate the EXACT big-number result as
+ // bigD0 * 10^exp
+ //
+ if (nDigits > SINGLE_MAX_NDIGITS) {
+ nDigits = SINGLE_MAX_NDIGITS + 1;
+ digits[SINGLE_MAX_NDIGITS] = '1';
+ }
+ FDBigInteger bigD0 = new FDBigInteger(iValue, digits, kDigits, nDigits);
+ exp = decExponent - nDigits;
+
+ int ieeeBits = Float.floatToRawIntBits(fValue); // IEEE-754 bits of float candidate
+ final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
+ final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
+ bigD0 = bigD0.multByPow52(D5, 0);
+ bigD0.makeImmutable(); // prevent bigD0 modification inside correctionLoop
+ FDBigInteger bigD = null;
+ int prevD2 = 0;
+
+ correctionLoop:
+ while (true) {
+ // here ieeeBits can't be NaN, Infinity or zero
+ int binexp = ieeeBits >>> SINGLE_EXP_SHIFT;
+ int bigBbits = ieeeBits & FloatConsts.SIGNIF_BIT_MASK;
+ if (binexp > 0) {
+ bigBbits |= SINGLE_FRACT_HOB;
+ } else { // Normalize denormalized numbers.
+ assert bigBbits != 0 : bigBbits; // floatToBigInt(0.0)
+ int leadingZeros = Integer.numberOfLeadingZeros(bigBbits);
+ int shift = leadingZeros - (31 - SINGLE_EXP_SHIFT);
+ bigBbits <<= shift;
+ binexp = 1 - shift;
+ }
+ binexp -= FloatConsts.EXP_BIAS;
+ int lowOrderZeros = Integer.numberOfTrailingZeros(bigBbits);
+ bigBbits >>>= lowOrderZeros;
+ final int bigIntExp = binexp - SINGLE_EXP_SHIFT + lowOrderZeros;
+ final int bigIntNBits = SINGLE_EXP_SHIFT + 1 - lowOrderZeros;
+
+ //
+ // Scale bigD, bigB appropriately for
+ // big-integer operations.
+ // Naively, we multiply by powers of ten
+ // and powers of two. What we actually do
+ // is keep track of the powers of 5 and
+ // powers of 2 we would use, then factor out
+ // common divisors before doing the work.
+ //
+ int B2 = B5; // powers of 2 in bigB
+ int D2 = D5; // powers of 2 in bigD
+ int Ulp2; // powers of 2 in halfUlp.
+ if (bigIntExp >= 0) {
+ B2 += bigIntExp;
+ } else {
+ D2 -= bigIntExp;
+ }
+ Ulp2 = B2;
+ // shift bigB and bigD left by a number s. t.
+ // halfUlp is still an integer.
+ int hulpbias;
+ if (binexp <= -FloatConsts.EXP_BIAS) {
+ // This is going to be a denormalized number
+ // (if not actually zero).
+ // half an ULP is at 2^-(FloatConsts.EXP_BIAS+SINGLE_EXP_SHIFT+1)
+ hulpbias = binexp + lowOrderZeros + FloatConsts.EXP_BIAS;
+ } else {
+ hulpbias = 1 + lowOrderZeros;
+ }
+ B2 += hulpbias;
+ D2 += hulpbias;
+ // if there are common factors of 2, we might just as well
+ // factor them out, as they add nothing useful.
+ int common2 = Math.min(B2, Math.min(D2, Ulp2));
+ B2 -= common2;
+ D2 -= common2;
+ Ulp2 -= common2;
+ // do multiplications by powers of 5 and 2
+ FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
+ if (bigD == null || prevD2 != D2) {
+ bigD = bigD0.leftShift(D2);
+ prevD2 = D2;
+ }
+ //
+ // to recap:
+ // bigB is the scaled-big-int version of our floating-point
+ // candidate.
+ // bigD is the scaled-big-int version of the exact value
+ // as we understand it.
+ // halfUlp is 1/2 an ulp of bigB, except for special cases
+ // of exact powers of 2
+ //
+ // the plan is to compare bigB with bigD, and if the difference
+ // is less than halfUlp, then we're satisfied. Otherwise,
+ // use the ratio of difference to halfUlp to calculate a fudge
+ // factor to add to the floating value, then go 'round again.
+ //
+ FDBigInteger diff;
+ int cmpResult;
+ boolean overvalue;
+ if ((cmpResult = bigB.cmp(bigD)) > 0) {
+ overvalue = true; // our candidate is too big.
+ diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
+ if ((bigIntNBits == 1) && (bigIntExp > -FloatConsts.EXP_BIAS + 1)) {
+ // candidate is a normalized exact power of 2 and
+ // is too big (larger than Float.MIN_NORMAL). We will be subtracting.
+ // For our purposes, ulp is the ulp of the
+ // next smaller range.
+ Ulp2 -= 1;
+ if (Ulp2 < 0) {
+ // rats. Cannot de-scale ulp this far.
+ // must scale diff in other direction.
+ Ulp2 = 0;
+ diff = diff.leftShift(1);
+ }
+ }
+ } else if (cmpResult < 0) {
+ overvalue = false; // our candidate is too small.
+ diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
+ } else {
+ // the candidate is exactly right!
+ // this happens with surprising frequency
+ break correctionLoop;
+ }
+ cmpResult = diff.cmpPow52(B5, Ulp2);
+ if ((cmpResult) < 0) {
+ // difference is small.
+ // this is close enough
+ break correctionLoop;
+ } else if (cmpResult == 0) {
+ // difference is exactly half an ULP
+ // round to some other value maybe, then finish
+ if ((ieeeBits & 1) != 0) { // half ties to even
+ ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
+ }
+ break correctionLoop;
+ } else {
+ // difference is non-trivial.
+ // could scale addend by ratio of difference to
+ // halfUlp here, if we bothered to compute that difference.
+ // Most of the time ( I hope ) it is about 1 anyway.
+ ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
+ if (ieeeBits == 0 || ieeeBits == FloatConsts.EXP_BIT_MASK) { // 0.0 or Float.POSITIVE_INFINITY
+ break correctionLoop; // oops. Fell off end of range.
+ }
+ continue; // try again.
+ }
+
+ }
+ if (isNegative) {
+ ieeeBits |= FloatConsts.SIGN_BIT_MASK;
+ }
+ return Float.intBitsToFloat(ieeeBits);
+ }
+
+
+ /**
+ * All the positive powers of 10 that can be
+ * represented exactly in double/float.
+ */
+ private static final double[] SMALL_10_POW = {
+ 1.0e0,
+ 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5,
+ 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10,
+ 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15,
+ 1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20,
+ 1.0e21, 1.0e22
+ };
+
+ private static final float[] SINGLE_SMALL_10_POW = {
+ 1.0e0f,
+ 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
+ 1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
+ };
+
+ private static final double[] BIG_10_POW = {
+ 1e16, 1e32, 1e64, 1e128, 1e256 };
+ private static final double[] TINY_10_POW = {
+ 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 };
+
+ private static final int MAX_SMALL_TEN = SMALL_10_POW.length-1;
+ private static final int SINGLE_MAX_SMALL_TEN = SINGLE_SMALL_10_POW.length-1;
+
+ }
+
+ /**
+ * Returns a <code>BinaryToASCIIConverter</code> for a <code>double</code>.
+ * The returned object is a <code>ThreadLocal</code> variable of this class.
+ *
+ * @param d The double precision value to convert.
+ * @return The converter.
+ */
+ public static BinaryToASCIIConverter getBinaryToASCIIConverter(double d) {
+ return getBinaryToASCIIConverter(d, true);
+ }
+
+ /**
+ * Returns a <code>BinaryToASCIIConverter</code> for a <code>double</code>.
+ * The returned object is a <code>ThreadLocal</code> variable of this class.
+ *
+ * @param d The double precision value to convert.
+ * @param isCompatibleFormat
+ * @return The converter.
+ */
+ static BinaryToASCIIConverter getBinaryToASCIIConverter(double d, boolean isCompatibleFormat) {
+ long dBits = Double.doubleToRawLongBits(d);
+ boolean isNegative = (dBits&DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign
+ long fractBits = dBits & DoubleConsts.SIGNIF_BIT_MASK;
+ int binExp = (int)( (dBits&DoubleConsts.EXP_BIT_MASK) >> EXP_SHIFT );
+ // Discover obvious special cases of NaN and Infinity.
+ if ( binExp == (int)(DoubleConsts.EXP_BIT_MASK>>EXP_SHIFT) ) {
+ if ( fractBits == 0L ){
+ return isNegative ? B2AC_NEGATIVE_INFINITY : B2AC_POSITIVE_INFINITY;
+ } else {
+ return B2AC_NOT_A_NUMBER;
+ }
+ }
+ // Finish unpacking
+ // Normalize denormalized numbers.
+ // Insert assumed high-order bit for normalized numbers.
+ // Subtract exponent bias.
+ int nSignificantBits;
+ if ( binExp == 0 ){
+ if ( fractBits == 0L ){
+ // not a denorm, just a 0!
+ return isNegative ? B2AC_NEGATIVE_ZERO : B2AC_POSITIVE_ZERO;
+ }
+ int leadingZeros = Long.numberOfLeadingZeros(fractBits);
+ int shift = leadingZeros-(63-EXP_SHIFT);
+ fractBits <<= shift;
+ binExp = 1 - shift;
+ nSignificantBits = 64-leadingZeros; // recall binExp is - shift count.
+ } else {
+ fractBits |= FRACT_HOB;
+ nSignificantBits = EXP_SHIFT+1;
+ }
+ binExp -= DoubleConsts.EXP_BIAS;
+ BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer();
+ buf.setSign(isNegative);
+ // call the routine that actually does all the hard work.
+ buf.dtoa(binExp, fractBits, nSignificantBits, isCompatibleFormat);
+ return buf;
+ }
+
+ private static BinaryToASCIIConverter getBinaryToASCIIConverter(float f) {
+ int fBits = Float.floatToRawIntBits( f );
+ boolean isNegative = (fBits&FloatConsts.SIGN_BIT_MASK) != 0;
+ int fractBits = fBits&FloatConsts.SIGNIF_BIT_MASK;
+ int binExp = (fBits&FloatConsts.EXP_BIT_MASK) >> SINGLE_EXP_SHIFT;
+ // Discover obvious special cases of NaN and Infinity.
+ if ( binExp == (FloatConsts.EXP_BIT_MASK>>SINGLE_EXP_SHIFT) ) {
+ if ( fractBits == 0L ){
+ return isNegative ? B2AC_NEGATIVE_INFINITY : B2AC_POSITIVE_INFINITY;
+ } else {
+ return B2AC_NOT_A_NUMBER;
+ }
+ }
+ // Finish unpacking
+ // Normalize denormalized numbers.
+ // Insert assumed high-order bit for normalized numbers.
+ // Subtract exponent bias.
+ int nSignificantBits;
+ if ( binExp == 0 ){
+ if ( fractBits == 0 ){
+ // not a denorm, just a 0!
+ return isNegative ? B2AC_NEGATIVE_ZERO : B2AC_POSITIVE_ZERO;
+ }
+ int leadingZeros = Integer.numberOfLeadingZeros(fractBits);
+ int shift = leadingZeros-(31-SINGLE_EXP_SHIFT);
+ fractBits <<= shift;
+ binExp = 1 - shift;
+ nSignificantBits = 32 - leadingZeros; // recall binExp is - shift count.
+ } else {
+ fractBits |= SINGLE_FRACT_HOB;
+ nSignificantBits = SINGLE_EXP_SHIFT+1;
+ }
+ binExp -= FloatConsts.EXP_BIAS;
+ BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer();
+ buf.setSign(isNegative);
+ // call the routine that actually does all the hard work.
+ buf.dtoa(binExp, ((long)fractBits)<<(EXP_SHIFT-SINGLE_EXP_SHIFT), nSignificantBits, true);
+ return buf;
+ }
+
+ @SuppressWarnings("fallthrough")
+ static ASCIIToBinaryConverter readJavaFormatString( String in ) throws NumberFormatException {
+ boolean isNegative = false;
+ boolean signSeen = false;
+ int decExp;
+ char c;
+
+ parseNumber:
+ try{
+ in = in.trim(); // don't fool around with white space.
+ // throws NullPointerException if null
+ int len = in.length();
+ if ( len == 0 ) {
+ throw new NumberFormatException("empty String");
+ }
+ int i = 0;
+ switch (in.charAt(i)){
+ case '-':
+ isNegative = true;
+ //FALLTHROUGH
+ case '+':
+ i++;
+ signSeen = true;
+ }
+ c = in.charAt(i);
+ if(c == 'N') { // Check for NaN
+ if((len-i)==NAN_LENGTH && in.indexOf(NAN_REP,i)==i) {
+ return A2BC_NOT_A_NUMBER;
+ }
+ // something went wrong, throw exception
+ break parseNumber;
+ } else if(c == 'I') { // Check for Infinity strings
+ if((len-i)==INFINITY_LENGTH && in.indexOf(INFINITY_REP,i)==i) {
+ return isNegative? A2BC_NEGATIVE_INFINITY : A2BC_POSITIVE_INFINITY;
+ }
+ // something went wrong, throw exception
+ break parseNumber;
+ } else if (c == '0') { // check for hexadecimal floating-point number
+ if (len > i+1 ) {
+ char ch = in.charAt(i+1);
+ if (ch == 'x' || ch == 'X' ) { // possible hex string
+ return parseHexString(in);
+ }
+ }
+ } // look for and process decimal floating-point string
+
+ char[] digits = new char[ len ];
+ int nDigits= 0;
+ boolean decSeen = false;
+ int decPt = 0;
+ int nLeadZero = 0;
+ int nTrailZero= 0;
+
+ skipLeadingZerosLoop:
+ while (i < len) {
+ c = in.charAt(i);
+ if (c == '0') {
+ nLeadZero++;
+ } else if (c == '.') {
+ if (decSeen) {
+ // already saw one ., this is the 2nd.
+ throw new NumberFormatException("multiple points");
+ }
+ decPt = i;
+ if (signSeen) {
+ decPt -= 1;
+ }
+ decSeen = true;
+ } else {
+ break skipLeadingZerosLoop;
+ }
+ i++;
+ }
+ digitLoop:
+ while (i < len) {
+ c = in.charAt(i);
+ if (c >= '1' && c <= '9') {
+ digits[nDigits++] = c;
+ nTrailZero = 0;
+ } else if (c == '0') {
+ digits[nDigits++] = c;
+ nTrailZero++;
+ } else if (c == '.') {
+ if (decSeen) {
+ // already saw one ., this is the 2nd.
+ throw new NumberFormatException("multiple points");
+ }
+ decPt = i;
+ if (signSeen) {
+ decPt -= 1;
+ }
+ decSeen = true;
+ } else {
+ break digitLoop;
+ }
+ i++;
+ }
+ nDigits -=nTrailZero;
+ //
+ // At this point, we've scanned all the digits and decimal
+ // point we're going to see. Trim off leading and trailing
+ // zeros, which will just confuse us later, and adjust
+ // our initial decimal exponent accordingly.
+ // To review:
+ // we have seen i total characters.
+ // nLeadZero of them were zeros before any other digits.
+ // nTrailZero of them were zeros after any other digits.
+ // if ( decSeen ), then a . was seen after decPt characters
+ // ( including leading zeros which have been discarded )
+ // nDigits characters were neither lead nor trailing
+ // zeros, nor point
+ //
+ //
+ // special hack: if we saw no non-zero digits, then the
+ // answer is zero!
+ // Unfortunately, we feel honor-bound to keep parsing!
+ //
+ boolean isZero = (nDigits == 0);
+ if ( isZero && nLeadZero == 0 ){
+ // we saw NO DIGITS AT ALL,
+ // not even a crummy 0!
+ // this is not allowed.
+ break parseNumber; // go throw exception
+ }
+ //
+ // Our initial exponent is decPt, adjusted by the number of
+ // discarded zeros. Or, if there was no decPt,
+ // then its just nDigits adjusted by discarded trailing zeros.
+ //
+ if ( decSeen ){
+ decExp = decPt - nLeadZero;
+ } else {
+ decExp = nDigits + nTrailZero;
+ }
+
+ //
+ // Look for 'e' or 'E' and an optionally signed integer.
+ //
+ if ( (i < len) && (((c = in.charAt(i) )=='e') || (c == 'E') ) ){
+ int expSign = 1;
+ int expVal = 0;
+ int reallyBig = Integer.MAX_VALUE / 10;
+ boolean expOverflow = false;
+ switch( in.charAt(++i) ){
+ case '-':
+ expSign = -1;
+ //FALLTHROUGH
+ case '+':
+ i++;
+ }
+ int expAt = i;
+ expLoop:
+ while ( i < len ){
+ if ( expVal >= reallyBig ){
+ // the next character will cause integer
+ // overflow.
+ expOverflow = true;
+ }
+ c = in.charAt(i++);
+ if(c>='0' && c<='9') {
+ expVal = expVal*10 + ( (int)c - (int)'0' );
+ } else {
+ i--; // back up.
+ break expLoop; // stop parsing exponent.
+ }
+ }
+ int expLimit = BIG_DECIMAL_EXPONENT + nDigits + nTrailZero;
+ if (expOverflow || (expVal > expLimit)) {
+ // There is still a chance that the exponent will be safe to
+ // use: if it would eventually decrease due to a negative
+ // decExp, and that number is below the limit. We check for
+ // that here.
+ if (!expOverflow && (expSign == 1 && decExp < 0)
+ && (expVal + decExp) < expLimit) {
+ // Cannot overflow: adding a positive and negative number.
+ decExp += expVal;
+ } else {
+ //
+ // The intent here is to end up with
+ // infinity or zero, as appropriate.
+ // The reason for yielding such a small decExponent,
+ // rather than something intuitive such as
+ // expSign*Integer.MAX_VALUE, is that this value
+ // is subject to further manipulation in
+ // doubleValue() and floatValue(), and I don't want
+ // it to be able to cause overflow there!
+ // (The only way we can get into trouble here is for
+ // really outrageous nDigits+nTrailZero, such as 2
+ // billion.)
+ //
+ decExp = expSign * expLimit;
+ }
+ } else {
+ // this should not overflow, since we tested
+ // for expVal > (MAX+N), where N >= abs(decExp)
+ decExp = decExp + expSign*expVal;
+ }
+
+ // if we saw something not a digit ( or end of string )
+ // after the [Ee][+-], without seeing any digits at all
+ // this is certainly an error. If we saw some digits,
+ // but then some trailing garbage, that might be ok.
+ // so we just fall through in that case.
+ // HUMBUG
+ if ( i == expAt ) {
+ break parseNumber; // certainly bad
+ }
+ }
+ //
+ // We parsed everything we could.
+ // If there are leftovers, then this is not good input!
+ //
+ if ( i < len &&
+ ((i != len - 1) ||
+ (in.charAt(i) != 'f' &&
+ in.charAt(i) != 'F' &&
+ in.charAt(i) != 'd' &&
+ in.charAt(i) != 'D'))) {
+ break parseNumber; // go throw exception
+ }
+ if(isZero) {
+ return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
+ }
+ return new ASCIIToBinaryBuffer(isNegative, decExp, digits, nDigits);
+ } catch ( StringIndexOutOfBoundsException e ){ }
+ throw new NumberFormatException("For input string: \"" + in + "\"");
+ }
+
+ private static class HexFloatPattern {
+ /**
+ * Grammar is compatible with hexadecimal floating-point constants
+ * described in section 6.4.4.2 of the C99 specification.
+ */
+ private static final Pattern VALUE = Pattern.compile(
+ //1 234 56 7 8 9
+ "([-+])?0[xX](((\\p{XDigit}+)\\.?)|((\\p{XDigit}*)\\.(\\p{XDigit}+)))[pP]([-+])?(\\p{Digit}+)[fFdD]?"
+ );
+ }
+
+ /**
+ * Converts string s to a suitable floating decimal; uses the
+ * double constructor and sets the roundDir variable appropriately
+ * in case the value is later converted to a float.
+ *
+ * @param s The <code>String</code> to parse.
+ */
+ static ASCIIToBinaryConverter parseHexString(String s) {
+ // Verify string is a member of the hexadecimal floating-point
+ // string language.
+ Matcher m = HexFloatPattern.VALUE.matcher(s);
+ boolean validInput = m.matches();
+ if (!validInput) {
+ // Input does not match pattern
+ throw new NumberFormatException("For input string: \"" + s + "\"");
+ } else { // validInput
+ //
+ // We must isolate the sign, significand, and exponent
+ // fields. The sign value is straightforward. Since
+ // floating-point numbers are stored with a normalized
+ // representation, the significand and exponent are
+ // interrelated.
+ //
+ // After extracting the sign, we normalized the
+ // significand as a hexadecimal value, calculating an
+ // exponent adjust for any shifts made during
+ // normalization. If the significand is zero, the
+ // exponent doesn't need to be examined since the output
+ // will be zero.
+ //
+ // Next the exponent in the input string is extracted.
+ // Afterwards, the significand is normalized as a *binary*
+ // value and the input value's normalized exponent can be
+ // computed. The significand bits are copied into a
+ // double significand; if the string has more logical bits
+ // than can fit in a double, the extra bits affect the
+ // round and sticky bits which are used to round the final
+ // value.
+ //
+ // Extract significand sign
+ String group1 = m.group(1);
+ boolean isNegative = ((group1 != null) && group1.equals("-"));
+
+ // Extract Significand magnitude
+ //
+ // Based on the form of the significand, calculate how the
+ // binary exponent needs to be adjusted to create a
+ // normalized//hexadecimal* floating-point number; that
+ // is, a number where there is one nonzero hex digit to
+ // the left of the (hexa)decimal point. Since we are
+ // adjusting a binary, not hexadecimal exponent, the
+ // exponent is adjusted by a multiple of 4.
+ //
+ // There are a number of significand scenarios to consider;
+ // letters are used in indicate nonzero digits:
+ //
+ // 1. 000xxxx => x.xxx normalized
+ // increase exponent by (number of x's - 1)*4
+ //
+ // 2. 000xxx.yyyy => x.xxyyyy normalized
+ // increase exponent by (number of x's - 1)*4
+ //
+ // 3. .000yyy => y.yy normalized
+ // decrease exponent by (number of zeros + 1)*4
+ //
+ // 4. 000.00000yyy => y.yy normalized
+ // decrease exponent by (number of zeros to right of point + 1)*4
+ //
+ // If the significand is exactly zero, return a properly
+ // signed zero.
+ //
+
+ String significandString = null;
+ int signifLength = 0;
+ int exponentAdjust = 0;
+ {
+ int leftDigits = 0; // number of meaningful digits to
+ // left of "decimal" point
+ // (leading zeros stripped)
+ int rightDigits = 0; // number of digits to right of
+ // "decimal" point; leading zeros
+ // must always be accounted for
+ //
+ // The significand is made up of either
+ //
+ // 1. group 4 entirely (integer portion only)
+ //
+ // OR
+ //
+ // 2. the fractional portion from group 7 plus any
+ // (optional) integer portions from group 6.
+ //
+ String group4;
+ if ((group4 = m.group(4)) != null) { // Integer-only significand
+ // Leading zeros never matter on the integer portion
+ significandString = stripLeadingZeros(group4);
+ leftDigits = significandString.length();
+ } else {
+ // Group 6 is the optional integer; leading zeros
+ // never matter on the integer portion
+ String group6 = stripLeadingZeros(m.group(6));
+ leftDigits = group6.length();
+
+ // fraction
+ String group7 = m.group(7);
+ rightDigits = group7.length();
+
+ // Turn "integer.fraction" into "integer"+"fraction"
+ significandString =
+ ((group6 == null) ? "" : group6) + // is the null
+ // check necessary?
+ group7;
+ }
+
+ significandString = stripLeadingZeros(significandString);
+ signifLength = significandString.length();
+
+ //
+ // Adjust exponent as described above
+ //
+ if (leftDigits >= 1) { // Cases 1 and 2
+ exponentAdjust = 4 * (leftDigits - 1);
+ } else { // Cases 3 and 4
+ exponentAdjust = -4 * (rightDigits - signifLength + 1);
+ }
+
+ // If the significand is zero, the exponent doesn't
+ // matter; return a properly signed zero.
+
+ if (signifLength == 0) { // Only zeros in input
+ return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
+ }
+ }
+
+ // Extract Exponent
+ //
+ // Use an int to read in the exponent value; this should
+ // provide more than sufficient range for non-contrived
+ // inputs. If reading the exponent in as an int does
+ // overflow, examine the sign of the exponent and
+ // significand to determine what to do.
+ //
+ String group8 = m.group(8);
+ boolean positiveExponent = (group8 == null) || group8.equals("+");
+ long unsignedRawExponent;
+ try {
+ unsignedRawExponent = Integer.parseInt(m.group(9));
+ }
+ catch (NumberFormatException e) {
+ // At this point, we know the exponent is
+ // syntactically well-formed as a sequence of
+ // digits. Therefore, if an NumberFormatException
+ // is thrown, it must be due to overflowing int's
+ // range. Also, at this point, we have already
+ // checked for a zero significand. Thus the signs
+ // of the exponent and significand determine the
+ // final result:
+ //
+ // significand
+ // + -
+ // exponent + +infinity -infinity
+ // - +0.0 -0.0
+ return isNegative ?
+ (positiveExponent ? A2BC_NEGATIVE_INFINITY : A2BC_NEGATIVE_ZERO)
+ : (positiveExponent ? A2BC_POSITIVE_INFINITY : A2BC_POSITIVE_ZERO);
+
+ }
+
+ long rawExponent =
+ (positiveExponent ? 1L : -1L) * // exponent sign
+ unsignedRawExponent; // exponent magnitude
+
+ // Calculate partially adjusted exponent
+ long exponent = rawExponent + exponentAdjust;
+
+ // Starting copying non-zero bits into proper position in
+ // a long; copy explicit bit too; this will be masked
+ // later for normal values.
+
+ boolean round = false;
+ boolean sticky = false;
+ int nextShift = 0;
+ long significand = 0L;
+ // First iteration is different, since we only copy
+ // from the leading significand bit; one more exponent
+ // adjust will be needed...
+
+ // IMPORTANT: make leadingDigit a long to avoid
+ // surprising shift semantics!
+ long leadingDigit = getHexDigit(significandString, 0);
+
+ //
+ // Left shift the leading digit (53 - (bit position of
+ // leading 1 in digit)); this sets the top bit of the
+ // significand to 1. The nextShift value is adjusted
+ // to take into account the number of bit positions of
+ // the leadingDigit actually used. Finally, the
+ // exponent is adjusted to normalize the significand
+ // as a binary value, not just a hex value.
+ //
+ if (leadingDigit == 1) {
+ significand |= leadingDigit << 52;
+ nextShift = 52 - 4;
+ // exponent += 0
+ } else if (leadingDigit <= 3) { // [2, 3]
+ significand |= leadingDigit << 51;
+ nextShift = 52 - 5;
+ exponent += 1;
+ } else if (leadingDigit <= 7) { // [4, 7]
+ significand |= leadingDigit << 50;
+ nextShift = 52 - 6;
+ exponent += 2;
+ } else if (leadingDigit <= 15) { // [8, f]
+ significand |= leadingDigit << 49;
+ nextShift = 52 - 7;
+ exponent += 3;
+ } else {
+ throw new AssertionError("Result from digit conversion too large!");
+ }
+ // The preceding if-else could be replaced by a single
+ // code block based on the high-order bit set in
+ // leadingDigit. Given leadingOnePosition,
+
+ // significand |= leadingDigit << (SIGNIFICAND_WIDTH - leadingOnePosition);
+ // nextShift = 52 - (3 + leadingOnePosition);
+ // exponent += (leadingOnePosition-1);
+
+ //
+ // Now the exponent variable is equal to the normalized
+ // binary exponent. Code below will make representation
+ // adjustments if the exponent is incremented after
+ // rounding (includes overflows to infinity) or if the
+ // result is subnormal.
+ //
+
+ // Copy digit into significand until the significand can't
+ // hold another full hex digit or there are no more input
+ // hex digits.
+ int i = 0;
+ for (i = 1;
+ i < signifLength && nextShift >= 0;
+ i++) {
+ long currentDigit = getHexDigit(significandString, i);
+ significand |= (currentDigit << nextShift);
+ nextShift -= 4;
+ }
+
+ // After the above loop, the bulk of the string is copied.
+ // Now, we must copy any partial hex digits into the
+ // significand AND compute the round bit and start computing
+ // sticky bit.
+
+ if (i < signifLength) { // at least one hex input digit exists
+ long currentDigit = getHexDigit(significandString, i);
+
+ // from nextShift, figure out how many bits need
+ // to be copied, if any
+ switch (nextShift) { // must be negative
+ case -1:
+ // three bits need to be copied in; can
+ // set round bit
+ significand |= ((currentDigit & 0xEL) >> 1);
+ round = (currentDigit & 0x1L) != 0L;
+ break;
+
+ case -2:
+ // two bits need to be copied in; can
+ // set round and start sticky
+ significand |= ((currentDigit & 0xCL) >> 2);
+ round = (currentDigit & 0x2L) != 0L;
+ sticky = (currentDigit & 0x1L) != 0;
+ break;
+
+ case -3:
+ // one bit needs to be copied in
+ significand |= ((currentDigit & 0x8L) >> 3);
+ // Now set round and start sticky, if possible
+ round = (currentDigit & 0x4L) != 0L;
+ sticky = (currentDigit & 0x3L) != 0;
+ break;
+
+ case -4:
+ // all bits copied into significand; set
+ // round and start sticky
+ round = ((currentDigit & 0x8L) != 0); // is top bit set?
+ // nonzeros in three low order bits?
+ sticky = (currentDigit & 0x7L) != 0;
+ break;
+
+ default:
+ throw new AssertionError("Unexpected shift distance remainder.");
+ // break;
+ }
+
+ // Round is set; sticky might be set.
+
+ // For the sticky bit, it suffices to check the
+ // current digit and test for any nonzero digits in
+ // the remaining unprocessed input.
+ i++;
+ while (i < signifLength && !sticky) {
+ currentDigit = getHexDigit(significandString, i);
+ sticky = sticky || (currentDigit != 0);
+ i++;
+ }
+
+ }
+ // else all of string was seen, round and sticky are
+ // correct as false.
+
+ // Float calculations
+ int floatBits = isNegative ? FloatConsts.SIGN_BIT_MASK : 0;
+ if (exponent >= FloatConsts.MIN_EXPONENT) {
+ if (exponent > FloatConsts.MAX_EXPONENT) {
+ // Float.POSITIVE_INFINITY
+ floatBits |= FloatConsts.EXP_BIT_MASK;
+ } else {
+ int threshShift = DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH - 1;
+ boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky;
+ int iValue = (int) (significand >>> threshShift);
+ if ((iValue & 3) != 1 || floatSticky) {
+ iValue++;
+ }
+ floatBits |= (((((int) exponent) + (FloatConsts.EXP_BIAS - 1))) << SINGLE_EXP_SHIFT) + (iValue >> 1);
+ }
+ } else {
+ if (exponent < FloatConsts.MIN_SUB_EXPONENT - 1) {
+ // 0
+ } else {
+ // exponent == -127 ==> threshShift = 53 - 2 + (-149) - (-127) = 53 - 24
+ int threshShift = (int) ((DoubleConsts.SIGNIFICAND_WIDTH - 2 + FloatConsts.MIN_SUB_EXPONENT) - exponent);
+ assert threshShift >= DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH;
+ assert threshShift < DoubleConsts.SIGNIFICAND_WIDTH;
+ boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky;
+ int iValue = (int) (significand >>> threshShift);
+ if ((iValue & 3) != 1 || floatSticky) {
+ iValue++;
+ }
+ floatBits |= iValue >> 1;
+ }
+ }
+ float fValue = Float.intBitsToFloat(floatBits);
+
+ // Check for overflow and update exponent accordingly.
+ if (exponent > DoubleConsts.MAX_EXPONENT) { // Infinite result
+ // overflow to properly signed infinity
+ return isNegative ? A2BC_NEGATIVE_INFINITY : A2BC_POSITIVE_INFINITY;
+ } else { // Finite return value
+ if (exponent <= DoubleConsts.MAX_EXPONENT && // (Usually) normal result
+ exponent >= DoubleConsts.MIN_EXPONENT) {
+
+ // The result returned in this block cannot be a
+ // zero or subnormal; however after the
+ // significand is adjusted from rounding, we could
+ // still overflow in infinity.
+
+ // AND exponent bits into significand; if the
+ // significand is incremented and overflows from
+ // rounding, this combination will update the
+ // exponent correctly, even in the case of
+ // Double.MAX_VALUE overflowing to infinity.
+
+ significand = ((( exponent +
+ (long) DoubleConsts.EXP_BIAS) <<
+ (DoubleConsts.SIGNIFICAND_WIDTH - 1))
+ & DoubleConsts.EXP_BIT_MASK) |
+ (DoubleConsts.SIGNIF_BIT_MASK & significand);
+
+ } else { // Subnormal or zero
+ // (exponent < DoubleConsts.MIN_EXPONENT)
+
+ if (exponent < (DoubleConsts.MIN_SUB_EXPONENT - 1)) {
+ // No way to round back to nonzero value
+ // regardless of significand if the exponent is
+ // less than -1075.
+ return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
+ } else { // -1075 <= exponent <= MIN_EXPONENT -1 = -1023
+ //
+ // Find bit position to round to; recompute
+ // round and sticky bits, and shift
+ // significand right appropriately.
+ //
+
+ sticky = sticky || round;
+ round = false;
+
+ // Number of bits of significand to preserve is
+ // exponent - abs_min_exp +1
+ // check:
+ // -1075 +1074 + 1 = 0
+ // -1023 +1074 + 1 = 52
+
+ int bitsDiscarded = 53 -
+ ((int) exponent - DoubleConsts.MIN_SUB_EXPONENT + 1);
+ assert bitsDiscarded >= 1 && bitsDiscarded <= 53;
+
+ // What to do here:
+ // First, isolate the new round bit
+ round = (significand & (1L << (bitsDiscarded - 1))) != 0L;
+ if (bitsDiscarded > 1) {
+ // create mask to update sticky bits; low
+ // order bitsDiscarded bits should be 1
+ long mask = ~((~0L) << (bitsDiscarded - 1));
+ sticky = sticky || ((significand & mask) != 0L);
+ }
+
+ // Now, discard the bits
+ significand = significand >> bitsDiscarded;
+
+ significand = ((((long) (DoubleConsts.MIN_EXPONENT - 1) + // subnorm exp.
+ (long) DoubleConsts.EXP_BIAS) <<
+ (DoubleConsts.SIGNIFICAND_WIDTH - 1))
+ & DoubleConsts.EXP_BIT_MASK) |
+ (DoubleConsts.SIGNIF_BIT_MASK & significand);
+ }
+ }
+
+ // The significand variable now contains the currently
+ // appropriate exponent bits too.
+
+ //
+ // Determine if significand should be incremented;
+ // making this determination depends on the least
+ // significant bit and the round and sticky bits.
+ //
+ // Round to nearest even rounding table, adapted from
+ // table 4.7 in "Computer Arithmetic" by IsraelKoren.
+ // The digit to the left of the "decimal" point is the
+ // least significant bit, the digits to the right of
+ // the point are the round and sticky bits
+ //
+ // Number Round(x)
+ // x0.00 x0.
+ // x0.01 x0.
+ // x0.10 x0.
+ // x0.11 x1. = x0. +1
+ // x1.00 x1.
+ // x1.01 x1.
+ // x1.10 x1. + 1
+ // x1.11 x1. + 1
+ //
+ boolean leastZero = ((significand & 1L) == 0L);
+ if ((leastZero && round && sticky) ||
+ ((!leastZero) && round)) {
+ significand++;
+ }
+
+ double value = isNegative ?
+ Double.longBitsToDouble(significand | DoubleConsts.SIGN_BIT_MASK) :
+ Double.longBitsToDouble(significand );
+
+ return new PreparedASCIIToBinaryBuffer(value, fValue);
+ }
+ }
+ }
+
+ /**
+ * Returns <code>s</code> with any leading zeros removed.
+ */
+ static String stripLeadingZeros(String s) {
+// return s.replaceFirst("^0+", "");
+ if(!s.isEmpty() && s.charAt(0)=='0') {
+ for(int i=1; i<s.length(); i++) {
+ if(s.charAt(i)!='0') {
+ return s.substring(i);
+ }
+ }
+ return "";
+ }
+ return s;
+ }
+
+ /**
+ * Extracts a hexadecimal digit from position <code>position</code>
+ * of string <code>s</code>.
+ */
+ static int getHexDigit(String s, int position) {
+ int value = Character.digit(s.charAt(position), 16);
+ if (value <= -1 || value >= 16) {
+ throw new AssertionError("Unexpected failure of digit conversion of " +
+ s.charAt(position));
+ }
+ return value;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/math/FormattedFloatingDecimal.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.math;
+
+import java.util.Arrays;
+
+public class FormattedFloatingDecimal{
+
+ public enum Form { SCIENTIFIC, COMPATIBLE, DECIMAL_FLOAT, GENERAL };
+
+
+ public static FormattedFloatingDecimal valueOf(double d, int precision, Form form){
+ FloatingDecimal.BinaryToASCIIConverter fdConverter =
+ FloatingDecimal.getBinaryToASCIIConverter(d, form == Form.COMPATIBLE);
+ return new FormattedFloatingDecimal(precision,form, fdConverter);
+ }
+
+ private int decExponentRounded;
+ private char[] mantissa;
+ private char[] exponent;
+
+ private static final ThreadLocal<Object> threadLocalCharBuffer =
+ new ThreadLocal<Object>() {
+ @Override
+ protected Object initialValue() {
+ return new char[20];
+ }
+ };
+
+ private static char[] getBuffer(){
+ return (char[]) threadLocalCharBuffer.get();
+ }
+
+ private FormattedFloatingDecimal(int precision, Form form, FloatingDecimal.BinaryToASCIIConverter fdConverter) {
+ if (fdConverter.isExceptional()) {
+ this.mantissa = fdConverter.toJavaFormatString().toCharArray();
+ this.exponent = null;
+ return;
+ }
+ char[] digits = getBuffer();
+ int nDigits = fdConverter.getDigits(digits);
+ int decExp = fdConverter.getDecimalExponent();
+ int exp;
+ boolean isNegative = fdConverter.isNegative();
+ switch (form) {
+ case COMPATIBLE:
+ exp = decExp;
+ this.decExponentRounded = exp;
+ fillCompatible(precision, digits, nDigits, exp, isNegative);
+ break;
+ case DECIMAL_FLOAT:
+ exp = applyPrecision(decExp, digits, nDigits, decExp + precision);
+ fillDecimal(precision, digits, nDigits, exp, isNegative);
+ this.decExponentRounded = exp;
+ break;
+ case SCIENTIFIC:
+ exp = applyPrecision(decExp, digits, nDigits, precision + 1);
+ fillScientific(precision, digits, nDigits, exp, isNegative);
+ this.decExponentRounded = exp;
+ break;
+ case GENERAL:
+ exp = applyPrecision(decExp, digits, nDigits, precision);
+ // adjust precision to be the number of digits to right of decimal
+ // the real exponent to be output is actually exp - 1, not exp
+ if (exp - 1 < -4 || exp - 1 >= precision) {
+ // form = Form.SCIENTIFIC;
+ precision--;
+ fillScientific(precision, digits, nDigits, exp, isNegative);
+ } else {
+ // form = Form.DECIMAL_FLOAT;
+ precision = precision - exp;
+ fillDecimal(precision, digits, nDigits, exp, isNegative);
+ }
+ this.decExponentRounded = exp;
+ break;
+ default:
+ assert false;
+ }
+ }
+
+ // returns the exponent after rounding has been done by applyPrecision
+ public int getExponentRounded() {
+ return decExponentRounded - 1;
+ }
+
+ public char[] getMantissa(){
+ return mantissa;
+ }
+
+ public char[] getExponent(){
+ return exponent;
+ }
+
+ /**
+ * Returns new decExp in case of overflow.
+ */
+ private static int applyPrecision(int decExp, char[] digits, int nDigits, int prec) {
+ if (prec >= nDigits || prec < 0) {
+ // no rounding necessary
+ return decExp;
+ }
+ if (prec == 0) {
+ // only one digit (0 or 1) is returned because the precision
+ // excludes all significant digits
+ if (digits[0] >= '5') {
+ digits[0] = '1';
+ Arrays.fill(digits, 1, nDigits, '0');
+ return decExp + 1;
+ } else {
+ Arrays.fill(digits, 0, nDigits, '0');
+ return decExp;
+ }
+ }
+ int q = digits[prec];
+ if (q >= '5') {
+ int i = prec;
+ q = digits[--i];
+ if ( q == '9' ) {
+ while ( q == '9' && i > 0 ){
+ q = digits[--i];
+ }
+ if ( q == '9' ){
+ // carryout! High-order 1, rest 0s, larger exp.
+ digits[0] = '1';
+ Arrays.fill(digits, 1, nDigits, '0');
+ return decExp+1;
+ }
+ }
+ digits[i] = (char)(q + 1);
+ Arrays.fill(digits, i+1, nDigits, '0');
+ } else {
+ Arrays.fill(digits, prec, nDigits, '0');
+ }
+ return decExp;
+ }
+
+ /**
+ * Fills mantissa and exponent char arrays for compatible format.
+ */
+ private void fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
+ int startIndex = isNegative ? 1 : 0;
+ if (exp > 0 && exp < 8) {
+ // print digits.digits.
+ if (nDigits < exp) {
+ int extraZeros = exp - nDigits;
+ mantissa = create(isNegative, nDigits + extraZeros + 2);
+ System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
+ Arrays.fill(mantissa, startIndex + nDigits, startIndex + nDigits + extraZeros, '0');
+ mantissa[startIndex + nDigits + extraZeros] = '.';
+ mantissa[startIndex + nDigits + extraZeros+1] = '0';
+ } else if (exp < nDigits) {
+ int t = Math.min(nDigits - exp, precision);
+ mantissa = create(isNegative, exp + 1 + t);
+ System.arraycopy(digits, 0, mantissa, startIndex, exp);
+ mantissa[startIndex + exp ] = '.';
+ System.arraycopy(digits, exp, mantissa, startIndex+exp+1, t);
+ } else { // exp == digits.length
+ mantissa = create(isNegative, nDigits + 2);
+ System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
+ mantissa[startIndex + nDigits ] = '.';
+ mantissa[startIndex + nDigits +1] = '0';
+ }
+ } else if (exp <= 0 && exp > -3) {
+ int zeros = Math.max(0, Math.min(-exp, precision));
+ int t = Math.max(0, Math.min(nDigits, precision + exp));
+ // write '0' s before the significant digits
+ if (zeros > 0) {
+ mantissa = create(isNegative, zeros + 2 + t);
+ mantissa[startIndex] = '0';
+ mantissa[startIndex+1] = '.';
+ Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
+ if (t > 0) {
+ // copy only when significant digits are within the precision
+ System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
+ }
+ } else if (t > 0) {
+ mantissa = create(isNegative, zeros + 2 + t);
+ mantissa[startIndex] = '0';
+ mantissa[startIndex + 1] = '.';
+ // copy only when significant digits are within the precision
+ System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
+ } else {
+ this.mantissa = create(isNegative, 1);
+ this.mantissa[startIndex] = '0';
+ }
+ } else {
+ if (nDigits > 1) {
+ mantissa = create(isNegative, nDigits + 1);
+ mantissa[startIndex] = digits[0];
+ mantissa[startIndex + 1] = '.';
+ System.arraycopy(digits, 1, mantissa, startIndex + 2, nDigits - 1);
+ } else {
+ mantissa = create(isNegative, 3);
+ mantissa[startIndex] = digits[0];
+ mantissa[startIndex + 1] = '.';
+ mantissa[startIndex + 2] = '0';
+ }
+ int e, expStartIntex;
+ boolean isNegExp = (exp <= 0);
+ if (isNegExp) {
+ e = -exp + 1;
+ expStartIntex = 1;
+ } else {
+ e = exp - 1;
+ expStartIntex = 0;
+ }
+ // decExponent has 1, 2, or 3, digits
+ if (e <= 9) {
+ exponent = create(isNegExp,1);
+ exponent[expStartIntex] = (char) (e + '0');
+ } else if (e <= 99) {
+ exponent = create(isNegExp,2);
+ exponent[expStartIntex] = (char) (e / 10 + '0');
+ exponent[expStartIntex+1] = (char) (e % 10 + '0');
+ } else {
+ exponent = create(isNegExp,3);
+ exponent[expStartIntex] = (char) (e / 100 + '0');
+ e %= 100;
+ exponent[expStartIntex+1] = (char) (e / 10 + '0');
+ exponent[expStartIntex+2] = (char) (e % 10 + '0');
+ }
+ }
+ }
+
+ private static char[] create(boolean isNegative, int size) {
+ if(isNegative) {
+ char[] r = new char[size +1];
+ r[0] = '-';
+ return r;
+ } else {
+ return new char[size];
+ }
+ }
+
+ /*
+ * Fills mantissa char arrays for DECIMAL_FLOAT format.
+ * Exponent should be equal to null.
+ */
+ private void fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
+ int startIndex = isNegative ? 1 : 0;
+ if (exp > 0) {
+ // print digits.digits.
+ if (nDigits < exp) {
+ mantissa = create(isNegative,exp);
+ System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
+ Arrays.fill(mantissa, startIndex + nDigits, startIndex + exp, '0');
+ // Do not append ".0" for formatted floats since the user
+ // may request that it be omitted. It is added as necessary
+ // by the Formatter.
+ } else {
+ int t = Math.min(nDigits - exp, precision);
+ mantissa = create(isNegative, exp + (t > 0 ? (t + 1) : 0));
+ System.arraycopy(digits, 0, mantissa, startIndex, exp);
+ // Do not append ".0" for formatted floats since the user
+ // may request that it be omitted. It is added as necessary
+ // by the Formatter.
+ if (t > 0) {
+ mantissa[startIndex + exp] = '.';
+ System.arraycopy(digits, exp, mantissa, startIndex + exp + 1, t);
+ }
+ }
+ } else if (exp <= 0) {
+ int zeros = Math.max(0, Math.min(-exp, precision));
+ int t = Math.max(0, Math.min(nDigits, precision + exp));
+ // write '0' s before the significant digits
+ if (zeros > 0) {
+ mantissa = create(isNegative, zeros + 2 + t);
+ mantissa[startIndex] = '0';
+ mantissa[startIndex+1] = '.';
+ Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
+ if (t > 0) {
+ // copy only when significant digits are within the precision
+ System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
+ }
+ } else if (t > 0) {
+ mantissa = create(isNegative, zeros + 2 + t);
+ mantissa[startIndex] = '0';
+ mantissa[startIndex + 1] = '.';
+ // copy only when significant digits are within the precision
+ System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
+ } else {
+ this.mantissa = create(isNegative, 1);
+ this.mantissa[startIndex] = '0';
+ }
+ }
+ }
+
+ /**
+ * Fills mantissa and exponent char arrays for SCIENTIFIC format.
+ */
+ private void fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
+ int startIndex = isNegative ? 1 : 0;
+ int t = Math.max(0, Math.min(nDigits - 1, precision));
+ if (t > 0) {
+ mantissa = create(isNegative, t + 2);
+ mantissa[startIndex] = digits[0];
+ mantissa[startIndex + 1] = '.';
+ System.arraycopy(digits, 1, mantissa, startIndex + 2, t);
+ } else {
+ mantissa = create(isNegative, 1);
+ mantissa[startIndex] = digits[0];
+ }
+ char expSign;
+ int e;
+ if (exp <= 0) {
+ expSign = '-';
+ e = -exp + 1;
+ } else {
+ expSign = '+' ;
+ e = exp - 1;
+ }
+ // decExponent has 1, 2, or 3, digits
+ if (e <= 9) {
+ exponent = new char[] { expSign,
+ '0', (char) (e + '0') };
+ } else if (e <= 99) {
+ exponent = new char[] { expSign,
+ (char) (e / 10 + '0'), (char) (e % 10 + '0') };
+ } else {
+ char hiExpChar = (char) (e / 100 + '0');
+ e %= 100;
+ exponent = new char[] { expSign,
+ hiExpChar, (char) (e / 10 + '0'), (char) (e % 10 + '0') };
+ }
+ }
+}
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/CleanerImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -39,7 +39,6 @@
import java.util.function.Function;
import sun.misc.InnocuousThread;
-import sun.misc.ManagedLocalsThread;
/**
* CleanerImpl manages a set of object references and corresponding cleaning actions.
@@ -130,8 +129,8 @@
*/
public void run() {
Thread t = Thread.currentThread();
- ManagedLocalsThread mlThread = (t instanceof ManagedLocalsThread)
- ? (ManagedLocalsThread) t
+ InnocuousThread mlThread = (t instanceof InnocuousThread)
+ ? (InnocuousThread) t
: null;
while (!phantomCleanableList.isListEmpty() ||
!weakCleanableList.isListEmpty() ||
@@ -787,4 +786,3 @@
}
}
-
--- a/jdk/src/java.base/share/classes/sun/misc/CompoundEnumeration.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.misc;
-
-import java.util.Enumeration;
-import java.util.NoSuchElementException;
-
-/*
- * A useful utility class that will enumerate over an array of
- * enumerations.
- */
-public class CompoundEnumeration<E> implements Enumeration<E> {
- private Enumeration<E>[] enums;
- private int index = 0;
-
- public CompoundEnumeration(Enumeration<E>[] enums) {
- this.enums = enums;
- }
-
- private boolean next() {
- while (index < enums.length) {
- if (enums[index] != null && enums[index].hasMoreElements()) {
- return true;
- }
- index++;
- }
- return false;
- }
-
- public boolean hasMoreElements() {
- return next();
- }
-
- public E nextElement() {
- if (!next()) {
- throw new NoSuchElementException();
- }
- return enums[index].nextElement();
- }
-}
--- a/jdk/src/java.base/share/classes/sun/misc/DoubleConsts.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.misc;
-
-/**
- * This class contains additional constants documenting limits of the
- * <code>double</code> type.
- *
- * @author Joseph D. Darcy
- */
-
-public class DoubleConsts {
- /**
- * Don't let anyone instantiate this class.
- */
- private DoubleConsts() {}
-
- public static final double POSITIVE_INFINITY = java.lang.Double.POSITIVE_INFINITY;
- public static final double NEGATIVE_INFINITY = java.lang.Double.NEGATIVE_INFINITY;
- public static final double NaN = java.lang.Double.NaN;
- public static final double MAX_VALUE = java.lang.Double.MAX_VALUE;
- public static final double MIN_VALUE = java.lang.Double.MIN_VALUE;
-
- /**
- * A constant holding the smallest positive normal value of type
- * <code>double</code>, 2<sup>-1022</sup>. It is equal to the
- * value returned by
- * <code>Double.longBitsToDouble(0x0010000000000000L)</code>.
- *
- * @since 1.5
- */
- public static final double MIN_NORMAL = 2.2250738585072014E-308;
-
-
- /**
- * The number of logical bits in the significand of a
- * <code>double</code> number, including the implicit bit.
- */
- public static final int SIGNIFICAND_WIDTH = 53;
-
- /**
- * Maximum exponent a finite <code>double</code> number may have.
- * It is equal to the value returned by
- * <code>Math.ilogb(Double.MAX_VALUE)</code>.
- */
- public static final int MAX_EXPONENT = 1023;
-
- /**
- * Minimum exponent a normalized <code>double</code> number may
- * have. It is equal to the value returned by
- * <code>Math.ilogb(Double.MIN_NORMAL)</code>.
- */
- public static final int MIN_EXPONENT = -1022;
-
- /**
- * The exponent the smallest positive <code>double</code>
- * subnormal value would have if it could be normalized..
- */
- public static final int MIN_SUB_EXPONENT = MIN_EXPONENT -
- (SIGNIFICAND_WIDTH - 1);
-
- /**
- * Bias used in representing a <code>double</code> exponent.
- */
- public static final int EXP_BIAS = 1023;
-
- /**
- * Bit mask to isolate the sign bit of a <code>double</code>.
- */
- public static final long SIGN_BIT_MASK = 0x8000000000000000L;
-
- /**
- * Bit mask to isolate the exponent field of a
- * <code>double</code>.
- */
- public static final long EXP_BIT_MASK = 0x7FF0000000000000L;
-
- /**
- * Bit mask to isolate the significand field of a
- * <code>double</code>.
- */
- public static final long SIGNIF_BIT_MASK = 0x000FFFFFFFFFFFFFL;
-
- static {
- // verify bit masks cover all bit positions and that the bit
- // masks are non-overlapping
- assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0L) &&
- (((SIGN_BIT_MASK & EXP_BIT_MASK) == 0L) &&
- ((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0L) &&
- ((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0L)));
- }
-}
--- a/jdk/src/java.base/share/classes/sun/misc/FDBigInteger.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1508 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package sun.misc;
-
-import java.math.BigInteger;
-import java.util.Arrays;
-//@ model import org.jmlspecs.models.JMLMath;
-
-/**
- * A simple big integer package specifically for floating point base conversion.
- */
-public /*@ spec_bigint_math @*/ class FDBigInteger {
-
- //
- // This class contains many comments that start with "/*@" mark.
- // They are behavourial specification in
- // the Java Modelling Language (JML):
- // http://www.eecs.ucf.edu/~leavens/JML//index.shtml
- //
-
- /*@
- @ public pure model static \bigint UNSIGNED(int v) {
- @ return v >= 0 ? v : v + (((\bigint)1) << 32);
- @ }
- @
- @ public pure model static \bigint UNSIGNED(long v) {
- @ return v >= 0 ? v : v + (((\bigint)1) << 64);
- @ }
- @
- @ public pure model static \bigint AP(int[] data, int len) {
- @ return (\sum int i; 0 <= 0 && i < len; UNSIGNED(data[i]) << (i*32));
- @ }
- @
- @ public pure model static \bigint pow52(int p5, int p2) {
- @ ghost \bigint v = 1;
- @ for (int i = 0; i < p5; i++) v *= 5;
- @ return v << p2;
- @ }
- @
- @ public pure model static \bigint pow10(int p10) {
- @ return pow52(p10, p10);
- @ }
- @*/
-
- static final int[] SMALL_5_POW = {
- 1,
- 5,
- 5 * 5,
- 5 * 5 * 5,
- 5 * 5 * 5 * 5,
- 5 * 5 * 5 * 5 * 5,
- 5 * 5 * 5 * 5 * 5 * 5,
- 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
- };
-
- static final long[] LONG_5_POW = {
- 1L,
- 5L,
- 5L * 5,
- 5L * 5 * 5,
- 5L * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- 5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
- };
-
- // Maximum size of cache of powers of 5 as FDBigIntegers.
- private static final int MAX_FIVE_POW = 340;
-
- // Cache of big powers of 5 as FDBigIntegers.
- private static final FDBigInteger POW_5_CACHE[];
-
- // Initialize FDBigInteger cache of powers of 5.
- static {
- POW_5_CACHE = new FDBigInteger[MAX_FIVE_POW];
- int i = 0;
- while (i < SMALL_5_POW.length) {
- FDBigInteger pow5 = new FDBigInteger(new int[]{SMALL_5_POW[i]}, 0);
- pow5.makeImmutable();
- POW_5_CACHE[i] = pow5;
- i++;
- }
- FDBigInteger prev = POW_5_CACHE[i - 1];
- while (i < MAX_FIVE_POW) {
- POW_5_CACHE[i] = prev = prev.mult(5);
- prev.makeImmutable();
- i++;
- }
- }
-
- // Zero as an FDBigInteger.
- public static final FDBigInteger ZERO = new FDBigInteger(new int[0], 0);
-
- // Ensure ZERO is immutable.
- static {
- ZERO.makeImmutable();
- }
-
- // Constant for casting an int to a long via bitwise AND.
- private static final long LONG_MASK = 0xffffffffL;
-
- //@ spec_public non_null;
- private int data[]; // value: data[0] is least significant
- //@ spec_public;
- private int offset; // number of least significant zero padding ints
- //@ spec_public;
- private int nWords; // data[nWords-1]!=0, all values above are zero
- // if nWords==0 -> this FDBigInteger is zero
- //@ spec_public;
- private boolean isImmutable = false;
-
- /*@
- @ public invariant 0 <= nWords && nWords <= data.length && offset >= 0;
- @ public invariant nWords == 0 ==> offset == 0;
- @ public invariant nWords > 0 ==> data[nWords - 1] != 0;
- @ public invariant (\forall int i; nWords <= i && i < data.length; data[i] == 0);
- @ public pure model \bigint value() {
- @ return AP(data, nWords) << (offset*32);
- @ }
- @*/
-
- /**
- * Constructs an <code>FDBigInteger</code> from data and padding. The
- * <code>data</code> parameter has the least significant <code>int</code> at
- * the zeroth index. The <code>offset</code> parameter gives the number of
- * zero <code>int</code>s to be inferred below the least significant element
- * of <code>data</code>.
- *
- * @param data An array containing all non-zero <code>int</code>s of the value.
- * @param offset An offset indicating the number of zero <code>int</code>s to pad
- * below the least significant element of <code>data</code>.
- */
- /*@
- @ requires data != null && offset >= 0;
- @ ensures this.value() == \old(AP(data, data.length) << (offset*32));
- @ ensures this.data == \old(data);
- @*/
- private FDBigInteger(int[] data, int offset) {
- this.data = data;
- this.offset = offset;
- this.nWords = data.length;
- trimLeadingZeros();
- }
-
- /**
- * Constructs an <code>FDBigInteger</code> from a starting value and some
- * decimal digits.
- *
- * @param lValue The starting value.
- * @param digits The decimal digits.
- * @param kDigits The initial index into <code>digits</code>.
- * @param nDigits The final index into <code>digits</code>.
- */
- /*@
- @ requires digits != null;
- @ requires 0 <= kDigits && kDigits <= nDigits && nDigits <= digits.length;
- @ requires (\forall int i; 0 <= i && i < nDigits; '0' <= digits[i] && digits[i] <= '9');
- @ ensures this.value() == \old(lValue * pow10(nDigits - kDigits) + (\sum int i; kDigits <= i && i < nDigits; (digits[i] - '0') * pow10(nDigits - i - 1)));
- @*/
- public FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits) {
- int n = Math.max((nDigits + 8) / 9, 2); // estimate size needed.
- data = new int[n]; // allocate enough space
- data[0] = (int) lValue; // starting value
- data[1] = (int) (lValue >>> 32);
- offset = 0;
- nWords = 2;
- int i = kDigits;
- int limit = nDigits - 5; // slurp digits 5 at a time.
- int v;
- while (i < limit) {
- int ilim = i + 5;
- v = (int) digits[i++] - (int) '0';
- while (i < ilim) {
- v = 10 * v + (int) digits[i++] - (int) '0';
- }
- multAddMe(100000, v); // ... where 100000 is 10^5.
- }
- int factor = 1;
- v = 0;
- while (i < nDigits) {
- v = 10 * v + (int) digits[i++] - (int) '0';
- factor *= 10;
- }
- if (factor != 1) {
- multAddMe(factor, v);
- }
- trimLeadingZeros();
- }
-
- /**
- * Returns an <code>FDBigInteger</code> with the numerical value
- * <code>5<sup>p5</sup> * 2<sup>p2</sup></code>.
- *
- * @param p5 The exponent of the power-of-five factor.
- * @param p2 The exponent of the power-of-two factor.
- * @return <code>5<sup>p5</sup> * 2<sup>p2</sup></code>
- */
- /*@
- @ requires p5 >= 0 && p2 >= 0;
- @ assignable \nothing;
- @ ensures \result.value() == \old(pow52(p5, p2));
- @*/
- public static FDBigInteger valueOfPow52(int p5, int p2) {
- if (p5 != 0) {
- if (p2 == 0) {
- return big5pow(p5);
- } else if (p5 < SMALL_5_POW.length) {
- int pow5 = SMALL_5_POW[p5];
- int wordcount = p2 >> 5;
- int bitcount = p2 & 0x1f;
- if (bitcount == 0) {
- return new FDBigInteger(new int[]{pow5}, wordcount);
- } else {
- return new FDBigInteger(new int[]{
- pow5 << bitcount,
- pow5 >>> (32 - bitcount)
- }, wordcount);
- }
- } else {
- return big5pow(p5).leftShift(p2);
- }
- } else {
- return valueOfPow2(p2);
- }
- }
-
- /**
- * Returns an <code>FDBigInteger</code> with the numerical value
- * <code>value * 5<sup>p5</sup> * 2<sup>p2</sup></code>.
- *
- * @param value The constant factor.
- * @param p5 The exponent of the power-of-five factor.
- * @param p2 The exponent of the power-of-two factor.
- * @return <code>value * 5<sup>p5</sup> * 2<sup>p2</sup></code>
- */
- /*@
- @ requires p5 >= 0 && p2 >= 0;
- @ assignable \nothing;
- @ ensures \result.value() == \old(UNSIGNED(value) * pow52(p5, p2));
- @*/
- public static FDBigInteger valueOfMulPow52(long value, int p5, int p2) {
- assert p5 >= 0 : p5;
- assert p2 >= 0 : p2;
- int v0 = (int) value;
- int v1 = (int) (value >>> 32);
- int wordcount = p2 >> 5;
- int bitcount = p2 & 0x1f;
- if (p5 != 0) {
- if (p5 < SMALL_5_POW.length) {
- long pow5 = SMALL_5_POW[p5] & LONG_MASK;
- long carry = (v0 & LONG_MASK) * pow5;
- v0 = (int) carry;
- carry >>>= 32;
- carry = (v1 & LONG_MASK) * pow5 + carry;
- v1 = (int) carry;
- int v2 = (int) (carry >>> 32);
- if (bitcount == 0) {
- return new FDBigInteger(new int[]{v0, v1, v2}, wordcount);
- } else {
- return new FDBigInteger(new int[]{
- v0 << bitcount,
- (v1 << bitcount) | (v0 >>> (32 - bitcount)),
- (v2 << bitcount) | (v1 >>> (32 - bitcount)),
- v2 >>> (32 - bitcount)
- }, wordcount);
- }
- } else {
- FDBigInteger pow5 = big5pow(p5);
- int[] r;
- if (v1 == 0) {
- r = new int[pow5.nWords + 1 + ((p2 != 0) ? 1 : 0)];
- mult(pow5.data, pow5.nWords, v0, r);
- } else {
- r = new int[pow5.nWords + 2 + ((p2 != 0) ? 1 : 0)];
- mult(pow5.data, pow5.nWords, v0, v1, r);
- }
- return (new FDBigInteger(r, pow5.offset)).leftShift(p2);
- }
- } else if (p2 != 0) {
- if (bitcount == 0) {
- return new FDBigInteger(new int[]{v0, v1}, wordcount);
- } else {
- return new FDBigInteger(new int[]{
- v0 << bitcount,
- (v1 << bitcount) | (v0 >>> (32 - bitcount)),
- v1 >>> (32 - bitcount)
- }, wordcount);
- }
- }
- return new FDBigInteger(new int[]{v0, v1}, 0);
- }
-
- /**
- * Returns an <code>FDBigInteger</code> with the numerical value
- * <code>2<sup>p2</sup></code>.
- *
- * @param p2 The exponent of 2.
- * @return <code>2<sup>p2</sup></code>
- */
- /*@
- @ requires p2 >= 0;
- @ assignable \nothing;
- @ ensures \result.value() == pow52(0, p2);
- @*/
- private static FDBigInteger valueOfPow2(int p2) {
- int wordcount = p2 >> 5;
- int bitcount = p2 & 0x1f;
- return new FDBigInteger(new int[]{1 << bitcount}, wordcount);
- }
-
- /**
- * Removes all leading zeros from this <code>FDBigInteger</code> adjusting
- * the offset and number of non-zero leading words accordingly.
- */
- /*@
- @ requires data != null;
- @ requires 0 <= nWords && nWords <= data.length && offset >= 0;
- @ requires nWords == 0 ==> offset == 0;
- @ ensures nWords == 0 ==> offset == 0;
- @ ensures nWords > 0 ==> data[nWords - 1] != 0;
- @*/
- private /*@ helper @*/ void trimLeadingZeros() {
- int i = nWords;
- if (i > 0 && (data[--i] == 0)) {
- //for (; i > 0 && data[i - 1] == 0; i--) ;
- while(i > 0 && data[i - 1] == 0) {
- i--;
- }
- this.nWords = i;
- if (i == 0) { // all words are zero
- this.offset = 0;
- }
- }
- }
-
- /**
- * Retrieves the normalization bias of the <code>FDBigIntger</code>. The
- * normalization bias is a left shift such that after it the highest word
- * of the value will have the 4 highest bits equal to zero:
- * {@code (highestWord & 0xf0000000) == 0}, but the next bit should be 1
- * {@code (highestWord & 0x08000000) != 0}.
- *
- * @return The normalization bias.
- */
- /*@
- @ requires this.value() > 0;
- @*/
- public /*@ pure @*/ int getNormalizationBias() {
- if (nWords == 0) {
- throw new IllegalArgumentException("Zero value cannot be normalized");
- }
- int zeros = Integer.numberOfLeadingZeros(data[nWords - 1]);
- return (zeros < 4) ? 28 + zeros : zeros - 4;
- }
-
- // TODO: Why is anticount param needed if it is always 32 - bitcount?
- /**
- * Left shifts the contents of one int array into another.
- *
- * @param src The source array.
- * @param idx The initial index of the source array.
- * @param result The destination array.
- * @param bitcount The left shift.
- * @param anticount The left anti-shift, e.g., <code>32-bitcount</code>.
- * @param prev The prior source value.
- */
- /*@
- @ requires 0 < bitcount && bitcount < 32 && anticount == 32 - bitcount;
- @ requires src.length >= idx && result.length > idx;
- @ assignable result[*];
- @ ensures AP(result, \old(idx + 1)) == \old((AP(src, idx) + UNSIGNED(prev) << (idx*32)) << bitcount);
- @*/
- private static void leftShift(int[] src, int idx, int result[], int bitcount, int anticount, int prev){
- for (; idx > 0; idx--) {
- int v = (prev << bitcount);
- prev = src[idx - 1];
- v |= (prev >>> anticount);
- result[idx] = v;
- }
- int v = prev << bitcount;
- result[0] = v;
- }
-
- /**
- * Shifts this <code>FDBigInteger</code> to the left. The shift is performed
- * in-place unless the <code>FDBigInteger</code> is immutable in which case
- * a new instance of <code>FDBigInteger</code> is returned.
- *
- * @param shift The number of bits to shift left.
- * @return The shifted <code>FDBigInteger</code>.
- */
- /*@
- @ requires this.value() == 0 || shift == 0;
- @ assignable \nothing;
- @ ensures \result == this;
- @
- @ also
- @
- @ requires this.value() > 0 && shift > 0 && this.isImmutable;
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() << shift);
- @
- @ also
- @
- @ requires this.value() > 0 && shift > 0 && this.isImmutable;
- @ assignable \nothing;
- @ ensures \result == this;
- @ ensures \result.value() == \old(this.value() << shift);
- @*/
- public FDBigInteger leftShift(int shift) {
- if (shift == 0 || nWords == 0) {
- return this;
- }
- int wordcount = shift >> 5;
- int bitcount = shift & 0x1f;
- if (this.isImmutable) {
- if (bitcount == 0) {
- return new FDBigInteger(Arrays.copyOf(data, nWords), offset + wordcount);
- } else {
- int anticount = 32 - bitcount;
- int idx = nWords - 1;
- int prev = data[idx];
- int hi = prev >>> anticount;
- int[] result;
- if (hi != 0) {
- result = new int[nWords + 1];
- result[nWords] = hi;
- } else {
- result = new int[nWords];
- }
- leftShift(data,idx,result,bitcount,anticount,prev);
- return new FDBigInteger(result, offset + wordcount);
- }
- } else {
- if (bitcount != 0) {
- int anticount = 32 - bitcount;
- if ((data[0] << bitcount) == 0) {
- int idx = 0;
- int prev = data[idx];
- for (; idx < nWords - 1; idx++) {
- int v = (prev >>> anticount);
- prev = data[idx + 1];
- v |= (prev << bitcount);
- data[idx] = v;
- }
- int v = prev >>> anticount;
- data[idx] = v;
- if(v==0) {
- nWords--;
- }
- offset++;
- } else {
- int idx = nWords - 1;
- int prev = data[idx];
- int hi = prev >>> anticount;
- int[] result = data;
- int[] src = data;
- if (hi != 0) {
- if(nWords == data.length) {
- data = result = new int[nWords + 1];
- }
- result[nWords++] = hi;
- }
- leftShift(src,idx,result,bitcount,anticount,prev);
- }
- }
- offset += wordcount;
- return this;
- }
- }
-
- /**
- * Returns the number of <code>int</code>s this <code>FDBigInteger</code> represents.
- *
- * @return Number of <code>int</code>s required to represent this <code>FDBigInteger</code>.
- */
- /*@
- @ requires this.value() == 0;
- @ ensures \result == 0;
- @
- @ also
- @
- @ requires this.value() > 0;
- @ ensures ((\bigint)1) << (\result - 1) <= this.value() && this.value() <= ((\bigint)1) << \result;
- @*/
- private /*@ pure @*/ int size() {
- return nWords + offset;
- }
-
-
- /**
- * Computes
- * <pre>
- * q = (int)( this / S )
- * this = 10 * ( this mod S )
- * Return q.
- * </pre>
- * This is the iteration step of digit development for output.
- * We assume that S has been normalized, as above, and that
- * "this" has been left-shifted accordingly.
- * Also assumed, of course, is that the result, q, can be expressed
- * as an integer, {@code 0 <= q < 10}.
- *
- * @param S The divisor of this <code>FDBigInteger</code>.
- * @return <code>q = (int)(this / S)</code>.
- */
- /*@
- @ requires !this.isImmutable;
- @ requires this.size() <= S.size();
- @ requires this.data.length + this.offset >= S.size();
- @ requires S.value() >= ((\bigint)1) << (S.size()*32 - 4);
- @ assignable this.nWords, this.offset, this.data, this.data[*];
- @ ensures \result == \old(this.value() / S.value());
- @ ensures this.value() == \old(10 * (this.value() % S.value()));
- @*/
- public int quoRemIteration(FDBigInteger S) throws IllegalArgumentException {
- assert !this.isImmutable : "cannot modify immutable value";
- // ensure that this and S have the same number of
- // digits. If S is properly normalized and q < 10 then
- // this must be so.
- int thSize = this.size();
- int sSize = S.size();
- if (thSize < sSize) {
- // this value is significantly less than S, result of division is zero.
- // just mult this by 10.
- int p = multAndCarryBy10(this.data, this.nWords, this.data);
- if(p!=0) {
- this.data[nWords++] = p;
- } else {
- trimLeadingZeros();
- }
- return 0;
- } else if (thSize > sSize) {
- throw new IllegalArgumentException("disparate values");
- }
- // estimate q the obvious way. We will usually be
- // right. If not, then we're only off by a little and
- // will re-add.
- long q = (this.data[this.nWords - 1] & LONG_MASK) / (S.data[S.nWords - 1] & LONG_MASK);
- long diff = multDiffMe(q, S);
- if (diff != 0L) {
- //@ assert q != 0;
- //@ assert this.offset == \old(Math.min(this.offset, S.offset));
- //@ assert this.offset <= S.offset;
-
- // q is too big.
- // add S back in until this turns +. This should
- // not be very many times!
- long sum = 0L;
- int tStart = S.offset - this.offset;
- //@ assert tStart >= 0;
- int[] sd = S.data;
- int[] td = this.data;
- while (sum == 0L) {
- for (int sIndex = 0, tIndex = tStart; tIndex < this.nWords; sIndex++, tIndex++) {
- sum += (td[tIndex] & LONG_MASK) + (sd[sIndex] & LONG_MASK);
- td[tIndex] = (int) sum;
- sum >>>= 32; // Signed or unsigned, answer is 0 or 1
- }
- //
- // Originally the following line read
- // "if ( sum !=0 && sum != -1 )"
- // but that would be wrong, because of the
- // treatment of the two values as entirely unsigned,
- // it would be impossible for a carry-out to be interpreted
- // as -1 -- it would have to be a single-bit carry-out, or +1.
- //
- assert sum == 0 || sum == 1 : sum; // carry out of division correction
- q -= 1;
- }
- }
- // finally, we can multiply this by 10.
- // it cannot overflow, right, as the high-order word has
- // at least 4 high-order zeros!
- int p = multAndCarryBy10(this.data, this.nWords, this.data);
- assert p == 0 : p; // Carry out of *10
- trimLeadingZeros();
- return (int) q;
- }
-
- /**
- * Multiplies this <code>FDBigInteger</code> by 10. The operation will be
- * performed in place unless the <code>FDBigInteger</code> is immutable in
- * which case a new <code>FDBigInteger</code> will be returned.
- *
- * @return The <code>FDBigInteger</code> multiplied by 10.
- */
- /*@
- @ requires this.value() == 0;
- @ assignable \nothing;
- @ ensures \result == this;
- @
- @ also
- @
- @ requires this.value() > 0 && this.isImmutable;
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() * 10);
- @
- @ also
- @
- @ requires this.value() > 0 && !this.isImmutable;
- @ assignable this.nWords, this.data, this.data[*];
- @ ensures \result == this;
- @ ensures \result.value() == \old(this.value() * 10);
- @*/
- public FDBigInteger multBy10() {
- if (nWords == 0) {
- return this;
- }
- if (isImmutable) {
- int[] res = new int[nWords + 1];
- res[nWords] = multAndCarryBy10(data, nWords, res);
- return new FDBigInteger(res, offset);
- } else {
- int p = multAndCarryBy10(this.data, this.nWords, this.data);
- if (p != 0) {
- if (nWords == data.length) {
- if (data[0] == 0) {
- System.arraycopy(data, 1, data, 0, --nWords);
- offset++;
- } else {
- data = Arrays.copyOf(data, data.length + 1);
- }
- }
- data[nWords++] = p;
- } else {
- trimLeadingZeros();
- }
- return this;
- }
- }
-
- /**
- * Multiplies this <code>FDBigInteger</code> by
- * <code>5<sup>p5</sup> * 2<sup>p2</sup></code>. The operation will be
- * performed in place if possible, otherwise a new <code>FDBigInteger</code>
- * will be returned.
- *
- * @param p5 The exponent of the power-of-five factor.
- * @param p2 The exponent of the power-of-two factor.
- * @return The multiplication result.
- */
- /*@
- @ requires this.value() == 0 || p5 == 0 && p2 == 0;
- @ assignable \nothing;
- @ ensures \result == this;
- @
- @ also
- @
- @ requires this.value() > 0 && (p5 > 0 && p2 >= 0 || p5 == 0 && p2 > 0 && this.isImmutable);
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() * pow52(p5, p2));
- @
- @ also
- @
- @ requires this.value() > 0 && p5 == 0 && p2 > 0 && !this.isImmutable;
- @ assignable this.nWords, this.data, this.data[*];
- @ ensures \result == this;
- @ ensures \result.value() == \old(this.value() * pow52(p5, p2));
- @*/
- public FDBigInteger multByPow52(int p5, int p2) {
- if (this.nWords == 0) {
- return this;
- }
- FDBigInteger res = this;
- if (p5 != 0) {
- int[] r;
- int extraSize = (p2 != 0) ? 1 : 0;
- if (p5 < SMALL_5_POW.length) {
- r = new int[this.nWords + 1 + extraSize];
- mult(this.data, this.nWords, SMALL_5_POW[p5], r);
- res = new FDBigInteger(r, this.offset);
- } else {
- FDBigInteger pow5 = big5pow(p5);
- r = new int[this.nWords + pow5.size() + extraSize];
- mult(this.data, this.nWords, pow5.data, pow5.nWords, r);
- res = new FDBigInteger(r, this.offset + pow5.offset);
- }
- }
- return res.leftShift(p2);
- }
-
- /**
- * Multiplies two big integers represented as int arrays.
- *
- * @param s1 The first array factor.
- * @param s1Len The number of elements of <code>s1</code> to use.
- * @param s2 The second array factor.
- * @param s2Len The number of elements of <code>s2</code> to use.
- * @param dst The product array.
- */
- /*@
- @ requires s1 != dst && s2 != dst;
- @ requires s1.length >= s1Len && s2.length >= s2Len && dst.length >= s1Len + s2Len;
- @ assignable dst[0 .. s1Len + s2Len - 1];
- @ ensures AP(dst, s1Len + s2Len) == \old(AP(s1, s1Len) * AP(s2, s2Len));
- @*/
- private static void mult(int[] s1, int s1Len, int[] s2, int s2Len, int[] dst) {
- for (int i = 0; i < s1Len; i++) {
- long v = s1[i] & LONG_MASK;
- long p = 0L;
- for (int j = 0; j < s2Len; j++) {
- p += (dst[i + j] & LONG_MASK) + v * (s2[j] & LONG_MASK);
- dst[i + j] = (int) p;
- p >>>= 32;
- }
- dst[i + s2Len] = (int) p;
- }
- }
-
- /**
- * Subtracts the supplied <code>FDBigInteger</code> subtrahend from this
- * <code>FDBigInteger</code>. Assert that the result is positive.
- * If the subtrahend is immutable, store the result in this(minuend).
- * If this(minuend) is immutable a new <code>FDBigInteger</code> is created.
- *
- * @param subtrahend The <code>FDBigInteger</code> to be subtracted.
- * @return This <code>FDBigInteger</code> less the subtrahend.
- */
- /*@
- @ requires this.isImmutable;
- @ requires this.value() >= subtrahend.value();
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() - subtrahend.value());
- @
- @ also
- @
- @ requires !subtrahend.isImmutable;
- @ requires this.value() >= subtrahend.value();
- @ assignable this.nWords, this.offset, this.data, this.data[*];
- @ ensures \result == this;
- @ ensures \result.value() == \old(this.value() - subtrahend.value());
- @*/
- public FDBigInteger leftInplaceSub(FDBigInteger subtrahend) {
- assert this.size() >= subtrahend.size() : "result should be positive";
- FDBigInteger minuend;
- if (this.isImmutable) {
- minuend = new FDBigInteger(this.data.clone(), this.offset);
- } else {
- minuend = this;
- }
- int offsetDiff = subtrahend.offset - minuend.offset;
- int[] sData = subtrahend.data;
- int[] mData = minuend.data;
- int subLen = subtrahend.nWords;
- int minLen = minuend.nWords;
- if (offsetDiff < 0) {
- // need to expand minuend
- int rLen = minLen - offsetDiff;
- if (rLen < mData.length) {
- System.arraycopy(mData, 0, mData, -offsetDiff, minLen);
- Arrays.fill(mData, 0, -offsetDiff, 0);
- } else {
- int[] r = new int[rLen];
- System.arraycopy(mData, 0, r, -offsetDiff, minLen);
- minuend.data = mData = r;
- }
- minuend.offset = subtrahend.offset;
- minuend.nWords = minLen = rLen;
- offsetDiff = 0;
- }
- long borrow = 0L;
- int mIndex = offsetDiff;
- for (int sIndex = 0; sIndex < subLen && mIndex < minLen; sIndex++, mIndex++) {
- long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow;
- mData[mIndex] = (int) diff;
- borrow = diff >> 32; // signed shift
- }
- for (; borrow != 0 && mIndex < minLen; mIndex++) {
- long diff = (mData[mIndex] & LONG_MASK) + borrow;
- mData[mIndex] = (int) diff;
- borrow = diff >> 32; // signed shift
- }
- assert borrow == 0L : borrow; // borrow out of subtract,
- // result should be positive
- minuend.trimLeadingZeros();
- return minuend;
- }
-
- /**
- * Subtracts the supplied <code>FDBigInteger</code> subtrahend from this
- * <code>FDBigInteger</code>. Assert that the result is positive.
- * If the this(minuend) is immutable, store the result in subtrahend.
- * If subtrahend is immutable a new <code>FDBigInteger</code> is created.
- *
- * @param subtrahend The <code>FDBigInteger</code> to be subtracted.
- * @return This <code>FDBigInteger</code> less the subtrahend.
- */
- /*@
- @ requires subtrahend.isImmutable;
- @ requires this.value() >= subtrahend.value();
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() - subtrahend.value());
- @
- @ also
- @
- @ requires !subtrahend.isImmutable;
- @ requires this.value() >= subtrahend.value();
- @ assignable subtrahend.nWords, subtrahend.offset, subtrahend.data, subtrahend.data[*];
- @ ensures \result == subtrahend;
- @ ensures \result.value() == \old(this.value() - subtrahend.value());
- @*/
- public FDBigInteger rightInplaceSub(FDBigInteger subtrahend) {
- assert this.size() >= subtrahend.size() : "result should be positive";
- FDBigInteger minuend = this;
- if (subtrahend.isImmutable) {
- subtrahend = new FDBigInteger(subtrahend.data.clone(), subtrahend.offset);
- }
- int offsetDiff = minuend.offset - subtrahend.offset;
- int[] sData = subtrahend.data;
- int[] mData = minuend.data;
- int subLen = subtrahend.nWords;
- int minLen = minuend.nWords;
- if (offsetDiff < 0) {
- int rLen = minLen;
- if (rLen < sData.length) {
- System.arraycopy(sData, 0, sData, -offsetDiff, subLen);
- Arrays.fill(sData, 0, -offsetDiff, 0);
- } else {
- int[] r = new int[rLen];
- System.arraycopy(sData, 0, r, -offsetDiff, subLen);
- subtrahend.data = sData = r;
- }
- subtrahend.offset = minuend.offset;
- subLen -= offsetDiff;
- offsetDiff = 0;
- } else {
- int rLen = minLen + offsetDiff;
- if (rLen >= sData.length) {
- subtrahend.data = sData = Arrays.copyOf(sData, rLen);
- }
- }
- //@ assert minuend == this && minuend.value() == \old(this.value());
- //@ assert mData == minuend.data && minLen == minuend.nWords;
- //@ assert subtrahend.offset + subtrahend.data.length >= minuend.size();
- //@ assert sData == subtrahend.data;
- //@ assert AP(subtrahend.data, subtrahend.data.length) << subtrahend.offset == \old(subtrahend.value());
- //@ assert subtrahend.offset == Math.min(\old(this.offset), minuend.offset);
- //@ assert offsetDiff == minuend.offset - subtrahend.offset;
- //@ assert 0 <= offsetDiff && offsetDiff + minLen <= sData.length;
- int sIndex = 0;
- long borrow = 0L;
- for (; sIndex < offsetDiff; sIndex++) {
- long diff = 0L - (sData[sIndex] & LONG_MASK) + borrow;
- sData[sIndex] = (int) diff;
- borrow = diff >> 32; // signed shift
- }
- //@ assert sIndex == offsetDiff;
- for (int mIndex = 0; mIndex < minLen; sIndex++, mIndex++) {
- //@ assert sIndex == offsetDiff + mIndex;
- long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow;
- sData[sIndex] = (int) diff;
- borrow = diff >> 32; // signed shift
- }
- assert borrow == 0L : borrow; // borrow out of subtract,
- // result should be positive
- subtrahend.nWords = sIndex;
- subtrahend.trimLeadingZeros();
- return subtrahend;
-
- }
-
- /**
- * Determines whether all elements of an array are zero for all indices less
- * than a given index.
- *
- * @param a The array to be examined.
- * @param from The index strictly below which elements are to be examined.
- * @return Zero if all elements in range are zero, 1 otherwise.
- */
- /*@
- @ requires 0 <= from && from <= a.length;
- @ ensures \result == (AP(a, from) == 0 ? 0 : 1);
- @*/
- private /*@ pure @*/ static int checkZeroTail(int[] a, int from) {
- while (from > 0) {
- if (a[--from] != 0) {
- return 1;
- }
- }
- return 0;
- }
-
- /**
- * Compares the parameter with this <code>FDBigInteger</code>. Returns an
- * integer accordingly as:
- * <pre>{@code
- * > 0: this > other
- * 0: this == other
- * < 0: this < other
- * }</pre>
- *
- * @param other The <code>FDBigInteger</code> to compare.
- * @return A negative value, zero, or a positive value according to the
- * result of the comparison.
- */
- /*@
- @ ensures \result == (this.value() < other.value() ? -1 : this.value() > other.value() ? +1 : 0);
- @*/
- public /*@ pure @*/ int cmp(FDBigInteger other) {
- int aSize = nWords + offset;
- int bSize = other.nWords + other.offset;
- if (aSize > bSize) {
- return 1;
- } else if (aSize < bSize) {
- return -1;
- }
- int aLen = nWords;
- int bLen = other.nWords;
- while (aLen > 0 && bLen > 0) {
- int a = data[--aLen];
- int b = other.data[--bLen];
- if (a != b) {
- return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
- }
- }
- if (aLen > 0) {
- return checkZeroTail(data, aLen);
- }
- if (bLen > 0) {
- return -checkZeroTail(other.data, bLen);
- }
- return 0;
- }
-
- /**
- * Compares this <code>FDBigInteger</code> with
- * <code>5<sup>p5</sup> * 2<sup>p2</sup></code>.
- * Returns an integer accordingly as:
- * <pre>{@code
- * > 0: this > other
- * 0: this == other
- * < 0: this < other
- * }</pre>
- * @param p5 The exponent of the power-of-five factor.
- * @param p2 The exponent of the power-of-two factor.
- * @return A negative value, zero, or a positive value according to the
- * result of the comparison.
- */
- /*@
- @ requires p5 >= 0 && p2 >= 0;
- @ ensures \result == (this.value() < pow52(p5, p2) ? -1 : this.value() > pow52(p5, p2) ? +1 : 0);
- @*/
- public /*@ pure @*/ int cmpPow52(int p5, int p2) {
- if (p5 == 0) {
- int wordcount = p2 >> 5;
- int bitcount = p2 & 0x1f;
- int size = this.nWords + this.offset;
- if (size > wordcount + 1) {
- return 1;
- } else if (size < wordcount + 1) {
- return -1;
- }
- int a = this.data[this.nWords -1];
- int b = 1 << bitcount;
- if (a != b) {
- return ( (a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
- }
- return checkZeroTail(this.data, this.nWords - 1);
- }
- return this.cmp(big5pow(p5).leftShift(p2));
- }
-
- /**
- * Compares this <code>FDBigInteger</code> with <code>x + y</code>. Returns a
- * value according to the comparison as:
- * <pre>{@code
- * -1: this < x + y
- * 0: this == x + y
- * 1: this > x + y
- * }</pre>
- * @param x The first addend of the sum to compare.
- * @param y The second addend of the sum to compare.
- * @return -1, 0, or 1 according to the result of the comparison.
- */
- /*@
- @ ensures \result == (this.value() < x.value() + y.value() ? -1 : this.value() > x.value() + y.value() ? +1 : 0);
- @*/
- public /*@ pure @*/ int addAndCmp(FDBigInteger x, FDBigInteger y) {
- FDBigInteger big;
- FDBigInteger small;
- int xSize = x.size();
- int ySize = y.size();
- int bSize;
- int sSize;
- if (xSize >= ySize) {
- big = x;
- small = y;
- bSize = xSize;
- sSize = ySize;
- } else {
- big = y;
- small = x;
- bSize = ySize;
- sSize = xSize;
- }
- int thSize = this.size();
- if (bSize == 0) {
- return thSize == 0 ? 0 : 1;
- }
- if (sSize == 0) {
- return this.cmp(big);
- }
- if (bSize > thSize) {
- return -1;
- }
- if (bSize + 1 < thSize) {
- return 1;
- }
- long top = (big.data[big.nWords - 1] & LONG_MASK);
- if (sSize == bSize) {
- top += (small.data[small.nWords - 1] & LONG_MASK);
- }
- if ((top >>> 32) == 0) {
- if (((top + 1) >>> 32) == 0) {
- // good case - no carry extension
- if (bSize < thSize) {
- return 1;
- }
- // here sum.nWords == this.nWords
- long v = (this.data[this.nWords - 1] & LONG_MASK);
- if (v < top) {
- return -1;
- }
- if (v > top + 1) {
- return 1;
- }
- }
- } else { // (top>>>32)!=0 guaranteed carry extension
- if (bSize + 1 > thSize) {
- return -1;
- }
- // here sum.nWords == this.nWords
- top >>>= 32;
- long v = (this.data[this.nWords - 1] & LONG_MASK);
- if (v < top) {
- return -1;
- }
- if (v > top + 1) {
- return 1;
- }
- }
- return this.cmp(big.add(small));
- }
-
- /**
- * Makes this <code>FDBigInteger</code> immutable.
- */
- /*@
- @ assignable this.isImmutable;
- @ ensures this.isImmutable;
- @*/
- public void makeImmutable() {
- this.isImmutable = true;
- }
-
- /**
- * Multiplies this <code>FDBigInteger</code> by an integer.
- *
- * @param i The factor by which to multiply this <code>FDBigInteger</code>.
- * @return This <code>FDBigInteger</code> multiplied by an integer.
- */
- /*@
- @ requires this.value() == 0;
- @ assignable \nothing;
- @ ensures \result == this;
- @
- @ also
- @
- @ requires this.value() != 0;
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() * UNSIGNED(i));
- @*/
- private FDBigInteger mult(int i) {
- if (this.nWords == 0) {
- return this;
- }
- int[] r = new int[nWords + 1];
- mult(data, nWords, i, r);
- return new FDBigInteger(r, offset);
- }
-
- /**
- * Multiplies this <code>FDBigInteger</code> by another <code>FDBigInteger</code>.
- *
- * @param other The <code>FDBigInteger</code> factor by which to multiply.
- * @return The product of this and the parameter <code>FDBigInteger</code>s.
- */
- /*@
- @ requires this.value() == 0;
- @ assignable \nothing;
- @ ensures \result == this;
- @
- @ also
- @
- @ requires this.value() != 0 && other.value() == 0;
- @ assignable \nothing;
- @ ensures \result == other;
- @
- @ also
- @
- @ requires this.value() != 0 && other.value() != 0;
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() * other.value());
- @*/
- private FDBigInteger mult(FDBigInteger other) {
- if (this.nWords == 0) {
- return this;
- }
- if (this.size() == 1) {
- return other.mult(data[0]);
- }
- if (other.nWords == 0) {
- return other;
- }
- if (other.size() == 1) {
- return this.mult(other.data[0]);
- }
- int[] r = new int[nWords + other.nWords];
- mult(this.data, this.nWords, other.data, other.nWords, r);
- return new FDBigInteger(r, this.offset + other.offset);
- }
-
- /**
- * Adds another <code>FDBigInteger</code> to this <code>FDBigInteger</code>.
- *
- * @param other The <code>FDBigInteger</code> to add.
- * @return The sum of the <code>FDBigInteger</code>s.
- */
- /*@
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() + other.value());
- @*/
- private FDBigInteger add(FDBigInteger other) {
- FDBigInteger big, small;
- int bigLen, smallLen;
- int tSize = this.size();
- int oSize = other.size();
- if (tSize >= oSize) {
- big = this;
- bigLen = tSize;
- small = other;
- smallLen = oSize;
- } else {
- big = other;
- bigLen = oSize;
- small = this;
- smallLen = tSize;
- }
- int[] r = new int[bigLen + 1];
- int i = 0;
- long carry = 0L;
- for (; i < smallLen; i++) {
- carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) )
- + ((i < small.offset ? 0L : (small.data[i - small.offset] & LONG_MASK)));
- r[i] = (int) carry;
- carry >>= 32; // signed shift.
- }
- for (; i < bigLen; i++) {
- carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) );
- r[i] = (int) carry;
- carry >>= 32; // signed shift.
- }
- r[bigLen] = (int) carry;
- return new FDBigInteger(r, 0);
- }
-
-
- /**
- * Multiplies a <code>FDBigInteger</code> by an int and adds another int. The
- * result is computed in place. This method is intended only to be invoked
- * from
- * <code>
- * FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits)
- * </code>.
- *
- * @param iv The factor by which to multiply this <code>FDBigInteger</code>.
- * @param addend The value to add to the product of this
- * <code>FDBigInteger</code> and <code>iv</code>.
- */
- /*@
- @ requires this.value()*UNSIGNED(iv) + UNSIGNED(addend) < ((\bigint)1) << ((this.data.length + this.offset)*32);
- @ assignable this.data[*];
- @ ensures this.value() == \old(this.value()*UNSIGNED(iv) + UNSIGNED(addend));
- @*/
- private /*@ helper @*/ void multAddMe(int iv, int addend) {
- long v = iv & LONG_MASK;
- // unroll 0th iteration, doing addition.
- long p = v * (data[0] & LONG_MASK) + (addend & LONG_MASK);
- data[0] = (int) p;
- p >>>= 32;
- for (int i = 1; i < nWords; i++) {
- p += v * (data[i] & LONG_MASK);
- data[i] = (int) p;
- p >>>= 32;
- }
- if (p != 0L) {
- data[nWords++] = (int) p; // will fail noisily if illegal!
- }
- }
-
- //
- // original doc:
- //
- // do this -=q*S
- // returns borrow
- //
- /**
- * Multiplies the parameters and subtracts them from this
- * <code>FDBigInteger</code>.
- *
- * @param q The integer parameter.
- * @param S The <code>FDBigInteger</code> parameter.
- * @return <code>this - q*S</code>.
- */
- /*@
- @ ensures nWords == 0 ==> offset == 0;
- @ ensures nWords > 0 ==> data[nWords - 1] != 0;
- @*/
- /*@
- @ requires 0 < q && q <= (1L << 31);
- @ requires data != null;
- @ requires 0 <= nWords && nWords <= data.length && offset >= 0;
- @ requires !this.isImmutable;
- @ requires this.size() == S.size();
- @ requires this != S;
- @ assignable this.nWords, this.offset, this.data, this.data[*];
- @ ensures -q <= \result && \result <= 0;
- @ ensures this.size() == \old(this.size());
- @ ensures this.value() + (\result << (this.size()*32)) == \old(this.value() - q*S.value());
- @ ensures this.offset == \old(Math.min(this.offset, S.offset));
- @ ensures \old(this.offset <= S.offset) ==> this.nWords == \old(this.nWords);
- @ ensures \old(this.offset <= S.offset) ==> this.offset == \old(this.offset);
- @ ensures \old(this.offset <= S.offset) ==> this.data == \old(this.data);
- @
- @ also
- @
- @ requires q == 0;
- @ assignable \nothing;
- @ ensures \result == 0;
- @*/
- private /*@ helper @*/ long multDiffMe(long q, FDBigInteger S) {
- long diff = 0L;
- if (q != 0) {
- int deltaSize = S.offset - this.offset;
- if (deltaSize >= 0) {
- int[] sd = S.data;
- int[] td = this.data;
- for (int sIndex = 0, tIndex = deltaSize; sIndex < S.nWords; sIndex++, tIndex++) {
- diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK);
- td[tIndex] = (int) diff;
- diff >>= 32; // N.B. SIGNED shift.
- }
- } else {
- deltaSize = -deltaSize;
- int[] rd = new int[nWords + deltaSize];
- int sIndex = 0;
- int rIndex = 0;
- int[] sd = S.data;
- for (; rIndex < deltaSize && sIndex < S.nWords; sIndex++, rIndex++) {
- diff -= q * (sd[sIndex] & LONG_MASK);
- rd[rIndex] = (int) diff;
- diff >>= 32; // N.B. SIGNED shift.
- }
- int tIndex = 0;
- int[] td = this.data;
- for (; sIndex < S.nWords; sIndex++, tIndex++, rIndex++) {
- diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK);
- rd[rIndex] = (int) diff;
- diff >>= 32; // N.B. SIGNED shift.
- }
- this.nWords += deltaSize;
- this.offset -= deltaSize;
- this.data = rd;
- }
- }
- return diff;
- }
-
-
- /**
- * Multiplies by 10 a big integer represented as an array. The final carry
- * is returned.
- *
- * @param src The array representation of the big integer.
- * @param srcLen The number of elements of <code>src</code> to use.
- * @param dst The product array.
- * @return The final carry of the multiplication.
- */
- /*@
- @ requires src.length >= srcLen && dst.length >= srcLen;
- @ assignable dst[0 .. srcLen - 1];
- @ ensures 0 <= \result && \result < 10;
- @ ensures AP(dst, srcLen) + (\result << (srcLen*32)) == \old(AP(src, srcLen) * 10);
- @*/
- private static int multAndCarryBy10(int[] src, int srcLen, int[] dst) {
- long carry = 0;
- for (int i = 0; i < srcLen; i++) {
- long product = (src[i] & LONG_MASK) * 10L + carry;
- dst[i] = (int) product;
- carry = product >>> 32;
- }
- return (int) carry;
- }
-
- /**
- * Multiplies by a constant value a big integer represented as an array.
- * The constant factor is an <code>int</code>.
- *
- * @param src The array representation of the big integer.
- * @param srcLen The number of elements of <code>src</code> to use.
- * @param value The constant factor by which to multiply.
- * @param dst The product array.
- */
- /*@
- @ requires src.length >= srcLen && dst.length >= srcLen + 1;
- @ assignable dst[0 .. srcLen];
- @ ensures AP(dst, srcLen + 1) == \old(AP(src, srcLen) * UNSIGNED(value));
- @*/
- private static void mult(int[] src, int srcLen, int value, int[] dst) {
- long val = value & LONG_MASK;
- long carry = 0;
- for (int i = 0; i < srcLen; i++) {
- long product = (src[i] & LONG_MASK) * val + carry;
- dst[i] = (int) product;
- carry = product >>> 32;
- }
- dst[srcLen] = (int) carry;
- }
-
- /**
- * Multiplies by a constant value a big integer represented as an array.
- * The constant factor is a long represent as two <code>int</code>s.
- *
- * @param src The array representation of the big integer.
- * @param srcLen The number of elements of <code>src</code> to use.
- * @param v0 The lower 32 bits of the long factor.
- * @param v1 The upper 32 bits of the long factor.
- * @param dst The product array.
- */
- /*@
- @ requires src != dst;
- @ requires src.length >= srcLen && dst.length >= srcLen + 2;
- @ assignable dst[0 .. srcLen + 1];
- @ ensures AP(dst, srcLen + 2) == \old(AP(src, srcLen) * (UNSIGNED(v0) + (UNSIGNED(v1) << 32)));
- @*/
- private static void mult(int[] src, int srcLen, int v0, int v1, int[] dst) {
- long v = v0 & LONG_MASK;
- long carry = 0;
- for (int j = 0; j < srcLen; j++) {
- long product = v * (src[j] & LONG_MASK) + carry;
- dst[j] = (int) product;
- carry = product >>> 32;
- }
- dst[srcLen] = (int) carry;
- v = v1 & LONG_MASK;
- carry = 0;
- for (int j = 0; j < srcLen; j++) {
- long product = (dst[j + 1] & LONG_MASK) + v * (src[j] & LONG_MASK) + carry;
- dst[j + 1] = (int) product;
- carry = product >>> 32;
- }
- dst[srcLen + 1] = (int) carry;
- }
-
- // Fails assertion for negative exponent.
- /**
- * Computes <code>5</code> raised to a given power.
- *
- * @param p The exponent of 5.
- * @return <code>5<sup>p</sup></code>.
- */
- private static FDBigInteger big5pow(int p) {
- assert p >= 0 : p; // negative power of 5
- if (p < MAX_FIVE_POW) {
- return POW_5_CACHE[p];
- }
- return big5powRec(p);
- }
-
- // slow path
- /**
- * Computes <code>5</code> raised to a given power.
- *
- * @param p The exponent of 5.
- * @return <code>5<sup>p</sup></code>.
- */
- private static FDBigInteger big5powRec(int p) {
- if (p < MAX_FIVE_POW) {
- return POW_5_CACHE[p];
- }
- // construct the value.
- // recursively.
- int q, r;
- // in order to compute 5^p,
- // compute its square root, 5^(p/2) and square.
- // or, let q = p / 2, r = p -q, then
- // 5^p = 5^(q+r) = 5^q * 5^r
- q = p >> 1;
- r = p - q;
- FDBigInteger bigq = big5powRec(q);
- if (r < SMALL_5_POW.length) {
- return bigq.mult(SMALL_5_POW[r]);
- } else {
- return bigq.mult(big5powRec(r));
- }
- }
-
- // for debugging ...
- /**
- * Converts this <code>FDBigInteger</code> to a hexadecimal string.
- *
- * @return The hexadecimal string representation.
- */
- public String toHexString(){
- if(nWords ==0) {
- return "0";
- }
- StringBuilder sb = new StringBuilder((nWords +offset)*8);
- for(int i= nWords -1; i>=0; i--) {
- String subStr = Integer.toHexString(data[i]);
- for(int j = subStr.length(); j<8; j++) {
- sb.append('0');
- }
- sb.append(subStr);
- }
- for(int i=offset; i>0; i--) {
- sb.append("00000000");
- }
- return sb.toString();
- }
-
- // for debugging ...
- /**
- * Converts this <code>FDBigInteger</code> to a <code>BigInteger</code>.
- *
- * @return The <code>BigInteger</code> representation.
- */
- public BigInteger toBigInteger() {
- byte[] magnitude = new byte[nWords * 4 + 1];
- for (int i = 0; i < nWords; i++) {
- int w = data[i];
- magnitude[magnitude.length - 4 * i - 1] = (byte) w;
- magnitude[magnitude.length - 4 * i - 2] = (byte) (w >> 8);
- magnitude[magnitude.length - 4 * i - 3] = (byte) (w >> 16);
- magnitude[magnitude.length - 4 * i - 4] = (byte) (w >> 24);
- }
- return new BigInteger(magnitude).shiftLeft(offset * 32);
- }
-
- // for debugging ...
- /**
- * Converts this <code>FDBigInteger</code> to a string.
- *
- * @return The string representation.
- */
- @Override
- public String toString(){
- return toBigInteger().toString();
- }
-}
--- a/jdk/src/java.base/share/classes/sun/misc/FloatConsts.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.misc;
-
-/**
- * This class contains additional constants documenting limits of the
- * <code>float</code> type.
- *
- * @author Joseph D. Darcy
- */
-
-public class FloatConsts {
- /**
- * Don't let anyone instantiate this class.
- */
- private FloatConsts() {}
-
- public static final float POSITIVE_INFINITY = java.lang.Float.POSITIVE_INFINITY;
- public static final float NEGATIVE_INFINITY = java.lang.Float.NEGATIVE_INFINITY;
- public static final float NaN = java.lang.Float.NaN;
- public static final float MAX_VALUE = java.lang.Float.MAX_VALUE;
- public static final float MIN_VALUE = java.lang.Float.MIN_VALUE;
-
- /**
- * A constant holding the smallest positive normal value of type
- * <code>float</code>, 2<sup>-126</sup>. It is equal to the value
- * returned by <code>Float.intBitsToFloat(0x00800000)</code>.
- */
- public static final float MIN_NORMAL = 1.17549435E-38f;
-
- /**
- * The number of logical bits in the significand of a
- * <code>float</code> number, including the implicit bit.
- */
- public static final int SIGNIFICAND_WIDTH = 24;
-
- /**
- * Maximum exponent a finite <code>float</code> number may have.
- * It is equal to the value returned by
- * <code>Math.ilogb(Float.MAX_VALUE)</code>.
- */
- public static final int MAX_EXPONENT = 127;
-
- /**
- * Minimum exponent a normalized <code>float</code> number may
- * have. It is equal to the value returned by
- * <code>Math.ilogb(Float.MIN_NORMAL)</code>.
- */
- public static final int MIN_EXPONENT = -126;
-
- /**
- * The exponent the smallest positive <code>float</code> subnormal
- * value would have if it could be normalized.
- */
- public static final int MIN_SUB_EXPONENT = MIN_EXPONENT -
- (SIGNIFICAND_WIDTH - 1);
-
- /**
- * Bias used in representing a <code>float</code> exponent.
- */
- public static final int EXP_BIAS = 127;
-
- /**
- * Bit mask to isolate the sign bit of a <code>float</code>.
- */
- public static final int SIGN_BIT_MASK = 0x80000000;
-
- /**
- * Bit mask to isolate the exponent field of a
- * <code>float</code>.
- */
- public static final int EXP_BIT_MASK = 0x7F800000;
-
- /**
- * Bit mask to isolate the significand field of a
- * <code>float</code>.
- */
- public static final int SIGNIF_BIT_MASK = 0x007FFFFF;
-
- static {
- // verify bit masks cover all bit positions and that the bit
- // masks are non-overlapping
- assert(((SIGN_BIT_MASK | EXP_BIT_MASK | SIGNIF_BIT_MASK) == ~0) &&
- (((SIGN_BIT_MASK & EXP_BIT_MASK) == 0) &&
- ((SIGN_BIT_MASK & SIGNIF_BIT_MASK) == 0) &&
- ((EXP_BIT_MASK & SIGNIF_BIT_MASK) == 0)));
- }
-}
--- a/jdk/src/java.base/share/classes/sun/misc/FloatingDecimal.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2552 +0,0 @@
-/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.misc;
-
-import java.util.Arrays;
-import java.util.regex.*;
-
-/**
- * A class for converting between ASCII and decimal representations of a single
- * or double precision floating point number. Most conversions are provided via
- * static convenience methods, although a <code>BinaryToASCIIConverter</code>
- * instance may be obtained and reused.
- */
-public class FloatingDecimal{
- //
- // Constants of the implementation;
- // most are IEEE-754 related.
- // (There are more really boring constants at the end.)
- //
- static final int EXP_SHIFT = DoubleConsts.SIGNIFICAND_WIDTH - 1;
- static final long FRACT_HOB = ( 1L<<EXP_SHIFT ); // assumed High-Order bit
- static final long EXP_ONE = ((long)DoubleConsts.EXP_BIAS)<<EXP_SHIFT; // exponent of 1.0
- static final int MAX_SMALL_BIN_EXP = 62;
- static final int MIN_SMALL_BIN_EXP = -( 63 / 3 );
- static final int MAX_DECIMAL_DIGITS = 15;
- static final int MAX_DECIMAL_EXPONENT = 308;
- static final int MIN_DECIMAL_EXPONENT = -324;
- static final int BIG_DECIMAL_EXPONENT = 324; // i.e. abs(MIN_DECIMAL_EXPONENT)
- static final int MAX_NDIGITS = 1100;
-
- static final int SINGLE_EXP_SHIFT = FloatConsts.SIGNIFICAND_WIDTH - 1;
- static final int SINGLE_FRACT_HOB = 1<<SINGLE_EXP_SHIFT;
- static final int SINGLE_MAX_DECIMAL_DIGITS = 7;
- static final int SINGLE_MAX_DECIMAL_EXPONENT = 38;
- static final int SINGLE_MIN_DECIMAL_EXPONENT = -45;
- static final int SINGLE_MAX_NDIGITS = 200;
-
- static final int INT_DECIMAL_DIGITS = 9;
-
- /**
- * Converts a double precision floating point value to a <code>String</code>.
- *
- * @param d The double precision value.
- * @return The value converted to a <code>String</code>.
- */
- public static String toJavaFormatString(double d) {
- return getBinaryToASCIIConverter(d).toJavaFormatString();
- }
-
- /**
- * Converts a single precision floating point value to a <code>String</code>.
- *
- * @param f The single precision value.
- * @return The value converted to a <code>String</code>.
- */
- public static String toJavaFormatString(float f) {
- return getBinaryToASCIIConverter(f).toJavaFormatString();
- }
-
- /**
- * Appends a double precision floating point value to an <code>Appendable</code>.
- * @param d The double precision value.
- * @param buf The <code>Appendable</code> with the value appended.
- */
- public static void appendTo(double d, Appendable buf) {
- getBinaryToASCIIConverter(d).appendTo(buf);
- }
-
- /**
- * Appends a single precision floating point value to an <code>Appendable</code>.
- * @param f The single precision value.
- * @param buf The <code>Appendable</code> with the value appended.
- */
- public static void appendTo(float f, Appendable buf) {
- getBinaryToASCIIConverter(f).appendTo(buf);
- }
-
- /**
- * Converts a <code>String</code> to a double precision floating point value.
- *
- * @param s The <code>String</code> to convert.
- * @return The double precision value.
- * @throws NumberFormatException If the <code>String</code> does not
- * represent a properly formatted double precision value.
- */
- public static double parseDouble(String s) throws NumberFormatException {
- return readJavaFormatString(s).doubleValue();
- }
-
- /**
- * Converts a <code>String</code> to a single precision floating point value.
- *
- * @param s The <code>String</code> to convert.
- * @return The single precision value.
- * @throws NumberFormatException If the <code>String</code> does not
- * represent a properly formatted single precision value.
- */
- public static float parseFloat(String s) throws NumberFormatException {
- return readJavaFormatString(s).floatValue();
- }
-
- /**
- * A converter which can process single or double precision floating point
- * values into an ASCII <code>String</code> representation.
- */
- public interface BinaryToASCIIConverter {
- /**
- * Converts a floating point value into an ASCII <code>String</code>.
- * @return The value converted to a <code>String</code>.
- */
- public String toJavaFormatString();
-
- /**
- * Appends a floating point value to an <code>Appendable</code>.
- * @param buf The <code>Appendable</code> to receive the value.
- */
- public void appendTo(Appendable buf);
-
- /**
- * Retrieves the decimal exponent most closely corresponding to this value.
- * @return The decimal exponent.
- */
- public int getDecimalExponent();
-
- /**
- * Retrieves the value as an array of digits.
- * @param digits The digit array.
- * @return The number of valid digits copied into the array.
- */
- public int getDigits(char[] digits);
-
- /**
- * Indicates the sign of the value.
- * @return {@code value < 0.0}.
- */
- public boolean isNegative();
-
- /**
- * Indicates whether the value is either infinite or not a number.
- *
- * @return <code>true</code> if and only if the value is <code>NaN</code>
- * or infinite.
- */
- public boolean isExceptional();
-
- /**
- * Indicates whether the value was rounded up during the binary to ASCII
- * conversion.
- *
- * @return <code>true</code> if and only if the value was rounded up.
- */
- public boolean digitsRoundedUp();
-
- /**
- * Indicates whether the binary to ASCII conversion was exact.
- *
- * @return <code>true</code> if any only if the conversion was exact.
- */
- public boolean decimalDigitsExact();
- }
-
- /**
- * A <code>BinaryToASCIIConverter</code> which represents <code>NaN</code>
- * and infinite values.
- */
- private static class ExceptionalBinaryToASCIIBuffer implements BinaryToASCIIConverter {
- private final String image;
- private boolean isNegative;
-
- public ExceptionalBinaryToASCIIBuffer(String image, boolean isNegative) {
- this.image = image;
- this.isNegative = isNegative;
- }
-
- @Override
- public String toJavaFormatString() {
- return image;
- }
-
- @Override
- public void appendTo(Appendable buf) {
- if (buf instanceof StringBuilder) {
- ((StringBuilder) buf).append(image);
- } else if (buf instanceof StringBuffer) {
- ((StringBuffer) buf).append(image);
- } else {
- assert false;
- }
- }
-
- @Override
- public int getDecimalExponent() {
- throw new IllegalArgumentException("Exceptional value does not have an exponent");
- }
-
- @Override
- public int getDigits(char[] digits) {
- throw new IllegalArgumentException("Exceptional value does not have digits");
- }
-
- @Override
- public boolean isNegative() {
- return isNegative;
- }
-
- @Override
- public boolean isExceptional() {
- return true;
- }
-
- @Override
- public boolean digitsRoundedUp() {
- throw new IllegalArgumentException("Exceptional value is not rounded");
- }
-
- @Override
- public boolean decimalDigitsExact() {
- throw new IllegalArgumentException("Exceptional value is not exact");
- }
- }
-
- private static final String INFINITY_REP = "Infinity";
- private static final int INFINITY_LENGTH = INFINITY_REP.length();
- private static final String NAN_REP = "NaN";
- private static final int NAN_LENGTH = NAN_REP.length();
-
- private static final BinaryToASCIIConverter B2AC_POSITIVE_INFINITY = new ExceptionalBinaryToASCIIBuffer(INFINITY_REP, false);
- private static final BinaryToASCIIConverter B2AC_NEGATIVE_INFINITY = new ExceptionalBinaryToASCIIBuffer("-" + INFINITY_REP, true);
- private static final BinaryToASCIIConverter B2AC_NOT_A_NUMBER = new ExceptionalBinaryToASCIIBuffer(NAN_REP, false);
- private static final BinaryToASCIIConverter B2AC_POSITIVE_ZERO = new BinaryToASCIIBuffer(false, new char[]{'0'});
- private static final BinaryToASCIIConverter B2AC_NEGATIVE_ZERO = new BinaryToASCIIBuffer(true, new char[]{'0'});
-
- /**
- * A buffered implementation of <code>BinaryToASCIIConverter</code>.
- */
- static class BinaryToASCIIBuffer implements BinaryToASCIIConverter {
- private boolean isNegative;
- private int decExponent;
- private int firstDigitIndex;
- private int nDigits;
- private final char[] digits;
- private final char[] buffer = new char[26];
-
- //
- // The fields below provide additional information about the result of
- // the binary to decimal digits conversion done in dtoa() and roundup()
- // methods. They are changed if needed by those two methods.
- //
-
- // True if the dtoa() binary to decimal conversion was exact.
- private boolean exactDecimalConversion = false;
-
- // True if the result of the binary to decimal conversion was rounded-up
- // at the end of the conversion process, i.e. roundUp() method was called.
- private boolean decimalDigitsRoundedUp = false;
-
- /**
- * Default constructor; used for non-zero values,
- * <code>BinaryToASCIIBuffer</code> may be thread-local and reused
- */
- BinaryToASCIIBuffer(){
- this.digits = new char[20];
- }
-
- /**
- * Creates a specialized value (positive and negative zeros).
- */
- BinaryToASCIIBuffer(boolean isNegative, char[] digits){
- this.isNegative = isNegative;
- this.decExponent = 0;
- this.digits = digits;
- this.firstDigitIndex = 0;
- this.nDigits = digits.length;
- }
-
- @Override
- public String toJavaFormatString() {
- int len = getChars(buffer);
- return new String(buffer, 0, len);
- }
-
- @Override
- public void appendTo(Appendable buf) {
- int len = getChars(buffer);
- if (buf instanceof StringBuilder) {
- ((StringBuilder) buf).append(buffer, 0, len);
- } else if (buf instanceof StringBuffer) {
- ((StringBuffer) buf).append(buffer, 0, len);
- } else {
- assert false;
- }
- }
-
- @Override
- public int getDecimalExponent() {
- return decExponent;
- }
-
- @Override
- public int getDigits(char[] digits) {
- System.arraycopy(this.digits,firstDigitIndex,digits,0,this.nDigits);
- return this.nDigits;
- }
-
- @Override
- public boolean isNegative() {
- return isNegative;
- }
-
- @Override
- public boolean isExceptional() {
- return false;
- }
-
- @Override
- public boolean digitsRoundedUp() {
- return decimalDigitsRoundedUp;
- }
-
- @Override
- public boolean decimalDigitsExact() {
- return exactDecimalConversion;
- }
-
- private void setSign(boolean isNegative) {
- this.isNegative = isNegative;
- }
-
- /**
- * This is the easy subcase --
- * all the significant bits, after scaling, are held in lvalue.
- * negSign and decExponent tell us what processing and scaling
- * has already been done. Exceptional cases have already been
- * stripped out.
- * In particular:
- * lvalue is a finite number (not Inf, nor NaN)
- * lvalue > 0L (not zero, nor negative).
- *
- * The only reason that we develop the digits here, rather than
- * calling on Long.toString() is that we can do it a little faster,
- * and besides want to treat trailing 0s specially. If Long.toString
- * changes, we should re-evaluate this strategy!
- */
- private void developLongDigits( int decExponent, long lvalue, int insignificantDigits ){
- if ( insignificantDigits != 0 ){
- // Discard non-significant low-order bits, while rounding,
- // up to insignificant value.
- long pow10 = FDBigInteger.LONG_5_POW[insignificantDigits] << insignificantDigits; // 10^i == 5^i * 2^i;
- long residue = lvalue % pow10;
- lvalue /= pow10;
- decExponent += insignificantDigits;
- if ( residue >= (pow10>>1) ){
- // round up based on the low-order bits we're discarding
- lvalue++;
- }
- }
- int digitno = digits.length -1;
- int c;
- if ( lvalue <= Integer.MAX_VALUE ){
- assert lvalue > 0L : lvalue; // lvalue <= 0
- // even easier subcase!
- // can do int arithmetic rather than long!
- int ivalue = (int)lvalue;
- c = ivalue%10;
- ivalue /= 10;
- while ( c == 0 ){
- decExponent++;
- c = ivalue%10;
- ivalue /= 10;
- }
- while ( ivalue != 0){
- digits[digitno--] = (char)(c+'0');
- decExponent++;
- c = ivalue%10;
- ivalue /= 10;
- }
- digits[digitno] = (char)(c+'0');
- } else {
- // same algorithm as above (same bugs, too )
- // but using long arithmetic.
- c = (int)(lvalue%10L);
- lvalue /= 10L;
- while ( c == 0 ){
- decExponent++;
- c = (int)(lvalue%10L);
- lvalue /= 10L;
- }
- while ( lvalue != 0L ){
- digits[digitno--] = (char)(c+'0');
- decExponent++;
- c = (int)(lvalue%10L);
- lvalue /= 10;
- }
- digits[digitno] = (char)(c+'0');
- }
- this.decExponent = decExponent+1;
- this.firstDigitIndex = digitno;
- this.nDigits = this.digits.length - digitno;
- }
-
- private void dtoa( int binExp, long fractBits, int nSignificantBits, boolean isCompatibleFormat)
- {
- assert fractBits > 0 ; // fractBits here can't be zero or negative
- assert (fractBits & FRACT_HOB)!=0 ; // Hi-order bit should be set
- // Examine number. Determine if it is an easy case,
- // which we can do pretty trivially using float/long conversion,
- // or whether we must do real work.
- final int tailZeros = Long.numberOfTrailingZeros(fractBits);
-
- // number of significant bits of fractBits;
- final int nFractBits = EXP_SHIFT+1-tailZeros;
-
- // reset flags to default values as dtoa() does not always set these
- // flags and a prior call to dtoa() might have set them to incorrect
- // values with respect to the current state.
- decimalDigitsRoundedUp = false;
- exactDecimalConversion = false;
-
- // number of significant bits to the right of the point.
- int nTinyBits = Math.max( 0, nFractBits - binExp - 1 );
- if ( binExp <= MAX_SMALL_BIN_EXP && binExp >= MIN_SMALL_BIN_EXP ){
- // Look more closely at the number to decide if,
- // with scaling by 10^nTinyBits, the result will fit in
- // a long.
- if ( (nTinyBits < FDBigInteger.LONG_5_POW.length) && ((nFractBits + N_5_BITS[nTinyBits]) < 64 ) ){
- //
- // We can do this:
- // take the fraction bits, which are normalized.
- // (a) nTinyBits == 0: Shift left or right appropriately
- // to align the binary point at the extreme right, i.e.
- // where a long int point is expected to be. The integer
- // result is easily converted to a string.
- // (b) nTinyBits > 0: Shift right by EXP_SHIFT-nFractBits,
- // which effectively converts to long and scales by
- // 2^nTinyBits. Then multiply by 5^nTinyBits to
- // complete the scaling. We know this won't overflow
- // because we just counted the number of bits necessary
- // in the result. The integer you get from this can
- // then be converted to a string pretty easily.
- //
- if ( nTinyBits == 0 ) {
- int insignificant;
- if ( binExp > nSignificantBits ){
- insignificant = insignificantDigitsForPow2(binExp-nSignificantBits-1);
- } else {
- insignificant = 0;
- }
- if ( binExp >= EXP_SHIFT ){
- fractBits <<= (binExp-EXP_SHIFT);
- } else {
- fractBits >>>= (EXP_SHIFT-binExp) ;
- }
- developLongDigits( 0, fractBits, insignificant );
- return;
- }
- //
- // The following causes excess digits to be printed
- // out in the single-float case. Our manipulation of
- // halfULP here is apparently not correct. If we
- // better understand how this works, perhaps we can
- // use this special case again. But for the time being,
- // we do not.
- // else {
- // fractBits >>>= EXP_SHIFT+1-nFractBits;
- // fractBits//= long5pow[ nTinyBits ];
- // halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits);
- // developLongDigits( -nTinyBits, fractBits, insignificantDigits(halfULP) );
- // return;
- // }
- //
- }
- }
- //
- // This is the hard case. We are going to compute large positive
- // integers B and S and integer decExp, s.t.
- // d = ( B / S )// 10^decExp
- // 1 <= B / S < 10
- // Obvious choices are:
- // decExp = floor( log10(d) )
- // B = d// 2^nTinyBits// 10^max( 0, -decExp )
- // S = 10^max( 0, decExp)// 2^nTinyBits
- // (noting that nTinyBits has already been forced to non-negative)
- // I am also going to compute a large positive integer
- // M = (1/2^nSignificantBits)// 2^nTinyBits// 10^max( 0, -decExp )
- // i.e. M is (1/2) of the ULP of d, scaled like B.
- // When we iterate through dividing B/S and picking off the
- // quotient bits, we will know when to stop when the remainder
- // is <= M.
- //
- // We keep track of powers of 2 and powers of 5.
- //
- int decExp = estimateDecExp(fractBits,binExp);
- int B2, B5; // powers of 2 and powers of 5, respectively, in B
- int S2, S5; // powers of 2 and powers of 5, respectively, in S
- int M2, M5; // powers of 2 and powers of 5, respectively, in M
-
- B5 = Math.max( 0, -decExp );
- B2 = B5 + nTinyBits + binExp;
-
- S5 = Math.max( 0, decExp );
- S2 = S5 + nTinyBits;
-
- M5 = B5;
- M2 = B2 - nSignificantBits;
-
- //
- // the long integer fractBits contains the (nFractBits) interesting
- // bits from the mantissa of d ( hidden 1 added if necessary) followed
- // by (EXP_SHIFT+1-nFractBits) zeros. In the interest of compactness,
- // I will shift out those zeros before turning fractBits into a
- // FDBigInteger. The resulting whole number will be
- // d * 2^(nFractBits-1-binExp).
- //
- fractBits >>>= tailZeros;
- B2 -= nFractBits-1;
- int common2factor = Math.min( B2, S2 );
- B2 -= common2factor;
- S2 -= common2factor;
- M2 -= common2factor;
-
- //
- // HACK!! For exact powers of two, the next smallest number
- // is only half as far away as we think (because the meaning of
- // ULP changes at power-of-two bounds) for this reason, we
- // hack M2. Hope this works.
- //
- if ( nFractBits == 1 ) {
- M2 -= 1;
- }
-
- if ( M2 < 0 ){
- // oops.
- // since we cannot scale M down far enough,
- // we must scale the other values up.
- B2 -= M2;
- S2 -= M2;
- M2 = 0;
- }
- //
- // Construct, Scale, iterate.
- // Some day, we'll write a stopping test that takes
- // account of the asymmetry of the spacing of floating-point
- // numbers below perfect powers of 2
- // 26 Sept 96 is not that day.
- // So we use a symmetric test.
- //
- int ndigit = 0;
- boolean low, high;
- long lowDigitDifference;
- int q;
-
- //
- // Detect the special cases where all the numbers we are about
- // to compute will fit in int or long integers.
- // In these cases, we will avoid doing FDBigInteger arithmetic.
- // We use the same algorithms, except that we "normalize"
- // our FDBigIntegers before iterating. This is to make division easier,
- // as it makes our fist guess (quotient of high-order words)
- // more accurate!
- //
- // Some day, we'll write a stopping test that takes
- // account of the asymmetry of the spacing of floating-point
- // numbers below perfect powers of 2
- // 26 Sept 96 is not that day.
- // So we use a symmetric test.
- //
- // binary digits needed to represent B, approx.
- int Bbits = nFractBits + B2 + (( B5 < N_5_BITS.length )? N_5_BITS[B5] : ( B5*3 ));
-
- // binary digits needed to represent 10*S, approx.
- int tenSbits = S2+1 + (( (S5+1) < N_5_BITS.length )? N_5_BITS[(S5+1)] : ( (S5+1)*3 ));
- if ( Bbits < 64 && tenSbits < 64){
- if ( Bbits < 32 && tenSbits < 32){
- // wa-hoo! They're all ints!
- int b = ((int)fractBits * FDBigInteger.SMALL_5_POW[B5] ) << B2;
- int s = FDBigInteger.SMALL_5_POW[S5] << S2;
- int m = FDBigInteger.SMALL_5_POW[M5] << M2;
- int tens = s * 10;
- //
- // Unroll the first iteration. If our decExp estimate
- // was too high, our first quotient will be zero. In this
- // case, we discard it and decrement decExp.
- //
- ndigit = 0;
- q = b / s;
- b = 10 * ( b % s );
- m *= 10;
- low = (b < m );
- high = (b+m > tens );
- assert q < 10 : q; // excessively large digit
- if ( (q == 0) && ! high ){
- // oops. Usually ignore leading zero.
- decExp--;
- } else {
- digits[ndigit++] = (char)('0' + q);
- }
- //
- // HACK! Java spec sez that we always have at least
- // one digit after the . in either F- or E-form output.
- // Thus we will need more than one digit if we're using
- // E-form
- //
- if ( !isCompatibleFormat ||decExp < -3 || decExp >= 8 ){
- high = low = false;
- }
- while( ! low && ! high ){
- q = b / s;
- b = 10 * ( b % s );
- m *= 10;
- assert q < 10 : q; // excessively large digit
- if ( m > 0L ){
- low = (b < m );
- high = (b+m > tens );
- } else {
- // hack -- m might overflow!
- // in this case, it is certainly > b,
- // which won't
- // and b+m > tens, too, since that has overflowed
- // either!
- low = true;
- high = true;
- }
- digits[ndigit++] = (char)('0' + q);
- }
- lowDigitDifference = (b<<1) - tens;
- exactDecimalConversion = (b == 0);
- } else {
- // still good! they're all longs!
- long b = (fractBits * FDBigInteger.LONG_5_POW[B5] ) << B2;
- long s = FDBigInteger.LONG_5_POW[S5] << S2;
- long m = FDBigInteger.LONG_5_POW[M5] << M2;
- long tens = s * 10L;
- //
- // Unroll the first iteration. If our decExp estimate
- // was too high, our first quotient will be zero. In this
- // case, we discard it and decrement decExp.
- //
- ndigit = 0;
- q = (int) ( b / s );
- b = 10L * ( b % s );
- m *= 10L;
- low = (b < m );
- high = (b+m > tens );
- assert q < 10 : q; // excessively large digit
- if ( (q == 0) && ! high ){
- // oops. Usually ignore leading zero.
- decExp--;
- } else {
- digits[ndigit++] = (char)('0' + q);
- }
- //
- // HACK! Java spec sez that we always have at least
- // one digit after the . in either F- or E-form output.
- // Thus we will need more than one digit if we're using
- // E-form
- //
- if ( !isCompatibleFormat || decExp < -3 || decExp >= 8 ){
- high = low = false;
- }
- while( ! low && ! high ){
- q = (int) ( b / s );
- b = 10 * ( b % s );
- m *= 10;
- assert q < 10 : q; // excessively large digit
- if ( m > 0L ){
- low = (b < m );
- high = (b+m > tens );
- } else {
- // hack -- m might overflow!
- // in this case, it is certainly > b,
- // which won't
- // and b+m > tens, too, since that has overflowed
- // either!
- low = true;
- high = true;
- }
- digits[ndigit++] = (char)('0' + q);
- }
- lowDigitDifference = (b<<1) - tens;
- exactDecimalConversion = (b == 0);
- }
- } else {
- //
- // We really must do FDBigInteger arithmetic.
- // Fist, construct our FDBigInteger initial values.
- //
- FDBigInteger Sval = FDBigInteger.valueOfPow52(S5, S2);
- int shiftBias = Sval.getNormalizationBias();
- Sval = Sval.leftShift(shiftBias); // normalize so that division works better
-
- FDBigInteger Bval = FDBigInteger.valueOfMulPow52(fractBits, B5, B2 + shiftBias);
- FDBigInteger Mval = FDBigInteger.valueOfPow52(M5 + 1, M2 + shiftBias + 1);
-
- FDBigInteger tenSval = FDBigInteger.valueOfPow52(S5 + 1, S2 + shiftBias + 1); //Sval.mult( 10 );
- //
- // Unroll the first iteration. If our decExp estimate
- // was too high, our first quotient will be zero. In this
- // case, we discard it and decrement decExp.
- //
- ndigit = 0;
- q = Bval.quoRemIteration( Sval );
- low = (Bval.cmp( Mval ) < 0);
- high = tenSval.addAndCmp(Bval,Mval)<=0;
-
- assert q < 10 : q; // excessively large digit
- if ( (q == 0) && ! high ){
- // oops. Usually ignore leading zero.
- decExp--;
- } else {
- digits[ndigit++] = (char)('0' + q);
- }
- //
- // HACK! Java spec sez that we always have at least
- // one digit after the . in either F- or E-form output.
- // Thus we will need more than one digit if we're using
- // E-form
- //
- if (!isCompatibleFormat || decExp < -3 || decExp >= 8 ){
- high = low = false;
- }
- while( ! low && ! high ){
- q = Bval.quoRemIteration( Sval );
- assert q < 10 : q; // excessively large digit
- Mval = Mval.multBy10(); //Mval = Mval.mult( 10 );
- low = (Bval.cmp( Mval ) < 0);
- high = tenSval.addAndCmp(Bval,Mval)<=0;
- digits[ndigit++] = (char)('0' + q);
- }
- if ( high && low ){
- Bval = Bval.leftShift(1);
- lowDigitDifference = Bval.cmp(tenSval);
- } else {
- lowDigitDifference = 0L; // this here only for flow analysis!
- }
- exactDecimalConversion = (Bval.cmp( FDBigInteger.ZERO ) == 0);
- }
- this.decExponent = decExp+1;
- this.firstDigitIndex = 0;
- this.nDigits = ndigit;
- //
- // Last digit gets rounded based on stopping condition.
- //
- if ( high ){
- if ( low ){
- if ( lowDigitDifference == 0L ){
- // it's a tie!
- // choose based on which digits we like.
- if ( (digits[firstDigitIndex+nDigits-1]&1) != 0 ) {
- roundup();
- }
- } else if ( lowDigitDifference > 0 ){
- roundup();
- }
- } else {
- roundup();
- }
- }
- }
-
- // add one to the least significant digit.
- // in the unlikely event there is a carry out, deal with it.
- // assert that this will only happen where there
- // is only one digit, e.g. (float)1e-44 seems to do it.
- //
- private void roundup() {
- int i = (firstDigitIndex + nDigits - 1);
- int q = digits[i];
- if (q == '9') {
- while (q == '9' && i > firstDigitIndex) {
- digits[i] = '0';
- q = digits[--i];
- }
- if (q == '9') {
- // carryout! High-order 1, rest 0s, larger exp.
- decExponent += 1;
- digits[firstDigitIndex] = '1';
- return;
- }
- // else fall through.
- }
- digits[i] = (char) (q + 1);
- decimalDigitsRoundedUp = true;
- }
-
- /**
- * Estimate decimal exponent. (If it is small-ish,
- * we could double-check.)
- *
- * First, scale the mantissa bits such that 1 <= d2 < 2.
- * We are then going to estimate
- * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5)
- * and so we can estimate
- * log10(d) ~=~ log10(d2) + binExp * log10(2)
- * take the floor and call it decExp.
- */
- static int estimateDecExp(long fractBits, int binExp) {
- double d2 = Double.longBitsToDouble( EXP_ONE | ( fractBits & DoubleConsts.SIGNIF_BIT_MASK ) );
- double d = (d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981;
- long dBits = Double.doubleToRawLongBits(d); //can't be NaN here so use raw
- int exponent = (int)((dBits & DoubleConsts.EXP_BIT_MASK) >> EXP_SHIFT) - DoubleConsts.EXP_BIAS;
- boolean isNegative = (dBits & DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign
- if(exponent>=0 && exponent<52) { // hot path
- long mask = DoubleConsts.SIGNIF_BIT_MASK >> exponent;
- int r = (int)(( (dBits&DoubleConsts.SIGNIF_BIT_MASK) | FRACT_HOB )>>(EXP_SHIFT-exponent));
- return isNegative ? (((mask & dBits) == 0L ) ? -r : -r-1 ) : r;
- } else if (exponent < 0) {
- return (((dBits&~DoubleConsts.SIGN_BIT_MASK) == 0) ? 0 :
- ( (isNegative) ? -1 : 0) );
- } else { //if (exponent >= 52)
- return (int)d;
- }
- }
-
- private static int insignificantDigits(int insignificant) {
- int i;
- for ( i = 0; insignificant >= 10L; i++ ) {
- insignificant /= 10L;
- }
- return i;
- }
-
- /**
- * Calculates
- * <pre>
- * insignificantDigitsForPow2(v) == insignificantDigits(1L<<v)
- * </pre>
- */
- private static int insignificantDigitsForPow2(int p2) {
- if(p2>1 && p2 < insignificantDigitsNumber.length) {
- return insignificantDigitsNumber[p2];
- }
- return 0;
- }
-
- /**
- * If insignificant==(1L << ixd)
- * i = insignificantDigitsNumber[idx] is the same as:
- * int i;
- * for ( i = 0; insignificant >= 10L; i++ )
- * insignificant /= 10L;
- */
- private static int[] insignificantDigitsNumber = {
- 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3,
- 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
- 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11,
- 12, 12, 12, 12, 13, 13, 13, 14, 14, 14,
- 15, 15, 15, 15, 16, 16, 16, 17, 17, 17,
- 18, 18, 18, 19
- };
-
- // approximately ceil( log2( long5pow[i] ) )
- private static final int[] N_5_BITS = {
- 0,
- 3,
- 5,
- 7,
- 10,
- 12,
- 14,
- 17,
- 19,
- 21,
- 24,
- 26,
- 28,
- 31,
- 33,
- 35,
- 38,
- 40,
- 42,
- 45,
- 47,
- 49,
- 52,
- 54,
- 56,
- 59,
- 61,
- };
-
- private int getChars(char[] result) {
- assert nDigits <= 19 : nDigits; // generous bound on size of nDigits
- int i = 0;
- if (isNegative) {
- result[0] = '-';
- i = 1;
- }
- if (decExponent > 0 && decExponent < 8) {
- // print digits.digits.
- int charLength = Math.min(nDigits, decExponent);
- System.arraycopy(digits, firstDigitIndex, result, i, charLength);
- i += charLength;
- if (charLength < decExponent) {
- charLength = decExponent - charLength;
- Arrays.fill(result,i,i+charLength,'0');
- i += charLength;
- result[i++] = '.';
- result[i++] = '0';
- } else {
- result[i++] = '.';
- if (charLength < nDigits) {
- int t = nDigits - charLength;
- System.arraycopy(digits, firstDigitIndex+charLength, result, i, t);
- i += t;
- } else {
- result[i++] = '0';
- }
- }
- } else if (decExponent <= 0 && decExponent > -3) {
- result[i++] = '0';
- result[i++] = '.';
- if (decExponent != 0) {
- Arrays.fill(result, i, i-decExponent, '0');
- i -= decExponent;
- }
- System.arraycopy(digits, firstDigitIndex, result, i, nDigits);
- i += nDigits;
- } else {
- result[i++] = digits[firstDigitIndex];
- result[i++] = '.';
- if (nDigits > 1) {
- System.arraycopy(digits, firstDigitIndex+1, result, i, nDigits - 1);
- i += nDigits - 1;
- } else {
- result[i++] = '0';
- }
- result[i++] = 'E';
- int e;
- if (decExponent <= 0) {
- result[i++] = '-';
- e = -decExponent + 1;
- } else {
- e = decExponent - 1;
- }
- // decExponent has 1, 2, or 3, digits
- if (e <= 9) {
- result[i++] = (char) (e + '0');
- } else if (e <= 99) {
- result[i++] = (char) (e / 10 + '0');
- result[i++] = (char) (e % 10 + '0');
- } else {
- result[i++] = (char) (e / 100 + '0');
- e %= 100;
- result[i++] = (char) (e / 10 + '0');
- result[i++] = (char) (e % 10 + '0');
- }
- }
- return i;
- }
-
- }
-
- private static final ThreadLocal<BinaryToASCIIBuffer> threadLocalBinaryToASCIIBuffer =
- new ThreadLocal<BinaryToASCIIBuffer>() {
- @Override
- protected BinaryToASCIIBuffer initialValue() {
- return new BinaryToASCIIBuffer();
- }
- };
-
- private static BinaryToASCIIBuffer getBinaryToASCIIBuffer() {
- return threadLocalBinaryToASCIIBuffer.get();
- }
-
- /**
- * A converter which can process an ASCII <code>String</code> representation
- * of a single or double precision floating point value into a
- * <code>float</code> or a <code>double</code>.
- */
- interface ASCIIToBinaryConverter {
-
- double doubleValue();
-
- float floatValue();
-
- }
-
- /**
- * A <code>ASCIIToBinaryConverter</code> container for a <code>double</code>.
- */
- static class PreparedASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
- private final double doubleVal;
- private final float floatVal;
-
- public PreparedASCIIToBinaryBuffer(double doubleVal, float floatVal) {
- this.doubleVal = doubleVal;
- this.floatVal = floatVal;
- }
-
- @Override
- public double doubleValue() {
- return doubleVal;
- }
-
- @Override
- public float floatValue() {
- return floatVal;
- }
- }
-
- static final ASCIIToBinaryConverter A2BC_POSITIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
- static final ASCIIToBinaryConverter A2BC_NEGATIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
- static final ASCIIToBinaryConverter A2BC_NOT_A_NUMBER = new PreparedASCIIToBinaryBuffer(Double.NaN, Float.NaN);
- static final ASCIIToBinaryConverter A2BC_POSITIVE_ZERO = new PreparedASCIIToBinaryBuffer(0.0d, 0.0f);
- static final ASCIIToBinaryConverter A2BC_NEGATIVE_ZERO = new PreparedASCIIToBinaryBuffer(-0.0d, -0.0f);
-
- /**
- * A buffered implementation of <code>ASCIIToBinaryConverter</code>.
- */
- static class ASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
- boolean isNegative;
- int decExponent;
- char digits[];
- int nDigits;
-
- ASCIIToBinaryBuffer( boolean negSign, int decExponent, char[] digits, int n)
- {
- this.isNegative = negSign;
- this.decExponent = decExponent;
- this.digits = digits;
- this.nDigits = n;
- }
-
- /**
- * Takes a FloatingDecimal, which we presumably just scanned in,
- * and finds out what its value is, as a double.
- *
- * AS A SIDE EFFECT, SET roundDir TO INDICATE PREFERRED
- * ROUNDING DIRECTION in case the result is really destined
- * for a single-precision float.
- */
- @Override
- public double doubleValue() {
- int kDigits = Math.min(nDigits, MAX_DECIMAL_DIGITS + 1);
- //
- // convert the lead kDigits to a long integer.
- //
- // (special performance hack: start to do it using int)
- int iValue = (int) digits[0] - (int) '0';
- int iDigits = Math.min(kDigits, INT_DECIMAL_DIGITS);
- for (int i = 1; i < iDigits; i++) {
- iValue = iValue * 10 + (int) digits[i] - (int) '0';
- }
- long lValue = (long) iValue;
- for (int i = iDigits; i < kDigits; i++) {
- lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
- }
- double dValue = (double) lValue;
- int exp = decExponent - kDigits;
- //
- // lValue now contains a long integer with the value of
- // the first kDigits digits of the number.
- // dValue contains the (double) of the same.
- //
-
- if (nDigits <= MAX_DECIMAL_DIGITS) {
- //
- // possibly an easy case.
- // We know that the digits can be represented
- // exactly. And if the exponent isn't too outrageous,
- // the whole thing can be done with one operation,
- // thus one rounding error.
- // Note that all our constructors trim all leading and
- // trailing zeros, so simple values (including zero)
- // will always end up here
- //
- if (exp == 0 || dValue == 0.0) {
- return (isNegative) ? -dValue : dValue; // small floating integer
- }
- else if (exp >= 0) {
- if (exp <= MAX_SMALL_TEN) {
- //
- // Can get the answer with one operation,
- // thus one roundoff.
- //
- double rValue = dValue * SMALL_10_POW[exp];
- return (isNegative) ? -rValue : rValue;
- }
- int slop = MAX_DECIMAL_DIGITS - kDigits;
- if (exp <= MAX_SMALL_TEN + slop) {
- //
- // We can multiply dValue by 10^(slop)
- // and it is still "small" and exact.
- // Then we can multiply by 10^(exp-slop)
- // with one rounding.
- //
- dValue *= SMALL_10_POW[slop];
- double rValue = dValue * SMALL_10_POW[exp - slop];
- return (isNegative) ? -rValue : rValue;
- }
- //
- // Else we have a hard case with a positive exp.
- //
- } else {
- if (exp >= -MAX_SMALL_TEN) {
- //
- // Can get the answer in one division.
- //
- double rValue = dValue / SMALL_10_POW[-exp];
- return (isNegative) ? -rValue : rValue;
- }
- //
- // Else we have a hard case with a negative exp.
- //
- }
- }
-
- //
- // Harder cases:
- // The sum of digits plus exponent is greater than
- // what we think we can do with one error.
- //
- // Start by approximating the right answer by,
- // naively, scaling by powers of 10.
- //
- if (exp > 0) {
- if (decExponent > MAX_DECIMAL_EXPONENT + 1) {
- //
- // Lets face it. This is going to be
- // Infinity. Cut to the chase.
- //
- return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
- }
- if ((exp & 15) != 0) {
- dValue *= SMALL_10_POW[exp & 15];
- }
- if ((exp >>= 4) != 0) {
- int j;
- for (j = 0; exp > 1; j++, exp >>= 1) {
- if ((exp & 1) != 0) {
- dValue *= BIG_10_POW[j];
- }
- }
- //
- // The reason for the weird exp > 1 condition
- // in the above loop was so that the last multiply
- // would get unrolled. We handle it here.
- // It could overflow.
- //
- double t = dValue * BIG_10_POW[j];
- if (Double.isInfinite(t)) {
- //
- // It did overflow.
- // Look more closely at the result.
- // If the exponent is just one too large,
- // then use the maximum finite as our estimate
- // value. Else call the result infinity
- // and punt it.
- // ( I presume this could happen because
- // rounding forces the result here to be
- // an ULP or two larger than
- // Double.MAX_VALUE ).
- //
- t = dValue / 2.0;
- t *= BIG_10_POW[j];
- if (Double.isInfinite(t)) {
- return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
- }
- t = Double.MAX_VALUE;
- }
- dValue = t;
- }
- } else if (exp < 0) {
- exp = -exp;
- if (decExponent < MIN_DECIMAL_EXPONENT - 1) {
- //
- // Lets face it. This is going to be
- // zero. Cut to the chase.
- //
- return (isNegative) ? -0.0 : 0.0;
- }
- if ((exp & 15) != 0) {
- dValue /= SMALL_10_POW[exp & 15];
- }
- if ((exp >>= 4) != 0) {
- int j;
- for (j = 0; exp > 1; j++, exp >>= 1) {
- if ((exp & 1) != 0) {
- dValue *= TINY_10_POW[j];
- }
- }
- //
- // The reason for the weird exp > 1 condition
- // in the above loop was so that the last multiply
- // would get unrolled. We handle it here.
- // It could underflow.
- //
- double t = dValue * TINY_10_POW[j];
- if (t == 0.0) {
- //
- // It did underflow.
- // Look more closely at the result.
- // If the exponent is just one too small,
- // then use the minimum finite as our estimate
- // value. Else call the result 0.0
- // and punt it.
- // ( I presume this could happen because
- // rounding forces the result here to be
- // an ULP or two less than
- // Double.MIN_VALUE ).
- //
- t = dValue * 2.0;
- t *= TINY_10_POW[j];
- if (t == 0.0) {
- return (isNegative) ? -0.0 : 0.0;
- }
- t = Double.MIN_VALUE;
- }
- dValue = t;
- }
- }
-
- //
- // dValue is now approximately the result.
- // The hard part is adjusting it, by comparison
- // with FDBigInteger arithmetic.
- // Formulate the EXACT big-number result as
- // bigD0 * 10^exp
- //
- if (nDigits > MAX_NDIGITS) {
- nDigits = MAX_NDIGITS + 1;
- digits[MAX_NDIGITS] = '1';
- }
- FDBigInteger bigD0 = new FDBigInteger(lValue, digits, kDigits, nDigits);
- exp = decExponent - nDigits;
-
- long ieeeBits = Double.doubleToRawLongBits(dValue); // IEEE-754 bits of double candidate
- final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
- final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
- bigD0 = bigD0.multByPow52(D5, 0);
- bigD0.makeImmutable(); // prevent bigD0 modification inside correctionLoop
- FDBigInteger bigD = null;
- int prevD2 = 0;
-
- correctionLoop:
- while (true) {
- // here ieeeBits can't be NaN, Infinity or zero
- int binexp = (int) (ieeeBits >>> EXP_SHIFT);
- long bigBbits = ieeeBits & DoubleConsts.SIGNIF_BIT_MASK;
- if (binexp > 0) {
- bigBbits |= FRACT_HOB;
- } else { // Normalize denormalized numbers.
- assert bigBbits != 0L : bigBbits; // doubleToBigInt(0.0)
- int leadingZeros = Long.numberOfLeadingZeros(bigBbits);
- int shift = leadingZeros - (63 - EXP_SHIFT);
- bigBbits <<= shift;
- binexp = 1 - shift;
- }
- binexp -= DoubleConsts.EXP_BIAS;
- int lowOrderZeros = Long.numberOfTrailingZeros(bigBbits);
- bigBbits >>>= lowOrderZeros;
- final int bigIntExp = binexp - EXP_SHIFT + lowOrderZeros;
- final int bigIntNBits = EXP_SHIFT + 1 - lowOrderZeros;
-
- //
- // Scale bigD, bigB appropriately for
- // big-integer operations.
- // Naively, we multiply by powers of ten
- // and powers of two. What we actually do
- // is keep track of the powers of 5 and
- // powers of 2 we would use, then factor out
- // common divisors before doing the work.
- //
- int B2 = B5; // powers of 2 in bigB
- int D2 = D5; // powers of 2 in bigD
- int Ulp2; // powers of 2 in halfUlp.
- if (bigIntExp >= 0) {
- B2 += bigIntExp;
- } else {
- D2 -= bigIntExp;
- }
- Ulp2 = B2;
- // shift bigB and bigD left by a number s. t.
- // halfUlp is still an integer.
- int hulpbias;
- if (binexp <= -DoubleConsts.EXP_BIAS) {
- // This is going to be a denormalized number
- // (if not actually zero).
- // half an ULP is at 2^-(DoubleConsts.EXP_BIAS+EXP_SHIFT+1)
- hulpbias = binexp + lowOrderZeros + DoubleConsts.EXP_BIAS;
- } else {
- hulpbias = 1 + lowOrderZeros;
- }
- B2 += hulpbias;
- D2 += hulpbias;
- // if there are common factors of 2, we might just as well
- // factor them out, as they add nothing useful.
- int common2 = Math.min(B2, Math.min(D2, Ulp2));
- B2 -= common2;
- D2 -= common2;
- Ulp2 -= common2;
- // do multiplications by powers of 5 and 2
- FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
- if (bigD == null || prevD2 != D2) {
- bigD = bigD0.leftShift(D2);
- prevD2 = D2;
- }
- //
- // to recap:
- // bigB is the scaled-big-int version of our floating-point
- // candidate.
- // bigD is the scaled-big-int version of the exact value
- // as we understand it.
- // halfUlp is 1/2 an ulp of bigB, except for special cases
- // of exact powers of 2
- //
- // the plan is to compare bigB with bigD, and if the difference
- // is less than halfUlp, then we're satisfied. Otherwise,
- // use the ratio of difference to halfUlp to calculate a fudge
- // factor to add to the floating value, then go 'round again.
- //
- FDBigInteger diff;
- int cmpResult;
- boolean overvalue;
- if ((cmpResult = bigB.cmp(bigD)) > 0) {
- overvalue = true; // our candidate is too big.
- diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
- if ((bigIntNBits == 1) && (bigIntExp > -DoubleConsts.EXP_BIAS + 1)) {
- // candidate is a normalized exact power of 2 and
- // is too big (larger than Double.MIN_NORMAL). We will be subtracting.
- // For our purposes, ulp is the ulp of the
- // next smaller range.
- Ulp2 -= 1;
- if (Ulp2 < 0) {
- // rats. Cannot de-scale ulp this far.
- // must scale diff in other direction.
- Ulp2 = 0;
- diff = diff.leftShift(1);
- }
- }
- } else if (cmpResult < 0) {
- overvalue = false; // our candidate is too small.
- diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
- } else {
- // the candidate is exactly right!
- // this happens with surprising frequency
- break correctionLoop;
- }
- cmpResult = diff.cmpPow52(B5, Ulp2);
- if ((cmpResult) < 0) {
- // difference is small.
- // this is close enough
- break correctionLoop;
- } else if (cmpResult == 0) {
- // difference is exactly half an ULP
- // round to some other value maybe, then finish
- if ((ieeeBits & 1) != 0) { // half ties to even
- ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
- }
- break correctionLoop;
- } else {
- // difference is non-trivial.
- // could scale addend by ratio of difference to
- // halfUlp here, if we bothered to compute that difference.
- // Most of the time ( I hope ) it is about 1 anyway.
- ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
- if (ieeeBits == 0 || ieeeBits == DoubleConsts.EXP_BIT_MASK) { // 0.0 or Double.POSITIVE_INFINITY
- break correctionLoop; // oops. Fell off end of range.
- }
- continue; // try again.
- }
-
- }
- if (isNegative) {
- ieeeBits |= DoubleConsts.SIGN_BIT_MASK;
- }
- return Double.longBitsToDouble(ieeeBits);
- }
-
- /**
- * Takes a FloatingDecimal, which we presumably just scanned in,
- * and finds out what its value is, as a float.
- * This is distinct from doubleValue() to avoid the extremely
- * unlikely case of a double rounding error, wherein the conversion
- * to double has one rounding error, and the conversion of that double
- * to a float has another rounding error, IN THE WRONG DIRECTION,
- * ( because of the preference to a zero low-order bit ).
- */
- @Override
- public float floatValue() {
- int kDigits = Math.min(nDigits, SINGLE_MAX_DECIMAL_DIGITS + 1);
- //
- // convert the lead kDigits to an integer.
- //
- int iValue = (int) digits[0] - (int) '0';
- for (int i = 1; i < kDigits; i++) {
- iValue = iValue * 10 + (int) digits[i] - (int) '0';
- }
- float fValue = (float) iValue;
- int exp = decExponent - kDigits;
- //
- // iValue now contains an integer with the value of
- // the first kDigits digits of the number.
- // fValue contains the (float) of the same.
- //
-
- if (nDigits <= SINGLE_MAX_DECIMAL_DIGITS) {
- //
- // possibly an easy case.
- // We know that the digits can be represented
- // exactly. And if the exponent isn't too outrageous,
- // the whole thing can be done with one operation,
- // thus one rounding error.
- // Note that all our constructors trim all leading and
- // trailing zeros, so simple values (including zero)
- // will always end up here.
- //
- if (exp == 0 || fValue == 0.0f) {
- return (isNegative) ? -fValue : fValue; // small floating integer
- } else if (exp >= 0) {
- if (exp <= SINGLE_MAX_SMALL_TEN) {
- //
- // Can get the answer with one operation,
- // thus one roundoff.
- //
- fValue *= SINGLE_SMALL_10_POW[exp];
- return (isNegative) ? -fValue : fValue;
- }
- int slop = SINGLE_MAX_DECIMAL_DIGITS - kDigits;
- if (exp <= SINGLE_MAX_SMALL_TEN + slop) {
- //
- // We can multiply fValue by 10^(slop)
- // and it is still "small" and exact.
- // Then we can multiply by 10^(exp-slop)
- // with one rounding.
- //
- fValue *= SINGLE_SMALL_10_POW[slop];
- fValue *= SINGLE_SMALL_10_POW[exp - slop];
- return (isNegative) ? -fValue : fValue;
- }
- //
- // Else we have a hard case with a positive exp.
- //
- } else {
- if (exp >= -SINGLE_MAX_SMALL_TEN) {
- //
- // Can get the answer in one division.
- //
- fValue /= SINGLE_SMALL_10_POW[-exp];
- return (isNegative) ? -fValue : fValue;
- }
- //
- // Else we have a hard case with a negative exp.
- //
- }
- } else if ((decExponent >= nDigits) && (nDigits + decExponent <= MAX_DECIMAL_DIGITS)) {
- //
- // In double-precision, this is an exact floating integer.
- // So we can compute to double, then shorten to float
- // with one round, and get the right answer.
- //
- // First, finish accumulating digits.
- // Then convert that integer to a double, multiply
- // by the appropriate power of ten, and convert to float.
- //
- long lValue = (long) iValue;
- for (int i = kDigits; i < nDigits; i++) {
- lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
- }
- double dValue = (double) lValue;
- exp = decExponent - nDigits;
- dValue *= SMALL_10_POW[exp];
- fValue = (float) dValue;
- return (isNegative) ? -fValue : fValue;
-
- }
- //
- // Harder cases:
- // The sum of digits plus exponent is greater than
- // what we think we can do with one error.
- //
- // Start by approximating the right answer by,
- // naively, scaling by powers of 10.
- // Scaling uses doubles to avoid overflow/underflow.
- //
- double dValue = fValue;
- if (exp > 0) {
- if (decExponent > SINGLE_MAX_DECIMAL_EXPONENT + 1) {
- //
- // Lets face it. This is going to be
- // Infinity. Cut to the chase.
- //
- return (isNegative) ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
- }
- if ((exp & 15) != 0) {
- dValue *= SMALL_10_POW[exp & 15];
- }
- if ((exp >>= 4) != 0) {
- int j;
- for (j = 0; exp > 0; j++, exp >>= 1) {
- if ((exp & 1) != 0) {
- dValue *= BIG_10_POW[j];
- }
- }
- }
- } else if (exp < 0) {
- exp = -exp;
- if (decExponent < SINGLE_MIN_DECIMAL_EXPONENT - 1) {
- //
- // Lets face it. This is going to be
- // zero. Cut to the chase.
- //
- return (isNegative) ? -0.0f : 0.0f;
- }
- if ((exp & 15) != 0) {
- dValue /= SMALL_10_POW[exp & 15];
- }
- if ((exp >>= 4) != 0) {
- int j;
- for (j = 0; exp > 0; j++, exp >>= 1) {
- if ((exp & 1) != 0) {
- dValue *= TINY_10_POW[j];
- }
- }
- }
- }
- fValue = Math.max(Float.MIN_VALUE, Math.min(Float.MAX_VALUE, (float) dValue));
-
- //
- // fValue is now approximately the result.
- // The hard part is adjusting it, by comparison
- // with FDBigInteger arithmetic.
- // Formulate the EXACT big-number result as
- // bigD0 * 10^exp
- //
- if (nDigits > SINGLE_MAX_NDIGITS) {
- nDigits = SINGLE_MAX_NDIGITS + 1;
- digits[SINGLE_MAX_NDIGITS] = '1';
- }
- FDBigInteger bigD0 = new FDBigInteger(iValue, digits, kDigits, nDigits);
- exp = decExponent - nDigits;
-
- int ieeeBits = Float.floatToRawIntBits(fValue); // IEEE-754 bits of float candidate
- final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
- final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
- bigD0 = bigD0.multByPow52(D5, 0);
- bigD0.makeImmutable(); // prevent bigD0 modification inside correctionLoop
- FDBigInteger bigD = null;
- int prevD2 = 0;
-
- correctionLoop:
- while (true) {
- // here ieeeBits can't be NaN, Infinity or zero
- int binexp = ieeeBits >>> SINGLE_EXP_SHIFT;
- int bigBbits = ieeeBits & FloatConsts.SIGNIF_BIT_MASK;
- if (binexp > 0) {
- bigBbits |= SINGLE_FRACT_HOB;
- } else { // Normalize denormalized numbers.
- assert bigBbits != 0 : bigBbits; // floatToBigInt(0.0)
- int leadingZeros = Integer.numberOfLeadingZeros(bigBbits);
- int shift = leadingZeros - (31 - SINGLE_EXP_SHIFT);
- bigBbits <<= shift;
- binexp = 1 - shift;
- }
- binexp -= FloatConsts.EXP_BIAS;
- int lowOrderZeros = Integer.numberOfTrailingZeros(bigBbits);
- bigBbits >>>= lowOrderZeros;
- final int bigIntExp = binexp - SINGLE_EXP_SHIFT + lowOrderZeros;
- final int bigIntNBits = SINGLE_EXP_SHIFT + 1 - lowOrderZeros;
-
- //
- // Scale bigD, bigB appropriately for
- // big-integer operations.
- // Naively, we multiply by powers of ten
- // and powers of two. What we actually do
- // is keep track of the powers of 5 and
- // powers of 2 we would use, then factor out
- // common divisors before doing the work.
- //
- int B2 = B5; // powers of 2 in bigB
- int D2 = D5; // powers of 2 in bigD
- int Ulp2; // powers of 2 in halfUlp.
- if (bigIntExp >= 0) {
- B2 += bigIntExp;
- } else {
- D2 -= bigIntExp;
- }
- Ulp2 = B2;
- // shift bigB and bigD left by a number s. t.
- // halfUlp is still an integer.
- int hulpbias;
- if (binexp <= -FloatConsts.EXP_BIAS) {
- // This is going to be a denormalized number
- // (if not actually zero).
- // half an ULP is at 2^-(FloatConsts.EXP_BIAS+SINGLE_EXP_SHIFT+1)
- hulpbias = binexp + lowOrderZeros + FloatConsts.EXP_BIAS;
- } else {
- hulpbias = 1 + lowOrderZeros;
- }
- B2 += hulpbias;
- D2 += hulpbias;
- // if there are common factors of 2, we might just as well
- // factor them out, as they add nothing useful.
- int common2 = Math.min(B2, Math.min(D2, Ulp2));
- B2 -= common2;
- D2 -= common2;
- Ulp2 -= common2;
- // do multiplications by powers of 5 and 2
- FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
- if (bigD == null || prevD2 != D2) {
- bigD = bigD0.leftShift(D2);
- prevD2 = D2;
- }
- //
- // to recap:
- // bigB is the scaled-big-int version of our floating-point
- // candidate.
- // bigD is the scaled-big-int version of the exact value
- // as we understand it.
- // halfUlp is 1/2 an ulp of bigB, except for special cases
- // of exact powers of 2
- //
- // the plan is to compare bigB with bigD, and if the difference
- // is less than halfUlp, then we're satisfied. Otherwise,
- // use the ratio of difference to halfUlp to calculate a fudge
- // factor to add to the floating value, then go 'round again.
- //
- FDBigInteger diff;
- int cmpResult;
- boolean overvalue;
- if ((cmpResult = bigB.cmp(bigD)) > 0) {
- overvalue = true; // our candidate is too big.
- diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
- if ((bigIntNBits == 1) && (bigIntExp > -FloatConsts.EXP_BIAS + 1)) {
- // candidate is a normalized exact power of 2 and
- // is too big (larger than Float.MIN_NORMAL). We will be subtracting.
- // For our purposes, ulp is the ulp of the
- // next smaller range.
- Ulp2 -= 1;
- if (Ulp2 < 0) {
- // rats. Cannot de-scale ulp this far.
- // must scale diff in other direction.
- Ulp2 = 0;
- diff = diff.leftShift(1);
- }
- }
- } else if (cmpResult < 0) {
- overvalue = false; // our candidate is too small.
- diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
- } else {
- // the candidate is exactly right!
- // this happens with surprising frequency
- break correctionLoop;
- }
- cmpResult = diff.cmpPow52(B5, Ulp2);
- if ((cmpResult) < 0) {
- // difference is small.
- // this is close enough
- break correctionLoop;
- } else if (cmpResult == 0) {
- // difference is exactly half an ULP
- // round to some other value maybe, then finish
- if ((ieeeBits & 1) != 0) { // half ties to even
- ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
- }
- break correctionLoop;
- } else {
- // difference is non-trivial.
- // could scale addend by ratio of difference to
- // halfUlp here, if we bothered to compute that difference.
- // Most of the time ( I hope ) it is about 1 anyway.
- ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
- if (ieeeBits == 0 || ieeeBits == FloatConsts.EXP_BIT_MASK) { // 0.0 or Float.POSITIVE_INFINITY
- break correctionLoop; // oops. Fell off end of range.
- }
- continue; // try again.
- }
-
- }
- if (isNegative) {
- ieeeBits |= FloatConsts.SIGN_BIT_MASK;
- }
- return Float.intBitsToFloat(ieeeBits);
- }
-
-
- /**
- * All the positive powers of 10 that can be
- * represented exactly in double/float.
- */
- private static final double[] SMALL_10_POW = {
- 1.0e0,
- 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5,
- 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10,
- 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15,
- 1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20,
- 1.0e21, 1.0e22
- };
-
- private static final float[] SINGLE_SMALL_10_POW = {
- 1.0e0f,
- 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
- 1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
- };
-
- private static final double[] BIG_10_POW = {
- 1e16, 1e32, 1e64, 1e128, 1e256 };
- private static final double[] TINY_10_POW = {
- 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 };
-
- private static final int MAX_SMALL_TEN = SMALL_10_POW.length-1;
- private static final int SINGLE_MAX_SMALL_TEN = SINGLE_SMALL_10_POW.length-1;
-
- }
-
- /**
- * Returns a <code>BinaryToASCIIConverter</code> for a <code>double</code>.
- * The returned object is a <code>ThreadLocal</code> variable of this class.
- *
- * @param d The double precision value to convert.
- * @return The converter.
- */
- public static BinaryToASCIIConverter getBinaryToASCIIConverter(double d) {
- return getBinaryToASCIIConverter(d, true);
- }
-
- /**
- * Returns a <code>BinaryToASCIIConverter</code> for a <code>double</code>.
- * The returned object is a <code>ThreadLocal</code> variable of this class.
- *
- * @param d The double precision value to convert.
- * @param isCompatibleFormat
- * @return The converter.
- */
- static BinaryToASCIIConverter getBinaryToASCIIConverter(double d, boolean isCompatibleFormat) {
- long dBits = Double.doubleToRawLongBits(d);
- boolean isNegative = (dBits&DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign
- long fractBits = dBits & DoubleConsts.SIGNIF_BIT_MASK;
- int binExp = (int)( (dBits&DoubleConsts.EXP_BIT_MASK) >> EXP_SHIFT );
- // Discover obvious special cases of NaN and Infinity.
- if ( binExp == (int)(DoubleConsts.EXP_BIT_MASK>>EXP_SHIFT) ) {
- if ( fractBits == 0L ){
- return isNegative ? B2AC_NEGATIVE_INFINITY : B2AC_POSITIVE_INFINITY;
- } else {
- return B2AC_NOT_A_NUMBER;
- }
- }
- // Finish unpacking
- // Normalize denormalized numbers.
- // Insert assumed high-order bit for normalized numbers.
- // Subtract exponent bias.
- int nSignificantBits;
- if ( binExp == 0 ){
- if ( fractBits == 0L ){
- // not a denorm, just a 0!
- return isNegative ? B2AC_NEGATIVE_ZERO : B2AC_POSITIVE_ZERO;
- }
- int leadingZeros = Long.numberOfLeadingZeros(fractBits);
- int shift = leadingZeros-(63-EXP_SHIFT);
- fractBits <<= shift;
- binExp = 1 - shift;
- nSignificantBits = 64-leadingZeros; // recall binExp is - shift count.
- } else {
- fractBits |= FRACT_HOB;
- nSignificantBits = EXP_SHIFT+1;
- }
- binExp -= DoubleConsts.EXP_BIAS;
- BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer();
- buf.setSign(isNegative);
- // call the routine that actually does all the hard work.
- buf.dtoa(binExp, fractBits, nSignificantBits, isCompatibleFormat);
- return buf;
- }
-
- private static BinaryToASCIIConverter getBinaryToASCIIConverter(float f) {
- int fBits = Float.floatToRawIntBits( f );
- boolean isNegative = (fBits&FloatConsts.SIGN_BIT_MASK) != 0;
- int fractBits = fBits&FloatConsts.SIGNIF_BIT_MASK;
- int binExp = (fBits&FloatConsts.EXP_BIT_MASK) >> SINGLE_EXP_SHIFT;
- // Discover obvious special cases of NaN and Infinity.
- if ( binExp == (FloatConsts.EXP_BIT_MASK>>SINGLE_EXP_SHIFT) ) {
- if ( fractBits == 0L ){
- return isNegative ? B2AC_NEGATIVE_INFINITY : B2AC_POSITIVE_INFINITY;
- } else {
- return B2AC_NOT_A_NUMBER;
- }
- }
- // Finish unpacking
- // Normalize denormalized numbers.
- // Insert assumed high-order bit for normalized numbers.
- // Subtract exponent bias.
- int nSignificantBits;
- if ( binExp == 0 ){
- if ( fractBits == 0 ){
- // not a denorm, just a 0!
- return isNegative ? B2AC_NEGATIVE_ZERO : B2AC_POSITIVE_ZERO;
- }
- int leadingZeros = Integer.numberOfLeadingZeros(fractBits);
- int shift = leadingZeros-(31-SINGLE_EXP_SHIFT);
- fractBits <<= shift;
- binExp = 1 - shift;
- nSignificantBits = 32 - leadingZeros; // recall binExp is - shift count.
- } else {
- fractBits |= SINGLE_FRACT_HOB;
- nSignificantBits = SINGLE_EXP_SHIFT+1;
- }
- binExp -= FloatConsts.EXP_BIAS;
- BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer();
- buf.setSign(isNegative);
- // call the routine that actually does all the hard work.
- buf.dtoa(binExp, ((long)fractBits)<<(EXP_SHIFT-SINGLE_EXP_SHIFT), nSignificantBits, true);
- return buf;
- }
-
- @SuppressWarnings("fallthrough")
- static ASCIIToBinaryConverter readJavaFormatString( String in ) throws NumberFormatException {
- boolean isNegative = false;
- boolean signSeen = false;
- int decExp;
- char c;
-
- parseNumber:
- try{
- in = in.trim(); // don't fool around with white space.
- // throws NullPointerException if null
- int len = in.length();
- if ( len == 0 ) {
- throw new NumberFormatException("empty String");
- }
- int i = 0;
- switch (in.charAt(i)){
- case '-':
- isNegative = true;
- //FALLTHROUGH
- case '+':
- i++;
- signSeen = true;
- }
- c = in.charAt(i);
- if(c == 'N') { // Check for NaN
- if((len-i)==NAN_LENGTH && in.indexOf(NAN_REP,i)==i) {
- return A2BC_NOT_A_NUMBER;
- }
- // something went wrong, throw exception
- break parseNumber;
- } else if(c == 'I') { // Check for Infinity strings
- if((len-i)==INFINITY_LENGTH && in.indexOf(INFINITY_REP,i)==i) {
- return isNegative? A2BC_NEGATIVE_INFINITY : A2BC_POSITIVE_INFINITY;
- }
- // something went wrong, throw exception
- break parseNumber;
- } else if (c == '0') { // check for hexadecimal floating-point number
- if (len > i+1 ) {
- char ch = in.charAt(i+1);
- if (ch == 'x' || ch == 'X' ) { // possible hex string
- return parseHexString(in);
- }
- }
- } // look for and process decimal floating-point string
-
- char[] digits = new char[ len ];
- int nDigits= 0;
- boolean decSeen = false;
- int decPt = 0;
- int nLeadZero = 0;
- int nTrailZero= 0;
-
- skipLeadingZerosLoop:
- while (i < len) {
- c = in.charAt(i);
- if (c == '0') {
- nLeadZero++;
- } else if (c == '.') {
- if (decSeen) {
- // already saw one ., this is the 2nd.
- throw new NumberFormatException("multiple points");
- }
- decPt = i;
- if (signSeen) {
- decPt -= 1;
- }
- decSeen = true;
- } else {
- break skipLeadingZerosLoop;
- }
- i++;
- }
- digitLoop:
- while (i < len) {
- c = in.charAt(i);
- if (c >= '1' && c <= '9') {
- digits[nDigits++] = c;
- nTrailZero = 0;
- } else if (c == '0') {
- digits[nDigits++] = c;
- nTrailZero++;
- } else if (c == '.') {
- if (decSeen) {
- // already saw one ., this is the 2nd.
- throw new NumberFormatException("multiple points");
- }
- decPt = i;
- if (signSeen) {
- decPt -= 1;
- }
- decSeen = true;
- } else {
- break digitLoop;
- }
- i++;
- }
- nDigits -=nTrailZero;
- //
- // At this point, we've scanned all the digits and decimal
- // point we're going to see. Trim off leading and trailing
- // zeros, which will just confuse us later, and adjust
- // our initial decimal exponent accordingly.
- // To review:
- // we have seen i total characters.
- // nLeadZero of them were zeros before any other digits.
- // nTrailZero of them were zeros after any other digits.
- // if ( decSeen ), then a . was seen after decPt characters
- // ( including leading zeros which have been discarded )
- // nDigits characters were neither lead nor trailing
- // zeros, nor point
- //
- //
- // special hack: if we saw no non-zero digits, then the
- // answer is zero!
- // Unfortunately, we feel honor-bound to keep parsing!
- //
- boolean isZero = (nDigits == 0);
- if ( isZero && nLeadZero == 0 ){
- // we saw NO DIGITS AT ALL,
- // not even a crummy 0!
- // this is not allowed.
- break parseNumber; // go throw exception
- }
- //
- // Our initial exponent is decPt, adjusted by the number of
- // discarded zeros. Or, if there was no decPt,
- // then its just nDigits adjusted by discarded trailing zeros.
- //
- if ( decSeen ){
- decExp = decPt - nLeadZero;
- } else {
- decExp = nDigits + nTrailZero;
- }
-
- //
- // Look for 'e' or 'E' and an optionally signed integer.
- //
- if ( (i < len) && (((c = in.charAt(i) )=='e') || (c == 'E') ) ){
- int expSign = 1;
- int expVal = 0;
- int reallyBig = Integer.MAX_VALUE / 10;
- boolean expOverflow = false;
- switch( in.charAt(++i) ){
- case '-':
- expSign = -1;
- //FALLTHROUGH
- case '+':
- i++;
- }
- int expAt = i;
- expLoop:
- while ( i < len ){
- if ( expVal >= reallyBig ){
- // the next character will cause integer
- // overflow.
- expOverflow = true;
- }
- c = in.charAt(i++);
- if(c>='0' && c<='9') {
- expVal = expVal*10 + ( (int)c - (int)'0' );
- } else {
- i--; // back up.
- break expLoop; // stop parsing exponent.
- }
- }
- int expLimit = BIG_DECIMAL_EXPONENT + nDigits + nTrailZero;
- if (expOverflow || (expVal > expLimit)) {
- // There is still a chance that the exponent will be safe to
- // use: if it would eventually decrease due to a negative
- // decExp, and that number is below the limit. We check for
- // that here.
- if (!expOverflow && (expSign == 1 && decExp < 0)
- && (expVal + decExp) < expLimit) {
- // Cannot overflow: adding a positive and negative number.
- decExp += expVal;
- } else {
- //
- // The intent here is to end up with
- // infinity or zero, as appropriate.
- // The reason for yielding such a small decExponent,
- // rather than something intuitive such as
- // expSign*Integer.MAX_VALUE, is that this value
- // is subject to further manipulation in
- // doubleValue() and floatValue(), and I don't want
- // it to be able to cause overflow there!
- // (The only way we can get into trouble here is for
- // really outrageous nDigits+nTrailZero, such as 2
- // billion.)
- //
- decExp = expSign * expLimit;
- }
- } else {
- // this should not overflow, since we tested
- // for expVal > (MAX+N), where N >= abs(decExp)
- decExp = decExp + expSign*expVal;
- }
-
- // if we saw something not a digit ( or end of string )
- // after the [Ee][+-], without seeing any digits at all
- // this is certainly an error. If we saw some digits,
- // but then some trailing garbage, that might be ok.
- // so we just fall through in that case.
- // HUMBUG
- if ( i == expAt ) {
- break parseNumber; // certainly bad
- }
- }
- //
- // We parsed everything we could.
- // If there are leftovers, then this is not good input!
- //
- if ( i < len &&
- ((i != len - 1) ||
- (in.charAt(i) != 'f' &&
- in.charAt(i) != 'F' &&
- in.charAt(i) != 'd' &&
- in.charAt(i) != 'D'))) {
- break parseNumber; // go throw exception
- }
- if(isZero) {
- return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
- }
- return new ASCIIToBinaryBuffer(isNegative, decExp, digits, nDigits);
- } catch ( StringIndexOutOfBoundsException e ){ }
- throw new NumberFormatException("For input string: \"" + in + "\"");
- }
-
- private static class HexFloatPattern {
- /**
- * Grammar is compatible with hexadecimal floating-point constants
- * described in section 6.4.4.2 of the C99 specification.
- */
- private static final Pattern VALUE = Pattern.compile(
- //1 234 56 7 8 9
- "([-+])?0[xX](((\\p{XDigit}+)\\.?)|((\\p{XDigit}*)\\.(\\p{XDigit}+)))[pP]([-+])?(\\p{Digit}+)[fFdD]?"
- );
- }
-
- /**
- * Converts string s to a suitable floating decimal; uses the
- * double constructor and sets the roundDir variable appropriately
- * in case the value is later converted to a float.
- *
- * @param s The <code>String</code> to parse.
- */
- static ASCIIToBinaryConverter parseHexString(String s) {
- // Verify string is a member of the hexadecimal floating-point
- // string language.
- Matcher m = HexFloatPattern.VALUE.matcher(s);
- boolean validInput = m.matches();
- if (!validInput) {
- // Input does not match pattern
- throw new NumberFormatException("For input string: \"" + s + "\"");
- } else { // validInput
- //
- // We must isolate the sign, significand, and exponent
- // fields. The sign value is straightforward. Since
- // floating-point numbers are stored with a normalized
- // representation, the significand and exponent are
- // interrelated.
- //
- // After extracting the sign, we normalized the
- // significand as a hexadecimal value, calculating an
- // exponent adjust for any shifts made during
- // normalization. If the significand is zero, the
- // exponent doesn't need to be examined since the output
- // will be zero.
- //
- // Next the exponent in the input string is extracted.
- // Afterwards, the significand is normalized as a *binary*
- // value and the input value's normalized exponent can be
- // computed. The significand bits are copied into a
- // double significand; if the string has more logical bits
- // than can fit in a double, the extra bits affect the
- // round and sticky bits which are used to round the final
- // value.
- //
- // Extract significand sign
- String group1 = m.group(1);
- boolean isNegative = ((group1 != null) && group1.equals("-"));
-
- // Extract Significand magnitude
- //
- // Based on the form of the significand, calculate how the
- // binary exponent needs to be adjusted to create a
- // normalized//hexadecimal* floating-point number; that
- // is, a number where there is one nonzero hex digit to
- // the left of the (hexa)decimal point. Since we are
- // adjusting a binary, not hexadecimal exponent, the
- // exponent is adjusted by a multiple of 4.
- //
- // There are a number of significand scenarios to consider;
- // letters are used in indicate nonzero digits:
- //
- // 1. 000xxxx => x.xxx normalized
- // increase exponent by (number of x's - 1)*4
- //
- // 2. 000xxx.yyyy => x.xxyyyy normalized
- // increase exponent by (number of x's - 1)*4
- //
- // 3. .000yyy => y.yy normalized
- // decrease exponent by (number of zeros + 1)*4
- //
- // 4. 000.00000yyy => y.yy normalized
- // decrease exponent by (number of zeros to right of point + 1)*4
- //
- // If the significand is exactly zero, return a properly
- // signed zero.
- //
-
- String significandString = null;
- int signifLength = 0;
- int exponentAdjust = 0;
- {
- int leftDigits = 0; // number of meaningful digits to
- // left of "decimal" point
- // (leading zeros stripped)
- int rightDigits = 0; // number of digits to right of
- // "decimal" point; leading zeros
- // must always be accounted for
- //
- // The significand is made up of either
- //
- // 1. group 4 entirely (integer portion only)
- //
- // OR
- //
- // 2. the fractional portion from group 7 plus any
- // (optional) integer portions from group 6.
- //
- String group4;
- if ((group4 = m.group(4)) != null) { // Integer-only significand
- // Leading zeros never matter on the integer portion
- significandString = stripLeadingZeros(group4);
- leftDigits = significandString.length();
- } else {
- // Group 6 is the optional integer; leading zeros
- // never matter on the integer portion
- String group6 = stripLeadingZeros(m.group(6));
- leftDigits = group6.length();
-
- // fraction
- String group7 = m.group(7);
- rightDigits = group7.length();
-
- // Turn "integer.fraction" into "integer"+"fraction"
- significandString =
- ((group6 == null) ? "" : group6) + // is the null
- // check necessary?
- group7;
- }
-
- significandString = stripLeadingZeros(significandString);
- signifLength = significandString.length();
-
- //
- // Adjust exponent as described above
- //
- if (leftDigits >= 1) { // Cases 1 and 2
- exponentAdjust = 4 * (leftDigits - 1);
- } else { // Cases 3 and 4
- exponentAdjust = -4 * (rightDigits - signifLength + 1);
- }
-
- // If the significand is zero, the exponent doesn't
- // matter; return a properly signed zero.
-
- if (signifLength == 0) { // Only zeros in input
- return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
- }
- }
-
- // Extract Exponent
- //
- // Use an int to read in the exponent value; this should
- // provide more than sufficient range for non-contrived
- // inputs. If reading the exponent in as an int does
- // overflow, examine the sign of the exponent and
- // significand to determine what to do.
- //
- String group8 = m.group(8);
- boolean positiveExponent = (group8 == null) || group8.equals("+");
- long unsignedRawExponent;
- try {
- unsignedRawExponent = Integer.parseInt(m.group(9));
- }
- catch (NumberFormatException e) {
- // At this point, we know the exponent is
- // syntactically well-formed as a sequence of
- // digits. Therefore, if an NumberFormatException
- // is thrown, it must be due to overflowing int's
- // range. Also, at this point, we have already
- // checked for a zero significand. Thus the signs
- // of the exponent and significand determine the
- // final result:
- //
- // significand
- // + -
- // exponent + +infinity -infinity
- // - +0.0 -0.0
- return isNegative ?
- (positiveExponent ? A2BC_NEGATIVE_INFINITY : A2BC_NEGATIVE_ZERO)
- : (positiveExponent ? A2BC_POSITIVE_INFINITY : A2BC_POSITIVE_ZERO);
-
- }
-
- long rawExponent =
- (positiveExponent ? 1L : -1L) * // exponent sign
- unsignedRawExponent; // exponent magnitude
-
- // Calculate partially adjusted exponent
- long exponent = rawExponent + exponentAdjust;
-
- // Starting copying non-zero bits into proper position in
- // a long; copy explicit bit too; this will be masked
- // later for normal values.
-
- boolean round = false;
- boolean sticky = false;
- int nextShift = 0;
- long significand = 0L;
- // First iteration is different, since we only copy
- // from the leading significand bit; one more exponent
- // adjust will be needed...
-
- // IMPORTANT: make leadingDigit a long to avoid
- // surprising shift semantics!
- long leadingDigit = getHexDigit(significandString, 0);
-
- //
- // Left shift the leading digit (53 - (bit position of
- // leading 1 in digit)); this sets the top bit of the
- // significand to 1. The nextShift value is adjusted
- // to take into account the number of bit positions of
- // the leadingDigit actually used. Finally, the
- // exponent is adjusted to normalize the significand
- // as a binary value, not just a hex value.
- //
- if (leadingDigit == 1) {
- significand |= leadingDigit << 52;
- nextShift = 52 - 4;
- // exponent += 0
- } else if (leadingDigit <= 3) { // [2, 3]
- significand |= leadingDigit << 51;
- nextShift = 52 - 5;
- exponent += 1;
- } else if (leadingDigit <= 7) { // [4, 7]
- significand |= leadingDigit << 50;
- nextShift = 52 - 6;
- exponent += 2;
- } else if (leadingDigit <= 15) { // [8, f]
- significand |= leadingDigit << 49;
- nextShift = 52 - 7;
- exponent += 3;
- } else {
- throw new AssertionError("Result from digit conversion too large!");
- }
- // The preceding if-else could be replaced by a single
- // code block based on the high-order bit set in
- // leadingDigit. Given leadingOnePosition,
-
- // significand |= leadingDigit << (SIGNIFICAND_WIDTH - leadingOnePosition);
- // nextShift = 52 - (3 + leadingOnePosition);
- // exponent += (leadingOnePosition-1);
-
- //
- // Now the exponent variable is equal to the normalized
- // binary exponent. Code below will make representation
- // adjustments if the exponent is incremented after
- // rounding (includes overflows to infinity) or if the
- // result is subnormal.
- //
-
- // Copy digit into significand until the significand can't
- // hold another full hex digit or there are no more input
- // hex digits.
- int i = 0;
- for (i = 1;
- i < signifLength && nextShift >= 0;
- i++) {
- long currentDigit = getHexDigit(significandString, i);
- significand |= (currentDigit << nextShift);
- nextShift -= 4;
- }
-
- // After the above loop, the bulk of the string is copied.
- // Now, we must copy any partial hex digits into the
- // significand AND compute the round bit and start computing
- // sticky bit.
-
- if (i < signifLength) { // at least one hex input digit exists
- long currentDigit = getHexDigit(significandString, i);
-
- // from nextShift, figure out how many bits need
- // to be copied, if any
- switch (nextShift) { // must be negative
- case -1:
- // three bits need to be copied in; can
- // set round bit
- significand |= ((currentDigit & 0xEL) >> 1);
- round = (currentDigit & 0x1L) != 0L;
- break;
-
- case -2:
- // two bits need to be copied in; can
- // set round and start sticky
- significand |= ((currentDigit & 0xCL) >> 2);
- round = (currentDigit & 0x2L) != 0L;
- sticky = (currentDigit & 0x1L) != 0;
- break;
-
- case -3:
- // one bit needs to be copied in
- significand |= ((currentDigit & 0x8L) >> 3);
- // Now set round and start sticky, if possible
- round = (currentDigit & 0x4L) != 0L;
- sticky = (currentDigit & 0x3L) != 0;
- break;
-
- case -4:
- // all bits copied into significand; set
- // round and start sticky
- round = ((currentDigit & 0x8L) != 0); // is top bit set?
- // nonzeros in three low order bits?
- sticky = (currentDigit & 0x7L) != 0;
- break;
-
- default:
- throw new AssertionError("Unexpected shift distance remainder.");
- // break;
- }
-
- // Round is set; sticky might be set.
-
- // For the sticky bit, it suffices to check the
- // current digit and test for any nonzero digits in
- // the remaining unprocessed input.
- i++;
- while (i < signifLength && !sticky) {
- currentDigit = getHexDigit(significandString, i);
- sticky = sticky || (currentDigit != 0);
- i++;
- }
-
- }
- // else all of string was seen, round and sticky are
- // correct as false.
-
- // Float calculations
- int floatBits = isNegative ? FloatConsts.SIGN_BIT_MASK : 0;
- if (exponent >= FloatConsts.MIN_EXPONENT) {
- if (exponent > FloatConsts.MAX_EXPONENT) {
- // Float.POSITIVE_INFINITY
- floatBits |= FloatConsts.EXP_BIT_MASK;
- } else {
- int threshShift = DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH - 1;
- boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky;
- int iValue = (int) (significand >>> threshShift);
- if ((iValue & 3) != 1 || floatSticky) {
- iValue++;
- }
- floatBits |= (((((int) exponent) + (FloatConsts.EXP_BIAS - 1))) << SINGLE_EXP_SHIFT) + (iValue >> 1);
- }
- } else {
- if (exponent < FloatConsts.MIN_SUB_EXPONENT - 1) {
- // 0
- } else {
- // exponent == -127 ==> threshShift = 53 - 2 + (-149) - (-127) = 53 - 24
- int threshShift = (int) ((DoubleConsts.SIGNIFICAND_WIDTH - 2 + FloatConsts.MIN_SUB_EXPONENT) - exponent);
- assert threshShift >= DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH;
- assert threshShift < DoubleConsts.SIGNIFICAND_WIDTH;
- boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky;
- int iValue = (int) (significand >>> threshShift);
- if ((iValue & 3) != 1 || floatSticky) {
- iValue++;
- }
- floatBits |= iValue >> 1;
- }
- }
- float fValue = Float.intBitsToFloat(floatBits);
-
- // Check for overflow and update exponent accordingly.
- if (exponent > DoubleConsts.MAX_EXPONENT) { // Infinite result
- // overflow to properly signed infinity
- return isNegative ? A2BC_NEGATIVE_INFINITY : A2BC_POSITIVE_INFINITY;
- } else { // Finite return value
- if (exponent <= DoubleConsts.MAX_EXPONENT && // (Usually) normal result
- exponent >= DoubleConsts.MIN_EXPONENT) {
-
- // The result returned in this block cannot be a
- // zero or subnormal; however after the
- // significand is adjusted from rounding, we could
- // still overflow in infinity.
-
- // AND exponent bits into significand; if the
- // significand is incremented and overflows from
- // rounding, this combination will update the
- // exponent correctly, even in the case of
- // Double.MAX_VALUE overflowing to infinity.
-
- significand = ((( exponent +
- (long) DoubleConsts.EXP_BIAS) <<
- (DoubleConsts.SIGNIFICAND_WIDTH - 1))
- & DoubleConsts.EXP_BIT_MASK) |
- (DoubleConsts.SIGNIF_BIT_MASK & significand);
-
- } else { // Subnormal or zero
- // (exponent < DoubleConsts.MIN_EXPONENT)
-
- if (exponent < (DoubleConsts.MIN_SUB_EXPONENT - 1)) {
- // No way to round back to nonzero value
- // regardless of significand if the exponent is
- // less than -1075.
- return isNegative ? A2BC_NEGATIVE_ZERO : A2BC_POSITIVE_ZERO;
- } else { // -1075 <= exponent <= MIN_EXPONENT -1 = -1023
- //
- // Find bit position to round to; recompute
- // round and sticky bits, and shift
- // significand right appropriately.
- //
-
- sticky = sticky || round;
- round = false;
-
- // Number of bits of significand to preserve is
- // exponent - abs_min_exp +1
- // check:
- // -1075 +1074 + 1 = 0
- // -1023 +1074 + 1 = 52
-
- int bitsDiscarded = 53 -
- ((int) exponent - DoubleConsts.MIN_SUB_EXPONENT + 1);
- assert bitsDiscarded >= 1 && bitsDiscarded <= 53;
-
- // What to do here:
- // First, isolate the new round bit
- round = (significand & (1L << (bitsDiscarded - 1))) != 0L;
- if (bitsDiscarded > 1) {
- // create mask to update sticky bits; low
- // order bitsDiscarded bits should be 1
- long mask = ~((~0L) << (bitsDiscarded - 1));
- sticky = sticky || ((significand & mask) != 0L);
- }
-
- // Now, discard the bits
- significand = significand >> bitsDiscarded;
-
- significand = ((((long) (DoubleConsts.MIN_EXPONENT - 1) + // subnorm exp.
- (long) DoubleConsts.EXP_BIAS) <<
- (DoubleConsts.SIGNIFICAND_WIDTH - 1))
- & DoubleConsts.EXP_BIT_MASK) |
- (DoubleConsts.SIGNIF_BIT_MASK & significand);
- }
- }
-
- // The significand variable now contains the currently
- // appropriate exponent bits too.
-
- //
- // Determine if significand should be incremented;
- // making this determination depends on the least
- // significant bit and the round and sticky bits.
- //
- // Round to nearest even rounding table, adapted from
- // table 4.7 in "Computer Arithmetic" by IsraelKoren.
- // The digit to the left of the "decimal" point is the
- // least significant bit, the digits to the right of
- // the point are the round and sticky bits
- //
- // Number Round(x)
- // x0.00 x0.
- // x0.01 x0.
- // x0.10 x0.
- // x0.11 x1. = x0. +1
- // x1.00 x1.
- // x1.01 x1.
- // x1.10 x1. + 1
- // x1.11 x1. + 1
- //
- boolean leastZero = ((significand & 1L) == 0L);
- if ((leastZero && round && sticky) ||
- ((!leastZero) && round)) {
- significand++;
- }
-
- double value = isNegative ?
- Double.longBitsToDouble(significand | DoubleConsts.SIGN_BIT_MASK) :
- Double.longBitsToDouble(significand );
-
- return new PreparedASCIIToBinaryBuffer(value, fValue);
- }
- }
- }
-
- /**
- * Returns <code>s</code> with any leading zeros removed.
- */
- static String stripLeadingZeros(String s) {
-// return s.replaceFirst("^0+", "");
- if(!s.isEmpty() && s.charAt(0)=='0') {
- for(int i=1; i<s.length(); i++) {
- if(s.charAt(i)!='0') {
- return s.substring(i);
- }
- }
- return "";
- }
- return s;
- }
-
- /**
- * Extracts a hexadecimal digit from position <code>position</code>
- * of string <code>s</code>.
- */
- static int getHexDigit(String s, int position) {
- int value = Character.digit(s.charAt(position), 16);
- if (value <= -1 || value >= 16) {
- throw new AssertionError("Unexpected failure of digit conversion of " +
- s.charAt(position));
- }
- return value;
- }
-}
--- a/jdk/src/java.base/share/classes/sun/misc/FormattedFloatingDecimal.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,349 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package sun.misc;
-
-import java.util.Arrays;
-
-public class FormattedFloatingDecimal{
-
- public enum Form { SCIENTIFIC, COMPATIBLE, DECIMAL_FLOAT, GENERAL };
-
-
- public static FormattedFloatingDecimal valueOf(double d, int precision, Form form){
- FloatingDecimal.BinaryToASCIIConverter fdConverter =
- FloatingDecimal.getBinaryToASCIIConverter(d, form == Form.COMPATIBLE);
- return new FormattedFloatingDecimal(precision,form, fdConverter);
- }
-
- private int decExponentRounded;
- private char[] mantissa;
- private char[] exponent;
-
- private static final ThreadLocal<Object> threadLocalCharBuffer =
- new ThreadLocal<Object>() {
- @Override
- protected Object initialValue() {
- return new char[20];
- }
- };
-
- private static char[] getBuffer(){
- return (char[]) threadLocalCharBuffer.get();
- }
-
- private FormattedFloatingDecimal(int precision, Form form, FloatingDecimal.BinaryToASCIIConverter fdConverter) {
- if (fdConverter.isExceptional()) {
- this.mantissa = fdConverter.toJavaFormatString().toCharArray();
- this.exponent = null;
- return;
- }
- char[] digits = getBuffer();
- int nDigits = fdConverter.getDigits(digits);
- int decExp = fdConverter.getDecimalExponent();
- int exp;
- boolean isNegative = fdConverter.isNegative();
- switch (form) {
- case COMPATIBLE:
- exp = decExp;
- this.decExponentRounded = exp;
- fillCompatible(precision, digits, nDigits, exp, isNegative);
- break;
- case DECIMAL_FLOAT:
- exp = applyPrecision(decExp, digits, nDigits, decExp + precision);
- fillDecimal(precision, digits, nDigits, exp, isNegative);
- this.decExponentRounded = exp;
- break;
- case SCIENTIFIC:
- exp = applyPrecision(decExp, digits, nDigits, precision + 1);
- fillScientific(precision, digits, nDigits, exp, isNegative);
- this.decExponentRounded = exp;
- break;
- case GENERAL:
- exp = applyPrecision(decExp, digits, nDigits, precision);
- // adjust precision to be the number of digits to right of decimal
- // the real exponent to be output is actually exp - 1, not exp
- if (exp - 1 < -4 || exp - 1 >= precision) {
- // form = Form.SCIENTIFIC;
- precision--;
- fillScientific(precision, digits, nDigits, exp, isNegative);
- } else {
- // form = Form.DECIMAL_FLOAT;
- precision = precision - exp;
- fillDecimal(precision, digits, nDigits, exp, isNegative);
- }
- this.decExponentRounded = exp;
- break;
- default:
- assert false;
- }
- }
-
- // returns the exponent after rounding has been done by applyPrecision
- public int getExponentRounded() {
- return decExponentRounded - 1;
- }
-
- public char[] getMantissa(){
- return mantissa;
- }
-
- public char[] getExponent(){
- return exponent;
- }
-
- /**
- * Returns new decExp in case of overflow.
- */
- private static int applyPrecision(int decExp, char[] digits, int nDigits, int prec) {
- if (prec >= nDigits || prec < 0) {
- // no rounding necessary
- return decExp;
- }
- if (prec == 0) {
- // only one digit (0 or 1) is returned because the precision
- // excludes all significant digits
- if (digits[0] >= '5') {
- digits[0] = '1';
- Arrays.fill(digits, 1, nDigits, '0');
- return decExp + 1;
- } else {
- Arrays.fill(digits, 0, nDigits, '0');
- return decExp;
- }
- }
- int q = digits[prec];
- if (q >= '5') {
- int i = prec;
- q = digits[--i];
- if ( q == '9' ) {
- while ( q == '9' && i > 0 ){
- q = digits[--i];
- }
- if ( q == '9' ){
- // carryout! High-order 1, rest 0s, larger exp.
- digits[0] = '1';
- Arrays.fill(digits, 1, nDigits, '0');
- return decExp+1;
- }
- }
- digits[i] = (char)(q + 1);
- Arrays.fill(digits, i+1, nDigits, '0');
- } else {
- Arrays.fill(digits, prec, nDigits, '0');
- }
- return decExp;
- }
-
- /**
- * Fills mantissa and exponent char arrays for compatible format.
- */
- private void fillCompatible(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
- int startIndex = isNegative ? 1 : 0;
- if (exp > 0 && exp < 8) {
- // print digits.digits.
- if (nDigits < exp) {
- int extraZeros = exp - nDigits;
- mantissa = create(isNegative, nDigits + extraZeros + 2);
- System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
- Arrays.fill(mantissa, startIndex + nDigits, startIndex + nDigits + extraZeros, '0');
- mantissa[startIndex + nDigits + extraZeros] = '.';
- mantissa[startIndex + nDigits + extraZeros+1] = '0';
- } else if (exp < nDigits) {
- int t = Math.min(nDigits - exp, precision);
- mantissa = create(isNegative, exp + 1 + t);
- System.arraycopy(digits, 0, mantissa, startIndex, exp);
- mantissa[startIndex + exp ] = '.';
- System.arraycopy(digits, exp, mantissa, startIndex+exp+1, t);
- } else { // exp == digits.length
- mantissa = create(isNegative, nDigits + 2);
- System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
- mantissa[startIndex + nDigits ] = '.';
- mantissa[startIndex + nDigits +1] = '0';
- }
- } else if (exp <= 0 && exp > -3) {
- int zeros = Math.max(0, Math.min(-exp, precision));
- int t = Math.max(0, Math.min(nDigits, precision + exp));
- // write '0' s before the significant digits
- if (zeros > 0) {
- mantissa = create(isNegative, zeros + 2 + t);
- mantissa[startIndex] = '0';
- mantissa[startIndex+1] = '.';
- Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
- if (t > 0) {
- // copy only when significant digits are within the precision
- System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
- }
- } else if (t > 0) {
- mantissa = create(isNegative, zeros + 2 + t);
- mantissa[startIndex] = '0';
- mantissa[startIndex + 1] = '.';
- // copy only when significant digits are within the precision
- System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
- } else {
- this.mantissa = create(isNegative, 1);
- this.mantissa[startIndex] = '0';
- }
- } else {
- if (nDigits > 1) {
- mantissa = create(isNegative, nDigits + 1);
- mantissa[startIndex] = digits[0];
- mantissa[startIndex + 1] = '.';
- System.arraycopy(digits, 1, mantissa, startIndex + 2, nDigits - 1);
- } else {
- mantissa = create(isNegative, 3);
- mantissa[startIndex] = digits[0];
- mantissa[startIndex + 1] = '.';
- mantissa[startIndex + 2] = '0';
- }
- int e, expStartIntex;
- boolean isNegExp = (exp <= 0);
- if (isNegExp) {
- e = -exp + 1;
- expStartIntex = 1;
- } else {
- e = exp - 1;
- expStartIntex = 0;
- }
- // decExponent has 1, 2, or 3, digits
- if (e <= 9) {
- exponent = create(isNegExp,1);
- exponent[expStartIntex] = (char) (e + '0');
- } else if (e <= 99) {
- exponent = create(isNegExp,2);
- exponent[expStartIntex] = (char) (e / 10 + '0');
- exponent[expStartIntex+1] = (char) (e % 10 + '0');
- } else {
- exponent = create(isNegExp,3);
- exponent[expStartIntex] = (char) (e / 100 + '0');
- e %= 100;
- exponent[expStartIntex+1] = (char) (e / 10 + '0');
- exponent[expStartIntex+2] = (char) (e % 10 + '0');
- }
- }
- }
-
- private static char[] create(boolean isNegative, int size) {
- if(isNegative) {
- char[] r = new char[size +1];
- r[0] = '-';
- return r;
- } else {
- return new char[size];
- }
- }
-
- /*
- * Fills mantissa char arrays for DECIMAL_FLOAT format.
- * Exponent should be equal to null.
- */
- private void fillDecimal(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
- int startIndex = isNegative ? 1 : 0;
- if (exp > 0) {
- // print digits.digits.
- if (nDigits < exp) {
- mantissa = create(isNegative,exp);
- System.arraycopy(digits, 0, mantissa, startIndex, nDigits);
- Arrays.fill(mantissa, startIndex + nDigits, startIndex + exp, '0');
- // Do not append ".0" for formatted floats since the user
- // may request that it be omitted. It is added as necessary
- // by the Formatter.
- } else {
- int t = Math.min(nDigits - exp, precision);
- mantissa = create(isNegative, exp + (t > 0 ? (t + 1) : 0));
- System.arraycopy(digits, 0, mantissa, startIndex, exp);
- // Do not append ".0" for formatted floats since the user
- // may request that it be omitted. It is added as necessary
- // by the Formatter.
- if (t > 0) {
- mantissa[startIndex + exp] = '.';
- System.arraycopy(digits, exp, mantissa, startIndex + exp + 1, t);
- }
- }
- } else if (exp <= 0) {
- int zeros = Math.max(0, Math.min(-exp, precision));
- int t = Math.max(0, Math.min(nDigits, precision + exp));
- // write '0' s before the significant digits
- if (zeros > 0) {
- mantissa = create(isNegative, zeros + 2 + t);
- mantissa[startIndex] = '0';
- mantissa[startIndex+1] = '.';
- Arrays.fill(mantissa, startIndex + 2, startIndex + 2 + zeros, '0');
- if (t > 0) {
- // copy only when significant digits are within the precision
- System.arraycopy(digits, 0, mantissa, startIndex + 2 + zeros, t);
- }
- } else if (t > 0) {
- mantissa = create(isNegative, zeros + 2 + t);
- mantissa[startIndex] = '0';
- mantissa[startIndex + 1] = '.';
- // copy only when significant digits are within the precision
- System.arraycopy(digits, 0, mantissa, startIndex + 2, t);
- } else {
- this.mantissa = create(isNegative, 1);
- this.mantissa[startIndex] = '0';
- }
- }
- }
-
- /**
- * Fills mantissa and exponent char arrays for SCIENTIFIC format.
- */
- private void fillScientific(int precision, char[] digits, int nDigits, int exp, boolean isNegative) {
- int startIndex = isNegative ? 1 : 0;
- int t = Math.max(0, Math.min(nDigits - 1, precision));
- if (t > 0) {
- mantissa = create(isNegative, t + 2);
- mantissa[startIndex] = digits[0];
- mantissa[startIndex + 1] = '.';
- System.arraycopy(digits, 1, mantissa, startIndex + 2, t);
- } else {
- mantissa = create(isNegative, 1);
- mantissa[startIndex] = digits[0];
- }
- char expSign;
- int e;
- if (exp <= 0) {
- expSign = '-';
- e = -exp + 1;
- } else {
- expSign = '+' ;
- e = exp - 1;
- }
- // decExponent has 1, 2, or 3, digits
- if (e <= 9) {
- exponent = new char[] { expSign,
- '0', (char) (e + '0') };
- } else if (e <= 99) {
- exponent = new char[] { expSign,
- (char) (e / 10 + '0'), (char) (e % 10 + '0') };
- } else {
- char hiExpChar = (char) (e / 100 + '0');
- e %= 100;
- exponent = new char[] { expSign,
- hiExpChar, (char) (e / 10 + '0'), (char) (e % 10 + '0') };
- }
- }
-}
--- a/jdk/src/java.base/share/classes/sun/misc/VM.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/misc/VM.java Thu Dec 24 10:34:31 2015 -0800
@@ -27,9 +27,6 @@
import static java.lang.Thread.State.*;
import java.util.Properties;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
public class VM {
@@ -288,10 +285,10 @@
}
/* Current count of objects pending for finalization */
- private static volatile int finalRefCount = 0;
+ private static volatile int finalRefCount;
/* Peak count of objects pending for finalization */
- private static volatile int peakFinalRefCount = 0;
+ private static volatile int peakFinalRefCount;
/*
* Gets the number of objects pending for finalization.
--- a/jdk/src/java.base/share/classes/sun/net/www/http/HttpCapture.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/net/www/http/HttpCapture.java Thu Dec 24 10:34:31 2015 -0800
@@ -54,12 +54,12 @@
* @author jccollet
*/
public class HttpCapture {
- private File file = null;
+ private File file;
private boolean incoming = true;
- private BufferedWriter out = null;
- private static boolean initialized = false;
- private static volatile ArrayList<Pattern> patterns = null;
- private static volatile ArrayList<String> capFiles = null;
+ private BufferedWriter out;
+ private static boolean initialized;
+ private static volatile ArrayList<Pattern> patterns;
+ private static volatile ArrayList<String> capFiles;
private static synchronized void init() {
initialized = true;
--- a/jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/net/www/http/HttpClient.java Thu Dec 24 10:34:31 2015 -0800
@@ -98,7 +98,7 @@
// from previous releases.
private static boolean retryPostProp = true;
- volatile boolean keepingAlive = false; /* this is a keep-alive connection */
+ volatile boolean keepingAlive; /* this is a keep-alive connection */
int keepAliveConnections = -1; /* number of keep-alives left */
/**Idle timeout value, in milliseconds. Zero means infinity,
--- a/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -51,14 +51,14 @@
protected final FileDescriptor fd;
// the local address to which the channel's socket is bound
- protected volatile InetSocketAddress localAddress = null;
+ protected volatile InetSocketAddress localAddress;
// need this lock to set local address
private final Object stateLock = new Object();
// close support
private ReadWriteLock closeLock = new ReentrantReadWriteLock();
- private volatile boolean open = true;
+ private volatile boolean closed;
// set true when accept operation is cancelled
private volatile boolean acceptKilled;
@@ -73,7 +73,7 @@
@Override
public final boolean isOpen() {
- return open;
+ return !closed;
}
/**
@@ -102,9 +102,9 @@
// synchronize with any threads using file descriptor/handle
closeLock.writeLock().lock();
try {
- if (!open)
+ if (closed)
return; // already closed
- open = false;
+ closed = true;
} finally {
closeLock.writeLock().unlock();
}
--- a/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -54,8 +54,8 @@
// protects state, localAddress, and remoteAddress
protected final Object stateLock = new Object();
- protected volatile InetSocketAddress localAddress = null;
- protected volatile InetSocketAddress remoteAddress = null;
+ protected volatile InetSocketAddress localAddress;
+ protected volatile InetSocketAddress remoteAddress;
// State, increases monotonically
static final int ST_UNINITIALIZED = -1;
@@ -78,7 +78,7 @@
// close support
private final ReadWriteLock closeLock = new ReentrantReadWriteLock();
- private volatile boolean open = true;
+ private volatile boolean closed;
// set true when exclusive binding is on and SO_REUSEADDR is emulated
private boolean isReuseAddress;
@@ -106,7 +106,7 @@
@Override
public final boolean isOpen() {
- return open;
+ return !closed;
}
/**
@@ -135,9 +135,9 @@
// synchronize with any threads initiating asynchronous operations
closeLock.writeLock().lock();
try {
- if (!open)
+ if (closed)
return; // already closed
- open = false;
+ closed = true;
} finally {
closeLock.writeLock().unlock();
}
--- a/jdk/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -58,8 +58,8 @@
private final ProtocolFamily family;
// IDs of native threads doing reads and writes, for signalling
- private volatile long readerThread = 0;
- private volatile long writerThread = 0;
+ private volatile long readerThread;
+ private volatile long writerThread;
// Cached InetAddress and port for unconnected DatagramChannels
// used by receive0
--- a/jdk/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/DatagramSocketAdaptor.java Thu Dec 24 10:34:31 2015 -0800
@@ -46,7 +46,7 @@
private final DatagramChannelImpl dc;
// Timeout "option" value for receives
- private volatile int timeout = 0;
+ private volatile int timeout;
// ## super will create a useless impl
private DatagramSocketAdaptor(DatagramChannelImpl dc) throws IOException {
--- a/jdk/src/java.base/share/classes/sun/nio/ch/FileLockImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/FileLockImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -31,7 +31,7 @@
public class FileLockImpl
extends FileLock
{
- private volatile boolean valid = true;
+ private volatile boolean invalid;
FileLockImpl(FileChannel channel, long position, long size, boolean shared)
{
@@ -44,25 +44,25 @@
}
public boolean isValid() {
- return valid;
+ return !invalid;
}
void invalidate() {
assert Thread.holdsLock(this);
- valid = false;
+ invalid = true;
}
public synchronized void release() throws IOException {
Channel ch = acquiredBy();
if (!ch.isOpen())
throw new ClosedChannelException();
- if (valid) {
+ if (isValid()) {
if (ch instanceof FileChannelImpl)
((FileChannelImpl)ch).release(this);
else if (ch instanceof AsynchronousFileChannelImpl)
((AsynchronousFileChannelImpl)ch).release(this);
else throw new AssertionError();
- valid = false;
+ invalidate();
}
}
}
--- a/jdk/src/java.base/share/classes/sun/nio/ch/MembershipKeyImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/MembershipKeyImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -43,8 +43,7 @@
private final NetworkInterface interf;
private final InetAddress source;
- // true when key is valid
- private volatile boolean valid = true;
+ private volatile boolean invalid;
// lock used when creating or accessing blockedSet
private Object stateLock = new Object();
@@ -134,12 +133,12 @@
}
public boolean isValid() {
- return valid;
+ return !invalid;
}
// package-private
void invalidate() {
- valid = false;
+ invalid = true;
}
public void drop() {
--- a/jdk/src/java.base/share/classes/sun/nio/ch/Net.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/Net.java Thu Dec 24 10:34:31 2015 -0800
@@ -32,7 +32,6 @@
import java.util.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import java.security.PrivilegedExceptionAction;
import sun.net.ExtendedOptionsImpl;
@@ -55,7 +54,7 @@
// -- Miscellaneous utilities --
- private static volatile boolean checkedIPv6 = false;
+ private static volatile boolean checkedIPv6;
private static volatile boolean isIPv6Available;
/**
--- a/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketAdaptor.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketAdaptor.java Thu Dec 24 10:34:31 2015 -0800
@@ -45,7 +45,7 @@
private final ServerSocketChannelImpl ssc;
// Timeout "option" value for accepts
- private volatile int timeout = 0;
+ private volatile int timeout;
public static ServerSocket create(ServerSocketChannelImpl ssc) {
try {
--- a/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -54,7 +54,7 @@
private int fdVal;
// ID of native thread currently blocked in this channel, for signalling
- private volatile long thread = 0;
+ private volatile long thread;
// Lock held by thread currently blocked in this channel
private final Object lock = new Object();
--- a/jdk/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/SocketAdaptor.java Thu Dec 24 10:34:31 2015 -0800
@@ -26,13 +26,11 @@
package sun.nio.ch;
import java.io.*;
-import java.lang.ref.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
-import java.util.*;
// Make a socket channel look like a socket.
@@ -55,7 +53,7 @@
private final SocketChannelImpl sc;
// Timeout "option" value for reads
- private volatile int timeout = 0;
+ private volatile int timeout;
private SocketAdaptor(SocketChannelImpl sc) throws SocketException {
super((SocketImpl) null);
--- a/jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -56,8 +56,8 @@
private final int fdVal;
// IDs of native threads doing reads and writes, for signalling
- private volatile long readerThread = 0;
- private volatile long writerThread = 0;
+ private volatile long readerThread;
+ private volatile long writerThread;
// Lock held by current reading or connecting thread
private final Object readLock = new Object();
--- a/jdk/src/java.base/share/classes/sun/nio/ch/Util.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/ch/Util.java Thu Dec 24 10:34:31 2015 -0800
@@ -25,13 +25,10 @@
package sun.nio.ch;
-import java.lang.ref.SoftReference;
import java.lang.reflect.*;
-import java.io.IOException;
import java.io.FileDescriptor;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
-import java.nio.channels.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
@@ -295,7 +292,7 @@
return pageSize;
}
- private static volatile Constructor<?> directByteBufferConstructor = null;
+ private static volatile Constructor<?> directByteBufferConstructor;
private static void initDBBConstructor() {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@@ -340,7 +337,7 @@
return dbb;
}
- private static volatile Constructor<?> directByteBufferRConstructor = null;
+ private static volatile Constructor<?> directByteBufferRConstructor;
private static void initDBBRConstructor() {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@@ -388,7 +385,7 @@
// -- Bug compatibility --
- private static volatile String bugLevel = null;
+ private static volatile String bugLevel;
static boolean atBugLevel(String bl) { // package-private
if (bugLevel == null) {
--- a/jdk/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/StreamDecoder.java Thu Dec 24 10:34:31 2015 -0800
@@ -39,10 +39,10 @@
private static final int MIN_BYTE_BUFFER_SIZE = 32;
private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
- private volatile boolean isOpen = true;
+ private volatile boolean closed;
private void ensureOpen() throws IOException {
- if (!isOpen)
+ if (closed)
throw new IOException("Stream closed");
}
@@ -188,15 +188,15 @@
public void close() throws IOException {
synchronized (lock) {
- if (!isOpen)
+ if (closed)
return;
implClose();
- isOpen = false;
+ closed = true;
}
}
private boolean isOpen() {
- return isOpen;
+ return !closed;
}
--- a/jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/StreamEncoder.java Thu Dec 24 10:34:31 2015 -0800
@@ -38,10 +38,10 @@
private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
- private volatile boolean isOpen = true;
+ private volatile boolean closed;
private void ensureOpen() throws IOException {
- if (!isOpen)
+ if (closed)
throw new IOException("Stream closed");
}
@@ -156,15 +156,15 @@
public void close() throws IOException {
synchronized (lock) {
- if (!isOpen)
+ if (closed)
return;
implClose();
- isOpen = false;
+ closed = true;
}
}
private boolean isOpen() {
- return isOpen;
+ return !closed;
}
--- a/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/reflect/MethodAccessorGenerator.java Thu Dec 24 10:34:31 2015 -0800
@@ -44,9 +44,9 @@
// Only used if forSerialization is true
private static final short NUM_SERIALIZATION_CPOOL_ENTRIES = (short) 2;
- private static volatile int methodSymnum = 0;
- private static volatile int constructorSymnum = 0;
- private static volatile int serializationConstructorSymnum = 0;
+ private static volatile int methodSymnum;
+ private static volatile int constructorSymnum;
+ private static volatile int serializationConstructorSymnum;
private Class<?> declaringClass;
private Class<?>[] parameterTypes;
--- a/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java Thu Dec 24 10:34:31 2015 -0800
@@ -299,7 +299,7 @@
}});
}
- private transient volatile Method[] memberMethods = null;
+ private transient volatile Method[] memberMethods;
/**
* Validates that a method is structurally appropriate for an
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherBox.java Thu Dec 24 10:34:31 2015 -0800
@@ -1105,41 +1105,6 @@
return fragLen;
}
-
- /*
- * Is this cipher available?
- *
- * This method can only be called by CipherSuite.BulkCipher.isAvailable()
- * to test the availability of a cipher suites. Please DON'T use it in
- * other places, otherwise, the behavior may be unexpected because we may
- * initialize AEAD cipher improperly in the method.
- */
- Boolean isAvailable() {
- // We won't know whether a cipher for a particular key size is
- // available until the cipher is successfully initialized.
- //
- // We do not initialize AEAD cipher in the constructor. Need to
- // initialize the cipher to ensure that the AEAD mode for a
- // particular key size is supported.
- if (cipherType == AEAD_CIPHER) {
- try {
- Authenticator authenticator =
- new Authenticator(protocolVersion);
- byte[] nonce = authenticator.sequenceNumber();
- byte[] iv = Arrays.copyOf(fixedIv,
- fixedIv.length + nonce.length);
- System.arraycopy(nonce, 0, iv, fixedIv.length, nonce.length);
- GCMParameterSpec spec = new GCMParameterSpec(tagSize * 8, iv);
-
- cipher.init(mode, key, spec, random);
- } catch (Exception e) {
- return Boolean.FALSE;
- }
- } // Otherwise, we have initialized the cipher in the constructor.
-
- return Boolean.TRUE;
- }
-
/**
* Sanity check the length of a fragment before decryption.
*
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuite.java Thu Dec 24 10:34:31 2015 -0800
@@ -77,12 +77,6 @@
// minimum priority for default enabled CipherSuites
static final int DEFAULT_SUITES_PRIORITY = 300;
- // Flag indicating if CipherSuite availability can change dynamically.
- // This is the case when we rely on a JCE cipher implementation that
- // may not be available in the installed JCE providers.
- // It is true because we might not have an ECC implementation.
- static final boolean DYNAMIC_AVAILABILITY = true;
-
private static final boolean ALLOW_ECC = Debug.getBooleanProperty
("com.sun.net.ssl.enableECC", true);
@@ -176,9 +170,6 @@
* Return whether this CipherSuite is available for use. A
* CipherSuite may be unavailable even if it is supported
* (i.e. allowed == true) if the required JCE cipher is not installed.
- * In some configuration, this situation may change over time, call
- * CipherSuiteList.clearAvailableCache() before this method to obtain
- * the most current status.
*/
boolean isAvailable() {
return allowed && keyExchange.isAvailable() && cipher.isAvailable();
@@ -471,10 +462,6 @@
B_AES_128_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 16, 12, 4, true),
B_AES_256_GCM(CIPHER_AES_GCM, AEAD_CIPHER, 32, 12, 4, true);
- // Map BulkCipher -> Boolean(available)
- private static final Map<BulkCipher,Boolean> availableCache =
- new HashMap<>(8);
-
// descriptive name including key size, e.g. AES/128
final String description;
@@ -518,6 +505,9 @@
// The secure random used to detect the cipher availability.
private static final SecureRandom secureRandom;
+ // runtime availability
+ private final boolean isAvailable;
+
static {
try {
secureRandom = JsseJce.getSecureRandom();
@@ -542,6 +532,17 @@
this.expandedKeySize = expandedKeySize;
this.exportable = true;
+
+ // availability of this bulk cipher
+ //
+ // Currently all supported ciphers except AES are always available
+ // via the JSSE internal implementations. We also assume AES/128 of
+ // CBC mode is always available since it is shipped with the SunJCE
+ // provider. However, AES/256 is unavailable when the default JCE
+ // policy jurisdiction files are installed because of key length
+ // restrictions.
+ this.isAvailable =
+ allowed ? isUnlimited(keySize, transformation) : false;
}
BulkCipher(String transformation, CipherType cipherType, int keySize,
@@ -558,6 +559,17 @@
this.expandedKeySize = keySize;
this.exportable = false;
+
+ // availability of this bulk cipher
+ //
+ // Currently all supported ciphers except AES are always available
+ // via the JSSE internal implementations. We also assume AES/128 of
+ // CBC mode is always available since it is shipped with the SunJCE
+ // provider. However, AES/256 is unavailable when the default JCE
+ // policy jurisdiction files are installed because of key length
+ // restrictions.
+ this.isAvailable =
+ allowed ? isUnlimited(keySize, transformation) : false;
}
/**
@@ -575,84 +587,27 @@
/**
* Test if this bulk cipher is available. For use by CipherSuite.
- *
- * Currently all supported ciphers except AES are always available
- * via the JSSE internal implementations. We also assume AES/128 of
- * CBC mode is always available since it is shipped with the SunJCE
- * provider. However, AES/256 is unavailable when the default JCE
- * policy jurisdiction files are installed because of key length
- * restrictions, and AEAD is unavailable when the underlying providers
- * do not support AEAD/GCM mode.
*/
boolean isAvailable() {
- if (allowed == false) {
- return false;
- }
-
- if ((this == B_AES_256) ||
- (this.cipherType == CipherType.AEAD_CIPHER)) {
- return isAvailable(this);
- }
-
- // always available
- return true;
- }
-
- // for use by CipherSuiteList.clearAvailableCache();
- static synchronized void clearAvailableCache() {
- if (DYNAMIC_AVAILABILITY) {
- availableCache.clear();
- }
+ return this.isAvailable;
}
- private static synchronized boolean isAvailable(BulkCipher cipher) {
- Boolean b = availableCache.get(cipher);
- if (b == null) {
- int keySizeInBits = cipher.keySize * 8;
- if (keySizeInBits > 128) { // need the JCE unlimited
- // strength jurisdiction policy
- try {
- if (Cipher.getMaxAllowedKeyLength(
- cipher.transformation) < keySizeInBits) {
- b = Boolean.FALSE;
- }
- } catch (Exception e) {
- b = Boolean.FALSE;
- }
- }
+ private static boolean isUnlimited(int keySize, String transformation) {
+ int keySizeInBits = keySize * 8;
+ if (keySizeInBits > 128) { // need the JCE unlimited
+ // strength jurisdiction policy
+ try {
+ if (Cipher.getMaxAllowedKeyLength(
+ transformation) < keySizeInBits) {
- if (b == null) {
- b = Boolean.FALSE; // may be reset to TRUE if
- // the cipher is available
- CipherBox temporary = null;
- try {
- SecretKey key = new SecretKeySpec(
- new byte[cipher.expandedKeySize],
- cipher.algorithm);
- IvParameterSpec iv;
- if (cipher.cipherType == CipherType.AEAD_CIPHER) {
- iv = new IvParameterSpec(
- new byte[cipher.fixedIvSize]);
- } else {
- iv = new IvParameterSpec(new byte[cipher.ivSize]);
- }
- temporary = cipher.newCipher(
- ProtocolVersion.DEFAULT_TLS,
- key, iv, secureRandom, true);
- b = temporary.isAvailable();
- } catch (NoSuchAlgorithmException e) {
- // not available
- } finally {
- if (temporary != null) {
- temporary.dispose();
- }
+ return false;
}
+ } catch (Exception e) {
+ return false;
}
-
- availableCache.put(cipher, b);
}
- return b.booleanValue();
+ return true;
}
@Override
--- a/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/CipherSuiteList.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -74,24 +74,12 @@
throw new IllegalArgumentException("CipherSuites may not be null");
}
cipherSuites = new ArrayList<CipherSuite>(names.length);
- // refresh available cache once if a CipherSuite is not available
- // (maybe new JCE providers have been installed)
- boolean refreshed = false;
for (int i = 0; i < names.length; i++) {
String suiteName = names[i];
CipherSuite suite = CipherSuite.valueOf(suiteName);
if (suite.isAvailable() == false) {
- if (refreshed == false) {
- // clear the cache so that the isAvailable() call below
- // does a full check
- clearAvailableCache();
- refreshed = true;
- }
- // still missing?
- if (suite.isAvailable() == false) {
- throw new IllegalArgumentException("Cannot support "
- + suiteName + " with currently installed providers");
- }
+ throw new IllegalArgumentException("Cannot support "
+ + suiteName + " with currently installed providers");
}
cipherSuites.add(suite);
}
@@ -195,16 +183,4 @@
}
s.putBytes16(suiteBytes);
}
-
- /**
- * Clear cache of available ciphersuites. If we support all ciphers
- * internally, there is no need to clear the cache and calling this
- * method has no effect.
- */
- static synchronized void clearAvailableCache() {
- if (CipherSuite.DYNAMIC_AVAILABILITY) {
- CipherSuite.BulkCipher.clearAvailableCache();
- JsseJce.clearEcAvailable();
- }
- }
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java Thu Dec 24 10:34:31 2015 -0800
@@ -55,11 +55,6 @@
private static final ProviderList fipsProviderList;
- // Flag indicating whether EC crypto is available.
- // If null, then we have not checked yet.
- // If yes, then all the EC based crypto we need is available.
- private static Boolean ecAvailable;
-
// Flag indicating whether Kerberos crypto is available.
// If true, then all the Kerberos-based crypto we need is available.
private static final boolean kerberosAvailable;
@@ -180,24 +175,8 @@
// no instantiation of this class
}
- static synchronized boolean isEcAvailable() {
- if (ecAvailable == null) {
- try {
- JsseJce.getSignature(SIGNATURE_ECDSA);
- JsseJce.getSignature(SIGNATURE_RAWECDSA);
- JsseJce.getKeyAgreement("ECDH");
- JsseJce.getKeyFactory("EC");
- JsseJce.getKeyPairGenerator("EC");
- ecAvailable = true;
- } catch (Exception e) {
- ecAvailable = false;
- }
- }
- return ecAvailable;
- }
-
- static synchronized void clearEcAvailable() {
- ecAvailable = null;
+ static boolean isEcAvailable() {
+ return EcAvailability.isAvailable;
}
static boolean isKerberosAvailable() {
@@ -399,4 +378,27 @@
}
}
+
+ // lazy initialization holder class idiom for static default parameters
+ //
+ // See Effective Java Second Edition: Item 71.
+ private static class EcAvailability {
+ // Is EC crypto available?
+ private final static boolean isAvailable;
+
+ static {
+ boolean mediator = true;
+ try {
+ JsseJce.getSignature(SIGNATURE_ECDSA);
+ JsseJce.getSignature(SIGNATURE_RAWECDSA);
+ JsseJce.getKeyAgreement("ECDH");
+ JsseJce.getKeyFactory("EC");
+ JsseJce.getKeyPairGenerator("EC");
+ } catch (Exception e) {
+ mediator = false;
+ }
+
+ isAvailable = mediator;
+ }
+ }
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLContextImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -52,18 +52,8 @@
private X509TrustManager trustManager;
private SecureRandom secureRandom;
- // supported and default protocols
- private ProtocolList defaultServerProtocolList;
- private ProtocolList defaultClientProtocolList;
- private ProtocolList supportedProtocolList;
-
- // supported and default cipher suites
- private CipherSuiteList defaultServerCipherSuiteList;
- private CipherSuiteList defaultClientCipherSuiteList;
- private CipherSuiteList supportedCipherSuiteList;
-
// DTLS cookie exchange manager
- private HelloCookieManager helloCookieManager;
+ private volatile HelloCookieManager helloCookieManager;
private StatusResponseManager statusResponseManager;
@@ -117,6 +107,7 @@
if (debug != null && Debug.isOn("sslctx")) {
System.out.println("done seeding SecureRandom");
}
+
isInitialized = true;
}
@@ -242,13 +233,20 @@
return ephemeralKeyManager;
}
+ // Used for DTLS in server mode only, see ServerHandshaker.
HelloCookieManager getHelloCookieManager() {
if (!isInitialized) {
throw new IllegalStateException("SSLContext is not initialized");
}
- if (helloCookieManager == null) {
- helloCookieManager = getHelloCookieManager(secureRandom);
+ if (helloCookieManager != null) {
+ return helloCookieManager;
+ }
+
+ synchronized (this) {
+ if (helloCookieManager == null) {
+ helloCookieManager = getHelloCookieManager(secureRandom);
+ }
}
return helloCookieManager;
@@ -263,78 +261,34 @@
return statusResponseManager;
}
- abstract SSLParameters getDefaultServerSSLParams();
- abstract SSLParameters getDefaultClientSSLParams();
- abstract SSLParameters getSupportedSSLParams();
+ // Get supported ProtocolList.
+ abstract ProtocolList getSuportedProtocolList();
+
+ // Get default ProtocolList for server mode.
+ abstract ProtocolList getServerDefaultProtocolList();
+
+ // Get default ProtocolList for client mode.
+ abstract ProtocolList getClientDefaultProtocolList();
- // Get supported ProtocolList.
- ProtocolList getSuportedProtocolList() {
- if (supportedProtocolList == null) {
- supportedProtocolList =
- new ProtocolList(getSupportedSSLParams().getProtocols());
- }
+ // Get supported CipherSuiteList.
+ abstract CipherSuiteList getSupportedCipherSuiteList();
- return supportedProtocolList;
- }
+ // Get default CipherSuiteList for server mode.
+ abstract CipherSuiteList getServerDefaultCipherSuiteList();
+
+ // Get default CipherSuiteList for client mode.
+ abstract CipherSuiteList getClientDefaultCipherSuiteList();
// Get default ProtocolList.
ProtocolList getDefaultProtocolList(boolean roleIsServer) {
- if (roleIsServer) {
- if (defaultServerProtocolList == null) {
- defaultServerProtocolList = new ProtocolList(
- getDefaultServerSSLParams().getProtocols());
- }
-
- return defaultServerProtocolList;
- } else {
- if (defaultClientProtocolList == null) {
- defaultClientProtocolList = new ProtocolList(
- getDefaultClientSSLParams().getProtocols());
- }
-
- return defaultClientProtocolList;
- }
- }
-
- // Get supported CipherSuiteList.
- CipherSuiteList getSupportedCipherSuiteList() {
- // The maintenance of cipher suites needs to be synchronized.
- synchronized (this) {
- // Clear cache of available ciphersuites.
- clearAvailableCache();
-
- if (supportedCipherSuiteList == null) {
- supportedCipherSuiteList = getApplicableCipherSuiteList(
- getSuportedProtocolList(), false);
- }
-
- return supportedCipherSuiteList;
- }
+ return roleIsServer ? getServerDefaultProtocolList()
+ : getClientDefaultProtocolList();
}
// Get default CipherSuiteList.
CipherSuiteList getDefaultCipherSuiteList(boolean roleIsServer) {
- // The maintenance of cipher suites needs to be synchronized.
- synchronized (this) {
- // Clear cache of available ciphersuites.
- clearAvailableCache();
-
- if (roleIsServer) {
- if (defaultServerCipherSuiteList == null) {
- defaultServerCipherSuiteList = getApplicableCipherSuiteList(
- getDefaultProtocolList(true), true);
- }
-
- return defaultServerCipherSuiteList;
- } else {
- if (defaultClientCipherSuiteList == null) {
- defaultClientCipherSuiteList = getApplicableCipherSuiteList(
- getDefaultProtocolList(false), true);
- }
-
- return defaultClientCipherSuiteList;
- }
- }
+ return roleIsServer ? getServerDefaultCipherSuiteList()
+ : getClientDefaultCipherSuiteList();
}
/**
@@ -342,8 +296,8 @@
* protocols. See: SSLSocket/SSLEngine.setEnabledProtocols()
*/
boolean isDefaultProtocolList(ProtocolList protocols) {
- return (protocols == defaultServerProtocolList) ||
- (protocols == defaultClientProtocolList);
+ return (protocols == getServerDefaultProtocolList()) ||
+ (protocols == getClientDefaultProtocolList());
}
/**
@@ -351,8 +305,8 @@
* protocols. See: SSLSocket/SSLEngine.setEnabledProtocols()
*/
boolean isDefaultCipherSuiteList(CipherSuiteList cipherSuites) {
- return (cipherSuites == defaultClientCipherSuiteList) ||
- (cipherSuites == defaultServerCipherSuiteList);
+ return (cipherSuites == getServerDefaultCipherSuiteList()) ||
+ (cipherSuites == getClientDefaultCipherSuiteList());
}
/*
@@ -405,24 +359,6 @@
return new CipherSuiteList(suites);
}
- /**
- * Clear cache of available ciphersuites. If we support all ciphers
- * internally, there is no need to clear the cache and calling this
- * method has no effect.
- *
- * Note that every call to clearAvailableCache() and the maintenance of
- * cipher suites need to be synchronized with this instance.
- */
- private void clearAvailableCache() {
- if (CipherSuite.DYNAMIC_AVAILABILITY) {
- supportedCipherSuiteList = null;
- defaultServerCipherSuiteList = null;
- defaultClientCipherSuiteList = null;
- CipherSuite.BulkCipher.clearAvailableCache();
- JsseJce.clearEcAvailable();
- }
- }
-
private static String[] getAvailableProtocols(
ProtocolVersion[] protocolCandidates) {
@@ -479,31 +415,28 @@
* @see SSLContext
*/
private abstract static class AbstractTLSContext extends SSLContextImpl {
- // parameters
- private static final SSLParameters defaultServerSSLParams;
- private static final SSLParameters supportedSSLParams;
+ private static final ProtocolList supportedProtocolList;
+ private static final ProtocolList serverDefaultProtocolList;
+
+ private static final CipherSuiteList supportedCipherSuiteList;
+ private static final CipherSuiteList serverDefaultCipherSuiteList;
static {
- // supported SSL parameters
- supportedSSLParams = new SSLParameters();
-
- // candidates for available protocols
- ProtocolVersion[] candidates;
-
if (SunJSSE.isFIPS()) {
- supportedSSLParams.setProtocols(new String[] {
+ supportedProtocolList = new ProtocolList(new String[] {
ProtocolVersion.TLS10.name,
ProtocolVersion.TLS11.name,
ProtocolVersion.TLS12.name
});
- candidates = new ProtocolVersion[] {
+ serverDefaultProtocolList = new ProtocolList(
+ getAvailableProtocols(new ProtocolVersion[] {
ProtocolVersion.TLS10,
ProtocolVersion.TLS11,
ProtocolVersion.TLS12
- };
+ }));
} else {
- supportedSSLParams.setProtocols(new String[] {
+ supportedProtocolList = new ProtocolList(new String[] {
ProtocolVersion.SSL20Hello.name,
ProtocolVersion.SSL30.name,
ProtocolVersion.TLS10.name,
@@ -511,28 +444,40 @@
ProtocolVersion.TLS12.name
});
- candidates = new ProtocolVersion[] {
+ serverDefaultProtocolList = new ProtocolList(
+ getAvailableProtocols(new ProtocolVersion[] {
ProtocolVersion.SSL20Hello,
ProtocolVersion.SSL30,
ProtocolVersion.TLS10,
ProtocolVersion.TLS11,
ProtocolVersion.TLS12
- };
+ }));
}
- defaultServerSSLParams = new SSLParameters();
- defaultServerSSLParams.setProtocols(
- getAvailableProtocols(candidates));
+ supportedCipherSuiteList = getApplicableCipherSuiteList(
+ supportedProtocolList, false); // all supported
+ serverDefaultCipherSuiteList = getApplicableCipherSuiteList(
+ serverDefaultProtocolList, true); // enabled only
}
@Override
- SSLParameters getDefaultServerSSLParams() {
- return defaultServerSSLParams;
+ ProtocolList getSuportedProtocolList() {
+ return supportedProtocolList;
}
@Override
- SSLParameters getSupportedSSLParams() {
- return supportedSSLParams;
+ CipherSuiteList getSupportedCipherSuiteList() {
+ return supportedCipherSuiteList;
+ }
+
+ @Override
+ ProtocolList getServerDefaultProtocolList() {
+ return serverDefaultProtocolList;
+ }
+
+ @Override
+ CipherSuiteList getServerDefaultCipherSuiteList() {
+ return serverDefaultCipherSuiteList;
}
@Override
@@ -552,30 +497,35 @@
* @see SSLContext
*/
public static final class TLS10Context extends AbstractTLSContext {
- private static final SSLParameters defaultClientSSLParams;
+ private static final ProtocolList clientDefaultProtocolList;
+ private static final CipherSuiteList clientDefaultCipherSuiteList;
static {
- // candidates for available protocols
- ProtocolVersion[] candidates;
if (SunJSSE.isFIPS()) {
- candidates = new ProtocolVersion[] {
+ clientDefaultProtocolList = new ProtocolList(
+ getAvailableProtocols(new ProtocolVersion[] {
ProtocolVersion.TLS10
- };
+ }));
} else {
- candidates = new ProtocolVersion[] {
+ clientDefaultProtocolList = new ProtocolList(
+ getAvailableProtocols(new ProtocolVersion[] {
ProtocolVersion.SSL30,
ProtocolVersion.TLS10
- };
+ }));
}
- defaultClientSSLParams = new SSLParameters();
- defaultClientSSLParams.setProtocols(
- getAvailableProtocols(candidates));
+ clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
+ clientDefaultProtocolList, true); // enabled only
}
@Override
- SSLParameters getDefaultClientSSLParams() {
- return defaultClientSSLParams;
+ ProtocolList getClientDefaultProtocolList() {
+ return clientDefaultProtocolList;
+ }
+
+ @Override
+ CipherSuiteList getClientDefaultCipherSuiteList() {
+ return clientDefaultCipherSuiteList;
}
}
@@ -585,32 +535,37 @@
* @see SSLContext
*/
public static final class TLS11Context extends AbstractTLSContext {
- private static final SSLParameters defaultClientSSLParams;
+ private static final ProtocolList clientDefaultProtocolList;
+ private static final CipherSuiteList clientDefaultCipherSuiteList;
static {
- // candidates for available protocols
- ProtocolVersion[] candidates;
if (SunJSSE.isFIPS()) {
- candidates = new ProtocolVersion[] {
+ clientDefaultProtocolList = new ProtocolList(
+ getAvailableProtocols(new ProtocolVersion[] {
ProtocolVersion.TLS10,
ProtocolVersion.TLS11
- };
+ }));
} else {
- candidates = new ProtocolVersion[] {
+ clientDefaultProtocolList = new ProtocolList(
+ getAvailableProtocols(new ProtocolVersion[] {
ProtocolVersion.SSL30,
ProtocolVersion.TLS10,
ProtocolVersion.TLS11
- };
+ }));
}
- defaultClientSSLParams = new SSLParameters();
- defaultClientSSLParams.setProtocols(
- getAvailableProtocols(candidates));
+ clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
+ clientDefaultProtocolList, true); // enabled only
}
@Override
- SSLParameters getDefaultClientSSLParams() {
- return defaultClientSSLParams;
+ ProtocolList getClientDefaultProtocolList() {
+ return clientDefaultProtocolList;
+ }
+
+ @Override
+ CipherSuiteList getClientDefaultCipherSuiteList() {
+ return clientDefaultCipherSuiteList;
}
}
@@ -620,34 +575,39 @@
* @see SSLContext
*/
public static final class TLS12Context extends AbstractTLSContext {
- private static final SSLParameters defaultClientSSLParams;
+ private static final ProtocolList clientDefaultProtocolList;
+ private static final CipherSuiteList clientDefaultCipherSuiteList;
static {
- // candidates for available protocols
- ProtocolVersion[] candidates;
if (SunJSSE.isFIPS()) {
- candidates = new ProtocolVersion[] {
+ clientDefaultProtocolList = new ProtocolList(
+ getAvailableProtocols(new ProtocolVersion[] {
ProtocolVersion.TLS10,
ProtocolVersion.TLS11,
ProtocolVersion.TLS12
- };
+ }));
} else {
- candidates = new ProtocolVersion[] {
+ clientDefaultProtocolList = new ProtocolList(
+ getAvailableProtocols(new ProtocolVersion[] {
ProtocolVersion.SSL30,
ProtocolVersion.TLS10,
ProtocolVersion.TLS11,
ProtocolVersion.TLS12
- };
+ }));
}
- defaultClientSSLParams = new SSLParameters();
- defaultClientSSLParams.setProtocols(
- getAvailableProtocols(candidates));
+ clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
+ clientDefaultProtocolList, true); // enabled only
}
@Override
- SSLParameters getDefaultClientSSLParams() {
- return defaultClientSSLParams;
+ ProtocolList getClientDefaultProtocolList() {
+ return clientDefaultProtocolList;
+ }
+
+ @Override
+ CipherSuiteList getClientDefaultCipherSuiteList() {
+ return clientDefaultCipherSuiteList;
}
}
@@ -719,7 +679,9 @@
*/
private static class CustomizedTLSContext extends AbstractTLSContext {
- private static final SSLParameters defaultClientSSLParams;
+ private static final ProtocolList clientDefaultProtocolList;
+ private static final CipherSuiteList clientDefaultCipherSuiteList;
+
private static IllegalArgumentException reservedException = null;
// Don't want a java.lang.LinkageError for illegal system property.
@@ -766,11 +728,13 @@
candidates = customizedTLSProtocols.toArray(candidates);
}
- defaultClientSSLParams = new SSLParameters();
- defaultClientSSLParams.setProtocols(
+ clientDefaultProtocolList = new ProtocolList(
getAvailableProtocols(candidates));
+ clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
+ clientDefaultProtocolList, true); // enabled only
} else {
- defaultClientSSLParams = null; // unlikely to be used
+ clientDefaultProtocolList = null; // unlikely to be used
+ clientDefaultCipherSuiteList = null; // unlikely to be used
}
}
@@ -781,8 +745,13 @@
}
@Override
- SSLParameters getDefaultClientSSLParams() {
- return defaultClientSSLParams;
+ ProtocolList getClientDefaultProtocolList() {
+ return clientDefaultProtocolList;
+ }
+
+ @Override
+ CipherSuiteList getClientDefaultCipherSuiteList() {
+ return clientDefaultCipherSuiteList;
}
}
@@ -795,71 +764,53 @@
// use the default constructor and methods
}
- /*
- * The SSLContext implementation for default "Default" algorithm
- *
- * @see SSLContext
- */
- public static final class DefaultSSLContext extends CustomizedTLSContext {
+ // lazy initialization holder class idiom for static default parameters
+ //
+ // See Effective Java Second Edition: Item 71.
+ private static final class DefaultManagersHolder {
private static final String NONE = "NONE";
private static final String P11KEYSTORE = "PKCS11";
- private static volatile SSLContextImpl defaultImpl;
+ private static final TrustManager[] trustManagers;
+ private static final KeyManager[] keyManagers;
- private static TrustManager[] defaultTrustManagers;
- private static KeyManager[] defaultKeyManagers;
+ static Exception reservedException = null;
- public DefaultSSLContext() throws Exception {
+ static {
+ TrustManager[] tmMediator;
try {
- super.engineInit(getDefaultKeyManager(),
- getDefaultTrustManager(), null);
+ tmMediator = getTrustManagers();
} catch (Exception e) {
- if (debug != null && Debug.isOn("defaultctx")) {
- System.out.println("default context init failed: " + e);
+ reservedException = e;
+ tmMediator = new TrustManager[0];
+ }
+ trustManagers = tmMediator;
+
+ if (reservedException == null) {
+ KeyManager[] kmMediator;
+ try {
+ kmMediator = getKeyManagers();
+ } catch (Exception e) {
+ reservedException = e;
+ kmMediator = new KeyManager[0];
}
- throw e;
- }
-
- if (defaultImpl == null) {
- defaultImpl = this;
+ keyManagers = kmMediator;
+ } else {
+ keyManagers = new KeyManager[0];
}
}
- @Override
- protected void engineInit(KeyManager[] km, TrustManager[] tm,
- SecureRandom sr) throws KeyManagementException {
- throw new KeyManagementException
- ("Default SSLContext is initialized automatically");
- }
-
- static synchronized SSLContextImpl getDefaultImpl() throws Exception {
- if (defaultImpl == null) {
- new DefaultSSLContext();
- }
- return defaultImpl;
- }
-
- private static synchronized TrustManager[] getDefaultTrustManager()
- throws Exception {
- if (defaultTrustManagers != null) {
- return defaultTrustManagers;
- }
-
+ private static TrustManager[] getTrustManagers() throws Exception {
KeyStore ks =
TrustManagerFactoryImpl.getCacertsKeyStore("defaultctx");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
- defaultTrustManagers = tmf.getTrustManagers();
- return defaultTrustManagers;
+ return tmf.getTrustManagers();
}
- private static synchronized KeyManager[] getDefaultKeyManager()
- throws Exception {
- if (defaultKeyManagers != null) {
- return defaultKeyManagers;
- }
+ private static KeyManager[] getKeyManagers() throws Exception {
final Map<String,String> props = new HashMap<>();
AccessController.doPrivileged(
@@ -956,8 +907,71 @@
kmf.init(ks, passwd);
}
- defaultKeyManagers = kmf.getKeyManagers();
- return defaultKeyManagers;
+ return kmf.getKeyManagers();
+ }
+ }
+
+ // lazy initialization holder class idiom for static default parameters
+ //
+ // See Effective Java Second Edition: Item 71.
+ private static final class DefaultSSLContextHolder {
+
+ private static final SSLContextImpl sslContext;
+ static Exception reservedException = null;
+
+ static {
+ SSLContextImpl mediator = null;
+ if (DefaultManagersHolder.reservedException != null) {
+ reservedException = DefaultManagersHolder.reservedException;
+ } else {
+ try {
+ mediator = new DefaultSSLContext();
+ } catch (Exception e) {
+ reservedException = e;
+ }
+ }
+
+ sslContext = mediator;
+ }
+ }
+
+ /*
+ * The SSLContext implementation for default "Default" algorithm
+ *
+ * @see SSLContext
+ */
+ public static final class DefaultSSLContext extends CustomizedTLSContext {
+
+ // public constructor for SSLContext.getInstance("Default")
+ public DefaultSSLContext() throws Exception {
+ if (DefaultManagersHolder.reservedException != null) {
+ throw DefaultManagersHolder.reservedException;
+ }
+
+ try {
+ super.engineInit(DefaultManagersHolder.keyManagers,
+ DefaultManagersHolder.trustManagers, null);
+ } catch (Exception e) {
+ if (debug != null && Debug.isOn("defaultctx")) {
+ System.out.println("default context init failed: " + e);
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ protected void engineInit(KeyManager[] km, TrustManager[] tm,
+ SecureRandom sr) throws KeyManagementException {
+ throw new KeyManagementException
+ ("Default SSLContext is initialized automatically");
+ }
+
+ static SSLContextImpl getDefaultImpl() throws Exception {
+ if (DefaultSSLContextHolder.reservedException != null) {
+ throw DefaultSSLContextHolder.reservedException;
+ }
+
+ return DefaultSSLContextHolder.sslContext;
}
}
@@ -971,39 +985,50 @@
* @see SSLContext
*/
private abstract static class AbstractDTLSContext extends SSLContextImpl {
- // parameters
- private static final SSLParameters defaultServerSSLParams;
- private static final SSLParameters supportedSSLParams;
+ private static final ProtocolList supportedProtocolList;
+ private static final ProtocolList serverDefaultProtocolList;
+
+ private static final CipherSuiteList supportedCipherSuiteList;
+ private static final CipherSuiteList serverDefaultCipherSuiteList;
static {
- // supported SSL parameters
- supportedSSLParams = new SSLParameters();
-
// Both DTLSv1.0 and DTLSv1.2 can be used in FIPS mode.
- supportedSSLParams.setProtocols(new String[] {
+ supportedProtocolList = new ProtocolList(new String[] {
ProtocolVersion.DTLS10.name,
ProtocolVersion.DTLS12.name
});
- // candidates for available protocols
- ProtocolVersion[] candidates = new ProtocolVersion[] {
+ // available protocols for server mode
+ serverDefaultProtocolList = new ProtocolList(
+ getAvailableProtocols(new ProtocolVersion[] {
ProtocolVersion.DTLS10,
ProtocolVersion.DTLS12
- };
+ }));
- defaultServerSSLParams = new SSLParameters();
- defaultServerSSLParams.setProtocols(
- getAvailableProtocols(candidates));
+ supportedCipherSuiteList = getApplicableCipherSuiteList(
+ supportedProtocolList, false); // all supported
+ serverDefaultCipherSuiteList = getApplicableCipherSuiteList(
+ serverDefaultProtocolList, true); // enabled only
}
@Override
- SSLParameters getDefaultServerSSLParams() {
- return defaultServerSSLParams;
+ ProtocolList getSuportedProtocolList() {
+ return supportedProtocolList;
}
@Override
- SSLParameters getSupportedSSLParams() {
- return supportedSSLParams;
+ CipherSuiteList getSupportedCipherSuiteList() {
+ return supportedCipherSuiteList;
+ }
+
+ @Override
+ ProtocolList getServerDefaultProtocolList() {
+ return serverDefaultProtocolList;
+ }
+
+ @Override
+ CipherSuiteList getServerDefaultCipherSuiteList() {
+ return serverDefaultCipherSuiteList;
}
@Override
@@ -1028,22 +1053,28 @@
* @see SSLContext
*/
public static final class DTLS10Context extends AbstractDTLSContext {
- private static final SSLParameters defaultClientSSLParams;
+ private static final ProtocolList clientDefaultProtocolList;
+ private static final CipherSuiteList clientDefaultCipherSuiteList;
static {
- // candidates for available protocols
- ProtocolVersion[] candidates = new ProtocolVersion[] {
+ // available protocols for client mode
+ clientDefaultProtocolList = new ProtocolList(
+ getAvailableProtocols(new ProtocolVersion[] {
ProtocolVersion.DTLS10
- };
+ }));
- defaultClientSSLParams = new SSLParameters();
- defaultClientSSLParams.setProtocols(
- getAvailableProtocols(candidates));
+ clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
+ clientDefaultProtocolList, true); // enabled only
}
@Override
- SSLParameters getDefaultClientSSLParams() {
- return defaultClientSSLParams;
+ ProtocolList getClientDefaultProtocolList() {
+ return clientDefaultProtocolList;
+ }
+
+ @Override
+ CipherSuiteList getClientDefaultCipherSuiteList() {
+ return clientDefaultCipherSuiteList;
}
}
@@ -1053,23 +1084,29 @@
* @see SSLContext
*/
public static final class DTLS12Context extends AbstractDTLSContext {
- private static final SSLParameters defaultClientSSLParams;
+ private static final ProtocolList clientDefaultProtocolList;
+ private static final CipherSuiteList clientDefaultCipherSuiteList;
static {
- // candidates for available protocols
- ProtocolVersion[] candidates = new ProtocolVersion[] {
+ // available protocols for client mode
+ clientDefaultProtocolList = new ProtocolList(
+ getAvailableProtocols(new ProtocolVersion[] {
ProtocolVersion.DTLS10,
ProtocolVersion.DTLS12
- };
+ }));
- defaultClientSSLParams = new SSLParameters();
- defaultClientSSLParams.setProtocols(
- getAvailableProtocols(candidates));
+ clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
+ clientDefaultProtocolList, true); // enabled only
}
@Override
- SSLParameters getDefaultClientSSLParams() {
- return defaultClientSSLParams;
+ ProtocolList getClientDefaultProtocolList() {
+ return clientDefaultProtocolList;
+ }
+
+ @Override
+ CipherSuiteList getClientDefaultCipherSuiteList() {
+ return clientDefaultCipherSuiteList;
}
}
@@ -1079,7 +1116,9 @@
* @see SSLContext
*/
private static class CustomizedDTLSContext extends AbstractDTLSContext {
- private static final SSLParameters defaultClientSSLParams;
+ private static final ProtocolList clientDefaultProtocolList;
+ private static final CipherSuiteList clientDefaultCipherSuiteList;
+
private static IllegalArgumentException reservedException = null;
// Don't want a java.lang.LinkageError for illegal system property.
@@ -1119,11 +1158,13 @@
candidates = customizedDTLSProtocols.toArray(candidates);
}
- defaultClientSSLParams = new SSLParameters();
- defaultClientSSLParams.setProtocols(
+ clientDefaultProtocolList = new ProtocolList(
getAvailableProtocols(candidates));
+ clientDefaultCipherSuiteList = getApplicableCipherSuiteList(
+ clientDefaultProtocolList, true); // enabled only
} else {
- defaultClientSSLParams = null; // unlikely to be used
+ clientDefaultProtocolList = null; // unlikely to be used
+ clientDefaultCipherSuiteList = null; // unlikely to be used
}
}
@@ -1134,8 +1175,13 @@
}
@Override
- SSLParameters getDefaultClientSSLParams() {
- return defaultClientSSLParams;
+ ProtocolList getClientDefaultProtocolList() {
+ return clientDefaultProtocolList;
+ }
+
+ @Override
+ CipherSuiteList getClientDefaultCipherSuiteList() {
+ return clientDefaultCipherSuiteList;
}
}
--- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -130,7 +130,7 @@
* also since counters make shorter debugging IDs than the big ones
* we use in the protocol for uniqueness-over-time.
*/
- private static volatile int counter = 0;
+ private static volatile int counter;
/*
* Use of session caches is globally enabled/disabled.
--- a/jdk/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/security/x509/X509CRLImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -1290,7 +1290,7 @@
implements Comparable<X509IssuerSerial> {
final X500Principal issuer;
final BigInteger serial;
- volatile int hashcode = 0;
+ volatile int hashcode;
/**
* Create an X509IssuerSerial.
@@ -1358,13 +1358,16 @@
* @return the hash code value
*/
public int hashCode() {
- if (hashcode == 0) {
- int result = 17;
- result = 37*result + issuer.hashCode();
- result = 37*result + serial.hashCode();
- hashcode = result;
+ int h = hashcode;
+ if (h == 0) {
+ h = 17;
+ h = 37*h + issuer.hashCode();
+ h = 37*h + serial.hashCode();
+ if (h != 0) {
+ hashcode = h;
+ }
}
- return hashcode;
+ return h;
}
@Override
--- a/jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/util/calendar/CalendarSystem.java Thu Dec 24 10:34:31 2015 -0800
@@ -25,13 +25,6 @@
package sun.util.calendar;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.security.AccessController;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.Properties;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -76,7 +69,7 @@
/////////////////////// Calendar Factory Methods /////////////////////////
- private static volatile boolean initialized = false;
+ private static volatile boolean initialized;
// Map of calendar names and calendar class names
private static ConcurrentMap<String, String> names;
--- a/jdk/src/java.base/share/classes/sun/util/locale/BaseLocale.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/util/locale/BaseLocale.java Thu Dec 24 10:34:31 2015 -0800
@@ -46,7 +46,7 @@
private final String region;
private final String variant;
- private volatile int hash = 0;
+ private volatile int hash;
// This method must be called only when creating the Locale.* constants.
private BaseLocale(String language, String region) {
@@ -147,7 +147,9 @@
h = 31 * h + script.hashCode();
h = 31 * h + region.hashCode();
h = 31 * h + variant.hashCode();
- hash = h;
+ if (h != 0) {
+ hash = h;
+ }
}
return h;
}
--- a/jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java Thu Dec 24 10:34:31 2015 -0800
@@ -114,20 +114,20 @@
}
}
- private volatile BreakIteratorProvider breakIteratorProvider = null;
- private volatile CollatorProvider collatorProvider = null;
- private volatile DateFormatProvider dateFormatProvider = null;
- private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider = null;
- private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider = null;
- private volatile NumberFormatProvider numberFormatProvider = null;
+ private volatile BreakIteratorProvider breakIteratorProvider;
+ private volatile CollatorProvider collatorProvider;
+ private volatile DateFormatProvider dateFormatProvider;
+ private volatile DateFormatSymbolsProvider dateFormatSymbolsProvider;
+ private volatile DecimalFormatSymbolsProvider decimalFormatSymbolsProvider;
+ private volatile NumberFormatProvider numberFormatProvider;
- private volatile CurrencyNameProvider currencyNameProvider = null;
- private volatile LocaleNameProvider localeNameProvider = null;
- private volatile TimeZoneNameProvider timeZoneNameProvider = null;
- private volatile CalendarDataProvider calendarDataProvider = null;
- private volatile CalendarNameProvider calendarNameProvider = null;
+ private volatile CurrencyNameProvider currencyNameProvider;
+ private volatile LocaleNameProvider localeNameProvider;
+ private volatile TimeZoneNameProvider timeZoneNameProvider;
+ private volatile CalendarDataProvider calendarDataProvider;
+ private volatile CalendarNameProvider calendarNameProvider;
- private volatile CalendarProvider calendarProvider = null;
+ private volatile CalendarProvider calendarProvider;
/*
* Getter methods for java.text.spi.* providers
--- a/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java Thu Dec 24 10:34:31 2015 -0800
@@ -107,7 +107,7 @@
* Default fallback adapter type, which should return something meaningful in any case.
* This is either CLDR or FALLBACK.
*/
- static volatile LocaleProviderAdapter.Type defaultLocaleProviderAdapter = null;
+ static volatile LocaleProviderAdapter.Type defaultLocaleProviderAdapter;
/**
* Adapter lookup cache.
--- a/jdk/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/share/classes/sun/util/resources/OpenListResourceBundle.java Thu Dec 24 10:34:31 2015 -0800
@@ -164,6 +164,6 @@
return new HashSet<>();
}
- private volatile Map<String, Object> lookup = null;
+ private volatile Map<String, Object> lookup;
private volatile Set<String> keyset;
}
--- a/jdk/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/unix/classes/sun/nio/ch/SinkChannelImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -47,7 +47,7 @@
int fdVal;
// ID of native thread doing write, for signalling
- private volatile long thread = 0;
+ private volatile long thread;
// Lock held by current reading thread
private final Object lock = new Object();
--- a/jdk/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/unix/classes/sun/nio/ch/SourceChannelImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -47,7 +47,7 @@
int fdVal;
// ID of native thread doing read, for signalling
- private volatile long thread = 0;
+ private volatile long thread;
// Lock held by current reading thread
private final Object lock = new Object();
--- a/jdk/src/java.base/unix/classes/sun/nio/fs/MimeTypesFileTypeDetector.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/unix/classes/sun/nio/fs/MimeTypesFileTypeDetector.java Thu Dec 24 10:34:31 2015 -0800
@@ -52,7 +52,7 @@
private Map<String,String> mimeTypeMap;
// set to true when file loaded
- private volatile boolean loaded = false;
+ private volatile boolean loaded;
public MimeTypesFileTypeDetector(Path filePath) {
mimeTypesFile = filePath;
--- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystemProvider.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixFileSystemProvider.java Thu Dec 24 10:34:31 2015 -0800
@@ -69,15 +69,16 @@
private void checkUri(URI uri) {
if (!uri.getScheme().equalsIgnoreCase(getScheme()))
throw new IllegalArgumentException("URI does not match this provider");
- if (uri.getAuthority() != null)
+ if (uri.getRawAuthority() != null)
throw new IllegalArgumentException("Authority component present");
- if (uri.getPath() == null)
+ String path = uri.getPath();
+ if (path == null)
throw new IllegalArgumentException("Path component is undefined");
- if (!uri.getPath().equals("/"))
+ if (!path.equals("/"))
throw new IllegalArgumentException("Path component should be '/'");
- if (uri.getQuery() != null)
+ if (uri.getRawQuery() != null)
throw new IllegalArgumentException("Query component present");
- if (uri.getFragment() != null)
+ if (uri.getRawFragment() != null)
throw new IllegalArgumentException("Fragment component present");
}
--- a/jdk/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/unix/classes/sun/nio/fs/UnixUriUtils.java Thu Dec 24 10:34:31 2015 -0800
@@ -49,11 +49,11 @@
String scheme = uri.getScheme();
if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
throw new IllegalArgumentException("URI scheme is not \"file\"");
- if (uri.getAuthority() != null)
+ if (uri.getRawAuthority() != null)
throw new IllegalArgumentException("URI has an authority component");
- if (uri.getFragment() != null)
+ if (uri.getRawFragment() != null)
throw new IllegalArgumentException("URI has a fragment component");
- if (uri.getQuery() != null)
+ if (uri.getRawQuery() != null)
throw new IllegalArgumentException("URI has a query component");
// compatibility with java.io.File
--- a/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/windows/classes/sun/nio/ch/WindowsSelectorImpl.java Thu Dec 24 10:34:31 2015 -0800
@@ -119,7 +119,7 @@
// Lock for interrupt triggering and clearing
private final Object interruptLock = new Object();
- private volatile boolean interruptTriggered = false;
+ private volatile boolean interruptTriggered;
WindowsSelectorImpl(SelectorProvider sp) throws IOException {
super(sp);
--- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java Thu Dec 24 10:34:31 2015 -0800
@@ -61,15 +61,16 @@
private void checkUri(URI uri) {
if (!uri.getScheme().equalsIgnoreCase(getScheme()))
throw new IllegalArgumentException("URI does not match this provider");
- if (uri.getAuthority() != null)
+ if (uri.getRawAuthority() != null)
throw new IllegalArgumentException("Authority component present");
- if (uri.getPath() == null)
+ String path = uri.getPath();
+ if (path == null)
throw new IllegalArgumentException("Path component is undefined");
- if (!uri.getPath().equals("/"))
+ if (!path.equals("/"))
throw new IllegalArgumentException("Path component should be '/'");
- if (uri.getQuery() != null)
+ if (uri.getRawQuery() != null)
throw new IllegalArgumentException("Query component present");
- if (uri.getFragment() != null)
+ if (uri.getRawFragment() != null)
throw new IllegalArgumentException("Fragment component present");
}
--- a/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUriSupport.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.base/windows/classes/sun/nio/fs/WindowsUriSupport.java Thu Dec 24 10:34:31 2015 -0800
@@ -123,16 +123,16 @@
String scheme = uri.getScheme();
if ((scheme == null) || !scheme.equalsIgnoreCase("file"))
throw new IllegalArgumentException("URI scheme is not \"file\"");
- if (uri.getFragment() != null)
+ if (uri.getRawFragment() != null)
throw new IllegalArgumentException("URI has a fragment component");
- if (uri.getQuery() != null)
+ if (uri.getRawQuery() != null)
throw new IllegalArgumentException("URI has a query component");
String path = uri.getPath();
if (path.equals(""))
throw new IllegalArgumentException("URI path component is empty");
// UNC
- String auth = uri.getAuthority();
+ String auth = uri.getRawAuthority();
if (auth != null && !auth.equals("")) {
String host = uri.getHost();
if (host == null)
--- a/jdk/src/java.datatransfer/share/classes/java/awt/datatransfer/DataFlavor.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.datatransfer/share/classes/java/awt/datatransfer/DataFlavor.java Thu Dec 24 10:34:31 2015 -0800
@@ -289,6 +289,8 @@
* representationClass = String
* mimeType = "text/html"
* </pre>
+ *
+ * @since 1.8
*/
public static DataFlavor selectionHtmlFlavor = initHtmlDataFlavor("selection");
@@ -301,6 +303,8 @@
* representationClass = String
* mimeType = "text/html"
* </pre>
+ *
+ * @since 1.8
*/
public static DataFlavor fragmentHtmlFlavor = initHtmlDataFlavor("fragment");
@@ -314,6 +318,8 @@
* representationClass = String
* mimeType = "text/html"
* </pre>
+ *
+ * @since 1.8
*/
public static DataFlavor allHtmlFlavor = initHtmlDataFlavor("all");
--- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/CMenuItem.m Thu Dec 24 10:34:31 2015 -0800
@@ -24,6 +24,7 @@
*/
#import <JavaNativeFoundation/JavaNativeFoundation.h>
+#include <Carbon/Carbon.h>
#import "CMenuItem.h"
#import "CMenu.h"
@@ -40,7 +41,7 @@
@implementation CMenuItem
- (id) initWithPeer:(jobject)peer asSeparator: (NSNumber *) asSeparator{
-AWT_ASSERT_APPKIT_THREAD;
+ AWT_ASSERT_APPKIT_THREAD;
self = [super initWithPeer:peer];
if (self) {
if ([asSeparator boolValue]) {
@@ -63,13 +64,48 @@
- (BOOL) worksWhenModal {
return YES;
}
+// This is a method written using Carbon framework methods to remove
+// All modifiers including "Shift" modifier.
+// Example 1: Shortcut set is "Command Shift m" returns "m"
+// Example 2: Shortcut set is "Command m" returns "m"
+// Example 3: Shortcut set is "Alt Shift ," returns ","
+
+CFStringRef createStringForKey(CGKeyCode keyCode)
+{
+ TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
+// currentKeyboard now contains the current input source
+ CFDataRef layoutData =
+ TISGetInputSourceProperty(currentKeyboard,
+ kTISPropertyUnicodeKeyLayoutData);
+// the UNICODE keyLayout is fetched from currentKeyboard in layoutData
+ const UCKeyboardLayout *keyboardLayout =
+ (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
+// A read-only data pointer is fetched from layoutData
+ UInt32 keysDown = 0;
+ UniChar chars[4];
+ UniCharCount realLength;
+
+ UCKeyTranslate(keyboardLayout,
+ keyCode,
+ kUCKeyActionDisplay,
+ 0,
+ LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysBit,
+ &keysDown,
+ sizeof(chars) / sizeof(chars[0]),
+ &realLength,
+ chars);
+ CFRelease(currentKeyboard);
+// Converts keyCode, modifier and dead-key state into UNICODE characters
+ return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1);
+}
// Events
- (void)handleAction:(NSMenuItem *)sender {
-AWT_ASSERT_APPKIT_THREAD;
+ AWT_ASSERT_APPKIT_THREAD;
JNIEnv *env = [ThreadUtilities getJNIEnv];
-JNF_COCOA_ENTER(env);
-
+ JNF_COCOA_ENTER(env);
+
// If we are called as a result of user pressing a shortcut, do nothing,
// because AVTView has already sent corresponding key event to the Java
// layer from performKeyEquivalent.
@@ -82,31 +118,37 @@
NSEvent *currEvent = [[NSApplication sharedApplication] currentEvent];
if ([currEvent type] == NSKeyDown) {
NSString *menuKey = [sender keyEquivalent];
- NSString *eventKey = [currEvent charactersIgnoringModifiers];
-
- // Apple uses characters from private Unicode range for some of the
- // keys, so we need to do the same translation here that we do
- // for the regular key down events
- if ([eventKey length] == 1) {
- unichar origChar = [eventKey characterAtIndex:0];
- unichar newChar = NsCharToJavaChar(origChar, 0);
- if (newChar == java_awt_event_KeyEvent_CHAR_UNDEFINED) {
- newChar = origChar;
- }
-
- eventKey = [NSString stringWithCharacters: &newChar length: 1];
- }
-
+// If shortcut is "Command Shift ," the menuKey gets the value ","
+// But [currEvent charactersIgnoringModifiers]; returns "<" and not ","
+// because the charactersIgnoreingModifiers does not ignore "Shift"
+// So a shortcut like "Command Shift m" will return "M" where as the
+// MenuKey will have the value "m". To remove this issue the below
+// createStringForKey is used.
+ NSString *eventKey = createStringForKey([currEvent keyCode]);
+
+// Apple uses characters from private Unicode range for some of the
+// keys, so we need to do the same translation here that we do
+// for the regular key down events
+ if ([eventKey length] == 1) {
+ unichar origChar = [eventKey characterAtIndex:0];
+ unichar newChar = NsCharToJavaChar(origChar, 0);
+ if (newChar == java_awt_event_KeyEvent_CHAR_UNDEFINED) {
+ newChar = origChar;
+ }
+
+ eventKey = [NSString stringWithCharacters: &newChar length: 1];
+ }
+
NSWindow *keyWindow = [NSApp keyWindow];
if ([menuKey isEqualToString:eventKey] && keyWindow != nil) {
return;
}
}
-
+
if (fIsCheckbox) {
static JNF_CLASS_CACHE(jc_CCheckboxMenuItem, "sun/lwawt/macosx/CCheckboxMenuItem");
static JNF_MEMBER_CACHE(jm_ckHandleAction, jc_CCheckboxMenuItem, "handleAction", "(Z)V");
-
+
// Send the opposite of what's currently checked -- the action
// indicates what state we're going to.
NSInteger state = [sender state];
@@ -115,26 +157,26 @@
} else {
static JNF_CLASS_CACHE(jc_CMenuItem, "sun/lwawt/macosx/CMenuItem");
static JNF_MEMBER_CACHE(jm_handleAction, jc_CMenuItem, "handleAction", "(JI)V"); // AWT_THREADING Safe (event)
-
+
NSUInteger modifiers = [currEvent modifierFlags];
jint javaModifiers = NsKeyModifiersToJavaModifiers(modifiers, NO);
-
+
JNFCallVoidMethod(env, fPeer, jm_handleAction, UTC(currEvent), javaModifiers); // AWT_THREADING Safe (event)
}
-JNF_COCOA_EXIT(env);
+ JNF_COCOA_EXIT(env);
}
- (void) setJavaLabel:(NSString *)theLabel shortcut:(NSString *)theKeyEquivalent modifierMask:(jint)modifiers {
-
+
NSUInteger modifierMask = 0;
-
+
if (![theKeyEquivalent isEqualToString:@""]) {
// Force the key equivalent to lower case if not using the shift key.
// Otherwise AppKit will draw a Shift glyph in the menu.
if ((modifiers & java_awt_event_KeyEvent_SHIFT_MASK) == 0) {
theKeyEquivalent = [theKeyEquivalent lowercaseString];
}
-
+
// Hack for the question mark -- SHIFT and / means use the question mark.
if ((modifiers & java_awt_event_KeyEvent_SHIFT_MASK) != 0 &&
[theKeyEquivalent isEqualToString:@"/"])
@@ -142,10 +184,10 @@
theKeyEquivalent = @"?";
modifiers &= ~java_awt_event_KeyEvent_SHIFT_MASK;
}
-
+
modifierMask = JavaModifiersToNsKeyModifiers(modifiers, NO);
}
-
+
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
[fMenuItem setKeyEquivalent:theKeyEquivalent];
[fMenuItem setKeyEquivalentModifierMask:modifierMask];
@@ -154,14 +196,14 @@
}
- (void) setJavaImage:(NSImage *)theImage {
-
+
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
[fMenuItem setImage:theImage];
}];
}
- (void) setJavaToolTipText:(NSString *)theText {
-
+
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
[fMenuItem setToolTip:theText];
}];
@@ -169,11 +211,11 @@
- (void)setJavaEnabled:(BOOL) enabled {
-
+
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
@synchronized(self) {
fIsEnabled = enabled;
-
+
// Warning: This won't work if the parent menu is disabled.
// See [CMenu syncFromJava]. We still need to call it here so
// the NSMenuItem itself gets properly updated.
@@ -183,7 +225,7 @@
}
- (BOOL)isEnabled {
-
+
BOOL enabled = NO;
@synchronized(self) {
enabled = fIsEnabled;
@@ -193,7 +235,7 @@
- (void)setJavaState:(BOOL)newState {
-
+
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
[fMenuItem setState:(newState ? NSOnState : NSOffState)];
}];
@@ -207,7 +249,7 @@
- (void)dealloc {
[fMenuItem release];
fMenuItem = nil;
-
+
[super dealloc];
}
@@ -240,7 +282,7 @@
/** Convert a Java keycode for SetMenuItemCmd */
static unichar AWTKeyToMacShortcut(jint awtKey, BOOL doShift) {
unichar macKey = 0;
-
+
if ((awtKey >= java_awt_event_KeyEvent_VK_0 && awtKey <= java_awt_event_KeyEvent_VK_9) ||
(awtKey >= java_awt_event_KeyEvent_VK_A && awtKey <= java_awt_event_KeyEvent_VK_Z))
{
@@ -255,68 +297,68 @@
} else {
// Special characters
switch (awtKey) {
- case java_awt_event_KeyEvent_VK_BACK_QUOTE : macKey = '`'; break;
- case java_awt_event_KeyEvent_VK_QUOTE : macKey = '\''; break;
-
- case java_awt_event_KeyEvent_VK_ESCAPE : macKey = 0x1B; break;
- case java_awt_event_KeyEvent_VK_SPACE : macKey = ' '; break;
- case java_awt_event_KeyEvent_VK_PAGE_UP : macKey = NSPageUpFunctionKey; break;
- case java_awt_event_KeyEvent_VK_PAGE_DOWN : macKey = NSPageDownFunctionKey; break;
- case java_awt_event_KeyEvent_VK_END : macKey = NSEndFunctionKey; break;
- case java_awt_event_KeyEvent_VK_HOME : macKey = NSHomeFunctionKey; break;
-
- case java_awt_event_KeyEvent_VK_LEFT : macKey = NSLeftArrowFunctionKey; break;
- case java_awt_event_KeyEvent_VK_UP : macKey = NSUpArrowFunctionKey; break;
- case java_awt_event_KeyEvent_VK_RIGHT : macKey = NSRightArrowFunctionKey; break;
- case java_awt_event_KeyEvent_VK_DOWN : macKey = NSDownArrowFunctionKey; break;
-
- case java_awt_event_KeyEvent_VK_COMMA : macKey = ','; break;
-
- // Mac OS doesn't distinguish between the two '-' keys...
- case java_awt_event_KeyEvent_VK_MINUS :
- case java_awt_event_KeyEvent_VK_SUBTRACT : macKey = '-'; break;
-
- // or the two '.' keys...
- case java_awt_event_KeyEvent_VK_DECIMAL :
- case java_awt_event_KeyEvent_VK_PERIOD : macKey = '.'; break;
-
- // or the two '/' keys.
- case java_awt_event_KeyEvent_VK_DIVIDE :
- case java_awt_event_KeyEvent_VK_SLASH : macKey = '/'; break;
-
- case java_awt_event_KeyEvent_VK_SEMICOLON : macKey = ';'; break;
- case java_awt_event_KeyEvent_VK_EQUALS : macKey = '='; break;
-
- case java_awt_event_KeyEvent_VK_OPEN_BRACKET : macKey = '['; break;
- case java_awt_event_KeyEvent_VK_BACK_SLASH : macKey = '\\'; break;
- case java_awt_event_KeyEvent_VK_CLOSE_BRACKET : macKey = ']'; break;
-
- case java_awt_event_KeyEvent_VK_MULTIPLY : macKey = '*'; break;
- case java_awt_event_KeyEvent_VK_ADD : macKey = '+'; break;
-
- case java_awt_event_KeyEvent_VK_HELP : macKey = NSHelpFunctionKey; break;
- case java_awt_event_KeyEvent_VK_TAB : macKey = NSTabCharacter; break;
- case java_awt_event_KeyEvent_VK_ENTER : macKey = NSNewlineCharacter; break;
- case java_awt_event_KeyEvent_VK_BACK_SPACE : macKey = NSBackspaceCharacter; break;
- case java_awt_event_KeyEvent_VK_DELETE : macKey = NSDeleteCharacter; break;
- case java_awt_event_KeyEvent_VK_CLEAR : macKey = NSClearDisplayFunctionKey; break;
- case java_awt_event_KeyEvent_VK_AMPERSAND : macKey = '&'; break;
- case java_awt_event_KeyEvent_VK_ASTERISK : macKey = '*'; break;
- case java_awt_event_KeyEvent_VK_QUOTEDBL : macKey = '\"'; break;
- case java_awt_event_KeyEvent_VK_LESS : macKey = '<'; break;
- case java_awt_event_KeyEvent_VK_GREATER : macKey = '>'; break;
- case java_awt_event_KeyEvent_VK_BRACELEFT : macKey = '{'; break;
- case java_awt_event_KeyEvent_VK_BRACERIGHT : macKey = '}'; break;
- case java_awt_event_KeyEvent_VK_AT : macKey = '@'; break;
- case java_awt_event_KeyEvent_VK_COLON : macKey = ':'; break;
- case java_awt_event_KeyEvent_VK_CIRCUMFLEX : macKey = '^'; break;
- case java_awt_event_KeyEvent_VK_DOLLAR : macKey = '$'; break;
- case java_awt_event_KeyEvent_VK_EXCLAMATION_MARK : macKey = '!'; break;
- case java_awt_event_KeyEvent_VK_LEFT_PARENTHESIS : macKey = '('; break;
- case java_awt_event_KeyEvent_VK_NUMBER_SIGN : macKey = '#'; break;
- case java_awt_event_KeyEvent_VK_PLUS : macKey = '+'; break;
- case java_awt_event_KeyEvent_VK_RIGHT_PARENTHESIS: macKey = ')'; break;
- case java_awt_event_KeyEvent_VK_UNDERSCORE : macKey = '_'; break;
+ case java_awt_event_KeyEvent_VK_BACK_QUOTE : macKey = '`'; break;
+ case java_awt_event_KeyEvent_VK_QUOTE : macKey = '\''; break;
+
+ case java_awt_event_KeyEvent_VK_ESCAPE : macKey = 0x1B; break;
+ case java_awt_event_KeyEvent_VK_SPACE : macKey = ' '; break;
+ case java_awt_event_KeyEvent_VK_PAGE_UP : macKey = NSPageUpFunctionKey; break;
+ case java_awt_event_KeyEvent_VK_PAGE_DOWN : macKey = NSPageDownFunctionKey; break;
+ case java_awt_event_KeyEvent_VK_END : macKey = NSEndFunctionKey; break;
+ case java_awt_event_KeyEvent_VK_HOME : macKey = NSHomeFunctionKey; break;
+
+ case java_awt_event_KeyEvent_VK_LEFT : macKey = NSLeftArrowFunctionKey; break;
+ case java_awt_event_KeyEvent_VK_UP : macKey = NSUpArrowFunctionKey; break;
+ case java_awt_event_KeyEvent_VK_RIGHT : macKey = NSRightArrowFunctionKey; break;
+ case java_awt_event_KeyEvent_VK_DOWN : macKey = NSDownArrowFunctionKey; break;
+
+ case java_awt_event_KeyEvent_VK_COMMA : macKey = ','; break;
+
+ // Mac OS doesn't distinguish between the two '-' keys...
+ case java_awt_event_KeyEvent_VK_MINUS :
+ case java_awt_event_KeyEvent_VK_SUBTRACT : macKey = '-'; break;
+
+ // or the two '.' keys...
+ case java_awt_event_KeyEvent_VK_DECIMAL :
+ case java_awt_event_KeyEvent_VK_PERIOD : macKey = '.'; break;
+
+ // or the two '/' keys.
+ case java_awt_event_KeyEvent_VK_DIVIDE :
+ case java_awt_event_KeyEvent_VK_SLASH : macKey = '/'; break;
+
+ case java_awt_event_KeyEvent_VK_SEMICOLON : macKey = ';'; break;
+ case java_awt_event_KeyEvent_VK_EQUALS : macKey = '='; break;
+
+ case java_awt_event_KeyEvent_VK_OPEN_BRACKET : macKey = '['; break;
+ case java_awt_event_KeyEvent_VK_BACK_SLASH : macKey = '\\'; break;
+ case java_awt_event_KeyEvent_VK_CLOSE_BRACKET : macKey = ']'; break;
+
+ case java_awt_event_KeyEvent_VK_MULTIPLY : macKey = '*'; break;
+ case java_awt_event_KeyEvent_VK_ADD : macKey = '+'; break;
+
+ case java_awt_event_KeyEvent_VK_HELP : macKey = NSHelpFunctionKey; break;
+ case java_awt_event_KeyEvent_VK_TAB : macKey = NSTabCharacter; break;
+ case java_awt_event_KeyEvent_VK_ENTER : macKey = NSNewlineCharacter; break;
+ case java_awt_event_KeyEvent_VK_BACK_SPACE : macKey = NSBackspaceCharacter; break;
+ case java_awt_event_KeyEvent_VK_DELETE : macKey = NSDeleteCharacter; break;
+ case java_awt_event_KeyEvent_VK_CLEAR : macKey = NSClearDisplayFunctionKey; break;
+ case java_awt_event_KeyEvent_VK_AMPERSAND : macKey = '&'; break;
+ case java_awt_event_KeyEvent_VK_ASTERISK : macKey = '*'; break;
+ case java_awt_event_KeyEvent_VK_QUOTEDBL : macKey = '\"'; break;
+ case java_awt_event_KeyEvent_VK_LESS : macKey = '<'; break;
+ case java_awt_event_KeyEvent_VK_GREATER : macKey = '>'; break;
+ case java_awt_event_KeyEvent_VK_BRACELEFT : macKey = '{'; break;
+ case java_awt_event_KeyEvent_VK_BRACERIGHT : macKey = '}'; break;
+ case java_awt_event_KeyEvent_VK_AT : macKey = '@'; break;
+ case java_awt_event_KeyEvent_VK_COLON : macKey = ':'; break;
+ case java_awt_event_KeyEvent_VK_CIRCUMFLEX : macKey = '^'; break;
+ case java_awt_event_KeyEvent_VK_DOLLAR : macKey = '$'; break;
+ case java_awt_event_KeyEvent_VK_EXCLAMATION_MARK : macKey = '!'; break;
+ case java_awt_event_KeyEvent_VK_LEFT_PARENTHESIS : macKey = '('; break;
+ case java_awt_event_KeyEvent_VK_NUMBER_SIGN : macKey = '#'; break;
+ case java_awt_event_KeyEvent_VK_PLUS : macKey = '+'; break;
+ case java_awt_event_KeyEvent_VK_RIGHT_PARENTHESIS: macKey = ')'; break;
+ case java_awt_event_KeyEvent_VK_UNDERSCORE : macKey = '_'; break;
}
}
return macKey;
@@ -330,27 +372,27 @@
JNIEXPORT void JNICALL
Java_sun_lwawt_macosx_CMenuItem_nativeSetLabel
(JNIEnv *env, jobject peer,
- jlong menuItemObj, jstring label,
- jchar shortcutKey, jint shortcutKeyCode, jint mods)
+ jlong menuItemObj, jstring label,
+ jchar shortcutKey, jint shortcutKeyCode, jint mods)
{
-JNF_COCOA_ENTER(env);
+ JNF_COCOA_ENTER(env);
NSString *theLabel = JNFJavaToNSString(env, label);
NSString *theKeyEquivalent = nil;
unichar macKey = shortcutKey;
-
+
if (macKey == 0) {
macKey = AWTKeyToMacShortcut(shortcutKeyCode, (mods & java_awt_event_KeyEvent_SHIFT_MASK) != 0);
}
-
+
if (macKey != 0) {
unichar equivalent[1] = {macKey};
theKeyEquivalent = [NSString stringWithCharacters:equivalent length:1];
} else {
theKeyEquivalent = @"";
}
-
+
[((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaLabel:theLabel shortcut:theKeyEquivalent modifierMask:mods];
-JNF_COCOA_EXIT(env);
+ JNF_COCOA_EXIT(env);
}
/*
@@ -362,10 +404,10 @@
Java_sun_lwawt_macosx_CMenuItem_nativeSetTooltip
(JNIEnv *env, jobject peer, jlong menuItemObj, jstring tooltip)
{
-JNF_COCOA_ENTER(env);
+ JNF_COCOA_ENTER(env);
NSString *theTooltip = JNFJavaToNSString(env, tooltip);
[((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaToolTipText:theTooltip];
-JNF_COCOA_EXIT(env);
+ JNF_COCOA_EXIT(env);
}
/*
@@ -377,9 +419,9 @@
Java_sun_lwawt_macosx_CMenuItem_nativeSetImage
(JNIEnv *env, jobject peer, jlong menuItemObj, jlong image)
{
-JNF_COCOA_ENTER(env);
+ JNF_COCOA_ENTER(env);
[((CMenuItem *)jlong_to_ptr(menuItemObj)) setJavaImage:(NSImage*)jlong_to_ptr(image)];
-JNF_COCOA_EXIT(env);
+ JNF_COCOA_EXIT(env);
}
/*
@@ -389,38 +431,38 @@
*/
JNIEXPORT jlong JNICALL
Java_sun_lwawt_macosx_CMenuItem_nativeCreate
- (JNIEnv *env, jobject peer, jlong parentCMenuObj, jboolean isSeparator)
+(JNIEnv *env, jobject peer, jlong parentCMenuObj, jboolean isSeparator)
{
-
+
CMenuItem *aCMenuItem = nil;
CMenu *parentCMenu = (CMenu *)jlong_to_ptr(parentCMenuObj);
-JNF_COCOA_ENTER(env);
-
+ JNF_COCOA_ENTER(env);
+
jobject cPeerObjGlobal = (*env)->NewGlobalRef(env, peer);
-
+
NSMutableArray *args = nil;
-
+
// Create a new item....
if (isSeparator == JNI_TRUE) {
args = [[NSMutableArray alloc] initWithObjects:[NSValue valueWithBytes:&cPeerObjGlobal objCType:@encode(jobject)], [NSNumber numberWithBool:YES], nil];
} else {
args = [[NSMutableArray alloc] initWithObjects:[NSValue valueWithBytes:&cPeerObjGlobal objCType:@encode(jobject)], [NSNumber numberWithBool:NO], nil];
}
-
+
[ThreadUtilities performOnMainThread:@selector(_createMenuItem_OnAppKitThread:) on:[CMenuItem alloc] withObject:args waitUntilDone:YES];
-
+
aCMenuItem = (CMenuItem *)[args objectAtIndex: 0];
-
+
if (aCMenuItem == nil) {
return 0L;
}
-
+
// and add it to the parent item.
[parentCMenu addJavaMenuItem: aCMenuItem];
-
+
// setLabel will be called after creation completes.
-
-JNF_COCOA_EXIT(env);
+
+ JNF_COCOA_EXIT(env);
return ptr_to_jlong(aCMenuItem);
}
@@ -433,10 +475,10 @@
Java_sun_lwawt_macosx_CMenuItem_nativeSetEnabled
(JNIEnv *env, jobject peer, jlong menuItemObj, jboolean enable)
{
-JNF_COCOA_ENTER(env);
+ JNF_COCOA_ENTER(env);
CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);
[item setJavaEnabled: (enable == JNI_TRUE)];
-JNF_COCOA_EXIT(env);
+ JNF_COCOA_EXIT(env);
}
/*
@@ -448,10 +490,10 @@
Java_sun_lwawt_macosx_CCheckboxMenuItem_nativeSetState
(JNIEnv *env, jobject peer, jlong menuItemObj, jboolean state)
{
-JNF_COCOA_ENTER(env);
+ JNF_COCOA_ENTER(env);
CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);
[item setJavaState: (state == JNI_TRUE)];
-JNF_COCOA_EXIT(env);
+ JNF_COCOA_EXIT(env);
}
/*
@@ -463,8 +505,8 @@
Java_sun_lwawt_macosx_CCheckboxMenuItem_nativeSetIsCheckbox
(JNIEnv *env, jobject peer, jlong menuItemObj)
{
-JNF_COCOA_ENTER(env);
+ JNF_COCOA_ENTER(env);
CMenuItem *item = (CMenuItem *)jlong_to_ptr(menuItemObj);
[item setIsCheckbox];
-JNF_COCOA_EXIT(env);
+ JNF_COCOA_EXIT(env);
}
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/jpeg/JPEGMetadata.java Thu Dec 24 10:34:31 2015 -0800
@@ -874,13 +874,13 @@
return chroma;
}
- boolean idsAreJFIF = true;
+ boolean idsAreJFIF = false;
- for (int i = 0; i < sof.componentSpecs.length; i++) {
- int id = sof.componentSpecs[i].componentId;
- if ((id < 1) || (id >= sof.componentSpecs.length)) {
- idsAreJFIF = false;
- }
+ int cid0 = sof.componentSpecs[0].componentId;
+ int cid1 = sof.componentSpecs[1].componentId;
+ int cid2 = sof.componentSpecs[2].componentId;
+ if ((cid0 == 1) && (cid1 == 2) && (cid2 == 3)) {
+ idsAreJFIF = true;
}
if (idsAreJFIF) {
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/png/PNGImageWriter.java Thu Dec 24 10:34:31 2015 -0800
@@ -193,7 +193,17 @@
// Return to end of chunk and flush to minimize buffering
stream.seek(pos);
- stream.flushBefore(pos);
+ try {
+ stream.flushBefore(pos);
+ } catch (IOException e) {
+ /*
+ * If flushBefore() fails we try to access startPos in finally
+ * block of write_IDAT(). We should update startPos to avoid
+ * IndexOutOfBoundException while seek() is happening.
+ */
+ this.startPos = stream.getStreamPosition();
+ throw e;
+ }
}
public int read() throws IOException {
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFIFD.java Thu Dec 24 10:34:31 2015 -0800
@@ -472,6 +472,14 @@
// Read tag number, value type, and value count.
int tagNumber = stream.readUnsignedShort();
int type = stream.readUnsignedShort();
+ int sizeOfType;
+ try {
+ sizeOfType = TIFFTag.getSizeOfType(type);
+ } catch (IllegalArgumentException ignored) {
+ // Continue with the next IFD entry.
+ stream.skipBytes(4);
+ continue;
+ }
int count = (int)stream.readUnsignedInt();
// Get the associated TIFFTag.
@@ -510,14 +518,14 @@
}
}
- int size = count*TIFFTag.getSizeOfType(type);
+ int size = count*sizeOfType;
if (size > 4 || tag.isIFDPointer()) {
// The IFD entry value is a pointer to the actual field value.
long offset = stream.readUnsignedInt();
// Check whether the the field value is within the stream.
if (haveStreamLength && offset + size > streamLength) {
- throw new IIOException("Field data is past end-of-stream");
+ continue;
}
// Add a TIFFIFDEntry as a placeholder. This avoids a mark,
--- a/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/imageio/plugins/tiff/TIFFImageWriter.java Thu Dec 24 10:34:31 2015 -0800
@@ -205,6 +205,10 @@
// Next available space.
private long nextSpace = 0L;
+ private long prevStreamPosition;
+ private long prevHeaderPosition;
+ private long prevNextSpace;
+
// Whether a sequence is being written.
private boolean isWritingSequence = false;
private boolean isInsertingEmpty = false;
@@ -2300,7 +2304,14 @@
public void write(IIOMetadata sm,
IIOImage iioimage,
ImageWriteParam p) throws IOException {
+ if (stream == null) {
+ throw new IllegalStateException("output == null!");
+ }
+ markPositions();
write(sm, iioimage, p, true, true);
+ if (abortRequested()) {
+ resetPositions();
+ }
}
private void writeHeader() throws IOException {
@@ -2333,7 +2344,7 @@
throw new IllegalStateException("output == null!");
}
if (iioimage == null) {
- throw new NullPointerException("image == null!");
+ throw new IllegalArgumentException("image == null!");
}
if(iioimage.hasRaster() && !canWriteRasters()) {
throw new UnsupportedOperationException
@@ -2767,7 +2778,7 @@
throw new IllegalStateException("Output not set!");
}
if (image == null) {
- throw new NullPointerException("image == null!");
+ throw new IllegalArgumentException("image == null!");
}
// Locate the position of the old IFD (ifd) and the location
@@ -2779,9 +2790,16 @@
// imageIndex is < -1 or is too big thereby satisfying the spec.
locateIFD(imageIndex, ifdpos, ifd);
+ markPositions();
+
// Seek to the position containing the pointer to the old IFD.
stream.seek(ifdpos[0]);
+ // Save the previous pointer value in case of abort.
+ stream.mark();
+ long prevPointerValue = stream.readUnsignedInt();
+ stream.reset();
+
// Update next space pointer in anticipation of next write.
if(ifdpos[0] + 4 > nextSpace) {
nextSpace = ifdpos[0] + 4;
@@ -2805,6 +2823,12 @@
// Update the new IFD to point to the old IFD.
stream.writeInt((int)ifd[0]);
// Don't need to update nextSpace here as already done in write().
+
+ if (abortRequested()) {
+ stream.seek(ifdpos[0]);
+ stream.writeInt((int)prevPointerValue);
+ resetPositions();
+ }
}
// ----- BEGIN insert/writeEmpty methods -----
@@ -2834,7 +2858,7 @@
}
if(imageType == null) {
- throw new NullPointerException("imageType == null!");
+ throw new IllegalArgumentException("imageType == null!");
}
if(width < 1 || height < 1) {
@@ -2891,6 +2915,10 @@
IIOMetadata imageMetadata,
List<? extends BufferedImage> thumbnails,
ImageWriteParam param) throws IOException {
+ if (stream == null) {
+ throw new IllegalStateException("output == null!");
+ }
+
checkParamsEmpty(imageType, width, height, thumbnails);
this.isWritingEmpty = true;
@@ -2901,8 +2929,12 @@
0, 0, emptySM.getWidth(), emptySM.getHeight(),
emptySM, imageType.getColorModel());
+ markPositions();
write(streamMetadata, new IIOImage(emptyImage, null, imageMetadata),
param, true, false);
+ if (abortRequested()) {
+ resetPositions();
+ }
}
public void endInsertEmpty() throws IOException {
@@ -3015,7 +3047,7 @@
throw new IllegalStateException("Output not set!");
}
if (region == null) {
- throw new NullPointerException("region == null!");
+ throw new IllegalArgumentException("region == null!");
}
if (region.getWidth() < 1) {
throw new IllegalArgumentException("region.getWidth() < 1!");
@@ -3200,7 +3232,7 @@
}
if (image == null) {
- throw new NullPointerException("image == null!");
+ throw new IllegalArgumentException("image == null!");
}
if (!inReplacePixelsNest) {
@@ -3559,6 +3591,20 @@
// ----- END replacePixels methods -----
+ // Save stream positions for use when aborted.
+ private void markPositions() throws IOException {
+ prevStreamPosition = stream.getStreamPosition();
+ prevHeaderPosition = headerPosition;
+ prevNextSpace = nextSpace;
+ }
+
+ // Reset to positions saved by markPositions().
+ private void resetPositions() throws IOException {
+ stream.seek(prevStreamPosition);
+ headerPosition = prevHeaderPosition;
+ nextSpace = prevNextSpace;
+ }
+
public void reset() {
super.reset();
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,10 +25,11 @@
package com.sun.media.sound;
+import java.util.Objects;
+
import javax.sound.midi.MidiDevice;
import javax.sound.midi.spi.MidiDeviceProvider;
-
/**
* Super class for MIDI input or output device provider.
*
@@ -127,7 +128,8 @@
}
@Override
- public final MidiDevice getDevice(MidiDevice.Info info) {
+ public final MidiDevice getDevice(final MidiDevice.Info info) {
+ Objects.requireNonNull(info);
if (info instanceof Info) {
readDeviceInfos();
MidiDevice[] devices = getDeviceCache();
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/JARSoundbankReader.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package com.sun.media.sound;
import java.io.BufferedReader;
@@ -32,6 +33,8 @@
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
+import java.util.Objects;
+
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.Soundbank;
import javax.sound.midi.spi.SoundbankReader;
@@ -112,6 +115,7 @@
public Soundbank getSoundbank(InputStream stream)
throws InvalidMidiDataException, IOException {
+ Objects.requireNonNull(stream);
return null;
}
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
package com.sun.media.sound;
+import java.util.Objects;
+
import javax.sound.midi.MidiDevice;
import javax.sound.midi.spi.MidiDeviceProvider;
@@ -42,6 +44,7 @@
@Override
public MidiDevice getDevice(final MidiDevice.Info info) {
+ Objects.requireNonNull(info);
if (RealTimeSequencer.info.equals(info)) {
return new RealTimeSequencer();
}
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
package com.sun.media.sound;
+import java.util.Objects;
+
import javax.sound.midi.MidiDevice;
import javax.sound.midi.spi.MidiDeviceProvider;
@@ -42,6 +44,7 @@
@Override
public MidiDevice getDevice(final MidiDevice.Info info) {
+ Objects.requireNonNull(info);
if (SoftSynthesizer.info.equals(info)) {
return new SoftSynthesizer();
}
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/StandardMidiFileWriter.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,21 +25,22 @@
package com.sun.media.sound;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.SequenceInputStream;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
-import java.io.IOException;
import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.io.SequenceInputStream;
+import java.util.Objects;
import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiEvent;
-import javax.sound.midi.MetaMessage;
import javax.sound.midi.Sequence;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.SysexMessage;
@@ -115,24 +116,16 @@
return typesArray;
}
- public boolean isFileTypeSupported(int type) {
- for(int i=0; i<types.length; i++) {
- if( type == types[i] ) {
- return true;
- }
+ public int write(Sequence in, int type, OutputStream out) throws IOException {
+ Objects.requireNonNull(out);
+ if (!isFileTypeSupported(type, in)) {
+ throw new IllegalArgumentException("Could not write MIDI file");
}
- return false;
- }
-
- public int write(Sequence in, int type, OutputStream out) throws IOException {
byte [] buffer = null;
int bytesRead = 0;
long bytesWritten = 0;
- if( !isFileTypeSupported(type,in) ) {
- throw new IllegalArgumentException("Could not write MIDI file");
- }
// First get the fileStream from this sequence
InputStream fileStream = getFileStream(type,in);
if (fileStream == null) {
@@ -149,6 +142,7 @@
}
public int write(Sequence in, int type, File out) throws IOException {
+ Objects.requireNonNull(in);
FileOutputStream fos = new FileOutputStream(out); // throws IOException
int bytesWritten = write( in, type, fos );
fos.close();
--- a/jdk/src/java.desktop/share/classes/java/awt/TextComponent.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/java/awt/TextComponent.java Thu Dec 24 10:34:31 2015 -0800
@@ -229,15 +229,21 @@
* @see java.awt.TextComponent#getText
*/
public synchronized void setText(String t) {
- boolean skipTextEvent = (text == null || text.isEmpty())
- && (t == null || t.isEmpty());
- text = (t != null) ? t : "";
+ if (t == null) {
+ t = "";
+ }
TextComponentPeer peer = (TextComponentPeer)this.peer;
- // Please note that we do not want to post an event
- // if TextArea.setText() or TextField.setText() replaces an empty text
- // by an empty text, that is, if component's text remains unchanged.
- if (peer != null && !skipTextEvent) {
- peer.setText(text);
+ if (peer != null) {
+ text = peer.getText();
+ // Please note that we do not want to post an event
+ // if TextArea.setText() or TextField.setText() replaces text
+ // by same text, that is, if component's text remains unchanged.
+ if (!t.equals(text)) {
+ text = t;
+ peer.setText(text);
+ }
+ } else {
+ text = t;
}
}
--- a/jdk/src/java.desktop/share/classes/java/awt/TextField.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/java/awt/TextField.java Thu Dec 24 10:34:31 2015 -0800
@@ -198,7 +198,7 @@
* @see java.awt.GraphicsEnvironment#isHeadless
*/
public TextField(String text, int columns) throws HeadlessException {
- super(text);
+ super(replaceEOL(text));
this.columns = (columns >= 0) ? columns : 0;
}
@@ -297,13 +297,32 @@
* @see java.awt.TextComponent#getText
*/
public void setText(String t) {
- super.setText(t);
+ super.setText(replaceEOL(t));
// This could change the preferred size of the Component.
invalidateIfValid();
}
/**
+ * Replaces EOL characters from the text variable with a space character.
+ * @param text the new text.
+ * @return Returns text after replacing EOL characters.
+ */
+ private static String replaceEOL(String text) {
+ if (text == null) {
+ return text;
+ }
+ String[] strEOLs = {System.lineSeparator(), "\n"};
+ for (String eol : strEOLs) {
+ if (text.contains(eol)) {
+ text = text.replace(eol, " ");
+ }
+ }
+ return text;
+ }
+
+
+ /**
* Indicates whether or not this text field has a
* character set for echoing.
* <p>
@@ -704,6 +723,7 @@
{
// HeadlessException will be thrown by TextComponent's readObject
s.defaultReadObject();
+ text = replaceEOL(text);
// Make sure the state we just read in for columns has legal values
if (columns < 0) {
--- a/jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/imageio/ImageIO.java Thu Dec 24 10:34:31 2015 -0800
@@ -564,9 +564,12 @@
if (stream != null) {
stream.mark();
}
- canDecode = spi.canDecodeInput(input);
- if (stream != null) {
- stream.reset();
+ try {
+ canDecode = spi.canDecodeInput(input);
+ } finally {
+ if (stream != null) {
+ stream.reset();
+ }
}
return canDecode;
--- a/jdk/src/java.desktop/share/classes/javax/sound/midi/MidiSystem.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/MidiSystem.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,6 +35,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Properties;
import java.util.Set;
@@ -179,10 +180,12 @@
* due to resource restrictions
* @throws IllegalArgumentException if the info object does not represent a
* MIDI device installed on the system
+ * @throws NullPointerException if {@code info} is {@code null}
* @see #getMidiDeviceInfo
*/
public static MidiDevice getMidiDevice(final MidiDevice.Info info)
throws MidiUnavailableException {
+ Objects.requireNonNull(info);
for (final MidiDeviceProvider provider : getMidiDeviceProviders()) {
if (provider.isDeviceSupported(info)) {
return provider.getDevice(info);
@@ -447,11 +450,13 @@
* @throws InvalidMidiDataException if the stream does not point to valid
* MIDI soundbank data recognized by the system
* @throws IOException if an I/O error occurred when loading the soundbank
+ * @throws NullPointerException if {@code stream} is {@code null}
* @see InputStream#markSupported
* @see InputStream#mark
*/
- public static Soundbank getSoundbank(InputStream stream)
- throws InvalidMidiDataException, IOException {
+ public static Soundbank getSoundbank(final InputStream stream)
+ throws InvalidMidiDataException, IOException {
+ Objects.requireNonNull(stream);
SoundbankReader sp = null;
Soundbank s = null;
@@ -479,9 +484,11 @@
* @throws InvalidMidiDataException if the URL does not point to valid MIDI
* soundbank data recognized by the system
* @throws IOException if an I/O error occurred when loading the soundbank
+ * @throws NullPointerException if {@code url} is {@code null}
*/
- public static Soundbank getSoundbank(URL url)
- throws InvalidMidiDataException, IOException {
+ public static Soundbank getSoundbank(final URL url)
+ throws InvalidMidiDataException, IOException {
+ Objects.requireNonNull(url);
SoundbankReader sp = null;
Soundbank s = null;
@@ -509,9 +516,11 @@
* @throws InvalidMidiDataException if the {@code File} does not point to
* valid MIDI soundbank data recognized by the system
* @throws IOException if an I/O error occurred when loading the soundbank
+ * @throws NullPointerException if {@code file} is {@code null}
*/
- public static Soundbank getSoundbank(File file)
- throws InvalidMidiDataException, IOException {
+ public static Soundbank getSoundbank(final File file)
+ throws InvalidMidiDataException, IOException {
+ Objects.requireNonNull(file);
SoundbankReader sp = null;
Soundbank s = null;
@@ -556,13 +565,15 @@
* MIDI file data recognized by the system
* @throws IOException if an I/O exception occurs while accessing the
* stream
+ * @throws NullPointerException if {@code stream} is {@code null}
* @see #getMidiFileFormat(URL)
* @see #getMidiFileFormat(File)
* @see InputStream#markSupported
* @see InputStream#mark
*/
- public static MidiFileFormat getMidiFileFormat(InputStream stream)
- throws InvalidMidiDataException, IOException {
+ public static MidiFileFormat getMidiFileFormat(final InputStream stream)
+ throws InvalidMidiDataException, IOException {
+ Objects.requireNonNull(stream);
List<MidiFileReader> providers = getMidiFileReaders();
MidiFileFormat format = null;
@@ -602,11 +613,13 @@
* @throws InvalidMidiDataException if the URL does not point to valid MIDI
* file data recognized by the system
* @throws IOException if an I/O exception occurs while accessing the URL
+ * @throws NullPointerException if {@code url} is {@code null}
* @see #getMidiFileFormat(InputStream)
* @see #getMidiFileFormat(File)
*/
- public static MidiFileFormat getMidiFileFormat(URL url)
- throws InvalidMidiDataException, IOException {
+ public static MidiFileFormat getMidiFileFormat(final URL url)
+ throws InvalidMidiDataException, IOException {
+ Objects.requireNonNull(url);
List<MidiFileReader> providers = getMidiFileReaders();
MidiFileFormat format = null;
@@ -646,11 +659,13 @@
* @throws InvalidMidiDataException if the {@code File} does not point to
* valid MIDI file data recognized by the system
* @throws IOException if an I/O exception occurs while accessing the file
+ * @throws NullPointerException if {@code file} is {@code null}
* @see #getMidiFileFormat(InputStream)
* @see #getMidiFileFormat(URL)
*/
- public static MidiFileFormat getMidiFileFormat(File file)
- throws InvalidMidiDataException, IOException {
+ public static MidiFileFormat getMidiFileFormat(final File file)
+ throws InvalidMidiDataException, IOException {
+ Objects.requireNonNull(file);
List<MidiFileReader> providers = getMidiFileReaders();
MidiFileFormat format = null;
@@ -699,11 +714,13 @@
* @throws InvalidMidiDataException if the stream does not point to valid
* MIDI file data recognized by the system
* @throws IOException if an I/O exception occurs while accessing the stream
+ * @throws NullPointerException if {@code stream} is {@code null}
* @see InputStream#markSupported
* @see InputStream#mark
*/
- public static Sequence getSequence(InputStream stream)
- throws InvalidMidiDataException, IOException {
+ public static Sequence getSequence(final InputStream stream)
+ throws InvalidMidiDataException, IOException {
+ Objects.requireNonNull(stream);
List<MidiFileReader> providers = getMidiFileReaders();
Sequence sequence = null;
@@ -743,9 +760,11 @@
* @throws InvalidMidiDataException if the URL does not point to valid MIDI
* file data recognized by the system
* @throws IOException if an I/O exception occurs while accessing the URL
+ * @throws NullPointerException if {@code url} is {@code null}
*/
- public static Sequence getSequence(URL url)
- throws InvalidMidiDataException, IOException {
+ public static Sequence getSequence(final URL url)
+ throws InvalidMidiDataException, IOException {
+ Objects.requireNonNull(url);
List<MidiFileReader> providers = getMidiFileReaders();
Sequence sequence = null;
@@ -787,9 +806,11 @@
* @throws InvalidMidiDataException if the File does not point to valid MIDI
* file data recognized by the system
* @throws IOException if an I/O exception occurs
+ * @throws NullPointerException if {@code file} is {@code null}
*/
- public static Sequence getSequence(File file)
- throws InvalidMidiDataException, IOException {
+ public static Sequence getSequence(final File file)
+ throws InvalidMidiDataException, IOException {
+ Objects.requireNonNull(file);
List<MidiFileReader> providers = getMidiFileReaders();
Sequence sequence = null;
@@ -870,8 +891,10 @@
* @param sequence the sequence for which MIDI file type support is queried
* @return the set of unique supported file types. If no file types are
* supported, returns an array of length 0.
+ * @throws NullPointerException if {@code sequence} is {@code null}
*/
- public static int[] getMidiFileTypes(Sequence sequence) {
+ public static int[] getMidiFileTypes(final Sequence sequence) {
+ Objects.requireNonNull(sequence);
List<MidiFileWriter> providers = getMidiFileWriters();
Set<Integer> allTypes = new HashSet<>();
@@ -903,8 +926,11 @@
* @param sequence the sequence for which file writing support is queried
* @return {@code true} if the file type is supported for this sequence,
* otherwise {@code false}
+ * @throws NullPointerException if {@code sequence} is {@code null}
*/
- public static boolean isFileTypeSupported(int fileType, Sequence sequence) {
+ public static boolean isFileTypeSupported(final int fileType,
+ final Sequence sequence) {
+ Objects.requireNonNull(sequence);
List<MidiFileWriter> providers = getMidiFileWriters();
@@ -929,10 +955,15 @@
* @throws IOException if an I/O exception occurs
* @throws IllegalArgumentException if the file format is not supported by
* the system
+ * @throws NullPointerException if {@code in} or {@code out} are
+ * {@code null}
* @see #isFileTypeSupported(int, Sequence)
* @see #getMidiFileTypes(Sequence)
*/
- public static int write(Sequence in, int fileType, OutputStream out) throws IOException {
+ public static int write(final Sequence in, final int fileType,
+ final OutputStream out) throws IOException {
+ Objects.requireNonNull(in);
+ Objects.requireNonNull(out);
List<MidiFileWriter> providers = getMidiFileWriters();
//$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
@@ -963,10 +994,15 @@
* @throws IOException if an I/O exception occurs
* @throws IllegalArgumentException if the file type is not supported by the
* system
+ * @throws NullPointerException if {@code in} or {@code out} are
+ * {@code null}
* @see #isFileTypeSupported(int, Sequence)
* @see #getMidiFileTypes(Sequence)
*/
- public static int write(Sequence in, int type, File out) throws IOException {
+ public static int write(final Sequence in, final int type, final File out)
+ throws IOException {
+ Objects.requireNonNull(in);
+ Objects.requireNonNull(out);
List<MidiFileWriter> providers = getMidiFileWriters();
//$$fb 2002-04-17: Fix for 4635287: Standard MidiFileWriter cannot write empty Sequences
--- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
package javax.sound.midi.spi;
import java.util.Arrays;
+import java.util.Objects;
import javax.sound.midi.MidiDevice;
@@ -46,8 +47,10 @@
* is queried
* @return {@code true} if the specified device is supported, otherwise
* {@code false}
+ * @throws NullPointerException if {@code info} is {@code null}
*/
public boolean isDeviceSupported(final MidiDevice.Info info) {
+ Objects.requireNonNull(info);
return Arrays.asList(getDeviceInfo()).contains(info);
}
@@ -67,6 +70,7 @@
* @throws IllegalArgumentException if the info object specified does not
* match the info object for a device supported by this
* {@code MidiDeviceProvider}
+ * @throws NullPointerException if {@code info} is {@code null}
*/
public abstract MidiDevice getDevice(MidiDevice.Info info);
}
--- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileReader.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileReader.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -60,6 +60,7 @@
* @throws InvalidMidiDataException if the stream does not point to valid
* MIDI file data recognized by the system
* @throws IOException if an I/O exception occurs
+ * @throws NullPointerException if {@code stream} is {@code null}
* @see InputStream#markSupported
* @see InputStream#mark
*/
@@ -76,6 +77,7 @@
* @throws InvalidMidiDataException if the URL does not point to valid MIDI
* file data recognized by the system
* @throws IOException if an I/O exception occurs
+ * @throws NullPointerException if {@code url} is {@code null}
*/
public abstract MidiFileFormat getMidiFileFormat(URL url)
throws InvalidMidiDataException, IOException;
@@ -90,6 +92,7 @@
* @throws InvalidMidiDataException if the {@code File} does not point to
* valid MIDI file data recognized by the system
* @throws IOException if an I/O exception occurs
+ * @throws NullPointerException if {@code file} is {@code null}
*/
public abstract MidiFileFormat getMidiFileFormat(File file)
throws InvalidMidiDataException, IOException;
@@ -110,6 +113,7 @@
* @throws InvalidMidiDataException if the stream does not point to valid
* MIDI file data recognized by the system
* @throws IOException if an I/O exception occurs
+ * @throws NullPointerException if {@code stream} is {@code null}
* @see InputStream#markSupported
* @see InputStream#mark
*/
@@ -126,6 +130,7 @@
* @throws InvalidMidiDataException if the URL does not point to valid MIDI
* file data recognized by the system
* @throws IOException if an I/O exception occurs
+ * @throws NullPointerException if {@code url} is {@code null}
*/
public abstract Sequence getSequence(URL url)
throws InvalidMidiDataException, IOException;
@@ -141,6 +146,7 @@
* @throws InvalidMidiDataException if the {@code File} does not point to
* valid MIDI file data recognized by the system
* @throws IOException if an I/O exception occurs
+ * @throws NullPointerException if {@code file} is {@code null}
*/
public abstract Sequence getSequence(File file)
throws InvalidMidiDataException, IOException;
--- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileWriter.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiFileWriter.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -58,6 +58,7 @@
* queried
* @return array of file types. If no file types are supported, returns an
* array of length 0.
+ * @throws NullPointerException if {@code sequence} is {@code null}
*/
public abstract int[] getMidiFileTypes(Sequence sequence);
@@ -88,6 +89,7 @@
* @param sequence the sequence for which file writing support is queried
* @return {@code true} if the file type is supported for this sequence,
* otherwise {@code false}
+ * @throws NullPointerException if {@code sequence} is {@code null}
*/
public boolean isFileTypeSupported(int fileType, Sequence sequence) {
@@ -111,6 +113,8 @@
* @throws IOException if an I/O exception occurs
* @throws IllegalArgumentException if the file type is not supported by
* this file writer
+ * @throws NullPointerException if {@code in} or {@code out} are
+ * {@code null}
* @see #isFileTypeSupported(int, Sequence)
* @see #getMidiFileTypes(Sequence)
*/
@@ -129,6 +133,8 @@
* @throws IOException if an I/O exception occurs
* @throws IllegalArgumentException if the file type is not supported by
* this file writer
+ * @throws NullPointerException if {@code in} or {@code out} are
+ * {@code null}
* @see #isFileTypeSupported(int, Sequence)
* @see #getMidiFileTypes(Sequence)
*/
--- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/SoundbankReader.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/SoundbankReader.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -52,6 +52,7 @@
* @throws InvalidMidiDataException if the URL does not point to valid MIDI
* soundbank data recognized by this soundbank reader
* @throws IOException if an I/O error occurs
+ * @throws NullPointerException if {@code url} is {@code null}
*/
public abstract Soundbank getSoundbank(URL url)
throws InvalidMidiDataException, IOException;
@@ -64,6 +65,7 @@
* @throws InvalidMidiDataException if the stream does not point to valid
* MIDI soundbank data recognized by this soundbank reader
* @throws IOException if an I/O error occurs
+ * @throws NullPointerException if {@code stream} is {@code null}
*/
public abstract Soundbank getSoundbank(InputStream stream)
throws InvalidMidiDataException, IOException;
@@ -76,6 +78,7 @@
* @throws InvalidMidiDataException if the file does not point to valid MIDI
* soundbank data recognized by this soundbank reader
* @throws IOException if an I/O error occurs
+ * @throws NullPointerException if {@code file} is {@code null}
*/
public abstract Soundbank getSoundbank(File file)
throws InvalidMidiDataException, IOException;
--- a/jdk/src/java.desktop/share/classes/javax/swing/JComponent.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/swing/JComponent.java Thu Dec 24 10:34:31 2015 -0800
@@ -618,6 +618,7 @@
* @return the {@code ComponentUI} object that renders this component
* @since 1.9
*/
+ @Transient
public ComponentUI getUI() {
return ui;
}
--- a/jdk/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/swing/filechooser/FileSystemView.java Thu Dec 24 10:34:31 2015 -0800
@@ -663,7 +663,9 @@
if(newFolder.exists()) {
throw new IOException("Directory already exists:" + newFolder.getAbsolutePath());
} else {
- newFolder.mkdirs();
+ if(!newFolder.mkdirs()) {
+ throw new IOException(newFolder.getAbsolutePath());
+ }
}
return newFolder;
@@ -773,7 +775,9 @@
if(newFolder.exists()) {
throw new IOException("Directory already exists:" + newFolder.getAbsolutePath());
} else {
- newFolder.mkdirs();
+ if(!newFolder.mkdirs()) {
+ throw new IOException(newFolder.getAbsolutePath());
+ }
}
return newFolder;
@@ -842,9 +846,10 @@
if(newFolder.exists()) {
throw new IOException("Directory already exists:" + newFolder.getAbsolutePath());
} else {
- newFolder.mkdirs();
+ if(!newFolder.mkdirs()) {
+ throw new IOException(newFolder.getAbsolutePath());
+ }
}
-
return newFolder;
}
--- a/jdk/src/java.desktop/share/classes/javax/swing/text/AbstractDocument.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/swing/text/AbstractDocument.java Thu Dec 24 10:34:31 2015 -0800
@@ -36,6 +36,7 @@
import sun.font.BidiUtils;
import sun.swing.SwingUtilities2;
+import sun.swing.text.UndoableEditLockSupport;
/**
* An implementation of the document interface to serve as a
@@ -275,6 +276,11 @@
* @see EventListenerList
*/
protected void fireUndoableEditUpdate(UndoableEditEvent e) {
+ if (e.getEdit() instanceof DefaultDocumentEvent) {
+ e = new UndoableEditEvent(e.getSource(),
+ new DefaultDocumentEventUndoableWrapper(
+ (DefaultDocumentEvent)e.getEdit()));
+ }
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
@@ -2952,6 +2958,88 @@
}
+ static class DefaultDocumentEventUndoableWrapper implements
+ UndoableEdit, UndoableEditLockSupport
+ {
+ final DefaultDocumentEvent dde;
+ public DefaultDocumentEventUndoableWrapper(DefaultDocumentEvent dde) {
+ this.dde = dde;
+ }
+
+ @Override
+ public void undo() throws CannotUndoException {
+ dde.undo();
+ }
+
+ @Override
+ public boolean canUndo() {
+ return dde.canUndo();
+ }
+
+ @Override
+ public void redo() throws CannotRedoException {
+ dde.redo();
+ }
+
+ @Override
+ public boolean canRedo() {
+ return dde.canRedo();
+ }
+
+ @Override
+ public void die() {
+ dde.die();
+ }
+
+ @Override
+ public boolean addEdit(UndoableEdit anEdit) {
+ return dde.addEdit(anEdit);
+ }
+
+ @Override
+ public boolean replaceEdit(UndoableEdit anEdit) {
+ return dde.replaceEdit(anEdit);
+ }
+
+ @Override
+ public boolean isSignificant() {
+ return dde.isSignificant();
+ }
+
+ @Override
+ public String getPresentationName() {
+ return dde.getPresentationName();
+ }
+
+ @Override
+ public String getUndoPresentationName() {
+ return dde.getUndoPresentationName();
+ }
+
+ @Override
+ public String getRedoPresentationName() {
+ return dde.getRedoPresentationName();
+ }
+
+ /**
+ * {@inheritDoc}
+ * @since 1.9
+ */
+ @Override
+ public void lockEdit() {
+ ((AbstractDocument)dde.getDocument()).writeLock();
+ }
+
+ /**
+ * {@inheritDoc}
+ * @since 1.9
+ */
+ @Override
+ public void unlockEdit() {
+ ((AbstractDocument)dde.getDocument()).writeUnlock();
+ }
+ }
+
/**
* This event used when firing document changes while Undo/Redo
* operations. It just wraps DefaultDocumentEvent and delegates
--- a/jdk/src/java.desktop/share/classes/javax/swing/undo/UndoManager.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/javax/swing/undo/UndoManager.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@
import javax.swing.event.*;
import javax.swing.UIManager;
import java.util.*;
+import sun.swing.text.UndoableEditLockSupport;
/**
* {@code UndoManager} manages a list of {@code UndoableEdits},
@@ -134,6 +135,11 @@
*/
@SuppressWarnings("serial") // Same-version serialization only
public class UndoManager extends CompoundEdit implements UndoableEditListener {
+ private enum Action {
+ UNDO,
+ REDO,
+ ANY
+ }
int indexOfNextAdd;
int limit;
@@ -369,13 +375,8 @@
* @throws CannotRedoException if one of the edits throws
* <code>CannotRedoException</code>
*/
- public synchronized void undoOrRedo() throws CannotRedoException,
- CannotUndoException {
- if (indexOfNextAdd == edits.size()) {
- undo();
- } else {
- redo();
- }
+ public void undoOrRedo() throws CannotRedoException, CannotUndoException {
+ tryUndoOrRedo(Action.ANY);
}
/**
@@ -407,16 +408,8 @@
* @see #canUndo
* @see #editToBeUndone
*/
- public synchronized void undo() throws CannotUndoException {
- if (inProgress) {
- UndoableEdit edit = editToBeUndone();
- if (edit == null) {
- throw new CannotUndoException();
- }
- undoTo(edit);
- } else {
- super.undo();
- }
+ public void undo() throws CannotUndoException {
+ tryUndoOrRedo(Action.UNDO);
}
/**
@@ -452,16 +445,90 @@
* @see #canRedo
* @see #editToBeRedone
*/
- public synchronized void redo() throws CannotRedoException {
- if (inProgress) {
- UndoableEdit edit = editToBeRedone();
- if (edit == null) {
- throw new CannotRedoException();
+ public void redo() throws CannotRedoException {
+ tryUndoOrRedo(Action.REDO);
+ }
+
+ private void tryUndoOrRedo(Action action) {
+ UndoableEditLockSupport lockSupport = null;
+ boolean undo;
+ synchronized (this) {
+ if (action == Action.ANY) {
+ undo = indexOfNextAdd == edits.size();
+ } else {
+ undo = action == Action.UNDO;
+ }
+ if (inProgress) {
+ UndoableEdit edit = undo ? editToBeUndone() : editToBeRedone();
+ if (edit == null) {
+ throw undo ? new CannotUndoException() :
+ new CannotRedoException();
+ }
+ lockSupport = getEditLockSupport(edit);
+ if (lockSupport == null) {
+ if (undo) {
+ undoTo(edit);
+ } else {
+ redoTo(edit);
+ }
+ return;
+ }
+ } else {
+ if (undo) {
+ super.undo();
+ } else {
+ super.redo();
+ }
+ return;
}
- redoTo(edit);
- } else {
- super.redo();
}
+ // the edit synchronization is required
+ while (true) {
+ lockSupport.lockEdit();
+ UndoableEditLockSupport editLockSupport = null;
+ try {
+ synchronized (this) {
+ if (action == Action.ANY) {
+ undo = indexOfNextAdd == edits.size();
+ }
+ if (inProgress) {
+ UndoableEdit edit = undo ? editToBeUndone() :
+ editToBeRedone();
+ if (edit == null) {
+ throw undo ? new CannotUndoException() :
+ new CannotRedoException();
+ }
+ editLockSupport = getEditLockSupport(edit);
+ if (editLockSupport == null ||
+ editLockSupport == lockSupport) {
+ if (undo) {
+ undoTo(edit);
+ } else {
+ redoTo(edit);
+ }
+ return;
+ }
+ } else {
+ if (undo) {
+ super.undo();
+ } else {
+ super.redo();
+ }
+ return;
+ }
+ }
+ } finally {
+ if (lockSupport != null) {
+ lockSupport.unlockEdit();
+ }
+ lockSupport = editLockSupport;
+ }
+ }
+ }
+
+ private UndoableEditLockSupport getEditLockSupport(UndoableEdit anEdit) {
+ return anEdit instanceof UndoableEditLockSupport ?
+ (UndoableEditLockSupport)anEdit : null;
}
/**
--- a/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/awt/image/SunVolatileImage.java Thu Dec 24 10:34:31 2015 -0800
@@ -70,6 +70,10 @@
{
this.comp = comp;
this.graphicsConfig = graphicsConfig;
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("Width (" + width + ")" +
+ " and height (" + height + ") cannot be <= 0");
+ }
this.width = width;
this.height = height;
this.forcedAccelSurfaceType = accType;
--- a/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/font/StandardGlyphVector.java Thu Dec 24 10:34:31 2015 -0800
@@ -445,13 +445,19 @@
}
public void setGlyphPosition(int ix, Point2D pos) {
+ if (ix < 0 || ix > glyphs.length) {
+ throw new IndexOutOfBoundsException("ix = " + ix);
+ }
+
initPositions();
int ix2 = ix << 1;
positions[ix2] = (float)pos.getX();
positions[ix2 + 1] = (float)pos.getY();
- clearCaches(ix);
+ if (ix < glyphs.length) {
+ clearCaches(ix);
+ }
addFlags(FLAG_HAS_POSITION_ADJUSTMENTS);
}
--- a/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/font/TrueTypeFont.java Thu Dec 24 10:34:31 2015 -0800
@@ -176,6 +176,13 @@
private String localeFamilyName;
private String localeFullName;
+ public TrueTypeFont(String platname, Object nativeNames, int fIndex,
+ boolean javaRasterizer)
+ throws FontFormatException
+ {
+ this(platname, nativeNames, fIndex, javaRasterizer, true);
+ }
+
/**
* - does basic verification of the file
* - reads the header table for this font (within a collection)
@@ -186,14 +193,17 @@
* or fails verification, or there's no usable cmap
*/
public TrueTypeFont(String platname, Object nativeNames, int fIndex,
- boolean javaRasterizer)
+ boolean javaRasterizer, boolean useFilePool)
throws FontFormatException {
super(platname, nativeNames);
useJavaRasterizer = javaRasterizer;
fontRank = Font2D.TTF_RANK;
try {
- verify();
+ verify(useFilePool);
init(fIndex);
+ if (!useFilePool) {
+ close();
+ }
} catch (Throwable t) {
close();
if (t instanceof FontFormatException) {
@@ -280,6 +290,10 @@
}
+ private synchronized FileChannel open() throws FontFormatException {
+ return open(true);
+ }
+
/* This is intended to be called, and the returned value used,
* from within a block synchronized on this font object.
* ie the channel returned may be nulled out at any time by "close()"
@@ -287,7 +301,8 @@
* Deadlock warning: FontManager.addToPool(..) acquires a global lock,
* which means nested locks may be in effect.
*/
- private synchronized FileChannel open() throws FontFormatException {
+ private synchronized FileChannel open(boolean usePool)
+ throws FontFormatException {
if (disposerRecord.channel == null) {
if (FontUtilities.isLogging()) {
FontUtilities.getLogger().info("open TTF: " + platName);
@@ -306,9 +321,11 @@
});
disposerRecord.channel = raf.getChannel();
fileSize = (int)disposerRecord.channel.size();
- FontManager fm = FontManagerFactory.getInstance();
- if (fm instanceof SunFontManager) {
- ((SunFontManager) fm).addToPool(this);
+ if (usePool) {
+ FontManager fm = FontManagerFactory.getInstance();
+ if (fm instanceof SunFontManager) {
+ ((SunFontManager) fm).addToPool(this);
+ }
}
} catch (NullPointerException e) {
close();
@@ -492,8 +509,8 @@
}
}
- private void verify() throws FontFormatException {
- open();
+ private void verify(boolean usePool) throws FontFormatException {
+ open(usePool);
}
/* sizes, in bytes, of TT/TTC header records */
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ArrayCache.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ArrayCache.java Thu Dec 24 10:34:31 2015 -0800
@@ -166,18 +166,31 @@
* @return new array size
*/
public static int getNewSize(final int curSize, final int needSize) {
+ // check if needSize is negative or integer overflow:
+ if (needSize < 0) {
+ // hard overflow failure - we can't even accommodate
+ // new items without overflowing
+ throw new ArrayIndexOutOfBoundsException(
+ "array exceeds maximum capacity !");
+ }
+ assert curSize >= 0;
final int initial = (curSize & MASK_CLR_1);
int size;
if (initial > THRESHOLD_ARRAY_SIZE) {
size = initial + (initial >> 1); // x(3/2)
} else {
- size = (initial) << 1; // x2
+ size = (initial << 1); // x2
}
// ensure the new size is >= needed size:
if (size < needSize) {
- // align to 4096:
+ // align to 4096 (may overflow):
size = ((needSize >> 12) + 1) << 12;
}
+ // check integer overflow:
+ if (size < 0) {
+ // resize to maximum capacity:
+ size = Integer.MAX_VALUE;
+ }
return size;
}
@@ -188,26 +201,29 @@
* @return new array size
*/
public static long getNewLargeSize(final long curSize, final long needSize) {
+ // check if needSize is negative or integer overflow:
+ if ((needSize >> 31L) != 0L) {
+ // hard overflow failure - we can't even accommodate
+ // new items without overflowing
+ throw new ArrayIndexOutOfBoundsException(
+ "array exceeds maximum capacity !");
+ }
+ assert curSize >= 0L;
long size;
if (curSize > THRESHOLD_HUGE_ARRAY_SIZE) {
size = curSize + (curSize >> 2L); // x(5/4)
} else if (curSize > THRESHOLD_LARGE_ARRAY_SIZE) {
size = curSize + (curSize >> 1L); // x(3/2)
} else {
- size = curSize << 1L; // x2
+ size = (curSize << 1L); // x2
}
// ensure the new size is >= needed size:
if (size < needSize) {
// align to 4096:
- size = ((needSize >> 12) + 1) << 12;
+ size = ((needSize >> 12L) + 1L) << 12L;
}
- if (size >= Integer.MAX_VALUE) {
- if (curSize >= Integer.MAX_VALUE) {
- // hard overflow failure - we can't even accommodate
- // new items without overflowing
- throw new ArrayIndexOutOfBoundsException(
- "array exceeds maximum capacity !");
- }
+ // check integer overflow:
+ if (size > Integer.MAX_VALUE) {
// resize to maximum capacity:
size = Integer.MAX_VALUE;
}
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/ByteArrayCache.java Thu Dec 24 10:34:31 2015 -0800
@@ -74,7 +74,7 @@
void putDirtyArray(final byte[] array, final int length) {
if (length != arraySize) {
if (doChecks) {
- System.out.println("ArrayCache: bad length = " + length);
+ MarlinUtils.logInfo("ArrayCache: bad length = " + length);
}
return;
}
@@ -98,7 +98,7 @@
{
if (length != arraySize) {
if (doChecks) {
- System.out.println("ArrayCache: bad length = " + length);
+ MarlinUtils.logInfo("ArrayCache: bad length = " + length);
}
return;
}
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatArrayCache.java Thu Dec 24 10:34:31 2015 -0800
@@ -75,7 +75,7 @@
void putDirtyArray(final float[] array, final int length) {
if (length != arraySize) {
if (doChecks) {
- System.out.println("ArrayCache: bad length = " + length);
+ MarlinUtils.logInfo("ArrayCache: bad length = " + length);
}
return;
}
@@ -99,7 +99,7 @@
{
if (length != arraySize) {
if (doChecks) {
- System.out.println("ArrayCache: bad length = " + length);
+ MarlinUtils.logInfo("ArrayCache: bad length = " + length);
}
return;
}
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatMath.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/FloatMath.java Thu Dec 24 10:34:31 2015 -0800
@@ -24,8 +24,8 @@
*/
package sun.java2d.marlin;
-import sun.misc.DoubleConsts;
-import sun.misc.FloatConsts;
+import jdk.internal.math.DoubleConsts;
+import jdk.internal.math.FloatConsts;
/**
* Faster Math ceil / floor routines derived from StrictMath
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/IntArrayCache.java Thu Dec 24 10:34:31 2015 -0800
@@ -74,7 +74,7 @@
void putDirtyArray(final int[] array, final int length) {
if (length != arraySize) {
if (doChecks) {
- System.out.println("ArrayCache: bad length = " + length);
+ MarlinUtils.logInfo("ArrayCache: bad length = " + length);
}
return;
}
@@ -98,7 +98,7 @@
{
if (length != arraySize) {
if (doChecks) {
- System.out.println("ArrayCache: bad length = " + length);
+ MarlinUtils.logInfo("ArrayCache: bad length = " + length);
}
return;
}
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinConst.java Thu Dec 24 10:34:31 2015 -0800
@@ -30,8 +30,8 @@
*/
interface MarlinConst {
// enable Logs (logger or stdout)
- static final boolean enableLogs = false;
- // enable Logger
+ static final boolean enableLogs = MarlinProperties.isLoggingEnabled();
+ // use Logger instead of stdout
static final boolean useLogger = enableLogs && MarlinProperties.isUseLogger();
// log new RendererContext
@@ -47,9 +47,10 @@
static final boolean doStats = enableLogs && MarlinProperties.isDoStats();
// do monitors
// disabled to reduce byte-code size a bit...
- static final boolean doMonitors = enableLogs && false; // MarlinProperties.isDoMonitors();
+ static final boolean doMonitors = false;
+// static final boolean doMonitors = enableLogs && MarlinProperties.isDoMonitors();
// do checks
- static final boolean doChecks = false; // MarlinProperties.isDoChecks();
+ static final boolean doChecks = enableLogs && MarlinProperties.isDoChecks();
// do AA range checks: disable when algorithm / code is stable
static final boolean DO_AA_RANGE_CHECK = false;
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinProperties.java Thu Dec 24 10:34:31 2015 -0800
@@ -136,6 +136,10 @@
// logging parameters
+ public static boolean isLoggingEnabled() {
+ return getBoolean("sun.java2d.renderer.log", "false");
+ }
+
public static boolean isUseLogger() {
return getBoolean("sun.java2d.renderer.useLogger", "false");
}
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/MarlinUtils.java Thu Dec 24 10:34:31 2015 -0800
@@ -27,12 +27,12 @@
public final class MarlinUtils {
- // TODO: use sun.util.logging.PlatformLogger once in JDK9
- private static final java.util.logging.Logger log;
+ // Marlin logger
+ private static final sun.util.logging.PlatformLogger log;
static {
if (MarlinConst.useLogger) {
- log = java.util.logging.Logger.getLogger("sun.java2d.marlin");
+ log = sun.util.logging.PlatformLogger.getLogger("sun.java2d.marlin");
} else {
log = null;
}
@@ -53,25 +53,11 @@
public static void logException(final String msg, final Throwable th) {
if (MarlinConst.useLogger) {
-// log.warning(msg, th);
- log.log(java.util.logging.Level.WARNING, msg, th);
+ log.warning(msg, th);
} else if (MarlinConst.enableLogs) {
System.out.print("WARNING: ");
System.out.println(msg);
th.printStackTrace(System.err);
}
}
-
- // Returns the caller's class and method's name; best effort
- // if cannot infer, return the logger's name.
- static String getCallerInfo(String className) {
- String sourceClassName = null;
- String sourceMethodName = null;
-
- if (sourceClassName != null) {
- return sourceClassName + " " + sourceMethodName;
- } else {
- return "unknown";
- }
- }
}
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Renderer.java Thu Dec 24 10:34:31 2015 -0800
@@ -629,6 +629,13 @@
}
if (edgeMinY != Float.POSITIVE_INFINITY) {
+ // if context is maked as DIRTY:
+ if (rdrCtx.dirty) {
+ // may happen if an exception if thrown in the pipeline processing:
+ // clear completely buckets arrays:
+ buckets_minY = 0;
+ buckets_maxY = boundsMaxY - boundsMinY;
+ }
// clear used part
if (edgeBuckets == edgeBuckets_initial) {
// fill only used part
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererContext.java Thu Dec 24 10:34:31 2015 -0800
@@ -31,7 +31,6 @@
import java.util.concurrent.atomic.AtomicInteger;
import static sun.java2d.marlin.ArrayCache.*;
import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator;
-import static sun.java2d.marlin.MarlinUtils.getCallerInfo;
import static sun.java2d.marlin.MarlinUtils.logInfo;
/**
@@ -39,7 +38,6 @@
*/
final class RendererContext implements MarlinConst {
- private static final String className = RendererContext.class.getName();
// RendererContext creation counter
private static final AtomicInteger contextCount = new AtomicInteger(1);
// RendererContext statistics
@@ -214,8 +212,7 @@
}
if (doLogOverSize) {
- logInfo("getDirtyByteArray[oversize]: length=\t" + length
- + "\tfrom=\t" + getCallerInfo(className));
+ logInfo("getDirtyByteArray[oversize]: length=\t" + length);
}
return new byte[length];
@@ -254,7 +251,7 @@
if (doLogWidenArray) {
logInfo("widenDirtyByteArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
- + needSize + "\tfrom=\t" + getCallerInfo(className));
+ + needSize);
}
return res;
}
@@ -275,8 +272,7 @@
}
if (doLogOverSize) {
- logInfo("getIntArray[oversize]: length=\t" + length + "\tfrom=\t"
- + getCallerInfo(className));
+ logInfo("getIntArray[oversize]: length=\t" + length);
}
return new int[length];
@@ -306,7 +302,7 @@
if (doLogWidenArray) {
logInfo("widenIntArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
- + needSize + "\tfrom=\t" + getCallerInfo(className));
+ + needSize);
}
return res;
}
@@ -338,8 +334,7 @@
}
if (doLogOverSize) {
- logInfo("getDirtyIntArray[oversize]: length=\t" + length
- + "\tfrom=\t" + getCallerInfo(className));
+ logInfo("getDirtyIntArray[oversize]: length=\t" + length);
}
return new int[length];
@@ -369,7 +364,7 @@
if (doLogWidenArray) {
logInfo("widenDirtyIntArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
- + needSize + "\tfrom=\t" + getCallerInfo(className));
+ + needSize);
}
return res;
}
@@ -399,8 +394,7 @@
}
if (doLogOverSize) {
- logInfo("getDirtyFloatArray[oversize]: length=\t" + length
- + "\tfrom=\t" + getCallerInfo(className));
+ logInfo("getDirtyFloatArray[oversize]: length=\t" + length);
}
return new float[length];
@@ -430,7 +424,7 @@
if (doLogWidenArray) {
logInfo("widenDirtyFloatArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
- + needSize + "\tfrom=\t" + getCallerInfo(className));
+ + needSize);
}
return res;
}
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/RendererStats.java Thu Dec 24 10:34:31 2015 -0800
@@ -25,6 +25,8 @@
package sun.java2d.marlin;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -32,6 +34,7 @@
import sun.java2d.marlin.stats.Histogram;
import sun.java2d.marlin.stats.Monitor;
import sun.java2d.marlin.stats.StatLong;
+import sun.awt.util.ThreadGroupUtils;
/**
* This class gathers global rendering statistics for debugging purposes only
@@ -237,22 +240,33 @@
private RendererStats() {
super();
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- dump();
- }
- });
+ AccessController.doPrivileged(
+ (PrivilegedAction<Void>) () -> {
+ final Thread hook = new Thread(
+ ThreadGroupUtils.getRootThreadGroup(),
+ new Runnable() {
+ @Override
+ public void run() {
+ dump();
+ }
+ },
+ "MarlinStatsHook"
+ );
+ hook.setContextClassLoader(null);
+ Runtime.getRuntime().addShutdownHook(hook);
- if (useDumpThread) {
- final Timer statTimer = new Timer("RendererStats");
- statTimer.scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- dump();
+ if (useDumpThread) {
+ final Timer statTimer = new Timer("RendererStats");
+ statTimer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ dump();
+ }
+ }, statDump, statDump);
}
- }, statDump, statDump);
- }
+ return null;
+ }
+ );
}
void dump() {
--- a/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java Thu Dec 24 10:34:31 2015 -0800
@@ -1265,14 +1265,15 @@
}
private void ensureSpace(final int n) {
- if (end + n > curves.length) {
+ // use substraction to avoid integer overflow:
+ if (curves.length - end < n) {
if (doStats) {
RendererContext.stats.stat_array_stroker_polystack_curves
.add(end + n);
}
curves = rdrCtx.widenDirtyFloatArray(curves, end, end + n);
}
- if (numCurves + 1 > curveTypes.length) {
+ if (curveTypes.length <= numCurves) {
if (doStats) {
RendererContext.stats.stat_array_stroker_polystack_curveTypes
.add(numCurves + 1);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.desktop/share/classes/sun/swing/text/UndoableEditLockSupport.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.swing.text;
+
+import javax.swing.undo.UndoableEdit;
+
+/**
+ * UndoableEdit support for undo/redo actions synchronization
+ * @since 1.9
+ */
+public interface UndoableEditLockSupport extends UndoableEdit {
+ /**
+ * lock the UndoableEdit for threadsafe undo/redo
+ */
+ void lockEdit();
+
+ /**
+ * unlock the UndoableEdit
+ */
+ void unlockEdit();
+}
--- a/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.cpp Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.cpp Thu Dec 24 10:34:31 2015 -0800
@@ -67,12 +67,6 @@
};
-const void *FontInstanceAdapter::getFontTable(LETag tableTag) const
-{
- size_t ignored = 0;
- return getFontTable(tableTag, ignored);
-}
-
static const LETag cacheMap[LAYOUTCACHE_ENTRIES] = {
GPOS_TAG, GDEF_TAG, GSUB_TAG, MORT_TAG, MORX_TAG, KERN_TAG
};
--- a/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.h Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/native/libfontmanager/FontInstanceAdapter.h Thu Dec 24 10:34:31 2015 -0800
@@ -85,7 +85,6 @@
// tables are cached with the native font scaler data
// only supports gsub, gpos, gdef, mort tables at present
- virtual const void *getFontTable(LETag tableTag) const;
virtual const void *getFontTable(LETag tableTag, size_t &len) const;
virtual void *getKernPairs() const {
--- a/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/native/libfontmanager/HBShaper.c Thu Dec 24 10:34:31 2015 -0800
@@ -80,15 +80,18 @@
float scale = 1.0f/64.0f;
unsigned int* glyphs;
float* positions;
+ int initialCount, glyphArrayLen, posArrayLen, maxGlyphs, storeadv;
+ unsigned int* indices;
+ jarray glyphArray, posArray, inxArray;
if (!init_JNI_IDs(env)) {
return 0;
}
- int initialCount = (*env)->GetIntField(env, gvdata, gvdCountFID);
- jarray glyphArray =
+ initialCount = (*env)->GetIntField(env, gvdata, gvdCountFID);
+ glyphArray =
(jarray)(*env)->GetObjectField(env, gvdata, gvdGlyphsFID);
- jarray posArray =
+ posArray =
(jarray)(*env)->GetObjectField(env, gvdata, gvdPositionsFID);
if (glyphArray == NULL || posArray == NULL)
@@ -101,9 +104,9 @@
// and re-invokes layout. I suppose this is expected to be rare
// because at least in a single threaded case there should be
// re-use of the same container, but it is a little wasteful/distateful.
- int glyphArrayLen = (*env)->GetArrayLength(env, glyphArray);
- int posArrayLen = (*env)->GetArrayLength(env, posArray);
- int maxGlyphs = glyphCount + initialCount;
+ glyphArrayLen = (*env)->GetArrayLength(env, glyphArray);
+ posArrayLen = (*env)->GetArrayLength(env, posArray);
+ maxGlyphs = glyphCount + initialCount;
if ((maxGlyphs > glyphArrayLen) ||
(maxGlyphs * 2 + 2 > posArrayLen))
{
@@ -125,7 +128,7 @@
x += glyphPos[i].x_advance * scale;
y += glyphPos[i].y_advance * scale;
}
- int storeadv = initialCount+glyphCount;
+ storeadv = initialCount+glyphCount;
// The final slot in the positions array is important
// because when the GlyphVector is created from this
// data it determines the overall advance of the glyphvector
@@ -138,11 +141,10 @@
(*env)->ReleasePrimitiveArrayCritical(env, glyphArray, glyphs, 0);
(*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, 0);
putFloat(env, startPt,positions[(storeadv*2)],positions[(storeadv*2)+1] );
- jarray inxArray =
+ inxArray =
(jarray)(*env)->GetObjectField(env, gvdata, gvdIndicesFID);
- unsigned int* indices =
+ indices =
(unsigned int*)(*env)->GetPrimitiveArrayCritical(env, inxArray, NULL);
- int prevCluster = -1;
for (i = 0; i < glyphCount; i++) {
int cluster = glyphInfo[i].cluster;
if (direction == HB_DIRECTION_LTR) {
--- a/jdk/src/java.desktop/share/native/libfontmanager/freetypeScaler.c Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/native/libfontmanager/freetypeScaler.c Thu Dec 24 10:34:31 2015 -0800
@@ -258,7 +258,7 @@
scalerInfo->fontData,
scalerInfo->fontDataLength);
if (bBuffer != NULL) {
- (*env)->CallObjectMethod(env, font2D,
+ (*env)->CallVoidMethod(env, font2D,
sunFontIDs.readFileMID, bBuffer);
error = FT_New_Memory_Face(scalerInfo->library,
--- a/jdk/src/java.desktop/share/native/libfontmanager/layout/LEFontInstance.h Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/native/libfontmanager/layout/LEFontInstance.h Thu Dec 24 10:34:31 2015 -0800
@@ -181,28 +181,6 @@
*
* Subclasses which represent composite fonts should always return <code>NULL</code>.
*
- * Note that implementing this function does not allow for range checking.
- * Subclasses that desire the safety of range checking must implement the
- * variation which has a length parameter.
- *
- * @param tableTag - the four byte table tag. (e.g. 'cmap')
- *
- * @return the address of the table in memory, or <code>NULL</code>
- * if the table doesn't exist.
- *
- * @stable ICU 2.8
- */
- virtual const void *getFontTable(LETag tableTag) const = 0;
-
- /**
- * This method reads a table from the font. Note that in general,
- * it only makes sense to call this method on an <code>LEFontInstance</code>
- * which represents a physical font - i.e. one which has been returned by
- * <code>getSubFont()</code>. This is because each subfont in a composite font
- * will have different tables, and there's no way to know which subfont to access.
- *
- * Subclasses which represent composite fonts should always return <code>NULL</code>.
- *
* This version sets a length, for range checking.
* Note that range checking can only be accomplished if this function is
* implemented in subclasses.
@@ -213,7 +191,7 @@
* if the table doesn't exist.
* @internal
*/
- virtual const void* getFontTable(LETag tableTag, size_t &length) const { length=-1; return getFontTable(tableTag); } /* -1 = unknown length */
+ virtual const void* getFontTable(LETag tableTag, size_t &length) const = 0;
virtual void *getKernPairs() const = 0;
virtual void setKernPairs(void *pairs) const = 0;
--- a/jdk/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/share/native/libjavajpeg/imageioJPEG.c Thu Dec 24 10:34:31 2015 -0800
@@ -1610,6 +1610,7 @@
int ret;
int h_samp0, h_samp1, h_samp2;
int v_samp0, v_samp1, v_samp2;
+ int cid0, cid1, cid2;
jboolean retval = JNI_FALSE;
imageIODataPtr data = (imageIODataPtr)jlong_to_ptr(ptr);
j_decompress_ptr cinfo;
@@ -1711,17 +1712,15 @@
}
} else if (!cinfo->saw_JFIF_marker && !IS_EXIF(cinfo)) {
/*
- * IJG assumes all unidentified 3-channels are YCbCr.
- * We assume that only if the second two channels are
- * subsampled (either horizontally or vertically). If not,
- * we assume RGB.
- *
- * 4776576: Some digital cameras output YCbCr JPEG images
- * that do not contain a JFIF APP0 marker but are only
- * vertically subsampled (no horizontal subsampling).
- * We should only assume this is RGB data if the subsampling
- * factors for the second two channels are the same as the
- * first (check both horizontal and vertical factors).
+ * In the absence of certain markers, IJG has interpreted
+ * component id's of [1,2,3] as meaning YCbCr.We follow that
+ * interpretation, which is additionally described in the Image
+ * I/O JPEG metadata spec.If that condition is not met here the
+ * next step will be to examine the subsampling factors, if
+ * there is any difference in subsampling factors we also assume
+ * YCbCr, only if both horizontal and vertical subsampling
+ * is same we assume JPEG color space as RGB.
+ * This is also described in the Image I/O JPEG metadata spec.
*/
h_samp0 = cinfo->comp_info[0].h_samp_factor;
h_samp1 = cinfo->comp_info[1].h_samp_factor;
@@ -1731,8 +1730,13 @@
v_samp1 = cinfo->comp_info[1].v_samp_factor;
v_samp2 = cinfo->comp_info[2].v_samp_factor;
- if ((h_samp1 == h_samp0) && (h_samp2 == h_samp0) &&
- (v_samp1 == v_samp0) && (v_samp2 == v_samp0))
+ cid0 = cinfo->comp_info[0].component_id;
+ cid1 = cinfo->comp_info[1].component_id;
+ cid2 = cinfo->comp_info[2].component_id;
+
+ if ((!(cid0 == 1 && cid1 == 2 && cid2 == 3)) &&
+ ((h_samp1 == h_samp0) && (h_samp2 == h_samp0) &&
+ (v_samp1 == v_samp0) && (v_samp2 == v_samp0)))
{
cinfo->jpeg_color_space = JCS_RGB;
/* output is already RGB, so it stays the same */
--- a/jdk/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/unix/classes/sun/awt/X11/XTrayIconPeer.java Thu Dec 24 10:34:31 2015 -0800
@@ -413,6 +413,7 @@
void addListeners() {
canvas.addMouseListener(eventProxy);
canvas.addMouseMotionListener(eventProxy);
+ eframe.addMouseListener(eventProxy);
}
long getWindow() {
--- a/jdk/src/java.desktop/unix/classes/sun/print/IPPPrintService.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/unix/classes/sun/print/IPPPrintService.java Thu Dec 24 10:34:31 2015 -0800
@@ -926,7 +926,10 @@
return copyflavors;
}
}
- return null;
+ DocFlavor[] flavor = new DocFlavor[2];
+ flavor[0] = DocFlavor.SERVICE_FORMATTED.PAGEABLE;
+ flavor[1] = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
+ return flavor;
}
--- a/jdk/src/java.desktop/unix/native/common/java2d/x11/X11SurfaceData.c Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/unix/native/common/java2d/x11/X11SurfaceData.c Thu Dec 24 10:34:31 2015 -0800
@@ -438,6 +438,15 @@
xsdo->drawable = drawable;
xsdo->isPixmap = JNI_FALSE;
} else {
+ /*
+ * width , height must be nonzero otherwise XCreatePixmap
+ * generates BadValue in error_handler
+ */
+ if (width <= 0 || height <= 0) {
+ JNU_ThrowOutOfMemoryError(env,
+ "Can't create offscreen surface");
+ return JNI_FALSE;
+ }
xsdo->isPixmap = JNI_TRUE;
/* REMIND: workaround for bug 4420220 on pgx32 boards:
don't use DGA with pixmaps unless USE_DGA_PIXMAPS is set.
--- a/jdk/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/java.desktop/windows/classes/sun/awt/Win32FontManager.java Thu Dec 24 10:34:31 2015 -0800
@@ -61,7 +61,7 @@
* enumerate (allow direct use) of EUDC fonts.
*/
eudcFont = new TrueTypeFont(eudcFile, null, 0,
- true);
+ true, false);
} catch (FontFormatException e) {
}
}
--- a/jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/AWTEventMonitor.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/jdk.accessibility/share/classes/com/sun/java/accessibility/util/AWTEventMonitor.java Thu Dec 24 10:34:31 2015 -0800
@@ -48,8 +48,6 @@
@jdk.Exported
public class AWTEventMonitor {
- static private boolean runningOnJDK1_4 = false;
-
/**
* The current component with keyboard focus.
*
@@ -638,15 +636,9 @@
* @see AWTEventMonitor
*/
public AWTEventsListener() {
- String version = System.getProperty("java.version");
- if (version != null) {
- runningOnJDK1_4 = (version.compareTo("1.4") >= 0);
- }
initializeIntrospection();
installListeners();
- if (runningOnJDK1_4) {
- MenuSelectionManager.defaultManager().addChangeListener(this);
- }
+ MenuSelectionManager.defaultManager().addChangeListener(this);
EventQueueMonitor.addTopLevelWindowListener(this);
}
@@ -848,15 +840,7 @@
case EventID.FOCUS:
c.removeFocusListener(this);
c.addFocusListener(this);
-
- if (runningOnJDK1_4) {
- processFocusGained();
-
- } else { // not runningOnJDK1_4
- if ((c != componentWithFocus_private) && c.hasFocus()) {
- componentWithFocus_private = c;
- }
- }
+ processFocusGained();
break;
case EventID.ITEM:
--- a/jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/src/jdk.accessibility/windows/classes/com/sun/java/accessibility/internal/AccessBridge.java Thu Dec 24 10:34:31 2015 -0800
@@ -140,10 +140,6 @@
// initialize AccessibleRole map
initAccessibleRoleMap();
- // determine which version of the JDK is running
- String version = getJavaVersionProperty();
- debugString("JDK version = "+version);
-
// initialize the methods that map HWNDs and Java top-level
// windows
initHWNDcalls();
@@ -215,9 +211,7 @@
} catch (Exception e) {}
/*
- Build the extendedVirtualNameSearchRoles array list. I chose this method
- because some of the Accessible Roles that need to be added to it are not
- available in all versions of the J2SE that we want to support.
+ Build the extendedVirtualNameSearchRoles array list.
*/
extendedVirtualNameSearchRoles.add (AccessibleRole.COMBO_BOX);
try {
@@ -5919,7 +5913,7 @@
*/
AccessibleRole.UNKNOWN,
- // These roles are only available in JDK 1.4
+ // These roles are available since JDK 1.4
/**
* A STATUS_BAR is an simple component that can contain
--- a/jdk/test/ProblemList.txt Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/ProblemList.txt Thu Dec 24 10:34:31 2015 -0800
@@ -310,8 +310,6 @@
# jdk_imageio
-javax/imageio/plugins/shared/WriteAfterAbort.java generic-all
-
############################################################################
# jdk_swing
--- a/jdk/test/java/awt/List/SetFontTest/SetFontTest.html Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/List/SetFontTest/SetFontTest.html Thu Dec 24 10:34:31 2015 -0800
@@ -38,6 +38,6 @@
<p> See the dialog box (usually in upper left corner) for instructions</p>
-<APPLET CODE="SetFontTest.class" WIDTH=200 HEIGHT=200></APPLET>
+<APPLET CODE="SetFontTest.class" WIDTH=200 HEIGHT=220></APPLET>
</body>
</html>
--- a/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.html Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-<!--
- @test
- @bug 6176814
- @summary Metalworks frame maximizes after the move
- @author Andrei.Dmitriev.Com area=Event
- @run applet MaximizedFrameTest.html
- -->
-<head>
-<title> </title>
-</head>
-<body>
-
-<h1>bug 6176814<br>Bug ID: 6176814 </h1>
-
-<p> This is an AUTOMATIC test, simply wait for completion </p>
-
-<APPLET CODE="MaximizedFrameTest.class" WIDTH=200 HEIGHT=200></APPLET>
-</body>
-</html>
--- a/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/Mouse/MaximizedFrameTest/MaximizedFrameTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2015 Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,91 +22,149 @@
*/
/*
- test
- @bug 6176814
- @summary Metalworks frame maximizes after the move
- @author Andrei.Dmitriev area=Event
- @run applet MaximizedFrameTest.html
-*/
+ @test
+ @bug 6176814 8132766
+ @summary Metalworks frame maximizes after the move
+ @run main MaximizedFrameTest
+ */
-import java.applet.Applet;
-import javax.swing.*;
-import java.awt.event.*;
-import java.awt.*;
+import java.awt.AWTException;
+import java.awt.Component;
+import java.awt.Point;
+import java.awt.Robot;
+import java.awt.event.InputEvent;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JFrame;
+import javax.swing.JLayeredPane;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
-public class MaximizedFrameTest extends Applet
-{
- final int ITERATIONS_COUNT = 20;
- Robot robot;
- Point framePosition;
- Point newFrameLocation;
- JFrame frame;
- Rectangle gcBounds;
- public static Object LOCK = new Object();
+public class MaximizedFrameTest {
+
+ final static int ITERATIONS_COUNT = 5;
+ private static JFrame frame;
+ private static Point tempMousePosition;
+ private static Component titleComponent;
- public void init()
- {
- String[] instructions =
- {
- "This is an AUTOMATIC test",
- "simply wait until it is done"
- };
+ public void init() {
JFrame.setDefaultLookAndFeelDecorated(true);
+
+ try {
+ UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
+ } catch (ClassNotFoundException | InstantiationException |
+ IllegalAccessException | UnsupportedLookAndFeelException ex) {
+ throw new RuntimeException("Test Failed. MetalLookAndFeel not set "
+ + "for frame");
+ }
+
frame = new JFrame("JFrame Maximization Test");
frame.pack();
frame.setSize(450, 260);
- }//End init()
+ frame.setVisible(true);
+ }
+
+ public void getTitleComponent() throws Exception {
+
+ SwingUtilities.invokeAndWait(new Runnable() {
+
+ @Override
+ public void run() {
+ JLayeredPane lPane = frame.getLayeredPane();
+ boolean titleFound = false;
+
+ for (int j = 0; j < lPane.getComponentsInLayer(
+ JLayeredPane.FRAME_CONTENT_LAYER.intValue()).length; j++) {
+
+ titleComponent = lPane.getComponentsInLayer(
+ JLayeredPane.FRAME_CONTENT_LAYER.intValue())[j];
- public void start ()
- {
- frame.setVisible(true);
- validate();
- JLayeredPane lPane = frame.getLayeredPane();
- // System.out.println("JFrame's LayeredPane " + lPane );
- Component titleComponent = null;
- boolean titleFound = false;
- for (int j=0; j < lPane.getComponentsInLayer(JLayeredPane.FRAME_CONTENT_LAYER.intValue()).length; j++){
- titleComponent = lPane.getComponentsInLayer(JLayeredPane.FRAME_CONTENT_LAYER.intValue())[j];
- if (titleComponent.getClass().getName().equals("javax.swing.plaf.metal.MetalTitlePane")){
- titleFound = true;
- break;
+ if (titleComponent.getClass().getName().equals(
+ "javax.swing.plaf.metal.MetalTitlePane")) {
+
+ titleFound = true;
+ break;
+ }
+ }
+
+ if (!titleFound) {
+ try {
+ dispose();
+ } catch (Exception ex) {
+ Logger.getLogger(MaximizedFrameTest.class.getName())
+ .log(Level.SEVERE, null, ex);
+ }
+ throw new RuntimeException("Test Failed. Unable to "
+ + "determine title component");
+ }
}
- }
- if ( !titleFound ){
- throw new RuntimeException("Test Failed. Unable to determine title's size.");
- }
- //--------------------------------
- // it is sufficient to get maximized Frame only once.
- Point tempMousePosition;
- framePosition = frame.getLocationOnScreen();
+ });
+ }
+
+ public void doMaximizeFrameTest() throws Exception {
+
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ Point framePosition = frame.getLocationOnScreen();
+
+ tempMousePosition = new Point(framePosition.x
+ + frame.getWidth() / 2, framePosition.y
+ + titleComponent.getHeight() / 2);
+ }
+ });
+
try {
- robot = new Robot();
- tempMousePosition = new Point(framePosition.x +
- frame.getWidth()/2,
- framePosition.y +
- titleComponent.getHeight()/2);
+ Robot robot = new Robot();
robot.mouseMove(tempMousePosition.x, tempMousePosition.y);
- for (int iteration=0; iteration < ITERATIONS_COUNT; iteration++){
+ robot.waitForIdle();
+
+ for (int iteration = 0; iteration < ITERATIONS_COUNT; iteration++) {
robot.mousePress(InputEvent.BUTTON1_MASK);
- gcBounds =
- GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0].getConfigurations()[0].getBounds();
- //Moving a mouse pointer less than a few pixels
- //leads to rising a double click event.
- //We have to use exceeded the AWT_MULTICLICK_SMUDGE
- //const value (which is 4 by default on GNOME) to test that.
+ robot.waitForIdle();
+
+ // Moving a mouse pointer less than a few pixels
+ // leads to rising a double click event.
+ // We have to use exceeded the AWT_MULTICLICK_SMUDGE
+ // const value (which is 4 by default on GNOME) to test that.
tempMousePosition.x += 5;
robot.mouseMove(tempMousePosition.x, tempMousePosition.y);
- robot.delay(70);
+ robot.waitForIdle();
robot.mouseRelease(InputEvent.BUTTON1_MASK);
- if ( frame.getExtendedState() != 0 ){
- throw new RuntimeException ("Test failed. JFrame was maximized. ExtendedState is : "+frame.getExtendedState());
+ robot.waitForIdle();
+
+ if (frame.getExtendedState() != 0) {
+ dispose();
+ throw new RuntimeException("Test failed. JFrame was "
+ + "maximized. ExtendedState is : "
+ + frame.getExtendedState());
}
- robot.delay(500);
- } //for iteration
+ }
+ } catch (AWTException e) {
+ dispose();
+ throw new RuntimeException("Test Failed. AWTException thrown.");
+ }
+ System.out.println("Test passed.");
+ }
- }catch(AWTException e) {
- throw new RuntimeException("Test Failed. AWTException thrown.");
+ private void dispose() throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ if (null != frame) {
+ frame.dispose();
+ }
}
- System.out.println("Test passed.");
- }// start()
-}// class
+ });
+ }
+
+ public static void main(String[] args) throws Exception {
+
+ MaximizedFrameTest maximizedFrameTest = new MaximizedFrameTest();
+ maximizedFrameTest.init();
+ maximizedFrameTest.getTitleComponent();
+ maximizedFrameTest.doMaximizeFrameTest();
+ maximizedFrameTest.dispose();
+ }
+}
--- a/jdk/test/java/awt/TextArea/TextAreaEditing/TextAreaEditing.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TextArea/TextAreaEditing/TextAreaEditing.java Thu Dec 24 10:34:31 2015 -0800
@@ -23,16 +23,23 @@
/*
@test
- @bug 8040322
+ @bug 8040322 8060137
+ @library ../../regtesthelpers
+ @build Util
@summary Test TextArea APIs replaceRange, insert, append & setText
@run main TextAreaEditing
*/
import java.awt.Frame;
+import java.awt.Robot;
import java.awt.TextArea;
+import java.awt.AWTException;
+import java.awt.event.KeyEvent;
+import test.java.awt.regtesthelpers.Util;
public class TextAreaEditing {
+ final static Robot robot = Util.createRobot();
private int testFailCount;
private boolean isTestFail;
private StringBuilder testFailMessage;
@@ -61,6 +68,7 @@
textArea.testReplaceRange();
textArea.testInsert();
textArea.testAppend();
+ textArea.testSetText();
textArea.checkFailures();
textArea.dispose();
}
@@ -119,6 +127,24 @@
checkTest("");
}
+ private void testSetText() {
+ textArea.setText(null);
+ textArea.requestFocus();
+ Util.clickOnComp(textArea, robot);
+ Util.waitForIdle(robot);
+ robot.keyPress(KeyEvent.VK_A);
+ robot.delay(5);
+ robot.keyRelease(KeyEvent.VK_A);
+ Util.waitForIdle(robot);
+ textArea.setText(null);
+ checkTest("");
+ textArea.setText("CaseSensitive");
+ checkTest("CaseSensitive");
+ textArea.setText("caseSensitive");
+ checkTest("caseSensitive");
+
+ }
+
private void checkTest(String str) {
if (str != null && !str.equals(textArea.getText())) {
testFailMessage.append("TestFail line : ");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/TextField/EOLTest/EOLTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ @test
+ @bug 8055197 7186036
+ @summary TextField should replace EOL character with space character
+ @run main EOLTest
+ */
+
+import java.awt.Frame;
+import java.awt.TextField;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+
+public class EOLTest {
+
+ private Frame mainFrame;
+ private TextField textField;
+ private String testStrEOL;
+ private boolean isTestFail;
+ private int testFailCount;
+ StringBuilder testFailMessage;
+ private String expectedString = "Row1 Row2 Row3";
+
+ public EOLTest() {
+ mainFrame = new Frame();
+ mainFrame.setSize(200, 200);
+ mainFrame.setVisible(true);
+ testFailMessage = new StringBuilder();
+ testStrEOL = "Row1" + System.lineSeparator() + "Row2\nRow3";
+ }
+
+ private void testConstructor1() {
+ textField = new TextField(testStrEOL);
+ textField.setSize(200, 100);
+ mainFrame.add(textField);
+ checkTest();
+ mainFrame.remove(textField);
+ }
+
+ private void testConstructor2() {
+ textField = new TextField(30);
+ textField.setSize(200, 100);
+ mainFrame.add(textField);
+ textField.setText(testStrEOL);
+ checkTest();
+ mainFrame.remove(textField);
+ }
+
+ private void testConstructor3() {
+ textField = new TextField(testStrEOL, 30);
+ textField.setSize(200, 100);
+ mainFrame.add(textField);
+ checkTest();
+ mainFrame.remove(textField);
+ }
+
+ private void testSetText() {
+ textField = new TextField();
+ textField.setSize(200, 100);
+ textField.setText(testStrEOL);
+ mainFrame.add(textField);
+ checkTest();
+ mainFrame.remove(textField);
+ }
+
+ private void testDeserialization() {
+ TextField textFieldToSerialize = new TextField(testStrEOL);
+ textFieldToSerialize.setSize(200, 100);
+ mainFrame.add(textFieldToSerialize);
+ try {
+ // Serialize TextField object "textFieldToSerialize".
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutput outStream = new ObjectOutputStream(baos);
+ outStream.writeObject(textFieldToSerialize);
+
+ // Search the text variable data through serialized object stream.
+ byte[] streamedBytes = baos.toByteArray();
+ int foundLoc = 0;
+ for (int i = 0; i < streamedBytes.length; ++i) {
+ if (streamedBytes[i] == expectedString.charAt(0)) {
+ foundLoc = i;
+ int j = 1;
+ for (; j < expectedString.length(); ++j) {
+ if (streamedBytes[i+j] != expectedString.charAt(j)) {
+ break;
+ }
+ }
+ if (j == expectedString.length()) {
+ break;
+ }
+ }
+ foundLoc = -1;
+ }
+
+ if (foundLoc == -1) {
+ // Could not find text data in serialized object stream.
+ throw new Exception("Could not find text data in serialized "
+ + "object stream.");
+ }
+ // Replace space character from serialized stream with
+ // EOL character for testing de-serialization.
+ String EOLChar = System.lineSeparator();
+ String newExpectedString = "";
+ for (int i = foundLoc, j = 0; j < expectedString.length(); ++i, ++j) {
+ newExpectedString += (char)(streamedBytes[i]);
+ if (streamedBytes[i] == ' ') {
+ int k = 0;
+ for (; k < EOLChar.length(); ++k) {
+ streamedBytes[i + k] = (byte) EOLChar.charAt(k);
+ }
+ i += k-1;
+ j += k-1;
+ }
+ }
+ // New line character varies with platform,
+ // ex. For windows '\r\n', for linux '\n'.
+ // While replacing space from serialized object stream, the length
+ // of EOL character will affect the expected string as well.
+ expectedString = newExpectedString;
+
+ // De-serialize TextField object stream.
+ ByteArrayInputStream bais = new ByteArrayInputStream(streamedBytes);
+ ObjectInput inStream = new ObjectInputStream(bais);
+ textField = (TextField) inStream.readObject();
+ } catch (Exception ex) {
+ // Serialization or De-serialization failed.
+ // Create textField with empty string to show failure.
+ ex.printStackTrace();
+ textField = new TextField();
+ }
+
+ checkTest();
+ mainFrame.remove(textFieldToSerialize);
+ }
+
+ private void checkTest() {
+ if (!textField.getText().equals(expectedString)) {
+ testFailMessage.append("TestFail line : ");
+ testFailMessage.append(Thread.currentThread().getStackTrace()[2].
+ getLineNumber());
+ testFailMessage.append(" TextField.getText() : \"");
+ testFailMessage.append(textField.getText());
+ testFailMessage.append("\" does not match expected string : \"");
+ testFailMessage.append(expectedString).append("\"");
+ testFailMessage.append(System.getProperty("line.separator"));
+ testFailCount++;
+ isTestFail = true;
+ }
+ }
+
+ private void checkFailures() {
+ if (isTestFail) {
+ testFailMessage.insert(0, "Test Fail count : " + testFailCount
+ + System.getProperty("line.separator"));
+ dispose();
+ throw new RuntimeException(testFailMessage.toString());
+ }
+ }
+
+ private void dispose() {
+ if (mainFrame != null) {
+ mainFrame.dispose();
+ }
+ }
+
+ public static void main(String[] args) {
+ EOLTest testEOL = new EOLTest();
+ testEOL.testConstructor1();
+ testEOL.testConstructor2();
+ testEOL.testConstructor3();
+ testEOL.testSetText();
+ testEOL.testDeserialization();
+ testEOL.checkFailures();
+ testEOL.dispose();
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/TextField/TextFieldEditing/TextFieldEditing.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ @test
+ @bug 8060137
+ @library ../../regtesthelpers
+ @build Util
+ @summary Test TextField setText API
+ @run main TextFieldEditing
+ */
+
+import java.awt.Frame;
+import java.awt.Robot;
+import java.awt.TextField;
+import java.awt.AWTException;
+import java.awt.event.KeyEvent;
+import test.java.awt.regtesthelpers.Util;
+
+public class TextFieldEditing {
+
+ final static Robot robot = Util.createRobot();
+ private int testFailCount;
+ private boolean isTestFail;
+ private StringBuilder testFailMessage;
+
+ private Frame mainFrame;
+ private TextField textField;
+
+ private TextFieldEditing() {
+ testFailMessage = new StringBuilder();
+ mainFrame = new Frame();
+ mainFrame.setSize(200, 200);
+
+ textField = new TextField();
+ mainFrame.add(textField);
+ mainFrame.setVisible(true);
+ }
+
+ private void dispose() {
+ if (mainFrame != null) {
+ mainFrame.dispose();
+ }
+ }
+
+ public static void main(String[] s) {
+ TextFieldEditing textField = new TextFieldEditing();
+ textField.testSetText();
+ textField.checkFailures();
+ textField.dispose();
+ }
+
+ private void testSetText() {
+ textField.setText(null);
+ textField.requestFocus();
+ Util.clickOnComp(textField, robot);
+ Util.waitForIdle(robot);
+ robot.keyPress(KeyEvent.VK_A);
+ robot.delay(5);
+ robot.keyRelease(KeyEvent.VK_A);
+ Util.waitForIdle(robot);
+ textField.setText(null);
+ checkTest("");
+ textField.setText("CaseSensitive");
+ checkTest("CaseSensitive");
+ textField.setText("caseSensitive");
+ checkTest("caseSensitive");
+ }
+
+ private void checkTest(String str) {
+ if (str != null && !str.equals(textField.getText())) {
+ testFailMessage.append("TestFail line : ");
+ testFailMessage.append(Thread.currentThread().getStackTrace()[2].
+ getLineNumber());
+ testFailMessage.append(" TextField string : \"");
+ testFailMessage.append(textField.getText());
+ testFailMessage.append("\" does not match expected string : \"");
+ testFailMessage.append(str).append("\"");
+ testFailMessage.append(System.getProperty("line.separator"));
+ testFailCount++;
+ isTestFail = true;
+ }
+ }
+
+ private void checkFailures() {
+ if (isTestFail) {
+ testFailMessage.insert(0, "Test Fail count : " + testFailCount
+ + System.getProperty("line.separator"));
+ dispose();
+ throw new RuntimeException(testFailMessage.toString());
+ }
+ }
+}
--- a/jdk/test/java/awt/TrayIcon/ActionCommand/ActionCommand.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/ActionCommand/ActionCommand.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -59,8 +59,11 @@
"and rerun test.");
} else if (System.getProperty("os.name").toLowerCase().startsWith("mac")){
isMacOS = true;
+ } else if (SystemTrayIconHelper.isOel7()) {
+ System.out.println("OEL 7 doesn't support double click in " +
+ "systray. Skipped");
+ return;
}
-
new ActionCommand().doTest();
}
}
--- a/jdk/test/java/awt/TrayIcon/ActionEventMask/ActionEventMask.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/ActionEventMask/ActionEventMask.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -66,6 +66,10 @@
} else {
if (System.getProperty("os.name").toLowerCase().startsWith("mac")) {
isMacOS = true;
+ } else if (SystemTrayIconHelper.isOel7()) {
+ System.out.println("OEL 7 doesn't support double click in " +
+ "systray. Skipped");
+ return;
}
new ActionEventMask().doTest();
}
--- a/jdk/test/java/awt/TrayIcon/ModalityTest/ModalityTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/ModalityTest/ModalityTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,6 +35,7 @@
*/
public class ModalityTest {
+ private static boolean isOEL7;
TrayIcon icon;
ExtendedRobot robot;
Dialog d;
@@ -80,7 +81,7 @@
"\"Always show all icons and notifications on the taskbar\" true " +
"to avoid this problem. Or change behavior only for Java SE tray " +
"icon and rerun test.");
-
+ isOEL7 = SystemTrayIconHelper.isOel7();
new ModalityTest().doTest();
}
}
@@ -225,6 +226,12 @@
Point iconPosition = SystemTrayIconHelper.getTrayIconLocation(icon);
if (iconPosition == null)
throw new RuntimeException("Unable to find the icon location!");
+ if (isOEL7) {
+ // close tray
+ robot.mouseMove(100,100);
+ robot.click(InputEvent.BUTTON1_MASK);
+ robot.waitForIdle(2000);
+ }
if (! d.isVisible())
throw new RuntimeException("FAIL: The modal dialog is not yet visible");
@@ -232,27 +239,35 @@
robot.mouseMove(iconPosition.x, iconPosition.y);
robot.waitForIdle(2000);
- SystemTrayIconHelper.doubleClick(robot);
+ if(!isOEL7) {
+ SystemTrayIconHelper.doubleClick(robot);
- if (! actionPerformed) {
- synchronized (actionLock) {
- try {
- actionLock.wait(3000);
- } catch (Exception e) {
+ if (!actionPerformed) {
+ synchronized (actionLock) {
+ try {
+ actionLock.wait(3000);
+ } catch (Exception e) {
+ }
}
}
+ if (!actionPerformed)
+ throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked");
}
- if (! actionPerformed)
- throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked");
for (int i = 0; i < buttonTypes.length; i++) {
mousePressed = false;
- robot.mousePress(buttonTypes[i]);
+ if(isOEL7) {
+ SystemTrayIconHelper.openTrayIfNeeded(robot);
+ robot.mouseMove(iconPosition.x, iconPosition.y);
+ robot.click(buttonTypes[i]);
+ } else {
+ robot.mousePress(buttonTypes[i]);
+ }
if (! mousePressed) {
synchronized (pressLock) {
try {
- pressLock.wait(3000);
+ pressLock.wait(6000);
} catch (Exception e) {
}
}
@@ -264,12 +279,18 @@
mouseReleased = false;
mouseClicked = false;
- robot.mouseRelease(buttonTypes[i]);
+ if(isOEL7) {
+ SystemTrayIconHelper.openTrayIfNeeded(robot);
+ robot.mouseMove(iconPosition.x, iconPosition.y);
+ robot.click(buttonTypes[i]);
+ } else {
+ robot.mouseRelease(buttonTypes[i]);
+ }
if (! mouseReleased) {
synchronized (releaseLock) {
try {
- releaseLock.wait(3000);
+ releaseLock.wait(6000);
} catch (Exception e) {
}
}
@@ -281,7 +302,7 @@
if (! mouseClicked) {
synchronized (clickLock) {
try {
- clickLock.wait(3000);
+ clickLock.wait(6000);
} catch (Exception e) {
}
}
@@ -290,13 +311,14 @@
throw new RuntimeException("FAIL: mouseClicked not triggered when " +
buttonNames[i] + " pressed & released");
}
+ if (!isOEL7) {
+ mouseMoved = false;
+ robot.mouseMove(iconPosition.x, iconPosition.y);
+ robot.glide(iconPosition.x + 100, iconPosition.y);
- mouseMoved = false;
- robot.mouseMove(iconPosition.x, iconPosition.y);
- robot.glide(iconPosition.x + 100, iconPosition.y);
-
- if (! mouseMoved)
- if (! SystemTrayIconHelper.skip(0) )
- throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon");
+ if (!mouseMoved)
+ if (!SystemTrayIconHelper.skip(0))
+ throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon");
+ }
}
}
--- a/jdk/test/java/awt/TrayIcon/MouseEventMask/MouseEventMaskTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/MouseEventMask/MouseEventMaskTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -71,6 +71,8 @@
"\"Always show all icons and notifications on the taskbar\" true " +
"to avoid this problem. Or change behavior only for Java SE tray " +
"icon and rerun test.");
+ } else if (SystemTrayIconHelper.isOel7()) {
+ return;
}
new MouseEventMaskTest().doTest();
}
--- a/jdk/test/java/awt/TrayIcon/MouseMovedTest/MouseMovedTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/MouseMovedTest/MouseMovedTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -31,7 +31,7 @@
* @summary Check for mouseMoved event for java.awt.TrayIcon
* @author Dmitriy Ermashov (dmitriy.ermashov@oracle.com)
* @library ../../../../lib/testlibrary
- * @build ExtendedRobot
+ * @build ExtendedRobot SystemTrayIconHelper
* @run main MouseMovedTest
*/
@@ -39,6 +39,14 @@
static volatile boolean moved;
public static void main(String[] args) throws Exception {
+ if (!SystemTray.isSupported()) {
+ return;
+ }
+
+ if (SystemTrayIconHelper.isOel7()) {
+ return;
+ }
+
moved = false;
TrayIcon icon = new TrayIcon(new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB), "Test icon");
--- a/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/FunctionalityCheck.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/FunctionalityCheck.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -51,6 +51,7 @@
boolean mouseReleased = false;
boolean mouseClicked = false;
boolean mouseMoved = false;
+ static boolean isOEL7;
static final int[] buttonTypes = {
InputEvent.BUTTON1_MASK,
@@ -69,6 +70,7 @@
System.out.println("SystemTray not supported on the platform under test. " +
"Marking the test passed");
} else {
+ isOEL7 = SystemTrayIconHelper.isOel7();
new FunctionalityCheck().doTest();
}
}
@@ -188,31 +190,44 @@
Point iconPosition = SystemTrayIconHelper.getTrayIconLocation(icon);
if (iconPosition == null)
throw new RuntimeException("Unable to find the icon location!");
+ if (isOEL7) {
+ // close tray
+ robot.mouseMove(100,100);
+ robot.click(InputEvent.BUTTON1_MASK);
+ robot.waitForIdle(2000);
+ }
robot.mouseMove(iconPosition.x, iconPosition.y);
- robot.waitForIdle(2000);
-
- SystemTrayIconHelper.doubleClick(robot);
+ robot.waitForIdle();
+ if(!isOEL7) {
+ SystemTrayIconHelper.doubleClick(robot);
- if (! actionPerformed) {
- synchronized (actionLock) {
- try {
- actionLock.wait(3000);
- } catch (Exception e) {
+ if (!actionPerformed) {
+ synchronized (actionLock) {
+ try {
+ actionLock.wait(3000);
+ } catch (Exception e) {
+ }
}
}
+ if (!actionPerformed)
+ throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked");
}
- if (! actionPerformed)
- throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked");
for (int i = 0; i < buttonTypes.length; i++) {
mousePressed = false;
- robot.mousePress(buttonTypes[i]);
+ if(isOEL7) {
+ SystemTrayIconHelper.openTrayIfNeeded(robot);
+ robot.mouseMove(iconPosition.x, iconPosition.y);
+ robot.click(buttonTypes[i]);
+ } else {
+ robot.mousePress(buttonTypes[i]);
+ }
if (! mousePressed) {
synchronized (pressLock) {
try {
- pressLock.wait(3000);
+ pressLock.wait(6000);
} catch (Exception e) {
}
}
@@ -224,12 +239,17 @@
mouseReleased = false;
mouseClicked = false;
- robot.mouseRelease(buttonTypes[i]);
-
+ if(isOEL7) {
+ SystemTrayIconHelper.openTrayIfNeeded(robot);
+ robot.mouseMove(iconPosition.x, iconPosition.y);
+ robot.click(buttonTypes[i]);
+ } else {
+ robot.mouseRelease(buttonTypes[i]);
+ }
if (! mouseReleased) {
synchronized (releaseLock) {
try {
- releaseLock.wait(3000);
+ releaseLock.wait(6000);
} catch (Exception e) {
}
}
@@ -242,7 +262,7 @@
if (! mouseClicked) {
synchronized (clickLock) {
try {
- clickLock.wait(3000);
+ clickLock.wait(6000);
} catch (Exception e) {
}
}
@@ -251,13 +271,14 @@
throw new RuntimeException("FAIL: mouseClicked not triggered when " +
buttonNames[i] + " pressed & released");
}
+ if(!isOEL7) {
+ mouseMoved = false;
+ robot.mouseMove(iconPosition.x + 100, iconPosition.y);
+ robot.glide(iconPosition.x, iconPosition.y);
- mouseMoved = false;
- robot.mouseMove(iconPosition.x + 100, iconPosition.y);
- robot.glide(iconPosition.x, iconPosition.y);
-
- if (! mouseMoved)
- if (! SystemTrayIconHelper.skip(0) )
- throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon");
+ if (!mouseMoved)
+ if (!SystemTrayIconHelper.skip(0))
+ throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon");
+ }
}
}
--- a/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/tray.policy Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/SecurityCheck/FunctionalityCheck/tray.policy Thu Dec 24 10:34:31 2015 -0800
@@ -5,6 +5,7 @@
permission java.util.PropertyPermission "resultsDir", "read";
permission java.util.PropertyPermission "user.home", "read";
permission java.util.PropertyPermission "os.name", "read";
+ permission java.util.PropertyPermission "os.version", "read";
permission java.awt.AWTPermission "accessEventQueue";
permission java.lang.RuntimePermission "setIO";
permission java.lang.RuntimePermission "accessDeclaredMembers";
@@ -17,5 +18,6 @@
permission java.util.PropertyPermission "java.class.path", "read";
permission java.awt.AWTPermission "readDisplayPixels";
permission java.awt.AWTPermission "watchMousePointer";
+
};
--- a/jdk/test/java/awt/TrayIcon/SystemTrayIconHelper.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/SystemTrayIconHelper.java Thu Dec 24 10:34:31 2015 -0800
@@ -66,7 +66,9 @@
for (int x = (int) (screenSize.getWidth()-width); x > 0; x--) {
for (int y = (int) (screenSize.getHeight()-height); y > (screenSize.getHeight()-50); y--) {
if (imagesEquals(((BufferedImage)icon.getImage()).getSubimage(0, 0, width, height), screen.getSubimage(x, y, width, height))) {
- return new Point(x+5, y+5);
+ Point point = new Point(x + 5, y + 5);
+ System.out.println("Icon location " + point);
+ return point;
}
}
}
@@ -91,6 +93,7 @@
point2d = (Point2D)m_getLocation.invoke(peer, new Object[]{model});
Point po = new Point((int)(point2d.getX()), (int)(point2d.getY()));
po.translate(10, -5);
+ System.out.println("Icon location " + po);
return po;
}catch(Exception e) {
e.printStackTrace();
@@ -101,12 +104,15 @@
// sun.awt.X11.XTrayIconPeer
Field f_peer = getField(java.awt.TrayIcon.class, "peer");
+ SystemTrayIconHelper.openTrayIfNeeded(robot);
+
Object peer = f_peer.get(icon);
Method m_getLOS = peer.getClass().getDeclaredMethod(
"getLocationOnScreen", new Class[]{});
m_getLOS.setAccessible(true);
Point point = (Point)m_getLOS.invoke(peer, new Object[]{});
point.translate(5, 5);
+ System.out.println("Icon location " + point);
return point;
} catch (Exception e) {
e.printStackTrace();
@@ -169,4 +175,38 @@
}
return false;
}
+
+ public static boolean openTrayIfNeeded(Robot robot) {
+ String sysv = System.getProperty("os.version");
+ System.out.println("System version is " + sysv);
+ //Additional step to raise the system try in Gnome 3 in OEL 7
+ if(isOel7()) {
+ System.out.println("OEL 7 detected");
+ GraphicsConfiguration gc = GraphicsEnvironment.
+ getLocalGraphicsEnvironment().getDefaultScreenDevice().
+ getDefaultConfiguration();
+ Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
+ if(insets.bottom > 0) {
+ Dimension screenSize = Toolkit.getDefaultToolkit()
+ .getScreenSize();
+ robot.mouseMove(screenSize.width - insets.bottom / 2,
+ screenSize.height - insets.bottom / 2);
+ robot.delay(50);
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ robot.delay(50);
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+ robot.waitForIdle();
+ robot.delay(1000);
+ System.out.println("Tray is opened");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isOel7() {
+ return System.getProperty("os.name").toLowerCase()
+ .contains("linux") && System.getProperty("os.version")
+ .toLowerCase().contains("el7");
+ }
}
--- a/jdk/test/java/awt/TrayIcon/TrayIconEventModifiers/TrayIconEventModifiersTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/TrayIconEventModifiers/TrayIconEventModifiersTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -121,6 +121,12 @@
};
}
+ if (SystemTrayIconHelper.isOel7()) {
+ System.out.println("OEL 7 doesn't support click modifiers in " +
+ "systray. Skipped");
+ return;
+ }
+
new TrayIconEventModifiersTest().doTest();
}
}
--- a/jdk/test/java/awt/TrayIcon/TrayIconEvents/TrayIconEventsTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/TrayIconEvents/TrayIconEventsTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,6 +37,7 @@
public class TrayIconEventsTest {
+ private static boolean isOEL7;
TrayIcon icon;
ExtendedRobot robot;
@@ -77,6 +78,7 @@
"\"Always show all icons and notifications on the taskbar\" true " +
"to avoid this problem. Or change behavior only for Java SE " +
"tray icon.");
+ isOEL7 = SystemTrayIconHelper.isOel7();
new TrayIconEventsTest().doTest();
}
}
@@ -195,31 +197,44 @@
Point iconPosition = SystemTrayIconHelper.getTrayIconLocation(icon);
if (iconPosition == null)
throw new RuntimeException("Unable to find the icon location!");
+ if (isOEL7) {
+ // close tray
+ robot.mouseMove(100,100);
+ robot.click(InputEvent.BUTTON1_MASK);
+ robot.waitForIdle(2000);
+ }
robot.mouseMove(iconPosition.x, iconPosition.y);
- robot.waitForIdle(2000);
-
- SystemTrayIconHelper.doubleClick(robot);
+ robot.waitForIdle();
+ if(!isOEL7) {
+ SystemTrayIconHelper.doubleClick(robot);
- if (! actionPerformed) {
- synchronized (actionLock) {
- try {
- actionLock.wait(10000);
- } catch (Exception e) {
+ if (!actionPerformed) {
+ synchronized (actionLock) {
+ try {
+ actionLock.wait(10000);
+ } catch (Exception e) {
+ }
}
}
+ if (!actionPerformed)
+ throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked");
}
- if (! actionPerformed)
- throw new RuntimeException("FAIL: ActionEvent not triggered when TrayIcon is double clicked");
for (int i = 0; i < buttonTypes.length; i++) {
mousePressed = false;
- robot.mousePress(buttonTypes[i]);
+ if(isOEL7) {
+ SystemTrayIconHelper.openTrayIfNeeded(robot);
+ robot.mouseMove(iconPosition.x, iconPosition.y);
+ robot.click(buttonTypes[i]);
+ } else {
+ robot.mousePress(buttonTypes[i]);
+ }
if (! mousePressed) {
synchronized (pressLock) {
try {
- pressLock.wait(3000);
+ pressLock.wait(6000);
} catch (Exception e) {
}
}
@@ -231,12 +246,18 @@
mouseReleased = false;
mouseClicked = false;
- robot.mouseRelease(buttonTypes[i]);
+ if(isOEL7) {
+ SystemTrayIconHelper.openTrayIfNeeded(robot);
+ robot.mouseMove(iconPosition.x, iconPosition.y);
+ robot.click(buttonTypes[i]);
+ } else {
+ robot.mouseRelease(buttonTypes[i]);
+ }
if (! mouseReleased) {
synchronized (releaseLock) {
try {
- releaseLock.wait(3000);
+ releaseLock.wait(6000);
} catch (Exception e) {
}
}
@@ -248,7 +269,7 @@
if (! mouseClicked) {
synchronized (clickLock) {
try {
- clickLock.wait(3000);
+ clickLock.wait(6000);
} catch (Exception e) {
}
}
@@ -258,12 +279,14 @@
buttonNames[i] + " pressed & released");
}
- mouseMoved = false;
- robot.mouseMove(iconPosition.x + 100, iconPosition.y);
- robot.glide(iconPosition.x, iconPosition.y);
+ if (!isOEL7) {
+ mouseMoved = false;
+ robot.mouseMove(iconPosition.x + 100, iconPosition.y);
+ robot.glide(iconPosition.x, iconPosition.y);
- if (! mouseMoved)
- if (! SystemTrayIconHelper.skip(0) )
- throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon");
+ if (!mouseMoved)
+ if (!SystemTrayIconHelper.skip(0))
+ throw new RuntimeException("FAIL: mouseMoved not triggered even when mouse moved over the icon");
+ }
}
}
--- a/jdk/test/java/awt/TrayIcon/TrayIconMouseTest/TrayIconMouseTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/TrayIconMouseTest/TrayIconMouseTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -66,6 +66,10 @@
} else {
if (System.getProperty("os.name").toLowerCase().startsWith("mac")) {
isMacOS = true;
+ } else if (SystemTrayIconHelper.isOel7()) {
+ System.out.println("OEL 7 doesn't support double click in " +
+ "systray. Skipped");
+ return;
}
new TrayIconMouseTest().doTest();
}
@@ -108,7 +112,7 @@
for (int i = 0; i < buttonTypes.length; i++) {
actionPerformed = false;
robot.click(buttonTypes[i]);
- robot.waitForIdle(2000);
+ robot.waitForIdle(6000);
if (isMacOS && actionPerformed && i == 2) {
@@ -155,7 +159,7 @@
if (! actionPerformed) {
synchronized (actionLock) {
try {
- actionLock.wait(3000);
+ actionLock.wait(6000);
} catch (Exception e) {
}
}
--- a/jdk/test/java/awt/TrayIcon/TrayIconPopup/TrayIconPopupTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/awt/TrayIcon/TrayIconPopup/TrayIconPopupTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -127,7 +127,7 @@
robot.mousePress(InputEvent.BUTTON3_MASK);
robot.delay(50);
robot.mouseRelease(InputEvent.BUTTON3_MASK);
- robot.delay(1000);
+ robot.delay(6000);
robot.mouseMove(window.getLocation().x + 10, window.getLocation().y + 10);
robot.mousePress(InputEvent.BUTTON3_MASK);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/font/GlyphVector/TestStandardGlyphVectorBug.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.awt.Font;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+
+/**
+ * @test
+ * @bug 7160052
+ * @run main TestStandardGlyphVectorBug
+ * @summary GlyphVector.setGlyphPosition should not throw an exception on valid input
+ */
+public class TestStandardGlyphVectorBug
+{
+ public static void main(String[] args)
+ {
+ Font defaultFont = new Font(null);
+ FontRenderContext defaultFrc = new FontRenderContext(new AffineTransform(),
+ true, true);
+ GlyphVector gv = defaultFont.createGlyphVector(defaultFrc, "test");
+
+ //this causes the bounds to be cached
+ //which is necessary to trigger the bug
+ gv.getGlyphLogicalBounds(0);
+
+ //this correctly gets the position of the overall advance
+ Point2D glyphPosition = gv.getGlyphPosition(gv.getNumGlyphs());
+
+ // this sets the position of the overall advance,
+ // but also incorrectly tries to clear the bounds cache
+ // of a specific glyph indexed by the glyphIndex parameter
+ // even if the glyphIndex represents the overall advance
+ // (i.e. if glyphIndex == getNumGlyphs())
+ gv.setGlyphPosition(gv.getNumGlyphs(), glyphPosition);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/image/VolatileImage/VolatileImageBug.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.image.VolatileImage;
+
+/**
+ * @test
+ * @bug 8140530
+ * @run main VolatileImageBug
+ * @summary Creating volatileimage(0,0) should throw IAE
+ */
+public class VolatileImageBug {
+ public static void main(String[] args) {
+
+ boolean iaeThrown = false;
+ GraphicsEnvironment ge = GraphicsEnvironment.
+ getLocalGraphicsEnvironment();
+ GraphicsConfiguration gc = ge.getDefaultScreenDevice().
+ getDefaultConfiguration();
+ try {
+ VolatileImage volatileImage = gc.createCompatibleVolatileImage(0, 0);
+ } catch (IllegalArgumentException iae) {
+ iaeThrown = true;
+ }
+ if (!iaeThrown) {
+ throw new RuntimeException ("IllegalArgumentException not thrown " +
+ "for createCompatibleVolatileImage(0,0)");
+ }
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/beans/XMLEncoder/javax_swing_JComponent.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.swing.JComponent;
+import javax.swing.plaf.ComponentUI;
+
+/*
+ * @test
+ * @bug 8131754
+ * @summary Tests JComponent encoding
+ */
+public final class javax_swing_JComponent extends AbstractTest<JComponent> {
+
+ public static void main(final String[] args) {
+ new javax_swing_JComponent().test(true);
+ }
+
+ protected JComponent getObject() {
+ return new SimpleJComponent();
+ }
+
+ protected JComponent getAnotherObject() {
+ return new CustomJComponent();
+ }
+
+ public static final class SimpleJComponent extends JComponent {
+
+ }
+
+ public static final class CustomJComponent extends JComponent {
+
+ public CustomJComponent() {
+ ui = new CustomUI();
+ }
+
+ @Override
+ public ComponentUI getUI() {
+ return ui;
+ }
+
+ @Override
+ public void setUI(final ComponentUI newUI) {
+ ui = newUI;
+ }
+ }
+
+ public static final class CustomUI extends ComponentUI {
+
+ public boolean getFlag() {
+ throw new Error();
+ }
+
+ public void setFlag(final boolean flag) {
+ throw new Error();
+ }
+ }
+}
--- a/jdk/test/java/lang/ProcessHandle/InfoTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/lang/ProcessHandle/InfoTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -49,7 +49,7 @@
/*
* @test
- * @bug 8077350 8081566 8081567 8098852
+ * @bug 8077350 8081566 8081567 8098852 8136597
* @build jdk.testlibrary.*
* @library /lib/testlibrary
* @summary Functions of ProcessHandle.Info
@@ -210,10 +210,12 @@
Assert.assertTrue(commandLine.contains(allArgs.get(i)),
"commandLine() must contain argument: " + allArgs.get(i));
}
- } else if (info.commandLine().isPresent()) {
+ } else if (info.commandLine().isPresent() &&
+ command.isPresent() &&
+ command.get().length() < info.commandLine().get().length()) {
// If we only have the commandLine() we can only do some basic checks...
String commandLine = info.commandLine().get();
- String javaExe = "java" + (Platform.isWindows() ? ".exe": "");
+ String javaExe = "java" + (Platform.isWindows() ? ".exe" : "");
int pos = commandLine.indexOf(javaExe);
Assert.assertTrue(pos > 0, "commandLine() should at least contain 'java'");
--- a/jdk/test/java/lang/ref/CleanerTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/lang/ref/CleanerTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -28,9 +28,9 @@
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Objects;
-import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -38,15 +38,21 @@
import jdk.internal.misc.CleanerImpl.WeakCleanable;
import jdk.internal.misc.CleanerImpl.SoftCleanable;
+import sun.hotspot.WhiteBox;
+
import org.testng.Assert;
import org.testng.TestNG;
import org.testng.annotations.Test;
/*
* @test
- * @library /lib/testlibrary
+ * @library /lib/testlibrary /test/lib
+ * @build sun.hotspot.WhiteBox
* @modules java.base/jdk.internal.misc
- * @run testng/othervm -Xmx4m CleanerTest
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run testng/othervm
+ * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
+ * -verbose:gc -Xmx4m CleanerTest
*/
@Test
@@ -54,6 +60,9 @@
// A common CleaningService used by the test for notifications
static final Cleaner COMMON = Cleaner.create();
+ // Access to WhiteBox utilities
+ static final WhiteBox whitebox = WhiteBox.getWhiteBox();
+
/**
* Test that sequences of the various actions on a Reference
* and on the Cleanable instance have the desired result.
@@ -265,50 +274,29 @@
}
/**
- * Check a set of semaphores having been released by cleanup handlers.
+ * Check a semaphore having been released by cleanup handler.
* Force a number of GC cycles to give the GC a chance to process
- * all the References and for the cleanup actions to be run.
+ * the Reference and for the cleanup action to be run.
*
- * @param semaphore a varargs list of Semaphores
- * @return true if all of the semaphores have at least 1 permit,
- * false otherwise.
+ * @param semaphore a Semaphore
+ * @return true if the semaphores has 1 permit, false otherwise.
*/
- static boolean checkCleaned(Semaphore... semaphore) {
- long[] cycles = new long[semaphore.length];
- long total = 0;
- for (int cycle = 0; cycle < 20; cycle++) {
- for (int i = 0; i < semaphore.length; i++) {
- long count = semaphore[i].availablePermits();
- if (count > 0 && cycles[i] == 0) {
- System.out.printf(" Cleanable[%d] cleaned in cycle: %d%n", i, cycle);
- cycles[i] = cycle;
- total += 1;
+ static boolean checkCleaned(Semaphore semaphore) {
+ int cycle = 0;
+ for (; cycle < 3; cycle++) {
+ try {
+ if (semaphore.tryAcquire(10L, TimeUnit.MILLISECONDS)) {
+ System.out.printf(" Cleanable cleaned in cycle: %d%n", cycle);
+ return true;
}
- }
-
- if (total == semaphore.length) {
- System.out.printf(" All cleanups done in cycle: %d, total: %d%n",
- cycle, total);
- for (int i = 0; i < semaphore.length; i++) {
- long count = semaphore[i].availablePermits();
- Assert.assertEquals(count, 1,
- "Cleanable invoked more than once, semaphore " + i);
- }
- return true; // all references freed
+ } catch (InterruptedException ie) {
+ // retry in outer loop
}
// Force GC
- memoryPressure();
+ whitebox.fullGC();
}
- // Not all objects have been cleaned
-
- for (int i = 0; i < semaphore.length; i++) {
- if (cycles[i] != 0) {
- System.out.printf(" Cleanable[%d] cleaned in cycle: %d%n", i, cycles[i]);
- } else {
- System.out.printf(" Cleanable[%d] not cleaned%n", i);
- }
- }
-
+ // Object has not been cleaned
+ System.out.printf(" Cleanable not cleaned%n");
return false; // Failing result
}
@@ -457,24 +445,6 @@
}
/**
- * MemoryPressure allocates memory to force a gc and to clear SoftReferences.
- */
- static void memoryPressure() {
- SoftReference<Object> soft = new SoftReference<>(new Object(), null);
- Vector<Object> root = new Vector<>();
- try {
- long free = 0;
- while (soft.get() != null) {
- long[] extra = new long[50_000];
- root.addElement(extra);
- }
- } catch (OutOfMemoryError mem) {
- // ignore
- root = null;
- }
- }
-
- /**
* CleanableCase encapsulates the objects used for a test.
* The reference to the object is not held directly,
* but in a Reference object that can be cleared.
--- a/jdk/test/java/net/URL/TestPort.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/net/URL/TestPort.java Thu Dec 24 10:34:31 2015 -0800
@@ -23,28 +23,33 @@
/*
* @test
- * @bug 4101492 4444213
+ * @bug 4101492 4444213 4906983
* @summary The java.net.URL constructor allows port < 0 and port > 65535
*/
import java.net.*;
public class TestPort {
- public static void main(String[] args) {
- URL url = null;
+ public static void main(String[] args) throws MalformedURLException {
+ // URLs are able to have port bigger than TCP-Port 65535 and
+ // to have no port (-1) at all.:
+ URL url = new URL("http","server",Integer.MAX_VALUE,"/path");
+ url = new URL("http://server:"+Integer.MAX_VALUE+"/path");
+ url = new URL("http://server/path");
+ url = new URL("http","server",-1,"/path");
+
try {
url = new URL("ftp", "java.sun.com", -20, "/pub/");
+ throw new RuntimeException("MalformedURLException not thrown!");
} catch (MalformedURLException e) {
- url = null;
+ // Everything fine. MalformedURLException expected
}
- if (url != null)
- throw new RuntimeException("MalformedURLException not thrown!");
+
try {
url = new URL("ftp://java.sun.com:-20/pub/");
+ throw new RuntimeException("MalformedURLException not thrown!");
} catch (MalformedURLException e) {
- url = null;
+ // Everything fine. MalformedURLException expected
}
- if (url != null)
- throw new RuntimeException("MalformedURLException not thrown!");
}
}
--- a/jdk/test/java/time/tck/java/time/TCKLocalDate.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/time/tck/java/time/TCKLocalDate.java Thu Dec 24 10:34:31 2015 -0800
@@ -2157,6 +2157,31 @@
}
//-----------------------------------------------------------------------
+ // toEpochSecond
+ //-----------------------------------------------------------------------
+ @DataProvider(name="epochSecond")
+ Object[][] provider_toEpochSecond() {
+ return new Object[][] {
+ {LocalDate.of(1858, 11, 17).toEpochSecond(LocalTime.MIDNIGHT, OFFSET_PONE), -3506720400L},
+ {LocalDate.of(1, 1, 1).toEpochSecond(LocalTime.NOON, OFFSET_PONE), -62135557200L},
+ {LocalDate.of(1995, 9, 27).toEpochSecond(LocalTime.of(5, 30), OFFSET_PTWO), 812172600L},
+ {LocalDate.of(1970, 1, 1).toEpochSecond(LocalTime.MIDNIGHT, OFFSET_MTWO), 7200L},
+ {LocalDate.of(-1, 12, 31).toEpochSecond(LocalTime.NOON, OFFSET_PONE), -62167266000L},
+ {LocalDate.of(1, 1, 1).toEpochSecond(LocalTime.MIDNIGHT, OFFSET_PONE),
+ Instant.ofEpochSecond(-62135600400L).getEpochSecond()},
+ {LocalDate.of(1995, 9, 27).toEpochSecond(LocalTime.NOON, OFFSET_PTWO),
+ Instant.ofEpochSecond(812196000L).getEpochSecond()},
+ {LocalDate.of(1995, 9, 27).toEpochSecond(LocalTime.of(5, 30), OFFSET_MTWO),
+ LocalDateTime.of(1995, 9, 27, 5, 30).toEpochSecond(OFFSET_MTWO)},
+ };
+ }
+
+ @Test(dataProvider="epochSecond")
+ public void test_toEpochSecond(long actual, long expected) {
+ assertEquals(actual, expected);
+ }
+
+ //-----------------------------------------------------------------------
// compareTo()
//-----------------------------------------------------------------------
@Test
--- a/jdk/test/java/time/tck/java/time/TCKLocalTime.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/time/tck/java/time/TCKLocalTime.java Thu Dec 24 10:34:31 2015 -0800
@@ -2433,6 +2433,32 @@
}
}
+ //-----------------------------------------------------------------------
+ // toEpochSecond()
+ //--------------------------------------------------------------------------
+ @DataProvider(name="epochSecond")
+ Object[][] provider__toEpochSecond() {
+ return new Object[][] {
+ {LocalTime.of(0, 0).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO), -7200L},
+ {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1965, 12, 31), OFFSET_PTWO), -126282600L},
+ {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1995, 5, 3), OFFSET_MTWO), 799507800L},
+ {LocalTime.of(0, 0).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO),
+ Instant.ofEpochSecond(-7200).getEpochSecond()},
+ {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1969, 12, 31), OFFSET_MTWO),
+ Instant.ofEpochSecond(-37800L).getEpochSecond()},
+ {LocalTime.of(11, 30).toEpochSecond(LocalDate.of(1970, 1, 1), OFFSET_PTWO),
+ LocalDateTime.of(1970, 1, 1, 11, 30).toEpochSecond(OFFSET_PTWO)},
+ };
+ }
+
+ @Test(dataProvider="epochSecond")
+ public void test_toEpochSecond(long actual, long expected) {
+ assertEquals(actual, expected);
+ }
+
+ //-----------------------------------------------------------------------
+ // toSecondOfDay_fromNanoOfDay_symmetry()
+ //-----------------------------------------------------------------------
@Test
public void test_toSecondOfDay_fromNanoOfDay_symmetry() {
LocalTime t = LocalTime.of(0, 0);
--- a/jdk/test/java/time/tck/java/time/TCKOffsetTime.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/time/tck/java/time/TCKOffsetTime.java Thu Dec 24 10:34:31 2015 -0800
@@ -134,6 +134,7 @@
private static final ZoneId ZONE_GAZA = ZoneId.of("Asia/Gaza");
private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1);
private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2);
+ private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2);
private static final LocalDate DATE = LocalDate.of(2008, 12, 3);
private OffsetTime TEST_11_30_59_500_PONE;
@@ -1149,6 +1150,29 @@
}
//-----------------------------------------------------------------------
+ // toEpochSecond()
+ //-----------------------------------------------------------------------
+ @DataProvider(name="epochSecond")
+ Object[][] provider_toEpochSecond() {
+ return new Object[][] {
+ {OffsetTime.of(0, 0, 0, 0, OFFSET_PTWO).toEpochSecond(LocalDate.of(1970, 1, 1)), -7200L},
+ {OffsetTime.of(11, 30, 0, 0, OFFSET_MTWO).toEpochSecond(LocalDate.of(1995, 9, 27)), 812208600L},
+ {OffsetTime.of(0, 0, 0, 0, OFFSET_PONE).toEpochSecond(LocalDate.of(1970, 1, 1)),
+ Instant.ofEpochSecond(-3600).getEpochSecond()},
+ {OffsetTime.of(11, 30, 0, 0, OFFSET_PTWO).toEpochSecond(LocalDate.of(1965, 12, 31)),
+ Instant.ofEpochSecond(-126282600L).getEpochSecond()},
+ {OffsetTime.of(11, 30, 0, 0, OFFSET_MTWO).toEpochSecond(LocalDate.of(1970, 1, 1)),
+ OffsetDateTime.of(LocalDate.of(1970, 1, 1), LocalTime.of(11, 30), OFFSET_MTWO)
+ .toEpochSecond()},
+ };
+ }
+
+ @Test(dataProvider="epochSecond")
+ public void test_toEpochSecond(long actual, long expected) {
+ assertEquals(actual, expected);
+ }
+
+ //-----------------------------------------------------------------------
// compareTo()
//-----------------------------------------------------------------------
@Test
--- a/jdk/test/java/util/Formatter/Basic-X.java.template Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/util/Formatter/Basic-X.java.template Thu Dec 24 10:34:31 2015 -0800
@@ -36,7 +36,7 @@
import java.text.DateFormatSymbols;
import java.util.*;
#if[double]
-import sun.misc.DoubleConsts;
+import jdk.internal.math.DoubleConsts;
#end[double]
import static java.util.Calendar.*;
--- a/jdk/test/java/util/Formatter/Basic.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/util/Formatter/Basic.java Thu Dec 24 10:34:31 2015 -0800
@@ -28,7 +28,7 @@
* 6344623 6369500 6534606 6282094 6286592 6476425 5063507 6469160 6476168
* 8059175
*
- * @modules java.base/sun.misc
+ * @modules java.base/jdk.internal.math
* @run shell/timeout=240 Basic.sh
*/
--- a/jdk/test/java/util/Formatter/BasicDouble.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/java/util/Formatter/BasicDouble.java Thu Dec 24 10:34:31 2015 -0800
@@ -36,7 +36,7 @@
import java.text.DateFormatSymbols;
import java.util.*;
-import sun.misc.DoubleConsts;
+import jdk.internal.math.DoubleConsts;
import static java.util.Calendar.*;
@@ -1174,6 +1174,10 @@
+
+
+
+
//---------------------------------------------------------------------
// %f - float, double, Double, BigDecimal
//---------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/imageio/plugins/jpeg/JpegImageColorSpaceTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8041501
+ * @summary Test verifies if there is no JFIF & EXIF header
+ * and sampling factor is same of JPEG image, then
+ * imageIO should not override colorspace determined
+ * in IJG library.
+ * @run main JpegImageColorSpaceTest
+ */
+
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+
+public class JpegImageColorSpaceTest {
+
+ public static void main(String args[]) throws Exception {
+
+ String fileName = "nomarkers.jpg";
+ String sep = System.getProperty("file.separator");
+ String dir = System.getProperty("test.src", ".");
+ String filePath = dir+sep+fileName;
+ System.out.println("Test file: " + filePath);
+ File imageFile = new File(filePath);
+
+ BufferedImage bufferedImage = ImageIO.read(imageFile);
+ int imageWidth = bufferedImage.getWidth();
+ int imageHeight = bufferedImage.getHeight();
+
+ for (int i = 0; i < imageWidth; i++) {
+ for(int j = 0; j < imageHeight; j++) {
+ /*
+ * Since image is white we check individual pixel values from
+ * BufferedImage to verify if ImageIO.read() is done with proper
+ * color space or not.
+ */
+ if (bufferedImage.getRGB(i, j) != Color.white.getRGB()) {
+ // color space is not proper
+ throw new RuntimeException("ColorSpace is not determined "
+ + "properly by ImageIO");
+ }
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/imageio/plugins/jpeg/JpegMetadataColorSpaceTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8074967
+ * @summary Test verifies if there is no JFIF & EXIF header
+ * and sampling factor is same of JPEG image, then
+ * JPEG colorspace should not be RGB.
+ * @run main JpegMetadataColorSpaceTest
+ */
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataFormatImpl;
+import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.stream.ImageInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+
+public class JpegMetadataColorSpaceTest {
+ public static void main(String[] args) throws IOException {
+ String fileName = "nomarkers.jpg";
+ String sep = System.getProperty("file.separator");
+ String dir = System.getProperty("test.src", ".");
+ String filePath = dir+sep+fileName;
+ System.out.println("Test file: " + filePath);
+ File file = new File(filePath);
+ ImageInputStream stream = ImageIO.createImageInputStream(file);
+ Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
+
+ if(readers.hasNext()) {
+ ImageReader reader = readers.next();
+ reader.setInput(stream);
+ IIOMetadata metadata = reader.getImageMetadata(0);
+
+ IIOMetadataNode standardTree = (IIOMetadataNode)
+ metadata.getAsTree
+ (IIOMetadataFormatImpl.standardMetadataFormatName);
+ IIOMetadataNode colorSpaceType = (IIOMetadataNode)
+ standardTree.getElementsByTagName("ColorSpaceType").item(0);
+ String colorSpaceName = colorSpaceType.getAttribute("name");
+ if(colorSpaceName.equals("RGB"))
+ throw new RuntimeException("Identified incorrect ColorSpace");
+ }
+ }
+}
Binary file jdk/test/javax/imageio/plugins/jpeg/nomarkers.jpg has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/imageio/plugins/png/PngForceStopWritingTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 6967419
+ * @summary Test verifies that when we force stop PNG writing to
+ * ImageOutputStream, it should not cause IndexOutOfBoundException.
+ * @run main PngForceStopWritingTest
+ */
+
+import java.awt.Color;
+import java.awt.GradientPaint;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.OutputStream;
+import javax.imageio.ImageIO;
+import javax.imageio.stream.ImageOutputStream;
+
+public class PngForceStopWritingTest {
+
+ public static void main(String[] args) throws IOException {
+
+ OutputStream outputStream = new NullOutputStream();
+ ImageOutputStream imageOutputStream =
+ ImageIO.createImageOutputStream(outputStream);
+ try {
+ ImageIO.write(createImage(2048),"PNG", imageOutputStream);
+ } catch (IOException e) {
+ imageOutputStream.close();
+ }
+ }
+
+ private static BufferedImage createImage(int size) {
+
+ BufferedImage image = new
+ BufferedImage(size, size, BufferedImage.TYPE_3BYTE_BGR);
+ Graphics2D g = image.createGraphics();
+ g.setPaint(new GradientPaint(0, 0, Color.blue, size, size, Color.red));
+ g.fillRect(0, 0, size, size);
+ g.dispose();
+ return image;
+ }
+
+ static class NullOutputStream extends OutputStream {
+ long count = 0;
+ @Override
+ public void write(int b) throws IOException {
+ count++;
+ if (count > 30000L) {
+ throw new IOException("Force stop image writing");
+ }
+ }
+ }
+}
--- a/jdk/test/javax/imageio/plugins/shared/WriteAfterAbort.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/javax/imageio/plugins/shared/WriteAfterAbort.java Thu Dec 24 10:34:31 2015 -0800
@@ -130,13 +130,25 @@
ImageWriterSpi.class, provider -> true, true);
// Validates all supported ImageWriters
+ int numFailures = 0;
while (iter.hasNext()) {
final WriteAfterAbort writeAfterAbort = new WriteAfterAbort();
final ImageWriter writer = iter.next().createWriterInstance();
System.out.println("ImageWriter = " + writer);
- writeAfterAbort.test(writer);
+ try {
+ writeAfterAbort.test(writer);
+ } catch (Exception e) {
+ System.err.println("Test failed for \""
+ + writer.getOriginatingProvider().getFormatNames()[0]
+ + "\" format.");
+ numFailures++;
+ }
}
- System.out.println("Test passed");
+ if (numFailures == 0) {
+ System.out.println("Test passed.");
+ } else {
+ throw new RuntimeException("Test failed.");
+ }
}
// Callbacks
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/imageio/plugins/tiff/WriteToSequenceAfterAbort.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriter;
+import javax.imageio.event.IIOWriteProgressListener;
+import javax.imageio.stream.ImageOutputStream;
+
+import static java.awt.image.BufferedImage.TYPE_BYTE_BINARY;
+import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+import java.util.Vector;
+import javax.imageio.IIOImage;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+
+/**
+ * @test
+ * @bug 8144245
+ * @summary Ensure aborting write works properly for a TIFF sequence.
+ */
+public final class WriteToSequenceAfterAbort implements IIOWriteProgressListener {
+
+ private volatile boolean abortFlag = true;
+ private volatile boolean isAbortCalled;
+ private volatile boolean isCompleteCalled;
+ private volatile boolean isProgressCalled;
+ private volatile boolean isStartedCalled;
+ private static final int WIDTH = 100;
+ private static final int HEIGHT = 100;
+ private static final int NUM_TILES_XY = 3;
+
+ private class TiledImage implements RenderedImage {
+ private final BufferedImage tile;
+ private final BufferedImage image;
+ private final int numXTiles, numYTiles;
+ private boolean isImageInitialized = false;
+
+ TiledImage(BufferedImage tile, int numXTiles, int numYTiles) {
+ this.tile = tile;
+ this.numXTiles = numXTiles;
+ this.numYTiles = numYTiles;
+ image = new BufferedImage(getWidth(), getHeight(), tile.getType());
+ }
+
+ @Override
+ public Vector<RenderedImage> getSources() {
+ return null;
+ }
+
+ @Override
+ public Object getProperty(String string) {
+ return java.awt.Image.UndefinedProperty;
+ }
+
+ @Override
+ public String[] getPropertyNames() {
+ return new String[0];
+ }
+
+ @Override
+ public ColorModel getColorModel() {
+ return tile.getColorModel();
+ }
+
+ @Override
+ public SampleModel getSampleModel() {
+ return tile.getSampleModel();
+ }
+
+ @Override
+ public int getWidth() {
+ return numXTiles*tile.getWidth();
+ }
+
+ @Override
+ public int getHeight() {
+ return numYTiles*tile.getHeight();
+ }
+
+ @Override
+ public int getMinX() {
+ return 0;
+ }
+
+ @Override
+ public int getMinY() {
+ return 0;
+ }
+
+ @Override
+ public int getNumXTiles() {
+ return numXTiles;
+ }
+
+ @Override
+ public int getNumYTiles() {
+ return numYTiles;
+ }
+
+ @Override
+ public int getMinTileX() {
+ return 0;
+ }
+
+ @Override
+ public int getMinTileY() {
+ return 0;
+ }
+
+ @Override
+ public int getTileWidth() {
+ return tile.getWidth();
+ }
+
+ @Override
+ public int getTileHeight() {
+ return tile.getHeight();
+ }
+
+ @Override
+ public int getTileGridXOffset() {
+ return 0;
+ }
+
+ @Override
+ public int getTileGridYOffset() {
+ return 0;
+ }
+
+ @Override
+ public Raster getTile(int x, int y) {
+ WritableRaster r = tile.getRaster();
+ return r.createWritableTranslatedChild(x*tile.getWidth(),
+ y*tile.getHeight());
+ }
+
+ @Override
+ public Raster getData() {
+ return getAsBufferedImage().getData();
+ }
+
+ @Override
+ public Raster getData(Rectangle r) {
+ return getAsBufferedImage().getData(r);
+ }
+
+ @Override
+ public WritableRaster copyData(WritableRaster wr) {
+ return getAsBufferedImage().copyData(wr);
+ }
+
+ public BufferedImage getAsBufferedImage() {
+ synchronized (image) {
+ if (!isImageInitialized) {
+ int tx0 = getMinTileX(), ty0 = getMinTileY();
+ int txN = tx0 + getNumXTiles(), tyN = ty0 + getNumYTiles();
+ for (int j = ty0; j < tyN; j++) {
+ for (int i = tx0; i < txN; i++) {
+ image.setData(getTile(i, j));
+ }
+ }
+ }
+ isImageInitialized = true;
+ }
+ return image;
+ }
+ }
+
+ private void test(final ImageWriter writer) throws IOException {
+ String suffix = writer.getOriginatingProvider().getFileSuffixes()[0];
+
+ // Image initialization
+ BufferedImage imageUpperLeft =
+ new BufferedImage(WIDTH, HEIGHT, TYPE_BYTE_BINARY);
+ Graphics2D g = imageUpperLeft.createGraphics();
+ g.setColor(Color.WHITE);
+ g.fillRect(0, 0, WIDTH/2, HEIGHT/2);
+ g.dispose();
+ BufferedImage imageLowerRight =
+ new BufferedImage(WIDTH, HEIGHT, TYPE_BYTE_BINARY);
+ g = imageLowerRight.createGraphics();
+ g.setColor(Color.WHITE);
+ g.fillRect(WIDTH/2, HEIGHT/2, WIDTH/2, HEIGHT/2);
+ g.dispose();
+ TiledImage[] images = new TiledImage[] {
+ new TiledImage(imageUpperLeft, NUM_TILES_XY, NUM_TILES_XY),
+ new TiledImage(imageUpperLeft, NUM_TILES_XY, NUM_TILES_XY),
+ new TiledImage(imageLowerRight, NUM_TILES_XY, NUM_TILES_XY),
+ new TiledImage(imageLowerRight, NUM_TILES_XY, NUM_TILES_XY)
+ };
+
+ // File initialization
+ File file = File.createTempFile("temp", "." + suffix);
+ file.deleteOnExit();
+ FileOutputStream fos = new SkipWriteOnAbortOutputStream(file);
+ ImageOutputStream ios = ImageIO.createImageOutputStream(fos);
+ writer.setOutput(ios);
+ writer.addIIOWriteProgressListener(this);
+
+ writer.prepareWriteSequence(null);
+ boolean[] abortions = new boolean[] {true, false, true, false};
+ for (int i = 0; i < 4; i++) {
+ abortFlag = abortions[i];
+ isAbortCalled = false;
+ isCompleteCalled = false;
+ isProgressCalled = false;
+ isStartedCalled = false;
+
+ TiledImage image = images[i];
+ if (abortFlag) {
+ // This write will be aborted, and file will not be touched
+ writer.writeToSequence(new IIOImage(image, null, null), null);
+ if (!isStartedCalled) {
+ throw new RuntimeException("Started should be called");
+ }
+ if (!isProgressCalled) {
+ throw new RuntimeException("Progress should be called");
+ }
+ if (!isAbortCalled) {
+ throw new RuntimeException("Abort should be called");
+ }
+ if (isCompleteCalled) {
+ throw new RuntimeException("Complete should not be called");
+ }
+ } else {
+ // This write should be completed successfully and the file should
+ // contain correct image data.
+ writer.writeToSequence(new IIOImage(image, null, null), null);
+ if (!isStartedCalled) {
+ throw new RuntimeException("Started should be called");
+ }
+ if (!isProgressCalled) {
+ throw new RuntimeException("Progress should be called");
+ }
+ if (isAbortCalled) {
+ throw new RuntimeException("Abort should not be called");
+ }
+ if (!isCompleteCalled) {
+ throw new RuntimeException("Complete should be called");
+ }
+ }
+ }
+
+ writer.endWriteSequence();
+ writer.dispose();
+ ios.close();
+
+ // Validates content of the file.
+ ImageReader reader = ImageIO.getImageReader(writer);
+ ImageInputStream iis = ImageIO.createImageInputStream(file);
+ reader.setInput(iis);
+ for (int i = 0; i < 2; i++) {
+ System.out.println("Testing image " + i);
+ BufferedImage imageRead = reader.read(i);
+ BufferedImage imageWrite = images[2 * i].getAsBufferedImage();
+ for (int x = 0; x < WIDTH; ++x) {
+ for (int y = 0; y < HEIGHT; ++y) {
+ if (imageRead.getRGB(x, y) != imageWrite.getRGB(x, y)) {
+ throw new RuntimeException("Test failed for image " + i);
+ }
+ }
+ }
+ }
+ }
+
+ public static void main(final String[] args) throws IOException {
+ WriteToSequenceAfterAbort writeAfterAbort = new WriteToSequenceAfterAbort();
+ ImageWriter writer = ImageIO.getImageWritersByFormatName("TIFF").next();
+ writeAfterAbort.test(writer);
+ System.out.println("Test passed.");
+ }
+
+ // Callbacks
+
+ @Override
+ public void imageComplete(ImageWriter source) {
+ isCompleteCalled = true;
+ }
+
+ @Override
+ public void imageProgress(ImageWriter source, float percentageDone) {
+ isProgressCalled = true;
+ if (percentageDone > 50 && abortFlag) {
+ source.abort();
+ }
+ }
+
+ @Override
+ public void imageStarted(ImageWriter source, int imageIndex) {
+ isStartedCalled = true;
+ }
+
+ @Override
+ public void writeAborted(final ImageWriter source) {
+ isAbortCalled = true;
+ }
+
+ @Override
+ public void thumbnailComplete(ImageWriter source) {
+ }
+
+ @Override
+ public void thumbnailProgress(ImageWriter source, float percentageDone) {
+ }
+
+ @Override
+ public void thumbnailStarted(ImageWriter source, int imageIndex,
+ int thumbnailIndex) {
+ }
+
+ /**
+ * We need to skip writes on abort, because content of the file after abort
+ * is undefined.
+ */
+ private class SkipWriteOnAbortOutputStream extends FileOutputStream {
+
+ SkipWriteOnAbortOutputStream(File file) throws FileNotFoundException {
+ super(file);
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ if (!abortFlag) {
+ super.write(b);
+ }
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ if (!abortFlag) {
+ super.write(b);
+ }
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ if (!abortFlag) {
+ super.write(b, off, len);
+ }
+ }
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/imageio/spi/MarkTryFinallyReproducer.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2015 Red Hat, Inc.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8144071
+ * @run main/othervm MarkTryFinallyReproducer
+ * @summary Test that call to canDecodeInput in ImageIO don't corrupt
+ * mark/reset stack in ImageInputStream
+ * @author Jiri Vanek
+ */
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.ByteOrder;
+import java.util.Locale;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.spi.IIORegistry;
+import javax.imageio.spi.ImageReaderSpi;
+import javax.imageio.stream.IIOByteBuffer;
+import javax.imageio.stream.ImageInputStream;
+
+
+public class MarkTryFinallyReproducer {
+
+ private static final byte[] bmp = new byte[]{
+ 127,127, 66, 77, -86, 0, 0, 0, 0, 0, 0, 0,
+ 122, 0, 0, 0, 108, 0, 0, 0, 4, 0, 0, 0, 4,
+ 0, 0, 0, 1, 0, 24, 0, 0, 0, 0, 0, 48, 0, 0,
+ 0, 19, 11, 0, 0, 19, 11, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 66, 71, 82, 115, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, -1, -1,
+ -1, -1, -1, 0, -1, 0, -1, -1, -1, -1, 0, 0, 0, -17,
+ 0, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, -1, -1, -1,
+ -1, 0, 0, 0, -1, -1, -1, -1, -1, -1, 0, 0, -1
+ };
+ //first two are evil, we are skipping them later. Others are normal BMP
+
+ private static class NotClosingImageInputStream implements ImageInputStream {
+
+ private final ImageInputStream src;
+
+ private NotClosingImageInputStream(ImageInputStream createImageInputStream) {
+ this.src = createImageInputStream;
+ }
+
+ @Override
+ public void setByteOrder(ByteOrder byteOrder) {
+ src.setByteOrder(byteOrder);
+ }
+
+ @Override
+ public ByteOrder getByteOrder() {
+ return src.getByteOrder();
+ }
+
+ @Override
+ public int read() throws IOException {
+ return src.read();
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ return src.read(b);
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ return src.read(b, off, len);
+ }
+
+ @Override
+ public void readBytes(IIOByteBuffer buf, int len) throws IOException {
+ src.readBytes(buf, len);
+ }
+
+ @Override
+ public boolean readBoolean() throws IOException {
+ return src.readBoolean();
+ }
+
+ @Override
+ public byte readByte() throws IOException {
+ return src.readByte();
+ }
+
+ @Override
+ public int readUnsignedByte() throws IOException {
+ return src.readUnsignedByte();
+ }
+
+ @Override
+ public short readShort() throws IOException {
+ return src.readShort();
+ }
+
+ @Override
+ public int readUnsignedShort() throws IOException {
+ return src.readUnsignedShort();
+ }
+
+ @Override
+ public char readChar() throws IOException {
+ return src.readChar();
+ }
+
+ @Override
+ public int readInt() throws IOException {
+ return src.readInt();
+ }
+
+ @Override
+ public long readUnsignedInt() throws IOException {
+ return src.readUnsignedInt();
+ }
+
+ @Override
+ public long readLong() throws IOException {
+ return src.readLong();
+ }
+
+ @Override
+ public float readFloat() throws IOException {
+ return src.readFloat();
+ }
+
+ @Override
+ public double readDouble() throws IOException {
+ return src.readDouble();
+ }
+
+ @Override
+ public String readLine() throws IOException {
+ return src.readLine();
+ }
+
+ @Override
+ public String readUTF() throws IOException {
+ return src.readUTF();
+ }
+
+ @Override
+ public void readFully(byte[] b, int off, int len) throws IOException {
+ src.readFully(b, off, len);
+ }
+
+ @Override
+ public void readFully(byte[] b) throws IOException {
+ src.readFully(b);
+ }
+
+ @Override
+ public void readFully(short[] s, int off, int len) throws IOException {
+ src.readFully(s, off, len);
+ }
+
+ @Override
+ public void readFully(char[] c, int off, int len) throws IOException {
+ src.readFully(c, off, len);
+ }
+
+ @Override
+ public void readFully(int[] i, int off, int len) throws IOException {
+ src.readFully(i, off, len);
+ }
+
+ @Override
+ public void readFully(long[] l, int off, int len) throws IOException {
+ src.readFully(l, off, len);
+ }
+
+ @Override
+ public void readFully(float[] f, int off, int len) throws IOException {
+ src.readFully(f, off, len);
+ }
+
+ @Override
+ public void readFully(double[] d, int off, int len) throws IOException {
+ src.readFully(d, off, len);
+ }
+
+ @Override
+ public long getStreamPosition() throws IOException {
+ return src.getStreamPosition();
+ }
+
+ @Override
+ public int getBitOffset() throws IOException {
+ return src.getBitOffset();
+ }
+
+ @Override
+ public void setBitOffset(int bitOffset) throws IOException {
+ src.setBitOffset(bitOffset);
+ }
+
+ @Override
+ public int readBit() throws IOException {
+ return src.readBit();
+ }
+
+ @Override
+ public long readBits(int numBits) throws IOException {
+ return src.readBits(numBits);
+ }
+
+ @Override
+ public long length() throws IOException {
+ return src.length();
+ }
+
+ @Override
+ public int skipBytes(int n) throws IOException {
+ return src.skipBytes(n);
+ }
+
+ @Override
+ public long skipBytes(long n) throws IOException {
+ return src.skipBytes(n);
+ }
+
+ @Override
+ public void seek(long pos) throws IOException {
+ src.seek(pos);
+ }
+
+ @Override
+ public void mark() {
+ src.mark();
+ }
+
+ @Override
+ public void reset() throws IOException {
+ src.reset();
+ }
+
+ @Override
+ public void flushBefore(long pos) throws IOException {
+ src.flushBefore(pos);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ src.flush();
+ }
+
+ @Override
+ public long getFlushedPosition() {
+ return src.getFlushedPosition();
+ }
+
+ @Override
+ public boolean isCached() {
+ return src.isCached();
+ }
+
+ @Override
+ public boolean isCachedMemory() {
+ return src.isCachedMemory();
+ }
+
+ @Override
+ public boolean isCachedFile() {
+ return src.isCachedFile();
+ }
+
+ @Override
+ public void close() throws IOException {
+ //the only important one. nothing
+ }
+ }
+
+ static final String readerClassName
+ = MarkTryFinallyReproducerSpi.class.getName();
+ static final String[] localNames = {"myNames"};
+ static final String[] localSuffixes = {"mySuffixes"};
+ static final String[] localMIMETypes = {"myMimes"};
+
+ public static class MarkTryFinallyReproducerSpi extends ImageReaderSpi {
+
+ public MarkTryFinallyReproducerSpi() {
+ super("MarkTryFinallyReproducerSpi",
+ "1.0",
+ localNames,
+ localSuffixes,
+ localMIMETypes,
+ readerClassName,
+ new Class[]{ImageInputStream.class},
+ new String[0],
+ false,
+ null,
+ null,
+ new String[0],
+ new String[0],
+ false,
+ null,
+ null,
+ new String[0],
+ new String[0]);
+ }
+
+ @Override
+ public String getDescription(Locale locale) {
+ return "";
+ }
+
+ @Override
+ public boolean canDecodeInput(Object input) throws IOException {
+ throw new IOException("Bad luck");
+ }
+
+ @Override
+ public ImageReader createReaderInstance(Object extension) {
+ return null;
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ MarkTryFinallyReproducerSpi spi = new MarkTryFinallyReproducerSpi();
+ IIORegistry.getDefaultInstance().registerServiceProvider(spi);
+
+ ImageInputStream iis1 =
+ new NotClosingImageInputStream(ImageIO.createImageInputStream(new ByteArrayInputStream(bmp)));
+ iis1.readByte();
+ iis1.mark();
+ long p1 = iis1.getStreamPosition();
+ iis1.readByte();
+ iis1.mark();
+ long p2 = iis1.getStreamPosition();
+ BufferedImage bi1 = ImageIO.read(iis1);
+ iis1.reset();
+ long pn2 = iis1.getStreamPosition();
+ iis1.reset();
+ long pn1 = iis1.getStreamPosition();
+ if (p1 != pn1 || p2!= pn2) {
+ throw new RuntimeException("Exception from call to canDecodeInput in ImageIO. " +
+ "Corrupted stack in ImageInputStream");
+ }
+
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/MBeanServer/ExceptionFactory.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.ArrayList;
+import javax.management.AttributeNotFoundException;
+import javax.management.BadAttributeValueExpException;
+import javax.management.BadBinaryOpValueExpException;
+import javax.management.BadStringOperationException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidApplicationException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMException;
+import javax.management.JMRuntimeException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanRegistrationException;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.OperationsException;
+import javax.management.ReflectionException;
+import javax.management.RuntimeErrorException;
+import javax.management.RuntimeMBeanException;
+import javax.management.RuntimeOperationsException;
+import javax.management.ServiceNotFoundException;
+import javax.management.StringValueExp;
+import javax.management.modelmbean.InvalidTargetObjectTypeException;
+import javax.management.modelmbean.XMLParseException;
+import javax.management.monitor.MonitorSettingException;
+import javax.management.openmbean.InvalidKeyException;
+import javax.management.openmbean.InvalidOpenTypeException;
+import javax.management.openmbean.KeyAlreadyExistsException;
+import javax.management.openmbean.OpenDataException;
+import javax.management.relation.InvalidRelationIdException;
+import javax.management.relation.InvalidRelationServiceException;
+import javax.management.relation.InvalidRelationTypeException;
+import javax.management.relation.InvalidRoleInfoException;
+import javax.management.relation.InvalidRoleValueException;
+import javax.management.relation.RelationException;
+import javax.management.relation.RelationNotFoundException;
+import javax.management.relation.RelationServiceNotRegisteredException;
+import javax.management.relation.RelationTypeNotFoundException;
+import javax.management.relation.RoleInfoNotFoundException;
+import javax.management.relation.RoleNotFoundException;
+import javax.management.remote.JMXProviderException;
+import javax.management.remote.JMXServerErrorException;
+
+/**
+ * |----- Original Description Coming From Tonga Original Source Code -------|
+ * | |
+ * | That class creates an ArrayList and fill it with an instance of each of |
+ * | the Exception class of the JMX API. |
+ * | It's dedicated to use by ExceptionTest. |
+ * |-------------------------------------------------------------------------|
+ */
+public class ExceptionFactory {
+
+ public static final ArrayList<Exception> exceptions =
+ new ArrayList<Exception>();
+
+ static {
+ String mes = "SQE";
+ exceptions.add(new AttributeNotFoundException());
+ exceptions.add(new BadAttributeValueExpException(mes));
+ exceptions.add(new BadBinaryOpValueExpException(new StringValueExp(mes)));
+ exceptions.add(new BadStringOperationException(mes));
+ exceptions.add(new InstanceAlreadyExistsException());
+ exceptions.add(new InstanceNotFoundException());
+ exceptions.add(new IntrospectionException());
+ exceptions.add(new InvalidApplicationException(mes));
+ exceptions.add(new InvalidAttributeValueException());
+ exceptions.add(new JMException());
+ exceptions.add(new JMRuntimeException());
+ exceptions.add(new ListenerNotFoundException());
+ exceptions.add(new MalformedObjectNameException());
+ exceptions.add(new MBeanException(new Exception(mes), mes));
+ exceptions.add(new MBeanRegistrationException(new Exception(mes), mes));
+ exceptions.add(new NotCompliantMBeanException());
+ exceptions.add(new OperationsException());
+ exceptions.add(new ReflectionException(new Exception(mes), mes));
+ exceptions.add(new RuntimeErrorException(new Error(mes), mes));
+ exceptions.add(new RuntimeMBeanException(new RuntimeException(mes), mes));
+ exceptions.add(new RuntimeOperationsException(new RuntimeException(mes), mes));
+ exceptions.add(new ServiceNotFoundException());
+ exceptions.add(new InvalidTargetObjectTypeException());
+ exceptions.add(new XMLParseException());
+ exceptions.add(new MonitorSettingException());
+ exceptions.add(new InvalidKeyException());
+ exceptions.add(new InvalidOpenTypeException());
+ exceptions.add(new KeyAlreadyExistsException());
+ exceptions.add(new OpenDataException());
+ exceptions.add(new InvalidRelationIdException());
+ exceptions.add(new InvalidRelationServiceException());
+ exceptions.add(new InvalidRelationTypeException());
+ exceptions.add(new InvalidRoleInfoException());
+ exceptions.add(new InvalidRoleValueException());
+ exceptions.add(new RelationException());
+ exceptions.add(new RelationNotFoundException());
+ exceptions.add(new RelationServiceNotRegisteredException());
+ exceptions.add(new RelationTypeNotFoundException());
+ exceptions.add(new RoleInfoNotFoundException());
+ exceptions.add(new RoleNotFoundException());
+ exceptions.add(new JMXProviderException());
+ exceptions.add(new JMXServerErrorException(mes, new Error(mes)));
+ ExceptionTest.Utils.debug(ExceptionTest.Utils.DEBUG_STANDARD,
+ "DataFactory::updateMap: Initialized" +
+ " an ArrayList with the " +
+ exceptions.size() + " exceptions of the JMX API");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/MBeanServer/ExceptionTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058865
+ * @summary Checks that exceptions are correctly wired (compared to reference).
+ * @author Olivier Lagneau
+ * @modules java.management
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD ExceptionTest
+ */
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Properties;
+import java.lang.reflect.Method;
+
+import java.lang.management.ManagementFactory;
+import javax.management.ObjectName;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+
+public class ExceptionTest {
+
+ /*
+ * First Debug properties and arguments are collect in expected
+ * map (argName, value) format, then calls original test's run method.
+ */
+ public static void main(String args[]) throws Exception {
+
+ System.out.println("=================================================");
+
+ // Parses parameters
+ Utils.parseDebugProperties();
+ Map<String, Object> map = Utils.parseParameters(args) ;
+
+ // Run test
+ ExceptionTest test = new ExceptionTest();
+ test.run(map);
+
+ }
+
+ public void run(Map<String, Object> args) {
+
+ System.out.println("ExceptionTest::run: Start");
+ int errorCount = 0;
+
+ JMXConnectorServer cs = null;
+ JMXConnector cc = null;
+
+ try {
+ // JMX MbeanServer used inside single VM as if remote.
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+
+ JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
+ cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ cs.start();
+
+ JMXServiceURL addr = cs.getAddress();
+ cc = JMXConnectorFactory.connect(addr);
+ MBeanServerConnection mbsc = cc.getMBeanServerConnection();
+
+ // ----
+ ObjectName objName =
+ new ObjectName(ExceptionThrower.EXCEPTION_THROWER_NAME);
+ System.out.println("ExceptionTest::run: Create and register MBean " + objName);
+ mbsc.createMBean("ExceptionThrower", objName);
+ System.out.println("---- OK\n");
+
+ // ----
+ System.out.println("ExceptionTest::run: Ask for exception(s)");
+ Object[] throwExceptionParam = new Object[1];
+ String[] throwExceptionSig = new String[]{"int"};
+
+ for (int i = 0; i < ExceptionFactory.exceptions.size(); i++) {
+ throwExceptionParam[0] = new Integer(i);
+
+ Exception ex =
+ (Exception)mbsc.invoke(objName,
+ "throwException", throwExceptionParam, throwExceptionSig);
+
+ if ( ! matches(ex, ExceptionFactory.exceptions.get(i)) ) {
+ errorCount++;
+ System.out.println("ExceptionTest::run: (ERROR) Received \n["
+ + ex + "]\nin place of\n["
+ + ExceptionFactory.exceptions.get(i) + "]");
+ } else {
+ System.out.println("OK [" + ex + "]");
+ }
+ }
+
+ System.out.println("---- DONE\n");
+
+ } catch (Exception e) {
+ Utils.printThrowable(e, true);
+ throw new RuntimeException();
+ } finally {
+ try {
+ // Close JMX Connector Client
+ cc.close();
+ // Stop connertor server
+ cs.stop();
+
+ } catch (Exception e) {
+ Utils.printThrowable(e, true);
+ throw new RuntimeException(
+ "Unable to either close connector client or stop connector server");
+ }
+ }
+
+ if (errorCount == 0) {
+ System.out.println("ExceptionTest::run: Done without any error");
+ } else {
+ System.out.println("ExceptionTest::run: Done with " + errorCount
+ + " error(s)");
+ throw new RuntimeException("errorCount = " + errorCount);
+ }
+
+ System.out.println("ExceptionTest::run: Done");
+ }
+
+ // Check both Exception are identical.
+ // That means:
+ // - none is null.
+ // - they are of the same Class.
+ // - if their respective messages aren't null they're equal.
+ // - if the message of one is null the message of the other is null too.
+ private boolean matches(Exception ex, Exception refex) {
+ if ( ex == null || refex == null ) {
+ System.out.println("(ERROR) Called with one or more null parameter; check "
+ + ex + " against " + refex);
+ return false;
+ }
+
+ String exClass = ex.getClass().getName();
+ String refexClass = refex.getClass().getName();
+
+ if ( ! exClass.equals(refexClass) ) {
+ System.out.println("(ERROR) Class names don't match; check ["
+ + exClass + "] against [" + refexClass + "]");
+ return false;
+ }
+
+ String exMes = ex.getMessage();
+ String refexMes = refex.getMessage();
+
+ if ( exMes != null && refexMes != null ) {
+ if ( ! exMes.equals(refexMes) ) {
+ System.out.println("(ERROR) Non null messages don't match; check ["
+ + exMes + "] against [" + refexMes + "]");
+ return false;
+ }
+ } else if ( (exMes == null && refexMes != null)
+ || (exMes != null && refexMes == null) ) {
+ System.out.println("(ERROR) Messages don't match; check [" + exMes
+ + "] against [" + refexMes + "]");
+ }
+
+ return true;
+ }
+
+ // Utility inner class coming from JMX Tonga test suite.
+ // Also used by ExceptionFactory.
+ static class Utils {
+
+ // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property
+ static final String DEBUG_HEADER = "[debug] ";
+
+ // DEBUG levels
+ static int selectedDebugLevel = 0;
+ static final int DEBUG_STANDARD = 1;
+ static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests
+ static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE;
+
+ static void parseDebugProperties() {
+ int level = 0;
+ Properties p = System.getProperties();
+
+ // get selected levels
+ if (p.getProperty("DEBUG_STANDARD") != null) {
+ level |= DEBUG_STANDARD;
+ }
+
+ if (p.getProperty("DEBUG_VERBOSE") != null) {
+ level |= DEBUG_VERBOSE;
+ }
+
+ if (p.getProperty("DEBUG_ALL") != null) {
+ level |= DEBUG_ALL;
+ }
+
+ selectedDebugLevel = level;
+ }
+
+ /**
+ * Reproduces the original parsing and collection of test parameters
+ * from the DTonga JMX test suite.
+ *
+ * Collects passed args and returns them in a map(argname, value) structure,
+ * which will be then propagated as necessary to various called methods.
+ */
+ static Map<String, Object> parseParameters(String args[])
+ throws Exception {
+ debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start");
+ HashMap<String, Object> map = new HashMap<>();
+
+ for ( int i = 0; i < args.length; i++ ) {
+ if ( args[i].trim().startsWith("-") ) {
+ if ((i+1) < args.length && !args[i+1].startsWith("-") ) {
+ debug(DEBUG_STANDARD,
+ "TestRoot::parseParameters: added in map = " +
+ args[i] +
+ " with value " +
+ args[i+1]) ;
+ map.put(args[i].trim(), args[i+1].trim()) ;
+ } else if ((i+1) < args.length && args[i+1].startsWith("-") ||
+ (i+1) == args.length ) {
+ debug(DEBUG_STANDARD,
+ "TestRoot::parseParameters: added in map = " +
+ args[i] +
+ " with null value") ;
+ map.put(args[i].trim(), null) ;
+ } else {
+ System.out.println(
+ "TestRoot::parseParameters: (WARNING) not added in map = " +
+ args[i]) ;
+ }
+ }
+ }
+
+ debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ;
+ return map ;
+ }
+
+ /**
+ * This method is to be used in all tests to print anything
+ * that is temporary.
+ * Printing is done only when debug is activated by the property DEBUG.
+ * Printing depends also on the DEBUG_LEVEL property.
+ * Here it encapsulates a System.out.println.
+ */
+ static void debug(int level, String line) {
+ if ((selectedDebugLevel & level) != 0) {
+ System.out.println(DEBUG_HEADER + line);
+ }
+ }
+
+ /**
+ * Do print stack trace when withStack is true.
+ * Does try to call getTargetException() and getTargetError() then
+ * print embedded stacks in the case of an Exception wrapping
+ * another Exception or an Error. Recurse until no more wrapping
+ * is found.
+ */
+ static void printThrowable(Throwable theThro, boolean withStack) {
+ try {
+ if (withStack) {
+ theThro.printStackTrace(System.out);
+ }
+ if (theThro instanceof Exception) {
+ Exception t = (Exception) theThro;
+ Method target = null;
+ String blank = " ";
+ try {
+ target = t.getClass().getMethod("getTargetException",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetException method could be there or not
+ }
+ System.out.println(blank + t.getClass() + "==>" + t.getMessage());
+ while (target != null) {
+ try {
+ t = (Exception) target.invoke(t,
+ (java.lang.Object[]) null);
+ } catch (Exception ee) {
+ t = null;
+ }
+ try {
+ if (t != null) {
+ blank = blank + " ";
+ System.out.println(blank + t.getClass() + "==>" +
+ t.getMessage());
+ try {
+ target =
+ t.getClass().getMethod("getTargetException",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetException method could be there or not }
+ }
+ } else {
+ target = null;
+ }
+ } catch (Exception ee) {
+ target = null;
+ }
+ }
+
+ // We may have exceptions wrapping an Error then it is
+ // getTargetError that is likely to be called
+ try {
+ target = ((Exception) theThro).getClass().getMethod("getTargetError",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetError method could be there or not
+ }
+ Throwable err = theThro;
+ while (target != null) {
+ try {
+ err = (Error) target.invoke(err,
+ (java.lang.Object[]) null);
+ } catch (Exception ee) {
+ err = null;
+ }
+ try {
+ if (err != null) {
+ blank = blank + " ";
+ System.out.println(blank + err.getClass() + "==>" +
+ err.getMessage());
+ if (withStack) {
+ err.printStackTrace(System.out);
+ }
+ try {
+ target = err.getClass().getMethod("getTargetError",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetError method could be there or not
+ }
+ } else {
+ target = null;
+ }
+ } catch (Exception ee) {
+ target = null;
+ }
+ }
+ } else {
+ System.out.println("Throwable is : " + theThro);
+ }
+ } catch (Throwable x) {
+ System.out.println("Exception : raised in printException : " + x);
+ }
+ }
+ }
+
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/MBeanServer/ExceptionThrower.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * This class defines a simple standard MBean.
+ */
+public class ExceptionThrower implements ExceptionThrowerMBean {
+
+ public static final String EXCEPTION_THROWER_NAME
+ = "sqe:type=ExceptionThrower";
+
+ public Exception throwException(int exceptionIndex) {
+ return ExceptionFactory.exceptions.get(exceptionIndex);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/MBeanServer/ExceptionThrowerMBean.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * This interface defines a simple standard MBean.
+ */
+public interface ExceptionThrowerMBean {
+ public Exception throwException(int exceptionIndex);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/Basic.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import javax.management.Descriptor;
+import javax.management.ImmutableDescriptor;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+
+/**
+ * Class Basic
+ * Basic Description
+ */
+public class Basic implements BasicMXBean, NotificationEmitter,
+ MBeanRegistration {
+
+ public static final String EXCEPTION_MESSAGE = "from Basic";
+ public static final String NOTIFICATION_MESSAGE = "from Basic";
+ /** Attribute : IntAtt */
+ private int intAtt = 0;
+ /** Attribute : IntegerAtt */
+ private Integer integerAtt = 0;
+ /** Attribute : BoolAtt */
+ private boolean boolAtt = false;
+ /** Attribute : BooleanAtt */
+ private Boolean booleanAtt = false;
+ /** Attribute : StringAtt */
+ private String stringAtt = null;
+ /** Attribute : DateAtt */
+ private Date dateAtt = null;
+ /** Attribute : ObjectNameAtt */
+ private ObjectName objectNameAtt = null;
+ /** Attribute : NotifDescriptorAsMapAtt */
+ private Map<String, String> notifDescriptorAsMapAtt = null;
+ /** Attribute : NotifDescriptorAtt */
+ private Descriptor notifDescriptorAtt = null;
+ /** Attribute : SqeParameter */
+ private SqeParameter sqeParameterAtt = null;
+
+ /* Creates a new instance of Basic */
+ @SqeDescriptorKey("CONSTRUCTOR Basic")
+ public Basic() {
+ }
+
+ /* Creates a new instance of Basic */
+ @SqeDescriptorKey("CONSTRUCTOR Basic")
+ public Basic(
+ @SqeDescriptorKey("CONSTRUCTOR PARAMETER SqeParameter") SqeParameter param) {
+ }
+
+ /**
+ * Get int attribute
+ */
+ public int getIntAtt() {
+ return intAtt;
+ }
+
+ /**
+ * Set int attribute
+ */
+ public void setIntAtt(int value) {
+ intAtt = value;
+ }
+
+ /**
+ * Get Integer attribute
+ */
+ public Integer getIntegerAtt() {
+ return integerAtt;
+ }
+
+ /**
+ * Set Integer attribute
+ */
+ public void setIntegerAtt(Integer value) {
+ integerAtt = value;
+ }
+
+ /**
+ * Get boolean attribute
+ */
+ public boolean getBoolAtt() {
+ return boolAtt;
+ }
+
+ /**
+ * Set boolean attribute
+ */
+ public void setBoolAtt(boolean value) {
+ boolAtt = value;
+ }
+
+ /**
+ * Get Boolean attribute
+ */
+ public Boolean getBooleanAtt() {
+ return booleanAtt;
+ }
+
+ /**
+ * Set Boolean attribute
+ */
+ public void setBooleanAtt(Boolean value) {
+ booleanAtt = value;
+ }
+
+ /**
+ * Get String attribute
+ */
+ public String getStringAtt() {
+ return stringAtt;
+ }
+
+ /**
+ * Set String attribute
+ */
+ public void setStringAtt(String value) {
+ stringAtt = value;
+ }
+
+ /**
+ * Get Date attribute
+ */
+ public Date getDateAtt() {
+ return dateAtt;
+ }
+
+ /**
+ * Set Date attribute
+ */
+ public void setDateAtt(Date value) {
+ dateAtt = value;
+ }
+
+ /**
+ * Get ObjectName attribute
+ */
+ public ObjectName getObjectNameAtt() {
+ return objectNameAtt;
+ }
+
+ /**
+ * Set ObjectName attribute
+ */
+ public void setObjectNameAtt(ObjectName value) {
+ objectNameAtt = value;
+ }
+
+ /**
+ * Get SqeParameter attribute
+ */
+ public SqeParameter getSqeParameterAtt() throws Exception {
+ if (sqeParameterAtt == null) {
+ sqeParameterAtt = new SqeParameter();
+ sqeParameterAtt.setGlop("INITIALIZED");
+ }
+
+ return sqeParameterAtt;
+ }
+
+ /**
+ * Set SqeParameter attribute
+ */
+ public void setSqeParameterAtt(SqeParameter value) {
+ sqeParameterAtt = value;
+ }
+
+ /**
+ * Get the Descriptor used to build the NotificationInfo
+ * of emitted notifications.
+ */
+ public Map<String, String> getNotifDescriptorAsMapAtt() {
+ if (notifDescriptorAsMapAtt == null) {
+ initNotifDescriptorAtt();
+ }
+
+ return notifDescriptorAsMapAtt;
+ }
+
+ /**
+ * Set the Descriptor used to build the NotificationInfo
+ * of emitted notifications.
+ * <br>A Map<String, Object> would better fit Descriptor needs but then
+ * it is not convertible according the MXBean specification so the MBean
+ * registration fails.
+ * As we plan to test our custom Descriptor finds its way into
+ * the metadata of emitted notifications, String is good enough.
+ */
+ public void setNotifDescriptorAsMapAtt(Map<String, String> value) {
+ notifDescriptorAsMapAtt = new HashMap<String, String>(value);
+ notifDescriptorAtt = new ImmutableDescriptor(value);
+ }
+
+ /**
+ * Do nothing
+ */
+ public void doNothing() {
+ // I said NOTHING !
+ }
+
+ /**
+ * Do take SqeParameter as a parameter
+ */
+ public void doWeird(SqeParameter param) {
+ }
+
+ /**
+ * Throw an Exception
+ */
+ public void throwException() throws Exception {
+ throw new Exception(EXCEPTION_MESSAGE);
+ }
+
+ /**
+ * Throw an Error
+ */
+ public void throwError() {
+ throw new InternalError(EXCEPTION_MESSAGE);
+ }
+
+ /**
+ * Reset all attributes
+ */
+ public void reset() {
+ intAtt = 0;
+ integerAtt = 0;
+ boolAtt = false;
+ booleanAtt = Boolean.FALSE;
+ stringAtt = null;
+ dateAtt = null;
+ objectNameAtt = null;
+ }
+
+ /**
+ * Returns the weather for the coming days
+ * @param verbose <code>boolean</code> verbosity
+ * @throws java.lang.Exception <code>storm</code>
+ * @return <code>ObjectName</code>
+ */
+ public Weather getWeather(boolean verbose)
+ throws java.lang.Exception {
+ return Weather.SUNNY;
+ }
+
+ // Starting here are the 4 methods of MBeanRegistration interface.
+ // We use that to grab the ObjectName the MBean is registered with.
+ //
+ public ObjectName preRegister(MBeanServer server, ObjectName name)
+ throws Exception {
+ // Grab a reference on the MBeanServer we're registered in.
+ mbs = server;
+ // Compute the name we're registered with.
+ if (name != null) {
+ mbeanName = name;
+ return name;
+ } else {
+ mbeanName =
+ new ObjectName("sqe:type=" + Basic.class.getName());
+ return mbeanName;
+ }
+ }
+
+ public void postRegister(Boolean registrationDone) {
+ // Do nothing
+ }
+
+ public void preDeregister() throws Exception {
+ // Do nothing
+ }
+
+ public void postDeregister() {
+ // Do nothing
+ }
+
+ /**
+ * Send one Notification of the provided notifType type.
+ */
+ public void sendNotification(String notifType) {
+ Notification notification = null;
+
+ if (notifType.equals(NOTIF_TYPE_0)) {
+ notification = new Notification(NOTIF_TYPE_0,
+ mbeanName,
+ seqNumber,
+ NOTIFICATION_MESSAGE);
+ } else if (notifType.equals(NOTIF_TYPE_1)) {
+ notification = new SqeNotification(NOTIF_TYPE_1,
+ mbeanName,
+ seqNumber,
+ NOTIFICATION_MESSAGE);
+ }
+
+ seqNumber++;
+ broadcaster.sendNotification(notification);
+ }
+
+ /**
+ * That method starts a set of threads, each thread sends a given number of
+ * notifications.
+ * The number of threads can be set via the attribute numOfNotificationSenders.
+ * The number of notification sent by each thread can be set via
+ * the attribute numOfNotificationSenderLoops.
+ * Depending on the parameter customNotification we send either custom
+ * notification(s) or MBeanServer registration and unregistration notification(s).
+ * When customNotification=true the total number of notification(s) sent is
+ * (numOfNotificationSenders * numOfNotificationSenderLoops). They are
+ * sequentially of type NOTIF_TYPE_0 then NOTIF_TYPE_1 and so on.
+ *
+ * When customNotification=false the total number of notification(s) sent is
+ * (numOfNotificationSenders * numOfNotificationSenderLoops) registration
+ * notification(s)
+ * +
+ * (numOfNotificationSenders * numOfNotificationSenderLoops) unregistration
+ * notification(s)
+ *
+ * @throws java.lang.Exception
+ */
+ public void sendNotificationWave(boolean customNotification) throws
+ Exception {
+ // Build the set of notification sender.
+ Collection<Callable<Integer>> tasks =
+ new HashSet<Callable<Integer>>(numOfNotificationSenders);
+
+ for (int i = 1; i <= numOfNotificationSenders; i++) {
+ tasks.add(new NotifSender(numOfNotificationSenderLoops,
+ customNotification, i));
+ }
+
+ // Start all notification sender in parallel.
+ ExecutorService execServ = null;
+ try {
+ execServ = Executors.newFixedThreadPool(numOfNotificationSenders);
+ List<Future<Integer>> taskHandlers = execServ.invokeAll(tasks);
+ checkNotifSenderThreadStatus(taskHandlers);
+ } finally {
+ if (!execServ.isShutdown()) {
+ execServ.shutdown();
+ }
+ }
+ }
+
+ public void setNumOfNotificationSenders(int value) {
+ numOfNotificationSenders = value;
+ }
+
+ public void setNumOfNotificationSenderLoops(int value) {
+ numOfNotificationSenderLoops = value;
+ }
+
+ /**
+ * MBean Notification support
+ * You shouldn't update these methods
+ */
+ // <editor-fold defaultstate="collapsed" desc=" Generated Code ">
+ public void addNotificationListener(NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ throws IllegalArgumentException {
+ broadcaster.addNotificationListener(listener, filter, handback);
+ }
+
+ public MBeanNotificationInfo[] getNotificationInfo() {
+ if (notifDescriptorAtt == null) {
+ initNotifDescriptorAtt();
+ }
+
+ return new MBeanNotificationInfo[]{
+ new MBeanNotificationInfo(new String[]{
+ NOTIF_TYPE_0
+ },
+ javax.management.Notification.class.getName(),
+ "Standard JMX Notification",
+ notifDescriptorAtt),
+ new MBeanNotificationInfo(new String[]{
+ NOTIF_TYPE_1
+ },
+ SqeNotification.class.getName(),
+ "SQE Notification",
+ notifDescriptorAtt)
+ };
+ }
+
+ public void removeNotificationListener(NotificationListener listener)
+ throws ListenerNotFoundException {
+ broadcaster.removeNotificationListener(listener);
+ }
+
+ public void removeNotificationListener(NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ throws ListenerNotFoundException {
+ broadcaster.removeNotificationListener(listener, filter, handback);
+ }
+ // </editor-fold>
+ private synchronized long getNextSeqNumber() {
+ return seqNumber++;
+ }
+
+ private void initNotifDescriptorAtt() {
+ String key = "CRABE";
+ String value = "TAMBOUR";
+ notifDescriptorAtt =
+ new ImmutableDescriptor(new String[]{key + "=" + value});
+ notifDescriptorAsMapAtt =
+ new HashMap<String, String>();
+ notifDescriptorAsMapAtt.put(key, value);
+ }
+
+ private void checkNotifSenderThreadStatus(
+ List<Future<Integer>> taskHandlers)
+ throws Exception {
+ String msgTag = "Basic::checkNotifSenderThreadStatus: ";
+ // Grab back status of each notification sender.
+ for (Future<Integer> f : taskHandlers) {
+ if (f.isCancelled()) {
+ String message = msgTag +
+ "---- ERROR : One thread has been cancelled";
+ System.out.println(message);
+ throw new RuntimeException(message);
+ } else {
+ Integer effectiveNumOfLoops = f.get();
+
+ if (effectiveNumOfLoops != numOfNotificationSenderLoops) {
+ String message = msgTag + "---- ERROR : One thread did " +
+ effectiveNumOfLoops + " loops in place of " +
+ numOfNotificationSenderLoops;
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+ }
+ }
+ }
+ //
+ private int numOfNotificationSenderLoops = 2;
+ private int numOfNotificationSenders = 13;
+
+ private class NotifSender implements Callable<Integer> {
+
+ private int cycles;
+ private boolean customNotification;
+ private int senderID;
+
+ public NotifSender(int cycles, boolean customNotification, int id) {
+ this.cycles = cycles;
+ this.customNotification = customNotification;
+ this.senderID = id;
+ }
+
+ public Integer call() throws Exception {
+ int callsDone = 0;
+
+ try {
+ for (int i = 1; i <= cycles; i++) {
+ if (customNotification) {
+ if (i % 2 == 0) {
+ sendNotification(NOTIF_TYPE_0);
+ } else {
+ sendNotification(NOTIF_TYPE_1);
+ }
+ } else {
+ ObjectName mbeanName = new ObjectName("SQE:type=" +
+ mbeanClassName + ",senderID=" + senderID);
+ mbs.createMBean(mbeanClassName, mbeanName);
+ mbs.unregisterMBean(mbeanName);
+ }
+ callsDone++;
+ }
+ } catch (Exception e) {
+ System.out.println("NotifSender::call: (ERROR) Thread [" + senderID +
+ "] failed after " + callsDone + " cycles");
+ throw e;
+ }
+
+ return Integer.valueOf(callsDone);
+ }
+ }
+
+ //
+ private long seqNumber;
+ private final NotificationBroadcasterSupport broadcaster =
+ new NotificationBroadcasterSupport();
+ private ObjectName mbeanName;
+ private MBeanServer mbs;
+ private String mbeanClassName = "Simple";
+
+ /**
+ * Notification types definitions. To use when creating JMX Notifications.
+ */
+ public static final String NOTIF_TYPE_0 =
+ "sqe.notification.a.type";
+ public static final String NOTIF_TYPE_1 =
+ "sqe.notification.b.type";
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/BasicMXBean.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Date;
+import java.util.Map;
+
+import javax.management.ObjectName;
+
+/**
+ * Interface BasicMBean
+ * Basic Description
+ */
+@SqeDescriptorKey("INTERFACE BasicMXBean")
+public interface BasicMXBean
+{
+ /**
+ * Get int attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE intAtt")
+ public int getIntAtt();
+
+ /**
+ * Set int attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE intAtt")
+ public void setIntAtt(int value);
+
+ /**
+ * Get Integer attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE integerAtt")
+ public Integer getIntegerAtt();
+
+ /**
+ * Set Integer attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE integerAtt")
+ public void setIntegerAtt(Integer value);
+
+ /**
+ * Get boolean attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE boolAtt")
+ public boolean getBoolAtt();
+
+ /**
+ * Set boolean attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE boolAtt")
+ public void setBoolAtt(boolean value);
+
+ /**
+ * Get Boolean attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE booleanAtt")
+ public Boolean getBooleanAtt();
+
+ /**
+ * Set Boolean attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE booleanAtt")
+ public void setBooleanAtt(Boolean value);
+
+ /**
+ * Get String attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE stringAtt")
+ public String getStringAtt();
+
+ /**
+ * Set String attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE stringAtt")
+ public void setStringAtt(String value);
+
+ /**
+ * Get Date attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE dateAtt")
+ public Date getDateAtt();
+
+ /**
+ * Set Date attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE dateAtt")
+ public void setDateAtt(Date value);
+
+ /**
+ * Get ObjectName attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE objectNameAtt")
+ public ObjectName getObjectNameAtt();
+
+ /**
+ * Set ObjectName attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE objectNameAtt")
+ public void setObjectNameAtt(ObjectName value);
+
+ /**
+ * Get SqeParameter attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE sqeParameterAtt")
+ public SqeParameter getSqeParameterAtt() throws Exception;
+
+ /**
+ * Set SqeParameter attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE sqeParameterAtt")
+ public void setSqeParameterAtt(SqeParameter value);
+
+ /**
+ * Set NumOfNotificationSenders attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE NumOfNotificationSenders")
+ public void setNumOfNotificationSenders(int value);
+
+ /**
+ * Set NumOfNotificationSenderLoops attribute
+ */
+ @SqeDescriptorKey("ATTRIBUTE NumOfNotificationSenderLoops")
+ public void setNumOfNotificationSenderLoops(int value);
+
+ /**
+ * do nothing
+ *
+ */
+ @SqeDescriptorKey("OPERATION doNothing")
+ public void doNothing();
+
+ /**
+ * Do take SqeParameter as a parameter
+ */
+ @SqeDescriptorKey("OPERATION doWeird")
+ public void doWeird(@SqeDescriptorKey("METHOD PARAMETER")SqeParameter param);
+
+ /**
+ * throw an Exception
+ *
+ */
+ @SqeDescriptorKey("OPERATION throwException")
+ public void throwException() throws Exception;
+
+ /**
+ * throw an Error
+ *
+ */
+ @SqeDescriptorKey("OPERATION throwError")
+ public void throwError();
+
+ /**
+ * reset all attributes
+ *
+ */
+ @SqeDescriptorKey("OPERATION reset")
+ public void reset();
+
+ /**
+ * returns the weather for the coming days
+ *
+ * @param verbose <code>boolean</code> verbosity
+ * @return <code>ObjectName</code>
+ */
+ @SqeDescriptorKey("OPERATION getWeather")
+ public Weather getWeather(@SqeDescriptorKey("METHOD PARAMETER")boolean verbose)
+ throws java.lang.Exception;
+
+ public enum Weather {
+ CLOUDY, SUNNY
+ }
+
+ @SqeDescriptorKey("ATTRIBUTE notifDescriptorAsMapAtt")
+ public Map<String, String> getNotifDescriptorAsMapAtt();
+
+ @SqeDescriptorKey("ATTRIBUTE notifDescriptorAsMapAtt")
+ public void setNotifDescriptorAsMapAtt(Map<String, String> value);
+
+ @SqeDescriptorKey("OPERATION sendNotification")
+ public void sendNotification(@SqeDescriptorKey("METHOD PARAMETER")String notifType);
+
+ @SqeDescriptorKey("OPERATION sendNotificationWave")
+ public void sendNotificationWave(boolean customNotification) throws Exception;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/MXBeanExceptionHandlingTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058865
+ * @summary Checks correct exception and error events from NotificationListener
+ * @author Olivier Lagneau
+ * @modules java.management
+ * @library /lib/testlibrary
+ * @compile Basic.java
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanExceptionHandlingTest -timeForNotificationInSeconds 3
+ */
+
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+import java.lang.management.ManagementFactory;
+import javax.management.MBeanServer;
+import javax.management.MBeanException;
+import javax.management.MBeanServerDelegate;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.RuntimeErrorException;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+public class MXBeanExceptionHandlingTest implements NotificationListener {
+
+ private static String BASIC_MXBEAN_CLASS_NAME = "Basic";
+
+ private long timeForNotificationInSeconds = 3L;
+ private int numOfNotifications = 2;
+ private BlockingQueue<Notification> notifList = null;
+
+
+ /*
+ * First Debug properties and arguments are collect in expected
+ * map (argName, value) format, then calls original test's run method.
+ */
+ public static void main(String args[]) throws Exception {
+
+ System.out.println("=================================================");
+
+ // Parses parameters
+ Utils.parseDebugProperties();
+ Map<String, Object> map = Utils.parseParameters(args) ;
+
+ // Run test
+ MXBeanExceptionHandlingTest test = new MXBeanExceptionHandlingTest();
+ test.run(map);
+
+ }
+
+ protected void parseArgs(Map<String, Object> args) throws Exception {
+
+ String arg = null;
+
+ // Init timeForNotificationInSeconds
+ // It is the maximum time in seconds we wait for a notification.
+ arg = (String)args.get("-timeForNotificationInSeconds") ;
+ if (arg != null) {
+ timeForNotificationInSeconds = (new Long(arg)).longValue();
+ }
+
+ }
+
+ public void run(Map<String, Object> args) {
+
+ System.out.println("MXBeanExceptionHandlingTest::run: Start") ;
+ int errorCount = 0 ;
+
+ try {
+ parseArgs(args);
+ notifList = new ArrayBlockingQueue<Notification>(numOfNotifications);
+
+ // JMX MbeanServer used inside single VM as if remote.
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+
+ JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
+ JMXConnectorServer cs =
+ JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ cs.start();
+
+ JMXServiceURL addr = cs.getAddress();
+ JMXConnector cc = JMXConnectorFactory.connect(addr);
+ MBeanServerConnection mbsc = cc.getMBeanServerConnection();
+
+ // ----
+ System.out.println("Add me as notification listener");
+ mbsc.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME,
+ this, null, null);
+ System.out.println("---- OK\n") ;
+
+ // ----
+ System.out.println("Create and register the MBean");
+ ObjectName objName = new ObjectName("sqe:type=Basic,protocol=rmi") ;
+ mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName);
+ System.out.println("---- OK\n") ;
+
+ // ----
+ System.out.println("Call method throwException on our MXBean");
+
+ try {
+ mbsc.invoke(objName, "throwException", null, null);
+ errorCount++;
+ System.out.println("(ERROR) Did not get awaited MBeanException") ;
+ } catch (MBeanException mbe) {
+ System.out.println("(OK) Got awaited MBeanException") ;
+ Throwable cause = mbe.getCause();
+
+ if ( cause instanceof java.lang.Exception ) {
+ System.out.println("(OK) Cause is of the right class") ;
+ String mess = cause.getMessage();
+
+ if ( mess.equals(Basic.EXCEPTION_MESSAGE ) ) {
+ System.out.println("(OK) Cause message is fine") ;
+ } else {
+ errorCount++;
+ System.out.println("(ERROR) Cause has message "
+ + cause.getMessage()
+ + " as we expect "
+ + Basic.EXCEPTION_MESSAGE) ;
+ }
+ } else {
+ errorCount++;
+ System.out.println("(ERROR) Cause is of class "
+ + cause.getClass().getName()
+ + " as we expect java.lang.Exception") ;
+ }
+ } catch (Exception e) {
+ errorCount++;
+ System.out.println("(ERROR) Did not get awaited MBeanException but "
+ + e) ;
+ Utils.printThrowable(e, true);
+ }
+ System.out.println("---- DONE\n") ;
+
+ // ----
+ System.out.println("Call method throwError on our MXBean");
+
+ try {
+ mbsc.invoke(objName, "throwError", null, null);
+ errorCount++;
+ System.out.println("(ERROR) Did not get awaited RuntimeErrorException") ;
+ } catch (RuntimeErrorException ree) {
+ System.out.println("(OK) Got awaited RuntimeErrorException") ;
+ Throwable cause = ree.getCause();
+
+ if ( cause instanceof java.lang.InternalError ) {
+ System.out.println("(OK) Cause is of the right class") ;
+ String mess = cause.getMessage();
+
+ if ( mess.equals(Basic.EXCEPTION_MESSAGE ) ) {
+ System.out.println("(OK) Cause message is fine") ;
+ } else {
+ errorCount++;
+ System.out.println("(ERROR) Cause has message "
+ + cause.getMessage()
+ + " as we expect "
+ + Basic.EXCEPTION_MESSAGE) ;
+ }
+ } else {
+ errorCount++;
+ System.out.println("(ERROR) Cause is of class "
+ + cause.getClass().getName()
+ + " as we expect java.lang.InternalError") ;
+ }
+ } catch (Exception e) {
+ errorCount++;
+ System.out.println("(ERROR) Did not get awaited RuntimeErrorException but "
+ + e) ;
+ Utils.printThrowable(e, true);
+ }
+ System.out.println("---- DONE\n") ;
+
+ // ----
+ System.out.println("Unregister the MBean");
+ mbsc.unregisterMBean(objName);
+ System.out.println("---- OK\n") ;
+
+ Thread.sleep(timeForNotificationInSeconds * 1000);
+ int numOfReceivedNotif = notifList.size();
+
+ if ( numOfReceivedNotif == numOfNotifications ) {
+ System.out.println("(OK) We received "
+ + numOfNotifications
+ + " Notifications") ;
+ } else {
+ errorCount++;
+ System.out.println("(ERROR) We received "
+ + numOfReceivedNotif
+ + " Notifications in place of "
+ + numOfNotifications) ;
+ }
+ } catch(Exception e) {
+ Utils.printThrowable(e, true) ;
+ throw new RuntimeException(e);
+ }
+
+ if ( errorCount == 0 ) {
+ System.out.println("MXBeanExceptionHandlingTest::run: Done without any error") ;
+ } else {
+ System.out.println("MXBeanExceptionHandlingTest::run: Done with "
+ + errorCount
+ + " error(s)") ;
+ throw new RuntimeException("errorCount = " + errorCount);
+ }
+ }
+
+ public void handleNotification(Notification notification, Object handback) {
+ System.out.println("MXBeanExceptionHandlingTest::handleNotification: Received "
+ + notification);
+ notifList.add(notification);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/MXBeanInteropTest1.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058865
+ * @summary Test all MXBeans available by default on the platform
+ * @author Olivier Lagneau
+ * @modules java.management
+ * @library /lib/testlibrary
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanInteropTest1
+ */
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import java.lang.management.ClassLoadingMXBean;
+import java.lang.management.CompilationMXBean;
+import java.lang.management.GarbageCollectorMXBean;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.management.MemoryManagerMXBean;
+import java.lang.management.MemoryPoolMXBean;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.management.RuntimeMXBean;
+import java.lang.management.ThreadMXBean;
+
+import javax.management.JMX;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+public class MXBeanInteropTest1 {
+
+ /*
+ * First Debug properties and arguments are collect in expected
+ * map (argName, value) format, then calls original test's run method.
+ */
+ public static void main(String args[]) throws Exception {
+
+ System.out.println("=================================================");
+
+ // Parses parameters
+ Utils.parseDebugProperties();
+ Map<String, Object> map = Utils.parseParameters(args) ;
+
+ // Run test
+ MXBeanInteropTest1 test = new MXBeanInteropTest1();
+ test.run(map);
+
+ }
+
+ public void run(Map<String, Object> args) {
+
+ System.out.println("MXBeanInteropTest1::run: Start") ;
+ int errorCount = 0 ;
+
+ try {
+ // JMX MbeanServer used inside single VM as if remote.
+ // MBeanServer mbs = MBeanServerFactory.newMBeanServer();
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+
+ JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
+ JMXConnectorServer cs =
+ JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ cs.start();
+
+ JMXServiceURL addr = cs.getAddress();
+ JMXConnector cc = JMXConnectorFactory.connect(addr);
+ MBeanServerConnection mbsc = cc.getMBeanServerConnection();
+
+ // Print out registered java.lang.management MXBeans found
+ // in the remote jvm.
+ printMBeans(mbsc) ;
+
+ // For each possible kind of JDK 5 defined MXBean, we retrieve its
+ // MBeanInfo and print it and we call all getters and print
+ // their output.
+ errorCount += doClassLoadingMXBeanTest(mbsc) ;
+ errorCount += doMemoryMXBeanTest(mbsc) ;
+ errorCount += doThreadMXBeanTest(mbsc) ;
+ errorCount += doRuntimeMXBeanTest(mbsc) ;
+ errorCount += doOperatingSystemMXBeanTest(mbsc) ;
+ errorCount += doCompilationMXBeanTest(mbsc) ;
+ errorCount += doGarbageCollectorMXBeanTest(mbsc) ;
+ errorCount += doMemoryManagerMXBeanTest(mbsc) ;
+ errorCount += doMemoryPoolMXBeanTest(mbsc) ;
+
+ // Terminate the JMX Client
+ cc.close();
+
+ } catch(Exception e) {
+ Utils.printThrowable(e, true) ;
+ throw new RuntimeException(e);
+ }
+
+ if ( errorCount == 0 ) {
+ System.out.println("MXBeanInteropTest1::run: Done without any error") ;
+ } else {
+ System.out.println("MXBeanInteropTest1::run: Done with "
+ + errorCount
+ + " error(s)") ;
+ throw new RuntimeException("errorCount = " + errorCount);
+ }
+ }
+
+ /**
+ * Prints all MBeans of domain java.lang.
+ * They are MBeans related to the JSR 174 that defines
+ * package java.lang.management.
+ */
+ private static void printMBeans(MBeanServerConnection mbsc) throws Exception {
+ ObjectName filterName = new ObjectName("java.lang:*");
+ Set<ObjectName> set = mbsc.queryNames(filterName, null);
+
+ if ( set.size() == 0 ) {
+ throw new RuntimeException("(ERROR) No MBean found with filter "
+ + filterName);
+ }
+
+ System.out.println("---- MBeans found in domain java.lang :");
+
+ for (Iterator<ObjectName> iter = set.iterator(); iter.hasNext(); ) {
+ System.out.println(iter.next().toString());
+ }
+
+ System.out.println("\n") ;
+ }
+
+
+ private final int doClassLoadingMXBeanTest(MBeanServerConnection mbsc) {
+ int errorCount = 0 ;
+ System.out.println("---- ClassLoadingMXBean") ;
+
+ try {
+ ObjectName classLoadingName =
+ new ObjectName(ManagementFactory.CLASS_LOADING_MXBEAN_NAME) ;
+ MBeanInfo mbInfo = mbsc.getMBeanInfo(classLoadingName);
+ errorCount += checkNonEmpty(mbInfo);
+ System.out.println("getMBeanInfo\t\t"
+ + mbInfo);
+ ClassLoadingMXBean classLoading = null;
+
+ classLoading = JMX.newMXBeanProxy(mbsc,
+ classLoadingName,
+ ClassLoadingMXBean.class) ;
+ System.out.println("getLoadedClassCount\t\t"
+ + classLoading.getLoadedClassCount());
+ System.out.println("getTotalLoadedClassCount\t\t"
+ + classLoading.getTotalLoadedClassCount());
+ System.out.println("getUnloadedClassCount\t\t"
+ + classLoading.getUnloadedClassCount());
+ System.out.println("isVerbose\t\t"
+ + classLoading.isVerbose());
+
+ System.out.println("---- OK\n") ;
+
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++ ;
+ System.out.println("---- ERROR\n") ;
+ }
+
+ return errorCount ;
+ }
+
+
+ private final int doMemoryMXBeanTest(MBeanServerConnection mbsc) {
+ int errorCount = 0 ;
+ System.out.println("---- MemoryMXBean") ;
+
+ try {
+ ObjectName memoryName =
+ new ObjectName(ManagementFactory.MEMORY_MXBEAN_NAME) ;
+ MBeanInfo mbInfo = mbsc.getMBeanInfo(memoryName);
+ errorCount += checkNonEmpty(mbInfo);
+ System.out.println("getMBeanInfo\t\t"
+ + mbInfo);
+ MemoryMXBean memory = null ;
+
+ memory =
+ JMX.newMXBeanProxy(mbsc,
+ memoryName,
+ MemoryMXBean.class,
+ true) ;
+ System.out.println("getMemoryHeapUsage\t\t"
+ + memory.getHeapMemoryUsage());
+ System.out.println("getNonHeapMemoryHeapUsage\t\t"
+ + memory.getNonHeapMemoryUsage());
+ System.out.println("getObjectPendingFinalizationCount\t\t"
+ + memory.getObjectPendingFinalizationCount());
+ System.out.println("isVerbose\t\t"
+ + memory.isVerbose());
+
+ System.out.println("---- OK\n") ;
+
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++ ;
+ System.out.println("---- ERROR\n") ;
+ }
+
+ return errorCount ;
+ }
+
+
+ private final int doThreadMXBeanTest(MBeanServerConnection mbsc) {
+ int errorCount = 0 ;
+ System.out.println("---- ThreadMXBean") ;
+
+ try {
+ ObjectName threadName =
+ new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME) ;
+ MBeanInfo mbInfo = mbsc.getMBeanInfo(threadName);
+ errorCount += checkNonEmpty(mbInfo);
+ System.out.println("getMBeanInfo\t\t" + mbInfo);
+ ThreadMXBean thread = null ;
+
+ thread =
+ JMX.newMXBeanProxy(mbsc,
+ threadName,
+ ThreadMXBean.class) ;
+ System.out.println("findMonitorDeadlockedThreads\t\t"
+ + thread.findMonitorDeadlockedThreads());
+ long[] threadIDs = thread.getAllThreadIds() ;
+ System.out.println("getAllThreadIds\t\t"
+ + threadIDs);
+
+ for ( long threadID : threadIDs ) {
+ System.out.println("getThreadInfo long\t\t"
+ + thread.getThreadInfo(threadID));
+ System.out.println("getThreadInfo long, int\t\t"
+ + thread.getThreadInfo(threadID, 2));
+ }
+
+ System.out.println("getThreadInfo long[]\t\t"
+ + thread.getThreadInfo(threadIDs));
+ System.out.println("getThreadInfo long[], int\t\t"
+ + thread.getThreadInfo(threadIDs, 2));
+ System.out.println("getDaemonThreadCount\t\t"
+ + thread.getDaemonThreadCount());
+ System.out.println("getPeakThreadCount\t\t"
+ + thread.getPeakThreadCount());
+ System.out.println("getThreadCount\t\t"
+ + thread.getThreadCount());
+ System.out.println("getTotalStartedThreadCount\t\t"
+ + thread.getTotalStartedThreadCount());
+ boolean supported = thread.isThreadContentionMonitoringSupported() ;
+ System.out.println("isThreadContentionMonitoringSupported\t\t"
+ + supported);
+
+ if ( supported ) {
+ System.out.println("isThreadContentionMonitoringEnabled\t\t"
+ + thread.isThreadContentionMonitoringEnabled());
+ }
+
+ supported = thread.isThreadCpuTimeSupported() ;
+ System.out.println("isThreadCpuTimeSupported\t\t"
+ + supported);
+
+ if ( supported ) {
+ System.out.println("isThreadCpuTimeEnabled\t\t"
+ + thread.isThreadCpuTimeEnabled());
+
+ for (long id : threadIDs) {
+ System.out.println("getThreadCpuTime(" + id + ")\t\t"
+ + thread.getThreadCpuTime(id));
+ System.out.println("getThreadUserTime(" + id + ")\t\t"
+ + thread.getThreadUserTime(id));
+ }
+ }
+
+ supported = thread.isCurrentThreadCpuTimeSupported() ;
+ System.out.println("isCurrentThreadCpuTimeSupported\t\t"
+ + supported);
+
+ if ( supported ) {
+ System.out.println("getCurrentThreadCpuTime\t\t"
+ + thread.getCurrentThreadCpuTime());
+ System.out.println("getCurrentThreadUserTime\t\t"
+ + thread.getCurrentThreadUserTime());
+ }
+
+ thread.resetPeakThreadCount() ;
+
+ System.out.println("---- OK\n") ;
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++ ;
+ System.out.println("---- ERROR\n") ;
+ }
+
+ return errorCount ;
+ }
+
+
+ private final int doRuntimeMXBeanTest(MBeanServerConnection mbsc) {
+ int errorCount = 0 ;
+ System.out.println("---- RuntimeMXBean") ;
+
+ try {
+ ObjectName runtimeName =
+ new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME) ;
+ MBeanInfo mbInfo = mbsc.getMBeanInfo(runtimeName);
+ errorCount += checkNonEmpty(mbInfo);
+ System.out.println("getMBeanInfo\t\t" + mbInfo);
+ RuntimeMXBean runtime = null;
+
+ runtime =
+ JMX.newMXBeanProxy(mbsc,
+ runtimeName,
+ RuntimeMXBean.class) ;
+ System.out.println("getClassPath\t\t"
+ + runtime.getClassPath());
+ System.out.println("getInputArguments\t\t"
+ + runtime.getInputArguments());
+ System.out.println("getLibraryPath\t\t"
+ + runtime.getLibraryPath());
+ System.out.println("getManagementSpecVersion\t\t"
+ + runtime.getManagementSpecVersion());
+ System.out.println("getName\t\t"
+ + runtime.getName());
+ System.out.println("getSpecName\t\t"
+ + runtime.getSpecName());
+ System.out.println("getSpecVendor\t\t"
+ + runtime.getSpecVendor());
+ System.out.println("getSpecVersion\t\t"
+ + runtime.getSpecVersion());
+ System.out.println("getStartTime\t\t"
+ + runtime.getStartTime());
+ System.out.println("getSystemProperties\t\t"
+ + runtime.getSystemProperties());
+ System.out.println("getUptime\t\t"
+ + runtime.getUptime());
+ System.out.println("getVmName\t\t"
+ + runtime.getVmName());
+ System.out.println("getVmVendor\t\t"
+ + runtime.getVmVendor());
+ System.out.println("getVmVersion\t\t"
+ + runtime.getVmVersion());
+ boolean supported = runtime.isBootClassPathSupported() ;
+ System.out.println("isBootClassPathSupported\t\t"
+ + supported);
+
+ if ( supported ) {
+ System.out.println("getBootClassPath\t\t"
+ + runtime.getBootClassPath());
+ }
+
+ System.out.println("---- OK\n") ;
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++ ;
+ System.out.println("---- ERROR\n") ;
+ }
+
+ return errorCount ;
+ }
+
+
+ private final int doOperatingSystemMXBeanTest(MBeanServerConnection mbsc) {
+ int errorCount = 0 ;
+ System.out.println("---- OperatingSystemMXBean") ;
+
+ try {
+ ObjectName operationName =
+ new ObjectName(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME) ;
+ MBeanInfo mbInfo = mbsc.getMBeanInfo(operationName);
+ errorCount += checkNonEmpty(mbInfo);
+ System.out.println("getMBeanInfo\t\t" + mbInfo);
+ OperatingSystemMXBean operation = null ;
+
+ operation =
+ JMX.newMXBeanProxy(mbsc,
+ operationName,
+ OperatingSystemMXBean.class) ;
+ System.out.println("getArch\t\t"
+ + operation.getArch());
+ System.out.println("getAvailableProcessors\t\t"
+ + operation.getAvailableProcessors());
+ System.out.println("getName\t\t"
+ + operation.getName());
+ System.out.println("getVersion\t\t"
+ + operation.getVersion());
+
+ System.out.println("---- OK\n") ;
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++ ;
+ System.out.println("---- ERROR\n") ;
+ }
+
+ return errorCount ;
+ }
+
+
+ private final int doCompilationMXBeanTest(MBeanServerConnection mbsc) {
+ int errorCount = 0 ;
+ System.out.println("---- CompilationMXBean") ;
+
+ try {
+ ObjectName compilationName =
+ new ObjectName(ManagementFactory.COMPILATION_MXBEAN_NAME);
+
+ if ( mbsc.isRegistered(compilationName) ) {
+ MBeanInfo mbInfo = mbsc.getMBeanInfo(compilationName);
+ errorCount += checkNonEmpty(mbInfo);
+ System.out.println("getMBeanInfo\t\t" + mbInfo);
+ CompilationMXBean compilation = null ;
+
+ compilation =
+ JMX.newMXBeanProxy(mbsc,
+ compilationName,
+ CompilationMXBean.class) ;
+ System.out.println("getName\t\t"
+ + compilation.getName());
+ boolean supported =
+ compilation.isCompilationTimeMonitoringSupported() ;
+ System.out.println("isCompilationTimeMonitoringSupported\t\t"
+ + supported);
+
+ if ( supported ) {
+ System.out.println("getTotalCompilationTime\t\t"
+ + compilation.getTotalCompilationTime());
+ }
+ }
+
+ System.out.println("---- OK\n") ;
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++ ;
+ System.out.println("---- ERROR\n") ;
+ }
+
+ return errorCount ;
+ }
+
+
+ private final int doGarbageCollectorMXBeanTest(MBeanServerConnection mbsc) {
+ int errorCount = 0 ;
+ System.out.println("---- GarbageCollectorMXBean") ;
+
+ try {
+ ObjectName filterName =
+ new ObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE
+ + ",*");
+ Set<ObjectName> onSet = mbsc.queryNames(filterName, null);
+
+ for (Iterator<ObjectName> iter = onSet.iterator(); iter.hasNext(); ) {
+ ObjectName garbageName = iter.next() ;
+ System.out.println("-------- " + garbageName) ;
+ MBeanInfo mbInfo = mbsc.getMBeanInfo(garbageName);
+ errorCount += checkNonEmpty(mbInfo);
+ System.out.println("getMBeanInfo\t\t" + mbInfo);
+ GarbageCollectorMXBean garbage = null ;
+
+ garbage =
+ JMX.newMXBeanProxy(mbsc,
+ garbageName,
+ GarbageCollectorMXBean.class) ;
+ System.out.println("getCollectionCount\t\t"
+ + garbage.getCollectionCount());
+ System.out.println("getCollectionTime\t\t"
+ + garbage.getCollectionTime());
+ }
+
+ System.out.println("---- OK\n") ;
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++ ;
+ System.out.println("---- ERROR\n") ;
+ }
+
+ return errorCount ;
+ }
+
+
+ private final int doMemoryManagerMXBeanTest(MBeanServerConnection mbsc) {
+ int errorCount = 0 ;
+ System.out.println("---- MemoryManagerMXBean") ;
+
+ try {
+ ObjectName filterName =
+ new ObjectName(ManagementFactory.MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE
+ + ",*");
+ Set<ObjectName> onSet = mbsc.queryNames(filterName, null);
+
+ for (Iterator<ObjectName> iter = onSet.iterator(); iter.hasNext(); ) {
+ ObjectName memoryManagerName = iter.next() ;
+ System.out.println("-------- " + memoryManagerName) ;
+ MBeanInfo mbInfo = mbsc.getMBeanInfo(memoryManagerName);
+ System.out.println("getMBeanInfo\t\t" + mbInfo);
+ errorCount += checkNonEmpty(mbInfo);
+ MemoryManagerMXBean memoryManager = null;
+
+ memoryManager =
+ JMX.newMXBeanProxy(mbsc,
+ memoryManagerName,
+ MemoryManagerMXBean.class) ;
+ System.out.println("getMemoryPoolNames\t\t"
+ + Arrays.deepToString(memoryManager.getMemoryPoolNames()));
+ System.out.println("getName\t\t"
+ + memoryManager.getName());
+ System.out.println("isValid\t\t"
+ + memoryManager.isValid());
+ }
+
+ System.out.println("---- OK\n") ;
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++ ;
+ System.out.println("---- ERROR\n") ;
+ }
+
+ return errorCount ;
+ }
+
+
+ private final int doMemoryPoolMXBeanTest(MBeanServerConnection mbsc) {
+ int errorCount = 0 ;
+ System.out.println("---- MemoryPoolMXBean") ;
+
+ try {
+ ObjectName filterName =
+ new ObjectName(ManagementFactory.MEMORY_POOL_MXBEAN_DOMAIN_TYPE
+ + ",*");
+ Set<ObjectName> onSet = mbsc.queryNames(filterName, null);
+
+ for (Iterator<ObjectName> iter = onSet.iterator(); iter.hasNext(); ) {
+ ObjectName memoryPoolName = iter.next() ;
+ System.out.println("-------- " + memoryPoolName) ;
+ MBeanInfo mbInfo = mbsc.getMBeanInfo(memoryPoolName);
+ errorCount += checkNonEmpty(mbInfo);
+ System.out.println("getMBeanInfo\t\t" + mbInfo);
+ MemoryPoolMXBean memoryPool = null;
+
+ memoryPool =
+ JMX.newMXBeanProxy(mbsc,
+ memoryPoolName,
+ MemoryPoolMXBean.class,
+ true) ;
+ System.out.println("getCollectionUsage\t\t"
+ + memoryPool.getCollectionUsage());
+ System.out.println("getMemoryManagerNames\t\t"
+ + Arrays.deepToString(memoryPool.getMemoryManagerNames()));
+ System.out.println("getName\t\t"
+ + memoryPool.getName());
+ System.out.println("getPeakUsage\t\t"
+ + memoryPool.getPeakUsage());
+ System.out.println("getType\t\t"
+ + memoryPool.getType());
+ System.out.println("getUsage\t\t"
+ + memoryPool.getUsage());
+ System.out.println("isValid\t\t"
+ + memoryPool.isValid());
+ boolean supported = memoryPool.isUsageThresholdSupported() ;
+ System.out.println("isUsageThresholdSupported\t\t"
+ + supported);
+
+ if ( supported ) {
+ System.out.println("getUsageThreshold\t\t"
+ + memoryPool.getUsageThreshold());
+ System.out.println("isUsageThresholdExceeded\t\t"
+ + memoryPool.isUsageThresholdExceeded());
+ System.out.println("getUsageThresholdCount\t\t"
+ + memoryPool.getUsageThresholdCount());
+ }
+
+ supported = memoryPool.isCollectionUsageThresholdSupported() ;
+ System.out.println("isCollectionUsageThresholdSupported\t\t"
+ + supported);
+
+ if ( supported ) {
+ System.out.println("getCollectionUsageThreshold\t\t"
+ + memoryPool.getCollectionUsageThreshold());
+ System.out.println("getCollectionUsageThresholdCount\t\t"
+ + memoryPool.getCollectionUsageThresholdCount());
+ System.out.println("isCollectionUsageThresholdExceeded\t\t"
+ + memoryPool.isCollectionUsageThresholdExceeded());
+ }
+
+ memoryPool.resetPeakUsage();
+ }
+
+ System.out.println("---- OK\n") ;
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++ ;
+ System.out.println("---- ERROR\n") ;
+ }
+
+ return errorCount ;
+ }
+
+
+ private int checkNonEmpty(MBeanInfo mbi) {
+ if ( mbi.toString().length() == 0 ) {
+ System.out.println("(ERROR) MBeanInfo is empty !");
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/MXBeanInteropTest2.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058865
+ * @summary Checks access to test MXBean
+ * @author Olivier Lagneau
+ * @modules java.management
+ * @library /lib/testlibrary
+ * @compile Basic.java
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanInteropTest2
+ */
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.Attribute;
+import javax.management.JMX;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanServer;
+import java.lang.management.ManagementFactory;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+public class MXBeanInteropTest2 {
+
+ private static String BASIC_MXBEAN_CLASS_NAME = "Basic";
+
+ /*
+ * First Debug properties and arguments are collect in expected
+ * map (argName, value) format, then calls original test's run method.
+ */
+ public static void main(String args[]) throws Exception {
+
+ System.out.println("=================================================");
+
+ // Parses parameters
+ Utils.parseDebugProperties();
+ Map<String, Object> map = Utils.parseParameters(args) ;
+
+ // Run test
+ MXBeanInteropTest2 test = new MXBeanInteropTest2();
+ test.run(map);
+
+ }
+
+ public void run(Map<String, Object> args) {
+
+ System.out.println("MXBeanInteropTest2::run: Start") ;
+ int errorCount = 0 ;
+
+ try {
+ // JMX MbeanServer used inside single VM as if remote.
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+
+ JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
+ JMXConnectorServer cs =
+ JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ cs.start();
+
+ JMXServiceURL addr = cs.getAddress();
+ JMXConnector cc = JMXConnectorFactory.connect(addr);
+ MBeanServerConnection mbsc = cc.getMBeanServerConnection();
+
+ // Prints all MBeans whatever the domain is.
+ printMBeans(mbsc) ;
+
+ // Call test body
+ errorCount += doBasicMXBeanTest(mbsc) ;
+
+ // Terminate the JMX Client
+ cc.close();
+
+ } catch(Exception e) {
+ Utils.printThrowable(e, true) ;
+ throw new RuntimeException(e);
+ }
+
+ if ( errorCount == 0 ) {
+ System.out.println("MXBeanInteropTest2::run: Done without any error") ;
+ } else {
+ System.out.println("MXBeanInteropTest2::run: Done with "
+ + errorCount
+ + " error(s)") ;
+ throw new RuntimeException("errorCount = " + errorCount);
+ }
+ }
+
+
+ /**
+ * Prints all MBeans whatever the domain is.
+ */
+ private static void printMBeans(MBeanServerConnection mbsc) throws Exception {
+ Set<ObjectName> set = mbsc.queryNames(null, null);
+ System.out.println("---- MBeans found :");
+
+ for (Iterator<ObjectName> iter = set.iterator(); iter.hasNext(); ) {
+ System.out.println(iter.next().toString());
+ }
+
+ System.out.println("\n") ;
+ }
+
+
+ private final int doBasicMXBeanTest(MBeanServerConnection mbsc) {
+ int errorCount = 0 ;
+ System.out.println("---- doBasicMXBeanTest") ;
+
+ try {
+ ObjectName objName =
+ new ObjectName("sqe:type=BasicMXBean") ;
+ mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName);
+ MBeanInfo mbInfo = mbsc.getMBeanInfo(objName);
+ printMBeanInfo(mbInfo);
+ System.out.println("---- OK\n") ;
+ System.out.println("getMBeanInfo\t\t"
+ + mbInfo);
+ System.out.println("---- OK\n") ;
+
+ System.out.println("Check mxbean field in the MBeanInfo");
+ String mxbeanField =
+ (String)mbInfo.getDescriptor().getFieldValue(JMX.MXBEAN_FIELD);
+
+ if ( mxbeanField == null || ! mxbeanField.equals("true")) {
+ System.out.println("---- ERROR : Improper mxbean field value "
+ + mxbeanField);
+ errorCount++;
+ }
+ System.out.println("---- OK\n") ;
+
+ System.out.println("Set attribute ObjectNameAtt");
+ Attribute att = new Attribute("ObjectNameAtt", objName);
+ mbsc.setAttribute(objName, att);
+ ObjectName value =
+ (ObjectName)mbsc.getAttribute(objName, "ObjectNameAtt");
+
+ if ( ! value.equals(objName) ) {
+ errorCount++;
+ System.out.println("---- ERROR : setAttribute failed, got "
+ + value
+ + " while expecting "
+ + objName);
+ }
+ System.out.println("---- OK\n") ;
+
+ System.out.println("Call operation doNothing");
+ mbsc.invoke(objName, "doNothing", null, null);
+ System.out.println("---- OK\n") ;
+
+ System.out.println("Call operation getWeather");
+ Object weather = mbsc.invoke(objName,
+ "getWeather",
+ new Object[]{Boolean.TRUE},
+ new String[]{"boolean"});
+ System.out.println("Weather is " + weather);
+ System.out.println("---- OK\n") ;
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++ ;
+ System.out.println("---- ERROR\n") ;
+ }
+
+ return errorCount ;
+ }
+
+ private void printMBeanInfo(MBeanInfo mbInfo) {
+ System.out.println("Description " + mbInfo.getDescription());
+
+ for (MBeanConstructorInfo ctor : mbInfo.getConstructors()) {
+ System.out.println("Constructor " + ctor.getName());
+ }
+
+ for (MBeanAttributeInfo att : mbInfo.getAttributes()) {
+ System.out.println("Attribute " + att.getName()
+ + " [" + att.getType() + "]");
+ }
+
+ for (MBeanOperationInfo oper : mbInfo.getOperations()) {
+ System.out.println("Operation " + oper.getName());
+ }
+
+ for (MBeanNotificationInfo notif : mbInfo.getNotifications()) {
+ System.out.println("Notification " + notif.getName());
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/MXBeanLoadingTest1.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058865
+ * @summary Checks correct collection of MXBean's class after unregistration
+ * @author Olivier Lagneau
+ * @modules java.management
+ * @library /lib/testlibrary
+ * @run main/othervm/timeout=300 MXBeanLoadingTest1
+ */
+
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.Map;
+import javax.management.Attribute;
+import javax.management.JMX;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.MXBean;
+import javax.management.ObjectName;
+import javax.management.loading.PrivateMLet;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
+public class MXBeanLoadingTest1 {
+
+ public static void main(String[] args) throws Exception {
+ MXBeanLoadingTest1 test = new MXBeanLoadingTest1();
+ test.run((Map<String, Object>)null);
+ }
+
+
+ public void run(Map<String, Object> args) {
+
+ System.out.println("MXBeanLoadingTest1::run: Start") ;
+
+ try {
+ System.out.println("We ensure no reference is retained on MXBean class"
+ + " after it is unregistered. We take time to perform"
+ + " some little extra check of Descriptors, MBean*Info.");
+
+ ClassLoader myClassLoader = MXBeanLoadingTest1.class.getClassLoader();
+
+ if (!(myClassLoader instanceof URLClassLoader)) {
+ String message = "(ERROR) Test's class loader is not " +
+ "a URLClassLoader";
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ URLClassLoader myURLClassLoader = (URLClassLoader) myClassLoader;
+ URL[] urls = myURLClassLoader.getURLs();
+ PrivateMLet mlet = new PrivateMLet(urls, null, false);
+ Class<?> shadowClass = mlet.loadClass(TestMXBean.class.getName());
+
+ if (shadowClass == TestMXBean.class) {
+ String message = "(ERROR) MLet got original TestMXBean, not shadow";
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+ shadowClass = null;
+
+ MBeanServer mbs = MBeanServerFactory.createMBeanServer();
+ ObjectName mletName = new ObjectName("x:type=mlet");
+ mbs.registerMBean(mlet, mletName);
+
+ ObjectName testName = new ObjectName("x:type=test");
+ mbs.createMBean(Test.class.getName(), testName, mletName);
+
+ // That test fails because the MXBean instance is accessed via
+ // a delegate OpenMBean which has
+ ClassLoader testLoader = mbs.getClassLoaderFor(testName);
+
+ if (testLoader != mlet) {
+ System.out.println("MLet " + mlet);
+ String message = "(ERROR) MXBean's class loader is not MLet: "
+ + testLoader;
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+ testLoader = null;
+
+
+ // Cycle get/set/get of the attribute of type Luis.
+ // We check the set is effective.
+ CompositeData cd_B = (CompositeData)mbs.getAttribute(testName, "B");
+ CompositeType compType_B = cd_B.getCompositeType();
+
+ CompositeDataSupport cds_B =
+ new CompositeDataSupport(compType_B,
+ new String[]{"something"},
+ new Object[]{Integer.valueOf(13)});
+ Attribute myAtt = new Attribute("B", cds_B);
+ mbs.setAttribute(testName, myAtt);
+
+ CompositeData cd_B2 = (CompositeData)mbs.getAttribute(testName, "B");
+
+ if ( ((Integer)cd_B2.get("something")).intValue() != 13 ) {
+ String message = "(ERROR) The setAttribute of att B did not work;"
+ + " expect Luis.something = 13 but got "
+ + cd_B2.get("something");
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ MBeanInfo info = mbs.getMBeanInfo(testName);
+ String mxbeanField =
+ (String)info.getDescriptor().getFieldValue(JMX.MXBEAN_FIELD);
+
+ if ( mxbeanField == null || ! mxbeanField.equals("true")) {
+ String message = "(ERROR) Improper mxbean field value "
+ + mxbeanField;
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ // Check the 2 attributes.
+ MBeanAttributeInfo[] attrs = info.getAttributes();
+
+ if ( attrs.length == 2 ) {
+ for (MBeanAttributeInfo mbai : attrs) {
+ String originalTypeFieldValue =
+ (String)mbai.getDescriptor().getFieldValue(JMX.ORIGINAL_TYPE_FIELD);
+ OpenType<?> openTypeFieldValue =
+ (OpenType<?>)mbai.getDescriptor().getFieldValue(JMX.OPEN_TYPE_FIELD);
+
+ if ( mbai.getName().equals("A") ) {
+ if ( !mbai.isReadable() || !mbai.isWritable()
+ || mbai.isIs()
+ || !mbai.getType().equals("int") ) {
+ String message = "(ERROR) Unexpected MBeanAttributeInfo for A "
+ + mbai;
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ if ( ! originalTypeFieldValue.equals("int") ) {
+ String message = "(ERROR) Unexpected originalType in Descriptor for A "
+ + originalTypeFieldValue;
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ if ( ! openTypeFieldValue.equals(SimpleType.INTEGER) ) {
+ String message = "(ERROR) Unexpected openType in Descriptor for A "
+ + originalTypeFieldValue;
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+ } else if ( mbai.getName().equals("B") ) {
+ if ( !mbai.isReadable() || !mbai.isWritable()
+ || mbai.isIs()
+ || !mbai.getType().equals("javax.management.openmbean.CompositeData") ) {
+ String message = "(ERROR) Unexpected MBeanAttributeInfo for B "
+ + mbai;
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ if ( ! originalTypeFieldValue.equals(Luis.class.getName()) ) {
+ String message = "(ERROR) Unexpected originalType in Descriptor for B "
+ + originalTypeFieldValue;
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ if ( ! openTypeFieldValue.equals(compType_B) ) {
+ String message = "(ERROR) Unexpected openType in Descriptor for B "
+ + compType_B;
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+ } else {
+ String message = "(ERROR) Unknown attribute name";
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+ }
+ } else {
+ String message = "(ERROR) Unexpected MBeanAttributeInfo array"
+ + Arrays.deepToString(attrs);
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ // Check the MXBean operation.
+ MBeanOperationInfo[] ops = info.getOperations();
+ // The impact is ACTION_INFO as for a standard MBean it is UNKNOWN,
+ // logged 6320104.
+ if (ops.length != 1 || !ops[0].getName().equals("bogus")
+ || ops[0].getSignature().length > 0
+ || !ops[0].getReturnType().equals("void")) {
+ String message = "(ERROR) Unexpected MBeanOperationInfo array "
+ + Arrays.deepToString(ops);
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ String originalTypeFieldValue =
+ (String)ops[0].getDescriptor().getFieldValue(JMX.ORIGINAL_TYPE_FIELD);
+ OpenType<?> openTypeFieldValue =
+ (OpenType<?>)ops[0].getDescriptor().getFieldValue(JMX.OPEN_TYPE_FIELD);
+
+ if ( ! originalTypeFieldValue.equals("void") ) {
+ String message = "(ERROR) Unexpected originalType in Descriptor for bogus "
+ + originalTypeFieldValue;
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ if ( ! openTypeFieldValue.equals(SimpleType.VOID) ) {
+ String message = "(ERROR) Unexpected openType in Descriptor for bogus "
+ + originalTypeFieldValue;
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ // Check there is 2 constructors.
+ if (info.getConstructors().length != 2) {
+ String message = "(ERROR) Wrong number of constructors " +
+ "in introspected bean: " +
+ Arrays.asList(info.getConstructors());
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ // Check MXBean class name.
+ if (!info.getClassName().endsWith("Test")) {
+ String message = "(ERROR) Wrong info class name: " +
+ info.getClassName();
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+
+ mbs.unregisterMBean(testName);
+ mbs.unregisterMBean(mletName);
+
+ WeakReference<PrivateMLet> mletRef =
+ new WeakReference<PrivateMLet>(mlet);
+ mlet = null;
+
+ System.out.println("MXBean registered and unregistered, waiting for " +
+ "garbage collector to collect class loader");
+
+ for (int i = 0; i < 10000 && mletRef.get() != null; i++) {
+ System.gc();
+ Thread.sleep(1);
+ }
+
+ if (mletRef.get() == null)
+ System.out.println("(OK) class loader was GC'd");
+ else {
+ String message = "(ERROR) Class loader was not GC'd";
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+ } catch(Exception e) {
+ Utils.printThrowable(e, true) ;
+ throw new RuntimeException(e);
+ }
+
+ System.out.println("MXBeanLoadingTest1::run: Done without any error") ;
+ }
+
+
+ // I agree the use of the MXBean annotation and the MXBean suffix for the
+ // interface name are redundant but however harmless.
+ //
+ @MXBean(true)
+ public static interface TestMXBean {
+ public void bogus();
+ public int getA();
+ public void setA(int a);
+ public Luis getB();
+ public void setB(Luis mi);
+ }
+
+
+ public static class Test implements TestMXBean {
+ private Luis luis = new Luis() ;
+ public Test() {}
+ public Test(int x) {}
+
+ public void bogus() {}
+ public int getA() {return 0;}
+ public void setA(int a) {}
+ public Luis getB() {return this.luis;}
+ public void setB(Luis luis) {this.luis = luis;}
+ }
+
+
+ public static class Luis {
+ private int something = 0;
+ public Luis() {}
+ public int getSomething() {return something;}
+ public void setSomething(int v) {something = v;}
+ public void doNothing() {}
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/MXBeanNotifTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,385 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058865
+ * @summary Checks MXBean proper registration both as its implementation class and interface
+ * @author Olivier Lagneau
+ * @modules java.management
+ * @library /lib/testlibrary
+ * @compile Basic.java
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanNotifTest -numOfNotifications 239 -timeForNotificationInSeconds 4
+ */
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import java.lang.management.ManagementFactory;
+
+import javax.management.Attribute;
+import javax.management.Descriptor;
+import javax.management.ImmutableDescriptor;
+import javax.management.MBeanServer;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+public class MXBeanNotifTest implements NotificationListener {
+
+ private static String BASIC_MXBEAN_CLASS_NAME = "Basic";
+ private static String BASIC_MXBEAN_INTERFACE_NAME = "BasicMXBean";
+
+ private long timeForNotificationInSeconds = 3L;
+ private int numOfNotifications = 1;
+ private BlockingQueue<Notification> notifList = null;
+ private int numOfNotifDescriptorElements = 13;
+
+ /*
+ * First Debug properties and arguments are collect in expected
+ * map (argName, value) format, then calls original test's run method.
+ */
+ public static void main(String args[]) throws Exception {
+
+ System.out.println("=================================================");
+
+ // Parses parameters
+ Utils.parseDebugProperties();
+ Map<String, Object> map = Utils.parseParameters(args) ;
+
+ // Run test
+ MXBeanNotifTest test = new MXBeanNotifTest();
+ test.run(map);
+
+ }
+
+ protected void parseArgs(Map<String, Object> args) throws Exception {
+
+ String arg = null;
+
+ // Init numOfNotifications
+ // It is the number of notifications we should trigger and check.
+ arg = (String)args.get("-numOfNotifications") ;
+ if (arg != null) {
+ numOfNotifications = (new Integer(arg)).intValue();
+ }
+
+ // Init timeForNotificationInSeconds
+ // It is the maximum time in seconds we wait for each notification.
+ arg = (String)args.get("-timeForEachNotificationInSeconds") ;
+ if (arg != null) {
+ timeForNotificationInSeconds = (new Long(arg)).longValue();
+ }
+
+ }
+
+ public void run(Map<String, Object> args) {
+
+ System.out.println("MXBeanNotifTest::run: Start") ;
+ int errorCount = 0 ;
+
+ try {
+ parseArgs(args);
+ notifList = new ArrayBlockingQueue<Notification>(numOfNotifications);
+
+ // JMX MbeanServer used inside single VM as if remote.
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+
+ JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
+ JMXConnectorServer cs =
+ JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ cs.start();
+
+ JMXServiceURL addr = cs.getAddress();
+ JMXConnector cc = JMXConnectorFactory.connect(addr);
+ MBeanServerConnection mbsc = cc.getMBeanServerConnection();
+
+ // ----
+ System.out.println("MXBeanNotifTest::run: Create and register the MBean");
+ ObjectName objName = new ObjectName("sqe:type=Basic,protocol=rmi") ;
+ mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName);
+ System.out.println("---- OK\n") ;
+
+ // ----
+ System.out.println("MXBeanNotifTest::run: Add me as notification listener");
+ mbsc.addNotificationListener(objName, this, null, null);
+
+ // ----
+ System.out.println("MXBeanNotifTest::run: Retrieve the Descriptor"
+ + " that should be in MBeanNotificationInfo");
+ TabularData tabData =
+ (TabularData)mbsc.getAttribute(objName, "NotifDescriptorAsMapAtt");
+ Map<String, String> descrMap = new HashMap<>();
+
+ for (Iterator<?> it = tabData.values().iterator(); it.hasNext(); ) {
+ CompositeData compData = (CompositeData)it.next();
+ descrMap.put((String)compData.get("key"),
+ (String)compData.get("value"));
+ }
+
+ Descriptor refNotifDescriptor = new ImmutableDescriptor(descrMap);
+ System.out.println("---- OK\n") ;
+
+ // ----
+ // Because the MBean holding the targeted attribute is MXBean, we
+ // should use for the setAttribute a converted form for the
+ // attribute value as described by the MXBean mapping rules.
+ // This explains all that lovely stuff for creating a
+ // TabularDataSupport.
+ //
+ // WARNING : the MBeanInfo of the MXBean used on opposite side
+ // is computed when the MBean is registered.
+ // It means the Descriptor considered for the MBeanNotificationInfo
+ // is not the one we set in the lines below, it is too late.
+ // However, we check that set is harmless when we check
+ // the MBeanNotificationInfo.
+ //
+ System.out.println("MXBeanNotifTest::run: Set a Map<String, String>"
+ + " attribute");
+ String typeName =
+ "java.util.Map<java.lang.String,java.lang.String>";
+ String[] keyValue = new String[] {"key", "value"};
+ OpenType<?>[] openTypes =
+ new OpenType<?>[] {SimpleType.STRING, SimpleType.STRING};
+ CompositeType rowType = new CompositeType(typeName, typeName,
+ keyValue, keyValue, openTypes);
+ TabularType tabType = new TabularType(typeName, typeName,
+ rowType, new String[]{"key"});
+ TabularDataSupport convertedDescrMap =
+ new TabularDataSupport(tabType);
+
+ for (int i = 0; i < numOfNotifDescriptorElements; i++) {
+ Object[] descrValue = {"field" + i, "value" + i};
+ CompositeData data =
+ new CompositeDataSupport(rowType, keyValue, descrValue);
+ convertedDescrMap.put(data);
+ }
+
+ Attribute descrAtt =
+ new Attribute("NotifDescriptorAsMapAtt", convertedDescrMap);
+ mbsc.setAttribute(objName, descrAtt);
+ System.out.println("---- OK\n") ;
+
+ // ----
+ System.out.println("MXBeanNotifTest::run: Compare the Descriptor from"
+ + " the MBeanNotificationInfo against a reference");
+ MBeanInfo mbInfo = mbsc.getMBeanInfo(objName);
+ errorCount += checkMBeanInfo(mbInfo, refNotifDescriptor);
+ System.out.println("---- DONE\n") ;
+
+ // ----
+ System.out.println("Check isInstanceOf(Basic)");
+
+ if ( ! mbsc.isInstanceOf(objName, BASIC_MXBEAN_CLASS_NAME) ) {
+ errorCount++;
+ System.out.println("---- ERROR isInstanceOf returned false\n") ;
+ } else {
+ System.out.println("---- OK\n") ;
+ }
+
+ // ----
+ System.out.println("Check isInstanceOf(BasicMXBean)");
+
+ if ( ! mbsc.isInstanceOf(objName, BASIC_MXBEAN_INTERFACE_NAME) ) {
+ errorCount++;
+ System.out.println("---- ERROR isInstanceOf returned false\n") ;
+ } else {
+ System.out.println("---- OK\n") ;
+ }
+
+ // ----
+ System.out.println("MXBeanNotifTest::run: Ask for "
+ + numOfNotifications + " notification(s)");
+ Object[] sendNotifParam = new Object[1];
+ String[] sendNotifSig = new String[]{"java.lang.String"};
+
+ for (int i = 0; i < numOfNotifications; i++) {
+ // Select which type of notification we ask for
+ if ( i % 2 == 0 ) {
+ sendNotifParam[0] = Basic.NOTIF_TYPE_0;
+ } else {
+ sendNotifParam[0] = Basic.NOTIF_TYPE_1;
+ }
+
+ // Trigger notification emission
+ mbsc.invoke(objName,
+ "sendNotification",
+ sendNotifParam,
+ sendNotifSig);
+
+ // Wait for it then check it when it comes early enough
+ Notification notif =
+ notifList.poll(timeForNotificationInSeconds,
+ TimeUnit.SECONDS) ;
+ // The very first notification is likely to come in slower than
+ // all the others. Because that test isn't targeting the speed
+ // notifications are delivered with, we prefer to secure it.
+ if (i == 0 && notif == null) {
+ System.out.println("MXBeanNotifTest::run: Wait extra "
+ + timeForNotificationInSeconds + " second(s) the "
+ + " very first notification");
+ notif = notifList.poll(timeForNotificationInSeconds,
+ TimeUnit.SECONDS);
+ }
+
+ if ( notif == null ) {
+ errorCount++;
+ System.out.println("---- ERROR No notification received"
+ + " within allocated " + timeForNotificationInSeconds
+ + " second(s) !");
+ } else {
+ errorCount +=
+ checkNotification(notif,
+ (String)sendNotifParam[0],
+ Basic.NOTIFICATION_MESSAGE,
+ objName);
+ }
+ }
+
+ int toc = 0;
+ while ( notifList.size() < 2 && toc < 10 ) {
+ Thread.sleep(499);
+ toc++;
+ }
+ System.out.println("---- DONE\n") ;
+ } catch(Exception e) {
+ Utils.printThrowable(e, true) ;
+ throw new RuntimeException(e);
+ }
+
+ if ( errorCount == 0 ) {
+ System.out.println("MXBeanNotifTest::run: Done without any error") ;
+ } else {
+ System.out.println("MXBeanNotifTest::run: Done with "
+ + errorCount
+ + " error(s)") ;
+ throw new RuntimeException("errorCount = " + errorCount);
+ }
+ }
+
+
+ private int checkMBeanInfo(MBeanInfo mbi, Descriptor refDescr) {
+ MBeanNotificationInfo[] notifsInfo = mbi.getNotifications();
+ int res = 0;
+
+ for (MBeanNotificationInfo mbni : notifsInfo) {
+ if ( mbni.getDescriptor().equals(refDescr) ) {
+ System.out.println("(OK)");
+ } else {
+ System.out.println("(ERROR) Descriptor of the notification is "
+ + mbni.getDescriptor()
+ + " as we expect "
+ + refDescr);
+ res++;
+ }
+ }
+
+ return res;
+ }
+
+
+ private int checkNotification(Notification notif,
+ String refType,
+ String refMessage,
+ ObjectName refSource) {
+ int res = 0;
+
+ Utils.debug(Utils.DEBUG_VERBOSE,
+ "\t getSource " + notif.getSource());
+ Utils.debug(Utils.DEBUG_VERBOSE,
+ "\t getMessage " + notif.getMessage());
+ Utils.debug(Utils.DEBUG_VERBOSE,
+ "\t getSequenceNumber " + notif.getSequenceNumber());
+ Utils.debug(Utils.DEBUG_VERBOSE,
+ "\t getTimeStamp " + notif.getTimeStamp());
+ Utils.debug(Utils.DEBUG_VERBOSE,
+ "\t getType " + notif.getType());
+ Utils.debug(Utils.DEBUG_VERBOSE,
+ "\t getUserData " + notif.getUserData());
+
+ if ( ! notif.getType().equals(refType) ) {
+ res++;
+ System.out.println("(ERROR) Type is not "
+ + refType + " in notification\n" + notif);
+ } else {
+ if ( notif.getType().equals(Basic.NOTIF_TYPE_0)
+ && ! (notif instanceof javax.management.Notification) ) {
+ res++;
+ System.out.println("(ERROR) Notification is not instance of "
+ + " javax.management.Notification but rather "
+ + notif.getClass().getName());
+ } else if ( notif.getType().equals(Basic.NOTIF_TYPE_1)
+ && ! (notif instanceof SqeNotification) ) {
+ res++;
+ System.out.println("(ERROR) Notification is not instance of "
+ + " javasoft.sqe.jmx.share.SqeNotification but rather "
+ + notif.getClass().getName());
+ }
+ }
+
+ if ( ! notif.getMessage().equals(refMessage) ) {
+ res++;
+ System.out.println("(ERROR) Message is not "
+ + refMessage + " in notification\n" + notif);
+ }
+
+ if ( ! notif.getSource().equals(refSource) ) {
+ res++;
+ System.out.println("(ERROR) Source is not "
+ + refSource + " in notification\n" + notif);
+ }
+
+ return res;
+ }
+
+ public void handleNotification(Notification notification, Object handback) {
+ Utils.debug(Utils.DEBUG_VERBOSE,
+ "MXBeanNotifTest::handleNotification: Received "
+ + notification);
+ notifList.add(notification);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/MXBeanWeirdParamTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058865
+ * @summary Checks that a serialized instance is not transmitted from an MXBean.
+ * All the communication should be done via Open Types
+ * @author Olivier Lagneau
+ * @modules java.management
+ * @library /lib/testlibrary
+ * @compile Basic.java
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanWeirdParamTest
+ */
+
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import java.lang.Process;
+import java.lang.management.ManagementFactory;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+import javax.management.ObjectName;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.JDKToolFinder;
+
+public class MXBeanWeirdParamTest {
+
+ private static String BASIC_MXBEAN_CLASS_NAME = "Basic";
+
+ private static final String CLIENT_CLASS_MAIN =
+ "MXBeanWeirdParamTest$ClientSide";
+
+ private JMXConnectorServer cs;
+
+ /*
+ * First Debug properties and arguments are collect in expected
+ * map (argName, value) format, then calls original test's run method.
+ */
+ public static void main(String args[]) throws Exception {
+
+ System.out.println("=================================================");
+
+ // Parses parameters
+ Utils.parseDebugProperties();
+ Map<String, Object> map = Utils.parseParameters(args) ;
+
+ // Run test
+ MXBeanWeirdParamTest test = new MXBeanWeirdParamTest();
+ test.run(map);
+
+ }
+
+ /*
+ * Create the MBeansServe side of the test and returns its address
+ */
+ private JMXServiceURL createServerSide() throws Exception {
+ final int NINETY_SECONDS = 90;
+
+ // We will use the platform mbean server
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+
+ JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
+ cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ cs.start();
+
+ Utils.waitReady(cs, NINETY_SECONDS);
+
+ JMXServiceURL addr = cs.getAddress();
+ return addr;
+ }
+
+
+ /*
+ * Creating command-line for running subprocess JVM:
+ *
+ * JVM command line is like:
+ * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main
+ *
+ * {defaultopts} are the default java options set by the framework.
+ *
+ */
+ private List<String> buildCommandLine() {
+ List<String> opts = new ArrayList<>();
+ opts.add(JDKToolFinder.getJDKTool("java"));
+ opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts()));
+ // We need to set WEIRD_PARAM propertty on the client-side
+ opts.add("-DWEIRD_PARAM");
+ opts.add("-cp");
+ opts.add(System.getProperty("test.class.path", "test.class.path"));
+ opts.add(CLIENT_CLASS_MAIN);
+
+ return opts;
+ }
+
+ /**
+ * Runs MXBeanWeirdParamTest$ClientSide with the passed options and redirects
+ * subprocess standard I/O to the current (parent) process. This provides a
+ * trace of what happens in the subprocess while it is runnning (and before
+ * it terminates).
+ *
+ * @param serviceUrlStr string representing the JMX service Url to connect to.
+ */
+ private int runClientSide(String serviceUrlStr) throws Exception {
+
+ // Building command-line
+ List<String> opts = buildCommandLine();
+ opts.add(serviceUrlStr);
+
+ // Launch separate JVM subprocess
+ int exitCode = 0;
+ String[] optsArray = opts.toArray(new String[0]);
+ ProcessBuilder pb = new ProcessBuilder(optsArray);
+ Process p = ProcessTools.startProcess("MXBeanWeirdParamTest$ClientSide", pb);
+
+ // Handling end of subprocess
+ try {
+ exitCode = p.waitFor();
+ if (exitCode != 0) {
+ System.out.println(
+ "Subprocess unexpected exit value of [" + exitCode +
+ "]. Expected 0.\n");
+ }
+ } catch (InterruptedException e) {
+ System.out.println("Parent process interrupted with exception : \n " + e + " :" );
+
+ // Parent thread unknown state, killing subprocess.
+ p.destroyForcibly();
+
+ throw new RuntimeException(
+ "Parent process interrupted with exception : \n " + e + " :" );
+ } finally {
+ return exitCode;
+ }
+
+ }
+
+ public void run(Map<String, Object> args) throws Exception {
+
+ System.out.println("MXBeanWeirdParamTest::run: Start") ;
+ int errorCount = 0;
+
+ try {
+ // Initialise the server side
+ JMXServiceURL urlToUse = createServerSide();
+
+ // Run client side
+ errorCount = runClientSide(urlToUse.toString());
+
+ if ( errorCount == 0 ) {
+ System.out.println("MXBeanWeirdParamTest::run: Done without any error") ;
+ } else {
+ System.out.println("MXBeanWeirdParamTest::run: Done with "
+ + errorCount
+ + " error(s)") ;
+ throw new RuntimeException("errorCount = " + errorCount);
+ }
+
+ cs.stop();
+
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ private static class ClientSide {
+ public static void main(String args[]) throws Exception {
+
+ int errorCount = 0 ;
+ String msgTag = "ClientSide::main: ";
+
+ try {
+
+ // Get a connection to remote mbean server
+ JMXServiceURL addr = new JMXServiceURL(args[0]);
+ JMXConnector cc = JMXConnectorFactory.connect(addr);
+ MBeanServerConnection mbsc = cc.getMBeanServerConnection();
+
+ // ----
+ System.out.println(msgTag + "Create and register the MBean");
+ ObjectName objName = new ObjectName("sqe:type=Basic,protocol=rmi") ;
+ mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName);
+ System.out.println(msgTag +"---- OK\n") ;
+
+ // ----
+ System.out.println(msgTag +"Get attribute SqeParameterAtt on our MXBean");
+ Object result = mbsc.getAttribute(objName, "SqeParameterAtt");
+ System.out.println(msgTag +"(OK) Got result of class "
+ + result.getClass().getName());
+ System.out.println(msgTag +"Received CompositeData is " + result);
+ System.out.println(msgTag +"---- OK\n") ;
+
+ // ----
+ // We use the value returned by getAttribute to perform the invoke.
+ System.out.println(msgTag +"Call operation doWeird on our MXBean [1]");
+ mbsc.invoke(objName, "doWeird",
+ new Object[]{result},
+ new String[]{"javax.management.openmbean.CompositeData"});
+ System.out.println(msgTag +"---- OK\n") ;
+
+ // ----
+ // We build the CompositeData ourselves that time.
+ System.out.println(msgTag +"Call operation doWeird on our MXBean [2]");
+ String typeName = "SqeParameter";
+ String[] itemNames = new String[] {"glop"};
+ OpenType<?>[] openTypes = new OpenType<?>[] {SimpleType.STRING};
+ CompositeType rowType = new CompositeType(typeName, typeName,
+ itemNames, itemNames, openTypes);
+ Object[] itemValues = {"HECTOR"};
+ CompositeData data =
+ new CompositeDataSupport(rowType, itemNames, itemValues);
+ TabularType tabType = new TabularType(typeName, typeName,
+ rowType, new String[]{"glop"});
+ TabularDataSupport tds = new TabularDataSupport(tabType);
+ tds.put(data);
+ System.out.println(msgTag +"Source CompositeData is " + data);
+ mbsc.invoke(objName, "doWeird",
+ new Object[]{data},
+ new String[]{"javax.management.openmbean.CompositeData"});
+ System.out.println(msgTag +"---- OK\n") ;
+
+ // ----
+ System.out.println(msgTag +"Unregister the MBean");
+ mbsc.unregisterMBean(objName);
+ System.out.println(msgTag +"---- OK\n") ;
+
+ // Terminate the JMX Client
+ cc.close();
+
+ } catch(Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++;
+ throw new RuntimeException(e);
+ } finally {
+ System.exit(errorCount);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/SqeDescriptorKey.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.management.DescriptorKey;
+
+/**
+ * That annotation is usable everywhere DescriptorKey is (and even more).
+ * It is for use to test that you can retrieve the SqeDescriptorKey into the
+ * appropriate Descriptor instances as built by the JMX runtime.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SqeDescriptorKey {
+ @DescriptorKey("sqeDescriptorKey")
+ String value();
+
+ // List descriptor fields that may be added or may be updated
+ // when retrieving an MBeanInfo using a JMXWS connection compared to the
+ // MBeanInfo returned by a local MBeanServer.
+ // The annotation format is :
+ // <descriptorFieldName>=<descriptorFieldValue>
+ // The values actually handled by the test suite are :
+ // openType=SimpleType.VOID
+ @DescriptorKey("descriptorFields")
+ String[] descriptorFields() default {};
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/SqeNotification.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.management.Notification;
+
+/**
+ * Could hold someday a specific semantic.
+ * For now it is used to have a Notification which of another class, no more.
+ */
+public class SqeNotification extends Notification {
+
+ /** Creates a new instance of SqeNotification */
+ public SqeNotification(String type, Object source, long sequenceNumber) {
+ super(type, source, sequenceNumber);
+ }
+
+ /** Creates a new instance of SqeNotification */
+ public SqeNotification(String type, Object source, long sequenceNumber,
+ long timeStamp) {
+ super(type, source, sequenceNumber, timeStamp);
+ }
+
+ /** Creates a new instance of SqeNotification */
+ public SqeNotification(String type, Object source, long sequenceNumber,
+ long timeStamp, String message) {
+ super(type, source, sequenceNumber, timeStamp, message);
+ }
+
+ /** Creates a new instance of SqeNotification */
+ public SqeNotification(String type, Object source, long sequenceNumber,
+ String message) {
+ super(type, source, sequenceNumber, message);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/SqeParameter.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.Serializable;
+
+/**
+ * That class is to use as an MBean operation parameter or returned value.
+ * The property Glop with its public getter + setter is only there to be
+ * reconstructible following MXBean specification, so that SqeParameter can be
+ * used for what it is designed to.
+ */
+public class SqeParameter implements Serializable {
+
+ private static boolean weird;
+ private String glop;
+
+ static {
+ if ( System.getProperty("WEIRD_PARAM") != null ) {
+ weird = true;
+ }
+ }
+
+ /**
+ * Creates a new instance of SqeParameter.
+ * <br>When the Java property WEIRD_PARAM is set, that constructor
+ * throws an exception.
+ * <br>That can be used to ensure the class is instantiated on server side
+ * but never on client side.
+ */
+ public SqeParameter() throws Exception {
+ if ( weird ) {
+ throw new Exception();
+ }
+ }
+
+ public String getGlop() {
+ return glop;
+ }
+
+ public void setGlop(String value) {
+ glop = value;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/mxbean/Utils.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Properties;
+import java.lang.reflect.Method;
+import javax.management.remote.JMXConnectorServerMBean;
+
+// utility class for MXBean* tests coming from JMX Tonga test suite
+class Utils {
+
+ // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property
+ private static final String DEBUG_HEADER = "[debug] ";
+
+ // DEBUG levels
+ private static int selectedDebugLevel = 0;
+ static final int DEBUG_STANDARD = 1;
+ static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests
+ static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE;
+
+ static void parseDebugProperties() {
+ int level = 0;
+ Properties p = System.getProperties();
+
+ // get selected levels
+ if (p.getProperty("DEBUG_STANDARD") != null) {
+ level |= DEBUG_STANDARD;
+ }
+
+ if (p.getProperty("DEBUG_VERBOSE") != null) {
+ level |= DEBUG_VERBOSE;
+ }
+
+ if (p.getProperty("DEBUG_ALL") != null) {
+ level |= DEBUG_ALL;
+ }
+
+ selectedDebugLevel = level;
+ }
+
+ /**
+ * Reproduces the original parsing and collection of test parameters
+ * from the DTonga JMX test suite.
+ *
+ * Collects passed args and returns them in a map(argname, value) structure,
+ * which will be then propagated as necessary to various called methods.
+ */
+ static Map<String, Object> parseParameters(String args[])
+ throws Exception {
+ Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start");
+ HashMap<String, Object> map = new HashMap<>();
+
+ for ( int i = 0; i < args.length; i++ ) {
+ if ( args[i].trim().startsWith("-") ) {
+ if ((i+1) < args.length && !args[i+1].startsWith("-") ) {
+ Utils.debug(DEBUG_STANDARD,
+ "TestRoot::parseParameters: added in map = " +
+ args[i] +
+ " with value " +
+ args[i+1]) ;
+ map.put(args[i].trim(), args[i+1].trim()) ;
+ } else if ((i+1) < args.length && args[i+1].startsWith("-") ||
+ (i+1) == args.length ) {
+ Utils.debug(DEBUG_STANDARD,
+ "TestRoot::parseParameters: added in map = " +
+ args[i] +
+ " with null value") ;
+ map.put(args[i].trim(), null) ;
+ } else {
+ System.out.println(
+ "TestRoot::parseParameters: (WARNING) not added in map = " +
+ args[i]) ;
+ }
+ }
+ }
+
+ Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ;
+ return map ;
+ }
+
+ /**
+ * This method is to be used in all tests to print anything
+ * that is temporary.
+ * Printing is done only when debug is activated by the property DEBUG.
+ * Printing depends also on the DEBUG_LEVEL property.
+ * Here it encapsulates a System.out.println.
+ */
+ public static void debug(int level, String line) {
+ if ((selectedDebugLevel & level) != 0) {
+ System.out.println(DEBUG_HEADER + line);
+ }
+ }
+
+ /**
+ * Do print stack trace when withStack is true.
+ * Does try to call getTargetException() and getTargetError() then
+ * print embedded stacks in the case of an Exception wrapping
+ * another Exception or an Error. Recurse until no more wrapping
+ * is found.
+ */
+ public static void printThrowable(Throwable theThro, boolean withStack) {
+ try {
+ if (withStack) {
+ theThro.printStackTrace(System.out);
+ }
+ if (theThro instanceof Exception) {
+ Exception t = (Exception) theThro;
+ Method target = null;
+ String blank = " ";
+ try {
+ target = t.getClass().getMethod("getTargetException",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetException method could be there or not
+ }
+ System.out.println(blank + t.getClass() + "==>" + t.getMessage());
+ while (target != null) {
+ try {
+ t = (Exception) target.invoke(t,
+ (java.lang.Object[]) null);
+ } catch (Exception ee) {
+ t = null;
+ }
+ try {
+ if (t != null) {
+ blank = blank + " ";
+ System.out.println(blank + t.getClass() + "==>" +
+ t.getMessage());
+ try {
+ target =
+ t.getClass().getMethod("getTargetException",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetException method could be there or not }
+ }
+ } else {
+ target = null;
+ }
+ } catch (Exception ee) {
+ target = null;
+ }
+ }
+
+ // We may have exceptions wrapping an Error then it is
+ // getTargetError that is likely to be called
+ try {
+ target = ((Exception) theThro).getClass().getMethod("getTargetError",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetError method could be there or not
+ }
+ Throwable err = theThro;
+ while (target != null) {
+ try {
+ err = (Error) target.invoke(err,
+ (java.lang.Object[]) null);
+ } catch (Exception ee) {
+ err = null;
+ }
+ try {
+ if (err != null) {
+ blank = blank + " ";
+ System.out.println(blank + err.getClass() + "==>" +
+ err.getMessage());
+ if (withStack) {
+ err.printStackTrace(System.out);
+ }
+ try {
+ target = err.getClass().getMethod("getTargetError",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetError method could be there or not
+ }
+ } else {
+ target = null;
+ }
+ } catch (Exception ee) {
+ target = null;
+ }
+ }
+ } else {
+ System.out.println("Throwable is : " + theThro);
+ }
+ } catch (Throwable x) {
+ System.out.println("Exception : raised in printException : " + x);
+ }
+ }
+
+ /**
+ * Wait up to maxTimeInSeconds second(s) the given JMX connector server
+ * comes up (which means isActive returns true).
+ * If it fails to do so we throw a RunTime exception.
+ */
+ public static void waitReady(JMXConnectorServerMBean server,
+ int maxTimeInSeconds) throws Exception {
+ int elapsed = 0;
+
+ while (!server.isActive() && elapsed < maxTimeInSeconds) {
+ Thread.sleep(1000);
+ elapsed++;
+ }
+
+ if (server.isActive()) {
+ String message = "Utils::waitReady: JMX connector server came up";
+ if ( elapsed == 0) {
+ message += " immediately";
+ } else {
+ message += " after " + elapsed + " seconds";
+ }
+ message += " [" + server.getAddress() + "]";
+ Utils.debug(DEBUG_STANDARD, message);
+ } else {
+ String message = "Utils::waitReady: (ERROR) JMX connector" +
+ " server didn't come up after " + elapsed + " seconds [" +
+ server.getAddress() + "]";
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/query/QueryData.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public abstract class QueryData {
+ protected int intValue = 9;
+ protected long longValue = 9L;
+ protected Integer integerValue = Integer.valueOf(9);
+ protected boolean booleanValue = true;
+ protected double doubleValue = 9D;
+ protected float floatValue = 9.0F;
+ protected String stringValue = "9";
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/query/QueryFactory.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.ArrayList;
+
+import javax.management.Query;
+import javax.management.QueryExp;
+import javax.management.ValueExp;
+
+/**
+ * Class used for building QueryExp instances of all every possible type
+ * in terms of JMX API members; note that several JMX classes are private
+ * and appears in the JDK API only by their serial form.
+ * Comments in each case of the big switch in method getQuery() details which
+ * API member we cover with a given query.
+ */
+public class QueryFactory extends QueryData {
+
+ private String mbeanClassName = "";
+ private String primitiveIntAttName = "IntAtt";
+ private String primitiveLongAttName = "LongAtt";
+ private String integerAttName = "IntegerAtt";
+ private String primitiveBooleanAttName = "BooleanAtt";
+ private String primitiveDoubleAttName = "DoubleAtt";
+ private String primitiveFloatAttName = "FloatAtt";
+ private String stringAttName = "StringAtt";
+ private ArrayList<QueryExp> queries = new ArrayList<QueryExp>();
+
+ /**
+ * Creates a new instance of QueryFactory.
+ * The name is the fully qualified class name of an MBean.
+ * There is severe constraints on that MBean that must:
+ * <ul>
+ * <li>extend QueryData in order to inherit attribute values.
+ * <li>define a RW attribute IntAtt of type int
+ * initialized to QueryData.longValue
+ * <li>define a RW attribute LongAtt of type long
+ * initialized to QueryData.intValue
+ * <li>define a RW attribute IntegerAtt of type Integer
+ * initialized to QueryData.integerValue
+ * <li>define a RW attribute BooleanAtt of type boolean
+ * initialized to QueryData.booleanValue
+ * <li>define a RW attribute DoubleAtt of type double
+ * initialized to QueryData.doubleValue
+ * <li>define a RW attribute FloatAtt of type float
+ * initialized to QueryData.floatValue
+ * <li>define a RW attribute StringAtt of type String
+ * initialized to QueryData.stringValue
+ * </ul>
+ */
+ public QueryFactory(String name) {
+ this.mbeanClassName = name;
+ }
+
+ /**
+ * Returns the highest index value the method getQuery supports.
+ * WARNING : returns 0 if buildQueries haven't been called first !
+ */
+ public int getSize() {
+ return queries.size();
+ }
+
+ /**
+ * Populates an ArrayList of QueryExp.
+ * Lowest index is 1.
+ * Highest index is returned by getSize().
+ * <br>The queries numbered 1 to 23 allow to cover all the underlying
+ * Java classes of the JMX API used to build queries.
+ */
+ public void buildQueries() {
+ if ( queries.size() == 0 ) {
+ int smallerIntValue = intValue - 1;
+ int biggerIntValue = intValue + 1;
+
+ // case 1:
+ // True if the MBean is of class mbeanClassName
+ // We cover javax.management.InstanceOfQueryExp
+ queries.add(Query.isInstanceOf(Query.value(mbeanClassName)));
+
+ // case 2:
+ // True if the MBean is of class mbeanClassName
+ // We cover javax.management.MatchQueryExp and
+ // javax.management.ClassAttributeValueExp
+ queries.add(Query.match(Query.classattr(),
+ Query.value(mbeanClassName)));
+
+ // case 3:
+ // True if an attribute named primitiveIntAttName of type int has
+ // the value intValue
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to EQ and javax.management.NumericValueExp
+ queries.add(Query.eq(Query.attr(primitiveIntAttName),
+ Query.value(intValue)));
+
+ // case 4:
+ // True if an attribute named primitiveLongAttName of type long has
+ // the value longValue
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to EQ and javax.management.NumericValueExp
+ queries.add(Query.eq(Query.attr(primitiveLongAttName),
+ Query.value(longValue)));
+
+ // case 5:
+ // True if an attribute named primitiveDoubleAttName of type double
+ // has the value doubleValue
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to EQ and javax.management.NumericValueExp
+ queries.add(Query.eq(Query.attr(primitiveDoubleAttName),
+ Query.value(doubleValue)));
+
+ // case 6:
+ // True if an attribute named primitiveFloatAttName of type float
+ // has the value floatValue
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to EQ and javax.management.NumericValueExp
+ queries.add(Query.eq(Query.attr(primitiveFloatAttName),
+ Query.value(floatValue)));
+
+ // case 7:
+ // True if an attribute named primitiveIntAttName of type int is
+ // hold by an MBean of class mbeanClassName and has
+ // the value intValue
+ // We cover javax.management.QualifiedAttributeValueExp
+ queries.add(Query.eq(Query.attr(mbeanClassName, primitiveIntAttName),
+ Query.value(intValue)));
+
+ // case 8:
+ // True if an attribute named stringAttName of type String has
+ // the value stringValue
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to EQ and javax.management.StringValueExp
+ queries.add(Query.eq(Query.attr(stringAttName),
+ Query.value(stringValue)));
+
+ // case 9:
+ // True if an attribute named integerAttName of type Integer has
+ // the value integerValue
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to EQ and javax.management.NumericValueExp
+ queries.add(Query.eq(Query.attr(integerAttName),
+ Query.value(integerValue)));
+
+ // case 10:
+ // True if an attribute named primitiveBooleanAttName of type boolean
+ // has the value booleanValue
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to EQ and javax.management.BooleanValueExp
+ queries.add(Query.eq(Query.attr(primitiveBooleanAttName),
+ Query.value(booleanValue)));
+
+ // case 11:
+ // True if an attribute named primitiveIntAttName of type int has
+ // not the value smallerIntValue
+ // We cover javax.management.NotQueryExp
+ queries.add(Query.not(Query.eq(Query.attr(primitiveIntAttName),
+ Query.value(smallerIntValue))));
+
+ // case 12:
+ // True if either
+ // an attribute named primitiveIntAttName of type int has
+ // the value intValue
+ // or
+ // an attribute named primitiveLongAttName of type long has
+ // the value longValue
+ // We cover javax.management.OrQueryExp
+ queries.add(Query.or(
+ Query.eq(Query.attr(primitiveIntAttName),
+ Query.value(intValue)),
+ Query.eq(Query.attr(primitiveLongAttName),
+ Query.value(longValue))));
+
+ // case 13:
+ // True if
+ // an attribute named primitiveIntAttName of type int has
+ // the value intValue
+ // and
+ // an attribute named primitiveLongAttName of type long has
+ // the value longValue
+ // We cover javax.management.AndQueryExp
+ queries.add(Query.and(
+ Query.eq(Query.attr(primitiveIntAttName),
+ Query.value(intValue)),
+ Query.eq(Query.attr(primitiveLongAttName),
+ Query.value(longValue))));
+
+ // case 14:
+ // True if an attribute named primitiveIntAttName of type int has
+ // the value intValue
+ // We cover javax.management.InQueryExp
+ ValueExp[] inArray = {Query.value(intValue)};
+ queries.add(Query.in(Query.attr(primitiveIntAttName), inArray));
+
+ // case 15:
+ // True if an attribute named primitiveIntAttName of type int has
+ // its value in between smallerIntValue and biggerIntValue
+ // We cover javax.management.BetweenRelQueryExp
+ queries.add(Query.between(Query.attr(primitiveIntAttName),
+ Query.value(smallerIntValue),
+ Query.value(biggerIntValue)));
+
+ // case 16:
+ // True if an attribute named primitiveIntAttName of type int has
+ // a value greater than smallerIntValue
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to GT
+ queries.add(Query.gt(Query.attr(primitiveIntAttName),
+ Query.value(smallerIntValue)));
+
+ // case 17:
+ // True if an attribute named primitiveIntAttName of type int has
+ // a value greater or equal to smallerIntValue
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to GE
+ queries.add(Query.geq(Query.attr(primitiveIntAttName),
+ Query.value(smallerIntValue)));
+
+ // case 18:
+ // True if an attribute named primitiveIntAttName of type int has
+ // a value smaller than biggerIntValue
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to LT
+ queries.add(Query.lt(Query.attr(primitiveIntAttName),
+ Query.value(biggerIntValue)));
+
+ // case 19:
+ // True if an attribute named primitiveIntAttName of type int has
+ // a value smaller or equal to biggerIntValue
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to LE
+ queries.add(Query.leq(Query.attr(primitiveIntAttName),
+ Query.value(biggerIntValue)));
+
+ // case 20:
+ // True if an attribute named primitiveIntAttName of type int has
+ // a value equal to intValue minus zero
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to MINUS
+ queries.add(Query.eq(Query.attr(primitiveIntAttName),
+ Query.minus(Query.value(intValue), Query.value(0))));
+
+ // case 21:
+ // True if an attribute named primitiveIntAttName of type int has
+ // a value equal to intValue plus zero
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to PLUS
+ queries.add(Query.eq(Query.attr(primitiveIntAttName),
+ Query.plus(Query.value(intValue), Query.value(0))));
+
+ // case 22:
+ // True if an attribute named primitiveIntAttName of type int has
+ // a value equal to intValue divided by one
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to DIV
+ queries.add(Query.eq(Query.attr(primitiveIntAttName),
+ Query.div(Query.value(intValue), Query.value(1))));
+
+ // case 23:
+ // True if an attribute named primitiveIntAttName of type int has
+ // a value equal to intValue multiplicated by one
+ // We cover javax.management.BinaryRelQueryExp with
+ // a relOp equal to TIMES
+ queries.add(Query.eq(Query.attr(primitiveIntAttName),
+ Query.times(Query.value(intValue), Query.value(1))));
+
+ // case 24:
+ // That query is a complex one that combines within a big AND
+ // queries with index 2 to 23 inclusive. But because a List is
+ // zero based, we must decrement all indexes by 1 when retrieving
+ // any previously stored query.
+ QueryExp q2_3 = Query.and(queries.get(2-1), queries.get(3-1));
+ QueryExp q4_5 = Query.and(queries.get(4-1), queries.get(5-1));
+ QueryExp q6_7 = Query.and(queries.get(6-1), queries.get(7-1));
+ QueryExp q8_9 = Query.and(queries.get(8-1), queries.get(9-1));
+ QueryExp q10_11 = Query.and(queries.get(10-1), queries.get(11-1));
+ QueryExp q12_13 = Query.and(queries.get(12-1), queries.get(13-1));
+ QueryExp q14_15 = Query.and(queries.get(14-1), queries.get(15-1));
+ QueryExp q16_17 = Query.and(queries.get(16-1), queries.get(17-1));
+ QueryExp q18_19 = Query.and(queries.get(18-1), queries.get(19-1));
+ QueryExp q20_21 = Query.and(queries.get(20-1), queries.get(21-1));
+ QueryExp q22_23 = Query.and(queries.get(22-1), queries.get(23-1));
+ QueryExp q2_5 = Query.and(q2_3, q4_5);
+ QueryExp q6_9 = Query.and(q6_7, q8_9);
+ QueryExp q10_13 = Query.and(q10_11, q12_13);
+ QueryExp q14_17 = Query.and(q14_15, q16_17);
+ QueryExp q18_21 = Query.and(q18_19, q20_21);
+ QueryExp q2_9 = Query.and(q2_5, q6_9);
+ QueryExp q10_17 = Query.and(q10_13, q14_17);
+ QueryExp q18_23 = Query.and(q18_21, q22_23);
+ QueryExp q2_17 = Query.and(q2_9, q10_17);
+ queries.add(Query.and(q2_17, q18_23));
+
+ // case 25:
+ // Complex query mixing AND and OR.
+ queries.add(Query.or(q6_9, q18_23));
+ }
+ }
+
+ /**
+ * Returns a QueryExp taken is the ArrayList populated by buildQueries().
+ * Lowest index is 1.
+ * Highest index is returned by getSize().
+ * <br>The queries numbered 1 to 23 allow to cover all the underlying
+ * Java classes of the JMX API used to build queries.
+ */
+ public QueryExp getQuery(int index) {
+ return queries.get(index - 1);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/query/ServerDelegate.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.management.remote.JMXServiceURL ;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+
+/**
+ * This class defines an MBean that can be registered and used on client side
+ * to handle informations or properties of the remote server.
+ *
+ * For example, this MBean can store IOR addresses
+ * of RMI/IIOP connector(s) used in a test.
+ *
+ * That MBean might not be used for testing purpose itself.
+ */
+public class ServerDelegate implements ServerDelegateMBean, MBeanRegistration {
+
+ private MBeanServer mbeanServer = null;
+ private List<JMXServiceURL> addresses = null;
+ private String port;
+ private static String javaVersion = System.getProperty("java.version");
+ private int sqeJmxwsCredentialsProviderCallCount = 0;
+ private String jmxwsCredentialsProviderUrl = null;
+ private int testJMXAuthenticatorCallCount = 0;
+ private Principal testJMXAuthenticatorPrincipal = null;
+
+ @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR ServerDelegate")
+ public ServerDelegate() {
+ addresses = new ArrayList<JMXServiceURL>();
+ }
+
+ public ObjectName preRegister(MBeanServer server, ObjectName name)
+ throws Exception {
+ // Initialize MBeanServer attribute
+ mbeanServer = server;
+ return name;
+ }
+ public void postRegister(Boolean registrationDone) {
+ }
+ public void preDeregister() throws Exception {
+ }
+ public void postDeregister() {
+ }
+
+ public void addAddress(JMXServiceURL url) {
+ addresses.add(url) ;
+ }
+
+ public List<JMXServiceURL> getAddresses() {
+ return addresses ;
+ }
+
+ public void setPort(String p) {
+ port = p ;
+ }
+
+ public String getPort() {
+ return port ;
+ }
+
+ public String getJavaVersion() {
+ return javaVersion;
+ }
+
+ public void sqeJmxwsCredentialsProviderCalled() {
+ sqeJmxwsCredentialsProviderCallCount++;
+ }
+
+ public int getSqeJmxwsCredentialsProviderCallCount() {
+ return sqeJmxwsCredentialsProviderCallCount;
+ }
+
+ public void setJmxwsCredentialsProviderUrl(String url) {
+ jmxwsCredentialsProviderUrl = url;
+ }
+
+ public String getJmxwsCredentialsProviderUrl() {
+ return jmxwsCredentialsProviderUrl;
+ }
+
+ public void testJMXAuthenticatorCalled() {
+ testJMXAuthenticatorCallCount++;
+ }
+
+ public int getTestJMXAuthenticatorCallCount() {
+ return testJMXAuthenticatorCallCount;
+ }
+
+ public void setTestJMXAuthenticatorPrincipal(Principal principal) {
+ testJMXAuthenticatorPrincipal = principal;
+ }
+
+ public String getTestJMXAuthenticatorPrincipalString() {
+ if ( testJMXAuthenticatorPrincipal != null ) {
+ return testJMXAuthenticatorPrincipal.toString();
+ }
+
+ return null;
+ }
+
+ /**
+ * Instantiates and registers a StandardMBean in the MBean server.
+ *
+ * @param implementationClassName
+ * The implementation class name of the MBean.
+ * @param interfaceClassName
+ * The management interface class name of the MBean.
+ * @param isMXBean
+ * If true, the resultant MBean is an MXBean.
+ * @param name
+ * The object name of the StandardMBean.
+ */
+ @SuppressWarnings("unchecked")
+ public void createStandardMBean(
+ String implementationClassName,
+ String interfaceClassName,
+ boolean isMXBean,
+ ObjectName name)
+ throws Exception {
+
+ Object implementation =
+ Class.forName(implementationClassName).newInstance();
+ Class<Object> interfaceClass = interfaceClassName == null ? null :
+ (Class<Object>)Class.forName(interfaceClassName);
+
+ // Create the StandardMBean
+ StandardMBean standardMBean = new StandardMBean(
+ implementation,
+ interfaceClass,
+ isMXBean);
+
+ // Register the StandardMBean
+ mbeanServer.registerMBean(standardMBean, name);
+ }
+
+ /**
+ * Instantiates and registers a StandardMBean in the MBean server.
+ * The object will use standard JMX design pattern to determine
+ * the management interface associated with the given implementation.
+ */
+ @SuppressWarnings("unchecked")
+ public void createStandardMBean(
+ String implementationClassName,
+ boolean isMXBean,
+ ObjectName name)
+ throws Exception {
+
+ createStandardMBean(implementationClassName, null, isMXBean, name);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/query/ServerDelegateMBean.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.security.Principal;
+import java.util.List;
+
+import javax.management.remote.JMXServiceURL ;
+import javax.management.ObjectName;
+
+@SqeDescriptorKey("INTERFACE ServerDelegateMBean")
+public interface ServerDelegateMBean {
+ @SqeDescriptorKey("ATTRIBUTE Address")
+ public void addAddress(JMXServiceURL url);
+
+ @SqeDescriptorKey("ATTRIBUTE Address")
+ public List<JMXServiceURL> getAddresses();
+
+ public String getPort();
+ public void setPort(String p);
+
+ public String getJavaVersion();
+
+ public void sqeJmxwsCredentialsProviderCalled();
+ public int getSqeJmxwsCredentialsProviderCallCount();
+
+ public void setJmxwsCredentialsProviderUrl(String url);
+ public String getJmxwsCredentialsProviderUrl();
+
+ public void testJMXAuthenticatorCalled();
+ public int getTestJMXAuthenticatorCallCount();
+
+ public void setTestJMXAuthenticatorPrincipal(Principal principal);
+ public String getTestJMXAuthenticatorPrincipalString();
+
+ public void createStandardMBean(
+ String implementationClassName,
+ String interfaceClassName,
+ boolean isMXBean,
+ ObjectName name)
+ throws Exception;
+
+ public void createStandardMBean(
+ String implementationClassName,
+ boolean isMXBean,
+ ObjectName name)
+ throws Exception;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/query/SqeDescriptorKey.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.management.DescriptorKey;
+
+/**
+ * That annotation is usable everywhere DescriptorKey is (and even more).
+ * It is for use to test that you can retrieve the SqeDescriptorKey into the
+ * appropriate Descriptor instances as built by the JMX runtime.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SqeDescriptorKey {
+ @DescriptorKey("sqeDescriptorKey")
+ String value();
+
+ // List descriptor fields that may be added or may be updated
+ // when retrieving an MBeanInfo using a JMXWS connection compared to the
+ // MBeanInfo returned by a local MBeanServer.
+ // The annotation format is :
+ // <descriptorFieldName>=<descriptorFieldValue>
+ // The values actually handled by the test suite are :
+ // openType=SimpleType.VOID
+ @DescriptorKey("descriptorFields")
+ String[] descriptorFields() default {};
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/query/SupportedQueryTypesTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058865
+ * @summary Tests most of the existing query types.
+ * @author Olivier Lagneau
+ * @modules java.management
+ * @compile TestQuery.java
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD SupportedQueryTypesTest -mbeanClassName TestQuery
+ */
+
+import java.util.Map ;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+import java.lang.reflect.Method;
+
+import java.lang.management.ManagementFactory;
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName ;
+import javax.management.QueryExp;
+
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+public class SupportedQueryTypesTest {
+
+ protected String mbeanClassName = null;
+
+ private MBeanServerConnection mbsc = null;
+
+
+ /*
+ * First Debug properties and arguments are collect in expected
+ * map (argName, value) format, then calls original test's run method.
+ */
+ public static void main(String args[]) throws Exception {
+
+ System.out.println("=================================================");
+
+ // Parses parameters
+ Utils.parseDebugProperties();
+ Map<String, Object> map = Utils.parseParameters(args) ;
+
+ // Run test
+ SupportedQueryTypesTest test = new SupportedQueryTypesTest();
+ test.run(map);
+
+ }
+
+ public void run(Map<String, Object> args) {
+ int errorCount = 0;
+
+ ObjectName on = null;
+ ObjectName serverDelegateObjectName = null;
+
+ JMXConnectorServer cs = null;
+ JMXConnector cc = null;
+
+ System.out.println("SupportedQueryTypesTest::run: Start") ;
+ try {
+ // JMX MbeanServer used inside single VM as if remote.
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+
+ JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
+ cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
+ cs.start();
+
+ JMXServiceURL addr = cs.getAddress();
+ cc = JMXConnectorFactory.connect(addr);
+ mbsc = cc.getMBeanServerConnection();
+
+
+ // Create and register the ServerDelegate MBean on the remote MBeanServer
+ String serverDelegateClassName = ServerDelegate.class.getName();
+ serverDelegateObjectName =
+ new ObjectName("defaultDomain:class=" + serverDelegateClassName);
+ mbsc.createMBean(serverDelegateClassName, serverDelegateObjectName);
+
+ // Retrieve the MBean class name
+ mbeanClassName = (String) args.get("-mbeanClassName") ;
+ on = new ObjectName("defaultDomain:class=" + mbeanClassName);
+
+ // Create and register the MBean on the remote MBeanServer
+ System.out.println("SupportedQueryTypesTest::run: CREATE " +
+ mbeanClassName + " on the remote MBeanServer with name "
+ + on);
+ mbsc.createMBean(mbeanClassName, on);
+
+ // Create a QueryFactory and setup which query we'll use.
+ QueryFactory queries = new QueryFactory(mbeanClassName);
+ queries.buildQueries();
+ int maxIndex = queries.getSize();
+ int minIndex = 1;
+
+ // Create a reference Set<ObjectName> to check later on
+ // the queryNames() results
+ Set<ObjectName> referenceNameSet = new HashSet<ObjectName>();
+ referenceNameSet.add(on);
+
+ // Create a reference Set<ObjectInstance> to check later on
+ // the queryMBeans() results
+ ObjectInstance oi = new ObjectInstance(on, mbeanClassName);
+ Set<ObjectInstance> referenceInstanceSet =
+ new HashSet<ObjectInstance>();
+ referenceInstanceSet.add(oi);
+
+ // Perform the queryNames and queryMBeans requests
+ for (int i = minIndex; i <= maxIndex; i++ ) {
+ QueryExp query = queries.getQuery(i);
+ System.out.println("----");
+ System.out.println("SupportedQueryTypesTest::run: Query # " + i);
+ System.out.println("query " + query);
+ errorCount +=
+ doQueryNames(query, referenceNameSet);
+ errorCount +=
+ doQueryMBeans(query, referenceInstanceSet);
+ }
+
+ } catch(Exception e) {
+ Utils.printThrowable(e, true);
+ errorCount++;
+
+ } finally {
+ // Do unregister the MBean
+ try {
+ if (mbsc.isRegistered(on)) {
+ mbsc.unregisterMBean(on);
+ }
+ if (mbsc.isRegistered(serverDelegateObjectName)) {
+ mbsc.unregisterMBean(serverDelegateObjectName);
+ }
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++;
+ }
+
+ try {
+ // Close JMX Connector Client
+ cc.close();
+ // Stop connertor server
+ cs.stop();
+
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++;
+ }
+ }
+
+ System.out.println("");
+ System.out.println("SupportedQueryTypesTest::run: Done") ;
+
+ // Handle result
+ if (errorCount == 0) {
+ System.out.println("SupportedQueryTypesTest::run: (OK)");
+ } else {
+ String message = "SupportedQueryTypesTest::run: (ERROR) Got " +
+ + errorCount + " error(s)";
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+ }
+
+
+ private int doQueryNames(QueryExp query, Set<ObjectName> referenceSet) {
+ int errorCount = 0;
+ System.out.println(" <*> Perform queryNames call ");
+
+ try {
+ // Call queryNames on the remote MBeanServer
+ Set<ObjectName> remoteSet = mbsc.queryNames(null, query);
+
+ // Compare the 2 Set<ObjectName>
+ errorCount += checkSet(remoteSet, referenceSet);
+
+ // Cleaning
+ remoteSet.clear();
+
+ } catch (Exception e) {
+ Utils.printThrowable(e, true);
+ errorCount++;
+ }
+
+ if ( errorCount == 0 ) {
+ System.out.println("\t(OK)");
+ } else {
+ System.out.println("\t(ERROR) Query failed");
+ }
+
+ return errorCount;
+ }
+
+
+ private int doQueryMBeans(QueryExp query, Set<ObjectInstance> referenceSet) {
+ int errorCount = 0;
+ System.out.println(" <*> Perform queryMBeans call ");
+
+ try {
+ // Call queryMBeans on the remote MBeanServer
+ Set<ObjectInstance> remoteSet = mbsc.queryMBeans(null, query);
+
+ // Compare the 2 Set<ObjectInstance>
+ errorCount += checkSet(remoteSet, referenceSet);
+
+ // Cleaning
+ remoteSet.clear();
+
+ } catch (Exception e) {
+ Utils.printThrowable(e, true);
+ errorCount++;
+ }
+
+ if ( errorCount == 0 ) {
+ System.out.println("\t(OK)");
+ } else {
+ System.out.println("\t(ERROR) Query failed");
+ }
+
+ return errorCount;
+ }
+
+ /**
+ * Pretty print of a Set content.
+ * When the Set isn't empty, toString() is called on each element.
+ * <br>The variable's name used to hold that Set is given via the setName
+ * parameter and used in the output.
+ */
+ private static void printSet(Set<?> printableSet, String setName) {
+ if ( printableSet.size() == 0 ) {
+ System.out.println("The Set " + setName + " is empty");
+ } else {
+ System.out.println("The Set " + setName + " contains :");
+
+ for (Iterator<?> it = printableSet.iterator(); it.hasNext();) {
+ Object elem = it.next();
+ System.out.println("\t" + elem.toString());
+ }
+ }
+ }
+
+
+ /**
+ * This method check the Set remoteSet is equal to
+ * the reference Set referenceSet,
+ * which means same size and content (order doesn't matter).
+ * <br>It returns 0 when the check is fine, otherwise 1.
+ */
+ private int checkSet(Set<?> remoteSet, Set<?> referenceSet) {
+ if ( ! remoteSet.equals(referenceSet) ) {
+ System.out.println("SupportedQueryTypesTest::checkSet:"
+ + " (ERROR) Set aren't as expected");
+ printSet(remoteSet, "remoteSet");
+ printSet(referenceSet, "referenceSet");
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ // Utility inner class coming from JMX Tonga test suite.
+ private static class Utils {
+
+ // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property
+ static final String DEBUG_HEADER = "[debug] ";
+
+ // DEBUG levels
+ static int selectedDebugLevel = 0;
+ static final int DEBUG_STANDARD = 1;
+ static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests
+ static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE;
+
+ static void parseDebugProperties() {
+ int level = 0;
+ Properties p = System.getProperties();
+
+ // get selected levels
+ if (p.getProperty("DEBUG_STANDARD") != null) {
+ level |= DEBUG_STANDARD;
+ }
+
+ if (p.getProperty("DEBUG_VERBOSE") != null) {
+ level |= DEBUG_VERBOSE;
+ }
+
+ if (p.getProperty("DEBUG_ALL") != null) {
+ level |= DEBUG_ALL;
+ }
+
+ selectedDebugLevel = level;
+ }
+
+ /**
+ * Reproduces the original parsing and collection of test parameters
+ * from the DTonga JMX test suite.
+ *
+ * Collects passed args and returns them in a map(argname, value) structure,
+ * which will be then propagated as necessary to various called methods.
+ */
+ static Map<String, Object> parseParameters(String args[])
+ throws Exception {
+ debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start");
+ HashMap<String, Object> map = new HashMap<>();
+
+ for ( int i = 0; i < args.length; i++ ) {
+ if ( args[i].trim().startsWith("-") ) {
+ if ((i+1) < args.length && !args[i+1].startsWith("-") ) {
+ debug(DEBUG_STANDARD,
+ "TestRoot::parseParameters: added in map = " +
+ args[i] +
+ " with value " +
+ args[i+1]) ;
+ map.put(args[i].trim(), args[i+1].trim()) ;
+ } else if ((i+1) < args.length && args[i+1].startsWith("-") ||
+ (i+1) == args.length ) {
+ debug(DEBUG_STANDARD,
+ "TestRoot::parseParameters: added in map = " +
+ args[i] +
+ " with null value") ;
+ map.put(args[i].trim(), null) ;
+ } else {
+ System.out.println(
+ "TestRoot::parseParameters: (WARNING) not added in map = " +
+ args[i]) ;
+ }
+ }
+ }
+
+ debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ;
+ return map ;
+ }
+
+ /**
+ * This method is to be used in all tests to print anything
+ * that is temporary.
+ * Printing is done only when debug is activated by the property DEBUG.
+ * Printing depends also on the DEBUG_LEVEL property.
+ * Here it encapsulates a System.out.println.
+ */
+ static void debug(int level, String line) {
+ if ((selectedDebugLevel & level) != 0) {
+ System.out.println(DEBUG_HEADER + line);
+ }
+ }
+
+ /**
+ * Do print stack trace when withStack is true.
+ * Does try to call getTargetException() and getTargetError() then
+ * print embedded stacks in the case of an Exception wrapping
+ * another Exception or an Error. Recurse until no more wrapping
+ * is found.
+ */
+ static void printThrowable(Throwable theThro, boolean withStack) {
+ try {
+ if (withStack) {
+ theThro.printStackTrace(System.out);
+ }
+ if (theThro instanceof Exception) {
+ Exception t = (Exception) theThro;
+ Method target = null;
+ String blank = " ";
+ try {
+ target = t.getClass().getMethod("getTargetException",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetException method could be there or not
+ }
+ System.out.println(blank + t.getClass() + "==>" + t.getMessage());
+ while (target != null) {
+ try {
+ t = (Exception) target.invoke(t,
+ (java.lang.Object[]) null);
+ } catch (Exception ee) {
+ t = null;
+ }
+ try {
+ if (t != null) {
+ blank = blank + " ";
+ System.out.println(blank + t.getClass() + "==>" +
+ t.getMessage());
+ try {
+ target =
+ t.getClass().getMethod("getTargetException",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetException method could be there or not }
+ }
+ } else {
+ target = null;
+ }
+ } catch (Exception ee) {
+ target = null;
+ }
+ }
+
+ // We may have exceptions wrapping an Error then it is
+ // getTargetError that is likely to be called
+ try {
+ target = ((Exception) theThro).getClass().getMethod("getTargetError",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetError method could be there or not
+ }
+ Throwable err = theThro;
+ while (target != null) {
+ try {
+ err = (Error) target.invoke(err,
+ (java.lang.Object[]) null);
+ } catch (Exception ee) {
+ err = null;
+ }
+ try {
+ if (err != null) {
+ blank = blank + " ";
+ System.out.println(blank + err.getClass() + "==>" +
+ err.getMessage());
+ if (withStack) {
+ err.printStackTrace(System.out);
+ }
+ try {
+ target = err.getClass().getMethod("getTargetError",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetError method could be there or not
+ }
+ } else {
+ target = null;
+ }
+ } catch (Exception ee) {
+ target = null;
+ }
+ }
+ } else {
+ System.out.println("Throwable is : " + theThro);
+ }
+ } catch (Throwable x) {
+ System.out.println("Exception : raised in printException : " + x);
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/query/TestQuery.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Class TestQuery
+ * MBean used for testing the types wired when using QueryExp.
+ * It is heavily linked to QueryFactory.
+ */
+public class TestQuery extends QueryData implements TestQueryMBean {
+
+ /**
+ * Attribute : BooleanAtt
+ */
+ private boolean booleanAtt = booleanValue;
+
+ /**
+ * Attribute : DoubleAtt
+ */
+ private double doubleAtt = doubleValue;
+
+ /**
+ * Attribute : FloatAtt
+ */
+ private float floatAtt = floatValue;
+
+ /**
+ * Attribute : IntAtt
+ */
+ private int intAtt = intValue;
+
+ /**
+ * Attribute : IntegerAtt
+ */
+ private Integer integerAtt = integerValue;
+
+ /**
+ * Attribute : LongAtt
+ */
+ private long longAtt = longValue;
+
+ /**
+ * Attribute : StringAtt
+ */
+ private String stringAtt = stringValue;
+
+ public TestQuery() {
+ }
+
+ /**
+ * Get Att of type boolean
+ */
+ public boolean getBooleanAtt() {
+ return booleanAtt;
+ }
+
+ /**
+ * Set Att of type boolean
+ */
+ public void setBooleanAtt(boolean value) {
+ booleanAtt = value;
+ }
+
+ /**
+ * Get Att of type double
+ */
+ public double getDoubleAtt() {
+ return doubleAtt;
+ }
+
+ /**
+ * Set Att of type double
+ */
+ public void setDoubleAtt(double value) {
+ doubleAtt = value;
+ }
+
+ /**
+ * Get Att of type float
+ */
+ public float getFloatAtt() {
+ return floatAtt;
+ }
+
+ /**
+ * Set Att of type float
+ */
+ public void setFloatAtt(float value) {
+ floatAtt = value;
+ }
+
+ /**
+ * Get Att of type int
+ */
+ public int getIntAtt() {
+ return intAtt;
+ }
+
+ /**
+ * Set Att of type int
+ */
+ public void setIntAtt(int value) {
+ intAtt = value;
+ }
+
+ /**
+ * Get Att of type Integer
+ */
+ public Integer getIntegerAtt() {
+ return integerAtt;
+ }
+
+ /**
+ * Set Att of type Integer
+ */
+ public void setIntegerAtt(Integer value) {
+ integerAtt = value;
+ }
+
+ /**
+ * Get Att of type long
+ */
+ public long getLongAtt() {
+ return longAtt;
+ }
+
+ /**
+ * Set Att of type long
+ */
+ public void setLongAtt(long value) {
+ longAtt = value;
+ }
+
+ /**
+ * Get Att of type String
+ */
+ public String getStringAtt() {
+ return stringAtt;
+ }
+
+ /**
+ * Set Att of type String
+ */
+ public void setStringAtt(String value) {
+ stringAtt = value;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/query/TestQueryMBean.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Interface TestQueryMBean
+ * MBean used for testing the types wired when using QueryExp.
+ * It is heavily linked to QueryFactory.
+ */
+public interface TestQueryMBean
+{
+ /**
+ * Get Att of type boolean
+ */
+ public boolean getBooleanAtt();
+
+ /**
+ * Set Att of type boolean
+ */
+ public void setBooleanAtt(boolean value);
+
+ /**
+ * Get Att of type double
+ */
+ public double getDoubleAtt();
+
+ /**
+ * Set Att of type double
+ */
+ public void setDoubleAtt(double value);
+
+ /**
+ * Get Att of type float
+ */
+ public float getFloatAtt();
+
+ /**
+ * Set Att of type float
+ */
+ public void setFloatAtt(float value);
+
+ /**
+ * Get Att of type int
+ */
+ public int getIntAtt();
+
+ /**
+ * Set Att of type int
+ */
+ public void setIntAtt(int value);
+
+ /**
+ * Get Att of type Integer
+ */
+ public Integer getIntegerAtt();
+
+ /**
+ * Set Att of type Integer
+ */
+ public void setIntegerAtt(Integer value);
+
+ /**
+ * Get Att of type long
+ */
+ public long getLongAtt();
+
+ /**
+ * Set Att of type long
+ */
+ public void setLongAtt(long value);
+
+ /**
+ * Get Att of type String
+ */
+ public String getStringAtt();
+
+ /**
+ * Set Att of type String
+ */
+ public void setStringAtt(String value);
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/AuthorizationTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058865
+ * @summary Checks various authentication behavior from remote jmx client
+ * @author Olivier Lagneau
+ * @modules java.management
+ * @library /lib/testlibrary
+ * @compile Simple.java
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException
+ * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials
+ * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username3 -Dpassword=password3 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedGetException
+ * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username5 -Dpassword=password5 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException
+ * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username6 -Dpassword=password6 AuthorizationTest -server -mapType x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException
+ * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username1 -Dpassword=password1 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials
+ * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username2 -Dpassword=password2 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedSetException -expectedInvokeException
+ * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username3 -Dpassword=password3 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException
+ * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username4 -Dpassword=password4 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedGetException -expectedSetException
+ * @run main/othervm/timeout=300/policy=java.policy.authorization -DDEBUG_STANDARD -Dusername=username5 -Dpassword=password5 AuthorizationTest -server -mapType x.access.file;x.password.file -populate -client -mapType credentials -expectedCreateException -expectedGetException -expectedSetException -expectedInvokeException
+ */
+
+import java.io.File;
+import java.util.Map ;
+import java.util.HashMap ;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import java.lang.management.ManagementFactory;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory ;
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+import javax.management.Attribute ;
+import javax.management.ObjectName ;
+
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.JDKToolFinder;
+
+public class AuthorizationTest {
+
+ static final String SERVER_CLASS_NAME = "AuthorizationTest";
+ static final String CLIENT_CLASS_NAME = "AuthorizationTest$ClientSide";
+ static final String CLIENT_CLASS_MAIN = CLIENT_CLASS_NAME;
+
+ static final String USERNAME_PROPERTY = "username";
+ static final String PASSWORD_PROPERTY = "password";
+
+ private JMXConnectorServer cs;
+
+ /*
+ * First Debug properties and arguments are collect in expected
+ * map (argName, value) format, then calls original test's run method.
+ */
+ public static void main(String args[]) throws Exception {
+
+ System.out.println("=================================================");
+
+ // Parses parameters
+ Utils.parseDebugProperties();
+
+ // Supported parameters list format is :
+ // "MainClass [-server <param-spec> ...] [-client <param-spec> ...]
+ // with <param-spec> either "-parami valuei" or "-parami"
+ HashMap<String, Object> serverMap = new HashMap<>() ;
+ int clientArgsIndex =
+ Utils.parseServerParameters(args, SERVER_CLASS_NAME, serverMap);
+
+ // Extract and records client params
+ String[] clientParams = null;
+ if (clientArgsIndex < args.length) {
+ int clientParamsSize = args.length - clientArgsIndex;
+ clientParams = new String[clientParamsSize];
+ System.arraycopy(args, clientArgsIndex, clientParams, 0, clientParamsSize);
+ } else {
+ clientParams = new String[0];
+ }
+
+ // Run test
+ AuthorizationTest test = new AuthorizationTest();
+ test.run(serverMap, clientParams);
+
+ }
+
+ /*
+ * Create the MBeansServer side of the test and returns its address
+ */
+ private JMXServiceURL createServerSide(Map<String, Object> serverMap)
+ throws Exception {
+ final int NINETY_SECONDS = 90;
+
+ System.out.println("AuthorizationTest::createServerSide: Start") ;
+
+ MBeanServer mbs = MBeanServerFactory.newMBeanServer();
+ JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
+
+ // Creates connection environment from server side params
+ HashMap<String, Object> env = new HashMap<>();
+ String value = null;
+
+ if ((value = (String)serverMap.get("-mapType")) != null) {
+ if (value.contains("x.access.file")) {
+ String accessFileStr = System.getProperty("test.src") +
+ File.separator + "access.properties";
+ env.put("jmx.remote.x.access.file", accessFileStr);
+ System.out.println("Added " + accessFileStr + " file as jmx.remote.x.access.file");
+ }
+ if (value.contains("x.password.file")) {
+ String passwordFileStr = System.getProperty("test.src") +
+ File.separator + "password.properties";
+ env.put("jmx.remote.x.password.file", passwordFileStr);
+ System.out.println("Added " + passwordFileStr + " file as jmx.remote.x.password.file");
+ }
+ }
+
+ if (serverMap.containsKey("-populate")) {
+ String populateClassName = "Simple";
+ ObjectName on =
+ new ObjectName("defaultDomain:class=Simple");
+
+ Utils.debug(Utils.DEBUG_STANDARD, "create and register Simple MBean") ;
+ mbs.createMBean(populateClassName, on);
+ }
+
+ cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
+ cs.start();
+
+ Utils.waitReady(cs, NINETY_SECONDS);
+
+ JMXServiceURL addr = cs.getAddress();
+
+ System.out.println("AuthorizationTest::createServerSide: Done.") ;
+
+ return addr;
+ }
+
+ /*
+ * Creating command-line for running subprocess JVM:
+ *
+ * JVM command line is like:
+ * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main
+ *
+ * {defaultopts} are the default java options set by the framework.
+ *
+ */
+ private List<String> buildCommandLine(String args[]) {
+ List<String> opts = new ArrayList<>();
+ opts.add(JDKToolFinder.getJDKTool("java"));
+ opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts()));
+
+ String usernameValue = System.getProperty(USERNAME_PROPERTY);
+ if (usernameValue != null) {
+ opts.add("-D" + USERNAME_PROPERTY + "=" + usernameValue);
+ }
+ String passwordValue = System.getProperty(PASSWORD_PROPERTY);
+ if (passwordValue != null) {
+ opts.add("-D" + PASSWORD_PROPERTY + "=" + passwordValue);
+ }
+
+ opts.add("-cp");
+ opts.add(System.getProperty("test.class.path", "test.class.path"));
+ opts.add(CLIENT_CLASS_MAIN);
+ opts.addAll(Arrays.asList(args));
+ return opts;
+ }
+
+ /**
+ * Runs AuthorizationTest$ClientSide with the passed options and redirects
+ * subprocess standard I/O to the current (parent) process. This provides a
+ * trace of what happens in the subprocess while it is runnning (and before
+ * it terminates).
+ *
+ * @param serviceUrlStr string representing the JMX service Url to connect to.
+ */
+ private int runClientSide(String args[], String serviceUrlStr) throws Exception {
+
+ // Building command-line
+ List<String> opts = buildCommandLine(args);
+ opts.add("-serviceUrl");
+ opts.add(serviceUrlStr);
+
+ // Launch separate JVM subprocess
+ int exitCode = 0;
+ String[] optsArray = opts.toArray(new String[0]);
+ ProcessBuilder pb = new ProcessBuilder(optsArray);
+ Process p = ProcessTools.startProcess("AuthorizationTest$ClientSide", pb);
+
+ // Handling end of subprocess
+ try {
+ exitCode = p.waitFor();
+ if (exitCode != 0) {
+ System.out.println(
+ "Subprocess unexpected exit value of [" + exitCode +
+ "]. Expected 0.\n");
+ }
+ } catch (InterruptedException e) {
+ System.out.println("Parent process interrupted with exception : \n " + e + " :" );
+
+ // Parent thread unknown state, killing subprocess.
+ p.destroyForcibly();
+
+ throw new RuntimeException(
+ "Parent process interrupted with exception : \n " + e + " :" );
+
+ } finally {
+ if (p.isAlive()) {
+ p.destroyForcibly();
+ }
+ return exitCode;
+ }
+
+ }
+
+ public void run(Map<String, Object> serverArgs, String clientArgs[]) {
+
+ System.out.println("AuthorizationTest::run: Start") ;
+ int errorCount = 0;
+
+ try {
+ // Initialise the server side
+ JMXServiceURL urlToUse = createServerSide(serverArgs);
+
+ // Run client side
+ errorCount = runClientSide(clientArgs, urlToUse.toString());
+
+ if ( errorCount == 0 ) {
+ System.out.println("AuthorizationTest::run: Done without any error") ;
+ } else {
+ System.out.println("AuthorizationTest::run: Done with "
+ + errorCount
+ + " error(s)") ;
+ throw new RuntimeException("errorCount = " + errorCount);
+ }
+
+ cs.stop();
+
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ private static class ClientSide {
+
+ private JMXConnector cc = null;
+ private MBeanServerConnection mbsc = null;
+
+ public static void main(String args[]) throws Exception {
+
+ // Parses parameters
+ Utils.parseDebugProperties();
+
+ // Supported parameters list format is : "MainClass [-client <param-spec> ...]
+ // with <param-spec> either "-parami valuei" or "-parami"
+ HashMap<String, Object> clientMap = new HashMap<>() ;
+ Utils.parseClientParameters(args, CLIENT_CLASS_NAME, clientMap);
+
+ // Run test
+ ClientSide test = new ClientSide();
+ test.run(clientMap);
+
+ }
+
+ public void run(Map<String, Object> args) {
+
+ int errorCount = 0 ;
+
+ try {
+ boolean expectedCreateException =
+ (args.containsKey("-expectedCreateException")) ? true : false ;
+ boolean expectedGetException =
+ (args.containsKey("-expectedGetException")) ? true : false ;
+ boolean expectedSetException =
+ (args.containsKey("-expectedSetException")) ? true : false ;
+ boolean expectedInvokeException =
+ (args.containsKey("-expectedInvokeException")) ? true : false ;
+ // JSR262 (see bug 6440374)
+ // There is no special JSR262 protocol operation for connect.
+ // The first request sent initiate the connection.
+ // In the JSR262 current implementation, getDefaultDomain is sent to
+ // the server in order to get the server part of the connection ID.
+ // => the connection may fail if no access permission on get requests.
+ boolean expectedConnectException =
+ (args.containsKey("-expectedConnectException")) ? true : false ;
+ // Before connection,
+ // remove the element of the Map with null values (not supported by RMI)
+ // See bug 4982668
+ args.remove("-expectedCreateException");
+ args.remove("-expectedGetException");
+ args.remove("-expectedSetException");
+ args.remove("-expectedInvokeException");
+ args.remove("-expectedConnectException");
+
+
+ // Here do connect to the JMX Server
+ String username = System.getProperty("username");
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::run: CONNECT on behalf of \"" + username + "\"");
+ doConnect(args, expectedConnectException);
+
+ // If the connection did not fail, perform some requests.
+ // At this stage the mbeanserver connection is up and running
+ if (mbsc != null) {
+ ObjectName on = new ObjectName("defaultDomain:class=Simple");
+
+ // Create request
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::run: CREATE on behalf of \"" +
+ username + "\"");
+ errorCount += doCreateRequest(mbsc,
+ new ObjectName("defaultDomain:class=Simple,user=" + username),
+ expectedCreateException);
+
+ // Get request
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::run: GET on behalf of \"" +
+ username + "\"");
+ errorCount += doGetRequest(mbsc, on, expectedGetException);
+
+ // Set request
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::run: SET on behalf of \"" +
+ username + "\"");
+ errorCount += doSetRequest(mbsc, on, expectedSetException);
+
+ // Invoke request
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::run: INVOKE on behalf of \"" +
+ username + "\"");
+ errorCount += doInvokeRequest(mbsc, on, expectedInvokeException);
+ }
+
+ } catch(Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++;
+ } finally {
+ // Terminate the JMX Client
+ try {
+ cc.close();
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++;
+ }
+ }
+
+ System.out.println("ClientSide::run: Done") ;
+
+ // Handle result
+ if (errorCount == 0) {
+ System.out.println("ClientSide::run: (OK) authorization test succeeded.");
+ } else {
+ String message = "AuthorizationTest$ClientSide::run: (ERROR) " +
+ " authorization test failed with " +
+ errorCount + " error(s)";
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+ }
+
+ protected void doConnect(Map<String, Object> args,
+ boolean expectedException) {
+
+ String msgTag = "ClientSide::doConnect";
+ boolean throwRuntimeException = false;
+ String message = "";
+
+ try {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::doConnect: Connect the client");
+
+ // Collect connection environment
+ HashMap<String, Object> env = new HashMap<>();
+
+ Object value = args.get("-mapType");
+ if (value != null) {
+ String username = System.getProperty("username");
+ String password = System.getProperty("password");
+ Utils.debug(Utils.DEBUG_STANDARD,
+ msgTag + "add \"jmx.remote.credentials\" = \"" +
+ username + "\", \"" + password + "\"");
+ env.put("jmx.remote.credentials",
+ new String[] { username , password });
+ }
+
+ // Get a connection to remote mbean server
+ JMXServiceURL addr = new JMXServiceURL((String)args.get("-serviceUrl"));
+ cc = JMXConnectorFactory.connect(addr,env);
+ mbsc = cc.getMBeanServerConnection();
+
+ if (expectedException) {
+ message = "ClientSide::doConnect: (ERROR) " +
+ "Connect did not fail with expected SecurityException";
+ System.out.println(message);
+ throwRuntimeException = true;
+ } else {
+ System.out.println("ClientSide::doConnect: (OK) Connect succeed");
+ }
+ } catch(Exception e) {
+ Utils.printThrowable(e, true);
+ if (expectedException) {
+ if (e instanceof java.lang.SecurityException) {
+ System.out.println("ClientSide::doConnect: (OK) " +
+ "Connect failed with expected SecurityException");
+ } else {
+ message = "ClientSide::doConnect: (ERROR) " +
+ "Create failed with " + e.getClass() +
+ " instead of expected SecurityException";
+ System.out.println(message);
+ throwRuntimeException = true;
+ }
+ } else {
+ message = "ClientSide::doConnect: (ERROR) " +
+ "Connect failed";
+ System.out.println(message);
+ throwRuntimeException = true;
+ }
+ }
+
+ // If the connection failed, or if the connection succeeded but should not,
+ // no need to go further => throw RuntimeException and exit the test
+ if (throwRuntimeException) {
+ throw new RuntimeException(message);
+ }
+ }
+
+ protected int doCreateRequest(MBeanServerConnection mbsc,
+ ObjectName on,
+ boolean expectedException) {
+ int errorCount = 0;
+
+ try {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::doCreateRequest: Create and register the MBean") ;
+
+ mbsc.createMBean("Simple", on) ;
+
+ if (expectedException) {
+ System.out.println("ClientSide::doCreateRequest: " +
+ "(ERROR) Create did not fail with expected SecurityException");
+ errorCount++;
+ } else {
+ System.out.println("ClientSide::doCreateRequest: (OK) Create succeed") ;
+ }
+ } catch(Exception e) {
+ Utils.printThrowable(e, true) ;
+ if (expectedException) {
+ if (e instanceof java.lang.SecurityException) {
+ System.out.println("ClientSide::doCreateRequest: " +
+ "(OK) Create failed with expected SecurityException") ;
+ } else {
+ System.out.println("ClientSide::doCreateRequest: " +
+ "(ERROR) Create failed with " +
+ e.getClass() + " instead of expected SecurityException");
+ errorCount++;
+ }
+ } else {
+ System.out.println("ClientSide::doCreateRequest: " +
+ "(ERROR) Create failed");
+ errorCount++;
+ }
+ }
+ return errorCount;
+ }
+
+ protected int doGetRequest(MBeanServerConnection mbsc,
+ ObjectName on,
+ boolean expectedException) {
+ int errorCount = 0;
+
+ try {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::doGetRequest: Get attributes of the MBean") ;
+
+ mbsc.getAttribute(on, "Attribute");
+
+ if (expectedException) {
+ System.out.println("ClientSide::doGetRequest: " +
+ "(ERROR) Get did not fail with expected SecurityException");
+ errorCount++;
+ } else {
+ System.out.println("ClientSide::doGetRequest: (OK) Get succeed") ;
+ }
+ } catch(Exception e) {
+ Utils.printThrowable(e, true) ;
+ if (expectedException) {
+ if (e instanceof java.lang.SecurityException) {
+ System.out.println("ClientSide::doGetRequest: " +
+ "(OK) Get failed with expected SecurityException") ;
+ } else {
+ System.out.println("ClientSide::doGetRequest: " +
+ "(ERROR) Get failed with " +
+ e.getClass() + " instead of expected SecurityException");
+ errorCount++;
+ }
+ } else {
+ System.out.println("ClientSide::doGetRequest: (ERROR) Get failed");
+ errorCount++;
+ }
+ }
+
+ return errorCount;
+ }
+
+ protected int doSetRequest(MBeanServerConnection mbsc,
+ ObjectName on,
+ boolean expectedException) {
+ int errorCount = 0;
+
+ try {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::doSetRequest: Set attributes of the MBean") ;
+
+ Attribute attribute = new Attribute("Attribute", "My value") ;
+ mbsc.setAttribute(on, attribute) ;
+
+ if (expectedException) {
+ System.out.println("ClientSide::doSetRequest: " +
+ "(ERROR) Set did not fail with expected SecurityException");
+ errorCount++;
+ } else {
+ System.out.println("ClientSide::doSetRequest: (OK) Set succeed") ;
+ }
+ } catch(Exception e) {
+ Utils.printThrowable(e, true) ;
+ if (expectedException) {
+ if (e instanceof java.lang.SecurityException) {
+ System.out.println("ClientSide::doSetRequest: " +
+ "(OK) Set failed with expected SecurityException") ;
+ } else {
+ System.out.println("ClientSide::doSetRequest: " +
+ "(ERROR) Set failed with " +
+ e.getClass() + " instead of expected SecurityException");
+ errorCount++;
+ }
+ } else {
+ System.out.println("ClientSide::doSetRequest: (ERROR) Set failed");
+ errorCount++;
+ }
+ }
+ return errorCount;
+ }
+
+ protected int doInvokeRequest(MBeanServerConnection mbsc,
+ ObjectName on,
+ boolean expectedException) {
+ int errorCount = 0;
+
+ try {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::doInvokeRequest: Invoke operations on the MBean") ;
+
+ mbsc.invoke(on, "operation", null, null) ;
+
+ if (expectedException) {
+ System.out.println("ClientSide::doInvokeRequest: " +
+ "(ERROR) Invoke did not fail with expected SecurityException");
+ errorCount++;
+ } else {
+ System.out.println("ClientSide::doInvokeRequest: (OK) Invoke succeed") ;
+ }
+ } catch(Exception e) {
+ Utils.printThrowable(e, true) ;
+ if (expectedException) {
+ if (e instanceof java.lang.SecurityException) {
+ System.out.println("ClientSide::doInvokeRequest: " +
+ "(OK) Invoke failed with expected SecurityException") ;
+ } else {
+ System.out.println("ClientSide::doInvokeRequest: " +
+ " (ERROR) Invoke failed with " +
+ e.getClass() + " instead of expected SecurityException");
+ errorCount++;
+ }
+ } else {
+ System.out.println("ClientSide::doInvokeRequest: " +
+ "(ERROR) Invoke failed");
+ errorCount++;
+ }
+ }
+ return errorCount;
+ }
+
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/MBS_Light.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import javax.security.auth.Subject;
+import java.security.Principal;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.management.MBeanRegistration ;
+import javax.management.MBeanServer ;
+import javax.management.ObjectName ;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationListener;
+import javax.management.Notification;
+
+public class MBS_Light extends NotificationBroadcasterSupport
+ implements MBS_LightMBean, MBeanRegistration, NotificationListener
+{
+ private RjmxMBeanParameter param = null ;
+ private String aString = "notset" ;
+ private int anInt = 0 ;
+ private MBeanServer mbs = null ;
+ private ObjectName objname = null ;
+ private Exception anException = null ;
+ private Error anError = null ;
+ private int count = 0;
+ private SimpleListener listener = new SimpleListener();
+
+ @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR MBS_Light")
+ public MBS_Light() {
+ }
+
+ @SqeDescriptorKey("ONE RjmxMBeanParameter PARAMETER CONSTRUCTOR MBS_Light")
+ public MBS_Light(@SqeDescriptorKey("CONSTRUCTOR PARAMETER param")
+ RjmxMBeanParameter param) {
+ this.param = param ;
+ }
+
+ @SqeDescriptorKey("ONE String PARAMETER CONSTRUCTOR MBS_Light")
+ public MBS_Light(@SqeDescriptorKey("CONSTRUCTOR PARAMETER param")String param) {
+ this.aString = param ;
+ }
+
+ // Getter for property param
+ public RjmxMBeanParameter getParam() {
+ return this.param ;
+ }
+
+ // Setter for property param
+ public void setParam(RjmxMBeanParameter param) {
+ this.param = param ;
+ }
+
+ // Getter for property aString
+ public String getAstring() {
+ return this.aString ;
+ }
+
+ // Setter for property aString
+ public void setAstring(String aString) {
+ this.aString = aString ;
+ }
+
+ // Getter for property anInt
+ public int getAnInt() {
+ return this.anInt ;
+ }
+
+ // Setter for property anInt
+ public void setAnInt(int anInt) {
+ this.anInt = anInt ;
+ }
+
+ // Getter for property anException
+ public Exception getAnException() {
+ return this.anException ;
+ }
+
+ // Setter for property anException
+ public void setAnException(Exception anException) {
+ this.anException = anException ;
+ }
+
+ // Getter for property anError
+ public Error getAnError() {
+ return this.anError ;
+ }
+
+ // Setter for property anError
+ public void setAnError(Error anError) {
+ this.anError = anError ;
+ }
+
+ // An operation
+ public RjmxMBeanParameter operate1(String name) {
+ return new RjmxMBeanParameter(name) ;
+ }
+
+ // An operation
+ public String operate2(RjmxMBeanParameter param) {
+ return param.name ;
+ }
+
+ // An operation
+ public void throwError() {
+ throw new Error("JSR-160-ERROR");
+ }
+
+ // An operation
+ public void throwException() throws Exception {
+ throw new Exception("JSR-160-EXCEPTION");
+ }
+
+ // MBeanRegistration method
+ public void postDeregister() {
+ }
+
+ // MBeanRegistration method
+ public void postRegister(Boolean registrationDone) {
+ }
+
+ // MBeanRegistration method
+ public void preDeregister()
+ throws Exception
+ {
+ }
+
+ // MBeanRegistration method
+ public ObjectName preRegister(MBeanServer server, ObjectName name)
+ throws Exception
+ {
+ this.mbs = server ;
+ if ( name == null ) {
+ this.objname = new ObjectName("protocol:class=MBS_Light") ;
+ }
+ else {
+ this.objname = name ;
+ }
+ return this.objname ;
+ }
+
+ public synchronized void handleNotification(Notification notification,
+ Object handback) {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "MBS_Light::handleNotification: " + notification);
+ listener.handleNotification(notification, handback);
+ }
+
+ // Send a notification
+ public void sendNotification() {
+ Notification notification =
+ new Notification("JSR160-TCK-NOTIFICATION", this, count++);
+ sendNotification(notification);
+ }
+
+ public Object waitForNotificationHB() {
+ return listener.waitForNotificationHB();
+ }
+
+ // Receive multi notifications and send back handbacks
+ public synchronized Object[] waitForMultiNotifications(String nb) {
+ return listener.waitForMultiNotifications(Integer.valueOf(nb).intValue());
+ }
+
+ // Receive a notification
+ public synchronized String waitForNotification() {
+ return listener.waitForNotification();
+ }
+
+ // Is the notification received
+ public synchronized Boolean notificationReceived() {
+ return Boolean.valueOf(listener.isNotificationReceived());
+ }
+
+ // The authorization Id
+ public String getAuthorizationId() {
+ AccessControlContext acc = AccessController.getContext();
+ Subject subject = Subject.getSubject(acc);
+ Set<Principal> principals = subject.getPrincipals();
+ Iterator<Principal> i = principals.iterator();
+ StringBuffer buffer = new StringBuffer();
+ while(i.hasNext()) {
+ Principal p = i.next();
+ buffer.append(p.getName());
+ if(i.hasNext())
+ buffer.append(" ");
+ }
+
+ return buffer.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/MBS_LightMBean.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+@SqeDescriptorKey("INTERFACE MBS_LightMBean")
+public interface MBS_LightMBean {
+ // Getter for property param
+ @SqeDescriptorKey("ATTRIBUTE Param")
+ public RjmxMBeanParameter getParam() ;
+
+ // Setter for property param
+ @SqeDescriptorKey("ATTRIBUTE Param")
+ public void setParam(RjmxMBeanParameter param) ;
+
+ // Getter for property aString
+ @SqeDescriptorKey("ATTRIBUTE Astring")
+ public String getAstring() ;
+
+ // Setter for property aString
+ @SqeDescriptorKey("ATTRIBUTE Astring")
+ public void setAstring(String aString) ;
+
+ // Getter for property anInt
+ @SqeDescriptorKey("ATTRIBUTE AnInt")
+ public int getAnInt() ;
+
+ // Setter for property anInt
+ @SqeDescriptorKey("ATTRIBUTE AnInt")
+ public void setAnInt(int anInt) ;
+
+ // Getter for property anException
+ @SqeDescriptorKey("ATTRIBUTE AnException")
+ public Exception getAnException() ;
+
+ // Setter for property anException
+ @SqeDescriptorKey("ATTRIBUTE AnException")
+ public void setAnException(Exception anException) ;
+
+ // Getter for property anError
+ @SqeDescriptorKey("ATTRIBUTE AnError")
+ public Error getAnError() ;
+
+ // Setter for property anError
+ @SqeDescriptorKey("ATTRIBUTE AnError")
+ public void setAnError(Error anError) ;
+
+ // An operation
+ @SqeDescriptorKey("OPERATION operate1")
+ public RjmxMBeanParameter operate1(
+ @SqeDescriptorKey("OPERATION PARAMETER name")String name) ;
+
+ // An operation
+ @SqeDescriptorKey("OPERATION operate2")
+ public String operate2(
+ @SqeDescriptorKey("OPERATION PARAMETER param")RjmxMBeanParameter param) ;
+
+ // Throws an error
+ @SqeDescriptorKey("OPERATION throwError")
+ public void throwError();
+
+ // Throws an exception
+ @SqeDescriptorKey("OPERATION throwException")
+ public void throwException() throws Exception;
+
+ // Send a notification
+ @SqeDescriptorKey("OPERATION sendNotification")
+ public void sendNotification();
+
+ // Receive a notification and return the type
+ @SqeDescriptorKey("OPERATION waitForNotification")
+ public String waitForNotification();
+
+ // Receive a notification and return the HandBack
+ @SqeDescriptorKey("OPERATION waitForNotificationHB")
+ public Object waitForNotificationHB();
+
+ // Receive multi notifications and return the HandBacks
+ @SqeDescriptorKey("OPERATION waitForMultiNotifications")
+ public Object[] waitForMultiNotifications(
+ @SqeDescriptorKey("OPERATION PARAMETER nb")String nb);
+
+ // Is the notification received
+ @SqeDescriptorKey("OPERATION notificationReceived")
+ public Boolean notificationReceived();
+
+ // Return the current authorization Id
+ @SqeDescriptorKey("OPERATION getAuthorizationId")
+ public String getAuthorizationId();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/RjmxMBeanParameter.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.Serializable ;
+
+/**
+ * That class is used to modelize a parameter to be used as MBean property
+ * value or MBean operation parameter or returned value.
+ */
+public class RjmxMBeanParameter implements Serializable {
+ public String name = "unset" ;
+
+ public RjmxMBeanParameter() {
+ }
+
+ public RjmxMBeanParameter(String name) {
+ this.name = name ;
+ }
+
+ public boolean equals(Object obj) {
+ if ( this.name.equals(((RjmxMBeanParameter)obj).name) ) {
+ return true ;
+ } else {
+ return false ;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/SecurityTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,800 @@
+/*
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8058865
+ * @summary Checks various secure ways of connecting from remote jmx client
+ * @author Olivier Lagneau
+ * @modules java.management
+ * @library /lib/testlibrary
+ * @compile MBS_Light.java ServerDelegate.java TestSampleLoginModule.java
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=SQE_username -Dpassword=SQE_password SecurityTest -server -mapType x.password.file -client -mapType credentials
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=UNKNOWN_username -Dpassword=SQE_password SecurityTest -server -mapType x.password.file -client -mapType credentials -expectedThrowable java.lang.SecurityException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=SQE_username -Dpassword=WRONG_password SecurityTest -server -mapType x.password.file -client -mapType credentials -expectedThrowable java.lang.SecurityException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dsusername=TestJMXAuthenticatorUsername -Dspassword=TestJMXAuthenticatorPassword -Dusername=TestJMXAuthenticatorUsername -Dpassword=TestJMXAuthenticatorPassword SecurityTest -server -mapType x.authenticator -client -mapType credentials
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dsusername=TestJMXAuthenticatorUsername -Dspassword=TestJMXAuthenticatorPassword -Dusername=AnotherTestJMXAuthenticatorUsername -Dpassword=TestJMXAuthenticatorPassword SecurityTest -server -mapType x.authenticator -client -mapType credentials -expectedThrowable java.lang.SecurityException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.PasswordFileAuthentication -client -mapType credentials
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config.UNKNOWN -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.PasswordFileAuthentication -client -mapType credentialss -expectedThrowable java.lang.SecurityException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.UnknownAuthentication -client -mapType credentials -expectedThrowable java.lang.SecurityException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dsusername=usernameSampleLoginModule -Dspassword=passwordSampleLoginModule -Dpassword.file=password.properties -Dusername=usernameSampleLoginModule -Dpassword=passwordSampleLoginModule SecurityTest -server -mapType x.login.config.SampleLoginModule -client -mapType credentials
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dsusername=usernameSampleLoginModule -Dspassword=passwordSampleLoginModule -Dpassword.file=password.properties -Dusername=AnotherUsernameSampleLoginModule -Dpassword=passwordSampleLoginModule SecurityTest -server -mapType x.login.config.SampleLoginModule -client -mapType credentials -expectedThrowable java.lang.SecurityException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword WRONG_password -expectedThrowable java.io.IOException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -expectedThrowable java.io.IOException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client -keystore keystoreClient -keystorepassword glopglop -truststore truststoreClient -truststorepassword glopglop
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client -keystore keystoreClient -keystorepassword WRONG_password -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -client -keystore keystoreClient -keystorepassword glopglop -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_MD5 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.md5 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_SHA SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.md5 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_MD5 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.sha -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=SSLv3 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.sslv3 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=TLSv1 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.sslv3 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
+ * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=SSLv3 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.tlsv1 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
+ */
+
+import java.io.File;
+import java.util.Map ;
+import java.util.HashMap ;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory ;
+import javax.management.MBeanServerConnection;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXConnectorServerFactory;
+import javax.management.remote.JMXServiceURL;
+
+import javax.management.Attribute ;
+import javax.management.ObjectName ;
+
+import javax.rmi.ssl.SslRMIClientSocketFactory;
+import javax.rmi.ssl.SslRMIServerSocketFactory;
+
+import java.security.Security;
+
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.JDKToolFinder;
+
+public class SecurityTest {
+
+ static final String SERVER_CLASS_NAME = "SecurityTest";
+ static final String CLIENT_CLASS_NAME = "SecurityTest$ClientSide";
+ static final String CLIENT_CLASS_MAIN = CLIENT_CLASS_NAME;
+
+ static final String USERNAME_PROPERTY = "username";
+ static final String PASSWORD_PROPERTY = "password";
+
+ static final String SERVER_DELEGATE_MBEAN_NAME =
+ "defaultDomain:class=ServerDelegate";
+
+ static final String RMI_SERVER_SOCKET_FACTORY_SSL = "rmi.server.socket.factory.ssl";
+ static final String RMI_CLIENT_SOCKET_FACTORY_SSL = "rmi.client.socket.factory.ssl";
+ static final String KEYSTORE_PROPNAME = "javax.net.ssl.keyStore";
+ static final String KEYSTORE_PWD_PROPNAME = "javax.net.ssl.keyStorePassword";
+ static final String TRUSTSTORE_PROPNAME = "javax.net.ssl.trustStore";
+ static final String TRUSTSTORE_PWD_PROPNAME = "javax.net.ssl.trustStorePassword";
+
+ static final String RMI_SSL_CLIENT_ENABLEDCIPHERSUITES =
+ "javax.rmi.ssl.client.enabledCipherSuites";
+ static final String RMI_SSL_CLIENT_ENABLEDPROTOCOLS =
+ "javax.rmi.ssl.client.enabledProtocols";
+
+ private JMXConnectorServer cs;
+
+ // Construct and set keyStore properties from given map
+ static void setKeyStoreProperties(Map<String, Object> map) {
+
+ String keyStore = (String) map.get("-keystore");
+ keyStore = buildSourcePath(keyStore);
+ System.setProperty(KEYSTORE_PROPNAME, keyStore);
+ System.out.println("keyStore location = \"" + keyStore + "\"");
+
+ String password = (String) map.get("-keystorepassword");
+ System.setProperty(KEYSTORE_PWD_PROPNAME, password);
+ System.out.println("keyStore password = " + password);
+
+ }
+
+ // Construct and set trustStore properties from given map
+ static void setTrustStoreProperties(Map<String, Object> map) {
+
+ String trustStore = (String) map.get("-truststore");
+ trustStore = buildSourcePath(trustStore);
+ System.setProperty(TRUSTSTORE_PROPNAME, trustStore);
+ System.out.println("trustStore location = \"" + trustStore + "\"");
+
+ String password = (String) map.get("-truststorepassword");
+ System.setProperty(TRUSTSTORE_PWD_PROPNAME, password);
+ System.out.println("trustStore password = " + password);
+
+ }
+
+ /*
+ * First Debug properties and arguments are collect in expected
+ * map (argName, value) format, then calls original test's run method.
+ */
+ public static void main(String args[]) throws Exception {
+
+ System.out.println("=================================================");
+
+ // Parses parameters
+ Utils.parseDebugProperties();
+
+ // Supported parameters list format is :
+ // "MainClass [-server <param-spec> ...] [-client <param-spec> ...]
+ // with <param-spec> either "-parami valuei" or "-parami"
+ HashMap<String, Object> serverMap = new HashMap<>() ;
+ int clientArgsIndex =
+ Utils.parseServerParameters(args, SERVER_CLASS_NAME, serverMap);
+
+ // Extract and records client params
+ String[] clientParams = null;
+ if (clientArgsIndex < args.length) {
+ int clientParamsSize = args.length - clientArgsIndex;
+ clientParams = new String[clientParamsSize];
+ System.arraycopy(args, clientArgsIndex, clientParams, 0, clientParamsSize);
+ } else {
+ clientParams = new String[0];
+ }
+
+ // Run test
+ SecurityTest test = new SecurityTest();
+ test.run(serverMap, clientParams);
+
+ }
+
+ // Return full path of filename in the test sopurce directory
+ private static String buildSourcePath(String filename) {
+ return System.getProperty("test.src") + File.separator + filename;
+ }
+
+ /*
+ * Collects security run params for server side.
+ */
+ private HashMap<String, Object> setServerSecurityEnv(Map<String, Object> map)
+ throws Exception {
+
+ // Creates Authentication environment from server side params
+ HashMap<String, Object> env = new HashMap<>();
+
+ // Retrieve and set keystore and truststore config if any
+ if (map.containsKey("-keystore") &&
+ map.get("-keystore") != null) {
+ setKeyStoreProperties(map);
+ }
+ System.out.println("Done keystore properties");
+
+ if (map.containsKey("-truststore") &&
+ map.get("-truststore") != null) {
+ setTrustStoreProperties(map);
+ }
+ System.out.println("Done truststore properties");
+
+ String value = null;
+ if ((value = (String)map.get("-mapType")) != null) {
+
+ // Case of remote password file with all authorized credentials
+ if (value.contains("x.password.file")) {
+ String passwordFileStr = buildSourcePath("password.properties");
+ env.put("jmx.remote.x.password.file", passwordFileStr);
+ System.out.println("Added " + passwordFileStr +
+ " file as jmx.remote.x.password.file");
+ }
+
+ // Case of dedicated authenticator class : TestJMXAuthenticator
+ if (value.contains("x.authenticator")) {
+ env.put("jmx.remote.authenticator", new TestJMXAuthenticator()) ;
+ System.out.println(
+ "Added \"jmx.remote.authenticator\" = TestJMXAuthenticator");
+ }
+
+ // Case of security config file with standard Authentication
+ if (value.contains("x.login.config.PasswordFileAuthentication")) {
+ String loginConfig = System.getProperty("login.config.file");
+
+ // Override the default JAAS configuration
+ System.setProperty("java.security.auth.login.config",
+ "file:" + loginConfig);
+ System.out.println("Overrided default JAAS configuration with " +
+ "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ;
+
+ env.put("jmx.remote.x.login.config", "PasswordFileAuthentication") ;
+ System.out.println(
+ "Added \"jmx.remote.x.login.config\" = " +
+ "\"PasswordFileAuthentication\"") ;
+
+ // redirects "password.file" property to file in ${test.src}
+ String passwordFileStr =
+ buildSourcePath(System.getProperty("password.file"));
+ System.setProperty("password.file", passwordFileStr);
+ System.out.println(
+ "Redirected \"password.file\" property value to = " +
+ passwordFileStr) ;
+ }
+
+ // Case of security config file with unexisting athentication config
+ if (value.contains("x.login.config.UnknownAuthentication")) {
+ String loginConfig = System.getProperty("login.config.file");
+
+ // Override the default JAAS configuration
+ System.setProperty("java.security.auth.login.config",
+ "file:" + loginConfig);
+ System.out.println("Overrided default JAAS configuration with " +
+ "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ;
+
+ env.put("jmx.remote.x.login.config", "UnknownAuthentication") ;
+ System.out.println(
+ "Added \"jmx.remote.x.login.config\" = " +
+ "\"UnknownAuthentication\"") ;
+
+ // redirects "password.file" property to file in ${test.src}
+ String passwordFileStr =
+ buildSourcePath(System.getProperty("password.file"));
+ System.setProperty("password.file", passwordFileStr);
+ System.out.println(
+ "Redirected \"password.file\" property value to = " +
+ passwordFileStr) ;
+ }
+
+ // Case of security config file with dedicated login module
+ if (value.contains("x.login.config.SampleLoginModule")) {
+ String loginConfig = System.getProperty("login.config.file");
+
+ // Override the default JAAS configuration
+ System.setProperty("java.security.auth.login.config",
+ "file:" + loginConfig);
+ System.out.println("Overrided default JAAS configuration with " +
+ "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ;
+
+ env.put("jmx.remote.x.login.config", "SampleLoginModule") ;
+ System.out.println(
+ "Added \"jmx.remote.x.login.config\" = " +
+ "\"SampleLoginModule\"") ;
+ }
+
+ // Simple rmi ssl authentication
+ if (value.contains(RMI_CLIENT_SOCKET_FACTORY_SSL)) {
+ env.put("jmx.remote.rmi.client.socket.factory",
+ new SslRMIClientSocketFactory()) ;
+ System.out.println(
+ "Added \"jmx.remote.rmi.client.socket.factory\"" +
+ " = SslRMIClientSocketFactory") ;
+ }
+
+ if (value.contains(RMI_SERVER_SOCKET_FACTORY_SSL)) {
+ if (value.contains(
+ "rmi.server.socket.factory.ssl.need.client.authentication")) {
+ // rmi ssl authentication with client authentication
+ env.put("jmx.remote.rmi.server.socket.factory",
+ new SslRMIServerSocketFactory(null, null, true)) ;
+ System.out.println(
+ "Added \"jmx.remote.rmi.server.socket.factory\"" +
+ " = SslRMIServerSocketFactory with client authentication") ;
+
+ } else if (value.contains("rmi.server.socket.factory.ssl.enabled.cipher.suites.md5")) {
+ // Allows all ciphering and protocols for testing purpose
+ Security.setProperty("jdk.tls.disabledAlgorithms", "");
+
+ env.put("jmx.remote.rmi.server.socket.factory",
+ new SslRMIServerSocketFactory(
+ new String[] {"SSL_RSA_WITH_RC4_128_MD5"}, null, false));
+ System.out.println(
+ "Added \"jmx.remote.rmi.server.socket.factory\"" +
+ " = SslRMIServerSocketFactory with SSL_RSA_WITH_RC4_128_MD5 cipher suite");
+
+ } else if (value.contains("rmi.server.socket.factory.ssl.enabled.cipher.suites.sha")) {
+ // Allows all ciphering and protocols for testing purpose
+ Security.setProperty("jdk.tls.disabledAlgorithms", "");
+
+ env.put("jmx.remote.rmi.server.socket.factory",
+ new SslRMIServerSocketFactory(
+ new String[] { "SSL_RSA_WITH_RC4_128_SHA" }, null, false)) ;
+ System.out.println(
+ "Added \"jmx.remote.rmi.server.socket.factory\"" +
+ " = SslRMIServerSocketFactory with SSL_RSA_WITH_RC4_128_SHA cipher suite") ;
+
+ } else if (value.contains("rmi.server.socket.factory.ssl.enabled.protocols.sslv3")) {
+ // Allows all ciphering and protocols for testing purpose
+ Security.setProperty("jdk.tls.disabledAlgorithms", "");
+
+ env.put("jmx.remote.rmi.server.socket.factory",
+ new SslRMIServerSocketFactory(null, new String[] {"SSLv3"}, false)) ;
+ System.out.println(
+ "Added \"jmx.remote.rmi.server.socket.factory\"" +
+ " = SslRMIServerSocketFactory with SSLv3 protocol") ;
+
+ } else if (value.contains("rmi.server.socket.factory.ssl.enabled.protocols.tlsv1")) {
+ // Allows all ciphering and protocols for testing purpose
+ Security.setProperty("jdk.tls.disabledAlgorithms", "");
+
+ env.put("jmx.remote.rmi.server.socket.factory",
+ new SslRMIServerSocketFactory(null, new String[] {"TLSv1"}, false)) ;
+ System.out.println(
+ "Added \"jmx.remote.rmi.server.socket.factory\"" +
+ " = SslRMIServerSocketFactory with TLSv1 protocol") ;
+
+ } else {
+ env.put("jmx.remote.rmi.server.socket.factory",
+ new SslRMIServerSocketFactory());
+ System.out.println(
+ "Added \"jmx.remote.rmi.server.socket.factory\"" +
+ " = SslRMIServerSocketFactory");
+ }
+ }
+ }
+
+ return env;
+ }
+
+ /*
+ * Create the MBeansServer side of the test and returns its address
+ */
+ private JMXServiceURL createServerSide(Map<String, Object> serverMap)
+ throws Exception {
+ final int NINETY_SECONDS = 90;
+
+ System.out.println("SecurityTest::createServerSide: Start") ;
+
+ // Prepare server side security env
+ HashMap<String, Object> env = setServerSecurityEnv(serverMap);
+
+ // Create and start mbean server and connector server
+ MBeanServer mbs = MBeanServerFactory.newMBeanServer();
+ JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
+ cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
+ cs.start();
+
+ // Waits availibility of connector server
+ Utils.waitReady(cs, NINETY_SECONDS);
+
+ JMXServiceURL addr = cs.getAddress();
+
+ System.out.println("SecurityTest::createServerSide: Done.") ;
+
+ return addr;
+ }
+
+ /*
+ * Creating command-line for running subprocess JVM:
+ *
+ * JVM command line is like:
+ * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main
+ *
+ * {defaultopts} are the default java options set by the framework.
+ *
+ */
+ private List<String> buildCommandLine(String args[]) {
+
+ System.out.println("SecurityTest::buildCommandLine: Start") ;
+
+ List<String> opts = new ArrayList<>();
+ opts.add(JDKToolFinder.getJDKTool("java"));
+ opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts()));
+
+ // We need to forward some properties to the client side
+ opts.add("-Dtest.src=" + System.getProperty("test.src"));
+
+ String usernameValue = System.getProperty(USERNAME_PROPERTY);
+ if (usernameValue != null) {
+ System.out.println("SecurityTest::buildCommandLine: "+
+ " forward username property to client side");
+ opts.add("-D" + USERNAME_PROPERTY + "=" + usernameValue);
+ }
+ String passwordValue = System.getProperty(PASSWORD_PROPERTY);
+ if (passwordValue != null) {
+ System.out.println("SecurityTest::buildCommandLine: "+
+ " forward password property to client side");
+ opts.add("-D" + PASSWORD_PROPERTY + "=" + passwordValue);
+ }
+
+ String enabledCipherSuites =
+ System.getProperty(RMI_SSL_CLIENT_ENABLEDCIPHERSUITES);
+ if (enabledCipherSuites != null) {
+ System.out.println("SecurityTest::buildCommandLine: "+
+ " forward enabledCipherSuites property to client side");
+ opts.add("-D" + RMI_SSL_CLIENT_ENABLEDCIPHERSUITES +
+ "=" + enabledCipherSuites);
+ }
+
+ String enabledProtocols =
+ System.getProperty(RMI_SSL_CLIENT_ENABLEDPROTOCOLS);
+ if (enabledProtocols != null) {
+ System.out.println("SecurityTest::buildCommandLine: "+
+ " forward enabledProtocols property to client side");
+ opts.add("-D" + RMI_SSL_CLIENT_ENABLEDPROTOCOLS +
+ "=" + enabledProtocols);
+ }
+
+ opts.add("-cp");
+ opts.add(System.getProperty("test.class.path", "test.class.path"));
+ opts.add(CLIENT_CLASS_MAIN);
+ opts.addAll(Arrays.asList(args));
+
+ System.out.println("SecurityTest::buildCommandLine: Done.") ;
+
+ return opts;
+ }
+
+ /**
+ * Runs SecurityTest$ClientSide with the passed options and redirects
+ * subprocess standard I/O to the current (parent) process. This provides a
+ * trace of what happens in the subprocess while it is runnning (and before
+ * it terminates).
+ *
+ * @param serviceUrlStr string representing the JMX service Url to connect to.
+ */
+ private int runClientSide(String args[], String serviceUrlStr) throws Exception {
+
+ System.out.println("SecurityTest::runClientSide: Start") ;
+
+ // Building command-line
+ List<String> opts = buildCommandLine(args);
+ opts.add("-serviceUrl");
+ opts.add(serviceUrlStr);
+
+ // Launch separate JVM subprocess
+ int exitCode = 0;
+ String[] optsArray = opts.toArray(new String[0]);
+ ProcessBuilder pb = new ProcessBuilder(optsArray);
+ Process p = ProcessTools.startProcess("SecurityTest$ClientSide", pb);
+
+ // Handling end of subprocess
+ try {
+ exitCode = p.waitFor();
+ if (exitCode != 0) {
+ System.out.println(
+ "Subprocess unexpected exit value of [" + exitCode +
+ "]. Expected 0.\n");
+ }
+ } catch (InterruptedException e) {
+ System.out.println("Parent process interrupted with exception : \n " + e + " :" );
+
+ // Parent thread unknown state, killing subprocess.
+ p.destroyForcibly();
+
+ throw new RuntimeException(
+ "Parent process interrupted with exception : \n " + e + " :" );
+
+ } finally {
+ if (p.isAlive()) {
+ p.destroyForcibly();
+ }
+
+ System.out.println("SecurityTest::runClientSide: Done") ;
+
+ return exitCode;
+ }
+
+ }
+
+ public void run(Map<String, Object> serverArgs, String clientArgs[]) {
+
+ System.out.println("SecurityTest::run: Start") ;
+ int errorCount = 0;
+
+ try {
+ // Initialise the server side
+ JMXServiceURL urlToUse = createServerSide(serverArgs);
+
+ // Run client side
+ errorCount = runClientSide(clientArgs, urlToUse.toString());
+
+ if ( errorCount == 0 ) {
+ System.out.println("SecurityTest::run: Done without any error") ;
+ } else {
+ System.out.println(
+ "SecurityTest::run: Done with " + errorCount + " error(s)");
+ throw new RuntimeException("errorCount = " + errorCount);
+ }
+
+ cs.stop();
+
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ private static class ClientSide {
+
+ private JMXConnector cc = null;
+ private MBeanServerConnection mbsc = null;
+
+ public static void main(String args[]) throws Exception {
+
+ // Parses parameters
+ Utils.parseDebugProperties();
+
+ // Supported parameters list format is : "MainClass [-client <param-spec> ...]
+ // with <param-spec> either "-parami valuei" or "-parami"
+ HashMap<String, Object> clientMap = new HashMap<>() ;
+ Utils.parseClientParameters(args, CLIENT_CLASS_NAME, clientMap);
+
+ // Run test
+ ClientSide test = new ClientSide();
+ test.run(clientMap);
+ }
+
+ public void run(Map<String, Object> args) {
+
+ System.out.println("ClientSide::run: Start");
+ int errorCount = 0;
+
+ try {
+ // Setup client side parameters
+ HashMap<String, Object> env = new HashMap<>();
+
+ // If needed allows all ciphering and protocols for testing purpose
+ if (System.getProperty(RMI_SSL_CLIENT_ENABLEDCIPHERSUITES) != null) {
+ Security.setProperty("jdk.tls.disabledAlgorithms", "");
+ }
+
+ // If needed allows all ciphering and protocols for testing purpose
+ if (System.getProperty(RMI_SSL_CLIENT_ENABLEDPROTOCOLS) != null) {
+ Security.setProperty("jdk.tls.disabledAlgorithms", "");
+ }
+
+ // Retrieve and set keystore and truststore config if any
+ if (args.containsKey("-keystore") &&
+ args.get("-keystore") != null) {
+ SecurityTest.setKeyStoreProperties(args);
+ }
+ if (args.containsKey("-truststore") &&
+ args.get("-truststore") != null) {
+ SecurityTest.setTrustStoreProperties(args);
+ }
+
+ Object value = args.get("-mapType");
+ if ((value != null) &&
+ value.equals("credentials")) {
+ String username = System.getProperty("username");
+ String password = System.getProperty("password");
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "add \"jmx.remote.credentials\" = \"" +
+ username + "\", \"" + password + "\"");
+ env.put("jmx.remote.credentials",
+ new String[] { username , password });
+ }
+
+ String expectedThrowable = (String) args.get("-expectedThrowable");
+
+ String authCallCountName = "-expectedAuthenticatorCallCount";
+ int authCallCountValue = 0;
+ if (args.containsKey(authCallCountName)) {
+ authCallCountValue =
+ (new Integer((String) args.get(authCallCountName))).intValue();
+ }
+
+ try {
+ // Get a connection to remote mbean server
+ JMXServiceURL addr = new JMXServiceURL((String)args.get("-serviceUrl"));
+ cc = JMXConnectorFactory.connect(addr,env);
+ mbsc = cc.getMBeanServerConnection();
+
+ // In case we should have got an exception
+ if (expectedThrowable != null) {
+ System.out.println("ClientSide::run: (ERROR) " +
+ " Connect did not fail with expected " + expectedThrowable);
+ errorCount++;
+ } else {
+ System.out.println("ClientSide::run: (OK) Connect succeed");
+ }
+ } catch (Throwable e) {
+ Utils.printThrowable(e, true);
+ if (expectedThrowable != null) {
+ if (Utils.compareThrowable(e, expectedThrowable)) {
+ System.out.println("ClientSide::run: (OK) " +
+ "Connect failed with expected " + expectedThrowable);
+ } else {
+ System.out.println("ClientSide::run: (ERROR) Connect failed with " +
+ e.getClass() + " instead of expected " +
+ expectedThrowable);
+ errorCount++;
+ }
+ } else {
+ System.out.println("ClientSide::run: (ERROR) " +
+ "Connect failed with exception");
+ errorCount++;
+ }
+ }
+
+ // Depending on the client state,
+ // perform some requests
+ if (mbsc != null && errorCount == 0) {
+ // Perform some little JMX requests
+ System.out.println("ClientSide::run: Start sending requests");
+
+ doRequests();
+
+ // In case authentication has been used we check how it did.
+ if (authCallCountValue != 0) {
+ errorCount += checkAuthenticator(mbsc, authCallCountValue);
+ }
+ }
+ } catch (Exception e) {
+ Utils.printThrowable(e, true);
+ errorCount++;
+ } finally {
+ // Terminate the JMX Client if any
+ if (cc != null) {
+ try {
+ cc.close();
+ } catch (Exception e) {
+ Utils.printThrowable(e, true) ;
+ errorCount++;
+ }
+ }
+ }
+
+ System.out.println("ClientSide::run: Done");
+
+ // Handle result
+ if (errorCount != 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ private void doRequests() throws Exception {
+
+ // Send some requests to the remote JMX server
+ ObjectName objName1 =
+ new ObjectName("TestDomain:class=MBS_Light,rank=1");
+ String mbeanClass = "MBS_Light";
+ Exception exception = new Exception("MY TEST EXCEPTION");
+ Attribute attException = new Attribute("AnException", exception);
+ Error error = new Error("MY TEST ERROR");
+ Attribute attError = new Attribute("AnError", error);
+ String opParamString = "TOTORO";
+ RjmxMBeanParameter opParam = new RjmxMBeanParameter(opParamString);
+ Object[] params1 = {opParamString};
+ String[] sig1 = {"java.lang.String"};
+ Object[] params2 = {opParam};
+ String[] sig2 = {"RjmxMBeanParameter"};
+
+ // Create and register the MBean
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::doRequests: Create and register the MBean");
+ mbsc.createMBean(mbeanClass, objName1);
+ if (!mbsc.isRegistered(objName1)) {
+ throw new Exception("Unable to register an MBean");
+ }
+
+ // Set attributes of the MBean
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::doRequests: Set attributes of the MBean");
+ mbsc.setAttribute(objName1, attException);
+ mbsc.setAttribute(objName1, attError);
+
+ // Get attributes of the MBean
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::doRequests: Get attributes of the MBean");
+ Exception retException =
+ (Exception) mbsc.getAttribute(objName1,"AnException");
+ if (!retException.getMessage().equals(exception.getMessage())) {
+ System.out.println("Expected = " + exception);
+ System.out.println("Got = " + retException);
+ throw new Exception("Attribute AnException not as expected");
+ }
+ Error retError = (Error) mbsc.getAttribute(objName1, "AnError");
+ if (!retError.getMessage().equals(error.getMessage())) {
+ System.out.println("Expected = " + error);
+ System.out.println("Got = " + retError);
+ throw new Exception("Attribute AnError not as expected");
+ }
+
+ // Invoke operations on the MBean
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::doRequests: Invoke operations on the MBean");
+ RjmxMBeanParameter res1 =
+ (RjmxMBeanParameter) mbsc.invoke(objName1, "operate1", params1, sig1);
+ if (!res1.equals(opParam)) {
+ System.out.println("Expected = " + opParam);
+ System.out.println("Got = " + res1);
+ throw new Exception("Operation operate1 behaved badly");
+ }
+ String res2 =
+ (String) mbsc.invoke(objName1, "operate2", params2, sig2);
+ if (!res2.equals(opParamString)) {
+ System.out.println("Expected = " + opParamString);
+ System.out.println("Got = " + res2);
+ throw new Exception("Operation operate2 behaved badly");
+ }
+
+ // Unregister the MBean
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "ClientSide::doRequests: Unregister the MBean");
+ mbsc.unregisterMBean(objName1);
+ if (mbsc.isRegistered(objName1)) {
+ throw new Exception("Unable to unregister an MBean");
+ }
+ }
+
+ /**
+ * Make some check about the instance of TestJMXAuthenticator.
+ * The authenticator is supposed to have set some properties on
+ * a ServerDelegate MBean.
+ * We compare the number of times it has been called with the expected value.
+ * We also check the Principal that has been given to the authenticator
+ * was not null.
+ * That method is of use to authentication with the JSR 262.
+ * @param mbs
+ * @param expectedAuthenticatorCallCount
+ * @return The number of errors encountered.
+ * @throws java.lang.Exception
+ */
+ protected int checkAuthenticator(MBeanServerConnection mbs,
+ int expectedAuthenticatorCallCount) throws Exception {
+ int errorCount = 0;
+
+ // Ensure the authenticator has been called the right number
+ // of times.
+ int callCount =
+ ((Integer) mbs.getAttribute(
+ new ObjectName(SERVER_DELEGATE_MBEAN_NAME),
+ "TestJMXAuthenticatorCallCount")).intValue();
+
+ if (callCount == expectedAuthenticatorCallCount) {
+ System.out.println("---- OK Authenticator has been called "
+ + expectedAuthenticatorCallCount + " time");
+ } else {
+ errorCount++;
+ System.out.println("---- ERROR Authenticator has been called " + callCount
+ + " times in place of " + expectedAuthenticatorCallCount);
+ }
+
+ // Ensure the provider has been called with
+ // a non null Principal.
+ String principalString =
+ (String) mbs.getAttribute(
+ new ObjectName(SERVER_DELEGATE_MBEAN_NAME),
+ "TestJMXAuthenticatorPrincipalString");
+
+ if (principalString == null) {
+ errorCount++;
+ System.out.println("---- ERROR Authenticator has been called"
+ + " with a null Principal");
+ } else {
+ if (principalString.length() > 0) {
+ System.out.println("---- OK Authenticator has been called"
+ + " with the Principal " + principalString);
+ } else {
+ errorCount++;
+ System.out.println("---- ERROR Authenticator has been called"
+ + " with an empty Principal");
+ }
+ }
+
+ return errorCount;
+ }
+
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/ServerDelegate.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.management.remote.JMXServiceURL ;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.StandardMBean;
+
+/**
+ * This class defines an MBean that can be registered and used on client side
+ * to handle informations or properties of the remote server.
+ *
+ * For example, this MBean can store IOR addresses
+ * of RMI/IIOP connector(s) used in a test.
+ *
+ * That MBean might not be used for testing purpose itself.
+ */
+public class ServerDelegate implements ServerDelegateMBean, MBeanRegistration {
+
+ private MBeanServer mbeanServer = null;
+ private List<JMXServiceURL> addresses = null;
+ private String port;
+ private static String javaVersion = System.getProperty("java.version");
+ private int sqeJmxwsCredentialsProviderCallCount = 0;
+ private String jmxwsCredentialsProviderUrl = null;
+ private int testJMXAuthenticatorCallCount = 0;
+ private Principal testJMXAuthenticatorPrincipal = null;
+
+ @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR ServerDelegate")
+ public ServerDelegate() {
+ addresses = new ArrayList<JMXServiceURL>();
+ }
+
+ public ObjectName preRegister(MBeanServer server, ObjectName name)
+ throws Exception {
+ // Initialize MBeanServer attribute
+ mbeanServer = server;
+ return name;
+ }
+ public void postRegister(Boolean registrationDone) {
+ }
+ public void preDeregister() throws Exception {
+ }
+ public void postDeregister() {
+ }
+
+ public void addAddress(JMXServiceURL url) {
+ addresses.add(url) ;
+ }
+
+ public List<JMXServiceURL> getAddresses() {
+ return addresses ;
+ }
+
+ public void setPort(String p) {
+ port = p ;
+ }
+
+ public String getPort() {
+ return port ;
+ }
+
+ public String getJavaVersion() {
+ return javaVersion;
+ }
+
+ public void sqeJmxwsCredentialsProviderCalled() {
+ sqeJmxwsCredentialsProviderCallCount++;
+ }
+
+ public int getSqeJmxwsCredentialsProviderCallCount() {
+ return sqeJmxwsCredentialsProviderCallCount;
+ }
+
+ public void setJmxwsCredentialsProviderUrl(String url) {
+ jmxwsCredentialsProviderUrl = url;
+ }
+
+ public String getJmxwsCredentialsProviderUrl() {
+ return jmxwsCredentialsProviderUrl;
+ }
+
+ public void testJMXAuthenticatorCalled() {
+ testJMXAuthenticatorCallCount++;
+ }
+
+ public int getTestJMXAuthenticatorCallCount() {
+ return testJMXAuthenticatorCallCount;
+ }
+
+ public void setTestJMXAuthenticatorPrincipal(Principal principal) {
+ testJMXAuthenticatorPrincipal = principal;
+ }
+
+ public String getTestJMXAuthenticatorPrincipalString() {
+ if ( testJMXAuthenticatorPrincipal != null ) {
+ return testJMXAuthenticatorPrincipal.toString();
+ }
+
+ return null;
+ }
+
+ /**
+ * Instantiates and registers a StandardMBean in the MBean server.
+ *
+ * @param implementationClassName
+ * The implementation class name of the MBean.
+ * @param interfaceClassName
+ * The management interface class name of the MBean.
+ * @param isMXBean
+ * If true, the resultant MBean is an MXBean.
+ * @param name
+ * The object name of the StandardMBean.
+ */
+ @SuppressWarnings("unchecked")
+ public void createStandardMBean(
+ String implementationClassName,
+ String interfaceClassName,
+ boolean isMXBean,
+ ObjectName name)
+ throws Exception {
+
+ Object implementation =
+ Class.forName(implementationClassName).newInstance();
+ Class<Object> interfaceClass = interfaceClassName == null ? null :
+ (Class<Object>)Class.forName(interfaceClassName);
+
+ // Create the StandardMBean
+ StandardMBean standardMBean = new StandardMBean(
+ implementation,
+ interfaceClass,
+ isMXBean);
+
+ // Register the StandardMBean
+ mbeanServer.registerMBean(standardMBean, name);
+ }
+
+ /**
+ * Instantiates and registers a StandardMBean in the MBean server.
+ * The object will use standard JMX design pattern to determine
+ * the management interface associated with the given implementation.
+ */
+ @SuppressWarnings("unchecked")
+ public void createStandardMBean(
+ String implementationClassName,
+ boolean isMXBean,
+ ObjectName name)
+ throws Exception {
+
+ createStandardMBean(implementationClassName, null, isMXBean, name);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/ServerDelegateMBean.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.security.Principal;
+import java.util.List;
+
+import javax.management.remote.JMXServiceURL ;
+import javax.management.ObjectName;
+
+@SqeDescriptorKey("INTERFACE ServerDelegateMBean")
+public interface ServerDelegateMBean {
+ @SqeDescriptorKey("ATTRIBUTE Address")
+ public void addAddress(JMXServiceURL url);
+
+ @SqeDescriptorKey("ATTRIBUTE Address")
+ public List<JMXServiceURL> getAddresses();
+
+ public String getPort();
+ public void setPort(String p);
+
+ public String getJavaVersion();
+
+ public void sqeJmxwsCredentialsProviderCalled();
+ public int getSqeJmxwsCredentialsProviderCallCount();
+
+ public void setJmxwsCredentialsProviderUrl(String url);
+ public String getJmxwsCredentialsProviderUrl();
+
+ public void testJMXAuthenticatorCalled();
+ public int getTestJMXAuthenticatorCallCount();
+
+ public void setTestJMXAuthenticatorPrincipal(Principal principal);
+ public String getTestJMXAuthenticatorPrincipalString();
+
+ public void createStandardMBean(
+ String implementationClassName,
+ String interfaceClassName,
+ boolean isMXBean,
+ ObjectName name)
+ throws Exception;
+
+ public void createStandardMBean(
+ String implementationClassName,
+ boolean isMXBean,
+ ObjectName name)
+ throws Exception;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/Simple.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+//import java.beans.ConstructorProperties;
+import javax.management.ConstructorParameters;
+
+/**
+ * This class defines a simple standard MBean.
+ */
+public class Simple implements SimpleMBean {
+
+ private String attribute = "initial_value";
+ private boolean operationInvoked = false;
+ private boolean operation2Invoked = false;
+
+ @SqeDescriptorKey("NO PARAMETER CONSTRUCTOR Simple")
+ public Simple() {
+ }
+
+ @SqeDescriptorKey("TWO PARAMETERS CONSTRUCTOR Simple")
+ @ConstructorParameters({"unused1", "unused2"})
+ public Simple(@SqeDescriptorKey("CONSTRUCTOR PARAMETER unused1")int unused1,
+ @SqeDescriptorKey("CONSTRUCTOR PARAMETER unused2")int unused2) {
+ }
+
+ public String getAttribute() {
+ return attribute;
+ }
+ public void setAttribute(String s) {
+ attribute = s;
+ }
+ public boolean getOperationInvoked() {
+ return operationInvoked;
+ }
+ public boolean getOperation2Invoked() {
+ return operation2Invoked;
+ }
+
+ public void operation() {
+ operationInvoked = true;
+ return;
+ }
+
+ public String operation2(int i) {
+ operation2Invoked = true;
+ return String.valueOf(i);
+ }
+
+ public void reset() {
+ attribute = "initial_value";
+ operationInvoked = false;
+ operation2Invoked = false;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/SimpleListener.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+// JDK
+import java.util.Vector;
+
+// JMX
+import javax.management.NotificationListener;
+import javax.management.Notification;
+
+public class SimpleListener implements NotificationListener {
+ private boolean received = false;
+ private String type = null;
+ private Object handback = null;
+ private Vector<Object> handbacks = new Vector<Object>();
+ private int nbrec = 0;
+
+ public synchronized void handleNotification(Notification notification,
+ Object handback) {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "SimpleListener::handleNotification :" + notification);
+ try {
+ received = true;
+ type = notification.getType();
+ this.handback = handback;
+ handbacks.add(handback);
+ nbrec++;
+ notify();
+ } catch(Exception e) {
+ System.out.println("(ERROR) SimpleListener::handleNotification :"
+ + " Caught exception "
+ + e) ;
+ }
+ }
+
+ public synchronized boolean isNotificationReceived() {
+ boolean ret = received;
+ reset();
+ return ret;
+ }
+
+ public synchronized Object[] waitForMultiNotifications(int nb) {
+ while(true) {
+ if(nbrec < nb) {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "SimpleListener::waitForMultiNotifications wait");
+ try {
+ wait();
+ } catch(InterruptedException ie) {
+ // OK : we wait for being interrupted
+ }
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "SimpleListener::waitForMultiNotifications wait over");
+ }
+ else
+ break;
+ }
+ Object[] ret = handbacks.toArray();
+ reset();
+ return ret;
+ }
+
+ private void reset() {
+ received = false;
+ handback = null;
+ handbacks.removeAllElements();
+ type = null;
+ }
+
+ public synchronized Object waitForNotificationHB() {
+ while(true) {
+ if(!received) {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "SimpleListener::waitForNotificationHB wait");
+ try {
+ wait();
+ } catch(InterruptedException ie) {
+ // OK : we wait for being interrupted
+ }
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "SimpleListener::waitForNotificationHB received");
+ }
+ else
+ break;
+ }
+ Object ret = handback;
+ reset();
+ return ret;
+ }
+
+ public synchronized String waitForNotification() {
+ while(true) {
+ if(!received) {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "SimpleListener::waitForNotification wait");
+ try {
+ wait();
+ } catch(InterruptedException ie) {
+ // OK : we wait for being interrupted
+ }
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "SimpleListener::waitForNotification received");
+ }
+ else
+ break;
+ }
+ String ret = type;
+ reset();
+ return ret;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/SimpleMBean.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * This interface defines a simple standard MBean.
+ */
+@SqeDescriptorKey("INTERFACE SimpleMBean")
+public interface SimpleMBean {
+
+ @SqeDescriptorKey("ATTRIBUTE Attribute")
+ public String getAttribute();
+
+ @SqeDescriptorKey("ATTRIBUTE Attribute")
+ public void setAttribute(String s);
+
+ @SqeDescriptorKey("ATTRIBUTE OperationInvoked")
+ public boolean getOperationInvoked();
+
+ @SqeDescriptorKey("ATTRIBUTE Operation2Invoked")
+ public boolean getOperation2Invoked();
+
+ // Void operation
+ // The associated MBeanOperationInfo is mapped to OpenMBeanOperationInfo
+ // => openType is added to the descriptor
+ @SqeDescriptorKey(value = "OPERATION operation",
+ descriptorFields = {"openType=SimpleType.VOID"})
+ public void operation();
+
+ @SqeDescriptorKey("OPERATION operation2")
+ public String operation2(int i);
+
+ // Void operation
+ // The associated MBeanOperationInfo is mapped to OpenMBeanOperationInfo
+ // => openType is added to the descriptor
+ @SqeDescriptorKey(value = "OPERATION reset",
+ descriptorFields = {"openType=SimpleType.VOID"})
+ public void reset();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/SqeDescriptorKey.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import javax.management.DescriptorKey;
+
+/**
+ * That annotation is usable everywhere DescriptorKey is (and even more).
+ * It is for use to test that you can retrieve the SqeDescriptorKey into the
+ * appropriate Descriptor instances as built by the JMX runtime.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SqeDescriptorKey {
+ @DescriptorKey("sqeDescriptorKey")
+ String value();
+
+ // List descriptor fields that may be added or may be updated
+ // when retrieving an MBeanInfo using a JMXWS connection compared to the
+ // MBeanInfo returned by a local MBeanServer.
+ // The annotation format is :
+ // <descriptorFieldName>=<descriptorFieldValue>
+ // The values actually handled by the test suite are :
+ // openType=SimpleType.VOID
+ @DescriptorKey("descriptorFields")
+ String[] descriptorFields() default {};
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/TestJMXAuthenticator.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.security.Principal;
+
+import javax.management.Attribute;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import javax.management.remote.JMXAuthenticator;
+import javax.management.remote.JMXPrincipal;
+import javax.security.auth.Subject;
+
+public final class TestJMXAuthenticator implements JMXAuthenticator {
+
+ private String protocol = "";
+ private MBeanServer mbs = null;
+
+ public TestJMXAuthenticator() {
+ }
+
+ public TestJMXAuthenticator(String protocol) {
+ this.protocol = protocol;
+ }
+
+ public TestJMXAuthenticator(String protocol, MBeanServer mbs) {
+ this.protocol = protocol;
+ this.mbs = mbs;
+ }
+
+ public Subject authenticate(Object credentials) {
+
+ String credentials_username = "";
+ String credentials_password = "";
+ Principal aPrincipal = null;
+
+ credentials_username = ((String[]) credentials)[0];
+ credentials_password = ((String[]) credentials)[1];
+
+ String authenticated_username = System.getProperty("susername");
+ String authenticated_password = System.getProperty("spassword");
+ String principal = System.getProperty("principal");
+
+ System.out.println("TestJMXAuthenticator::authenticate: Start");
+ System.out.println("TestJMXAuthenticator::authenticate: credentials username = " +
+ credentials_username);
+ System.out.println("TestJMXAuthenticator::authenticate: credentials password = " +
+ credentials_password);
+ System.out.println("TestJMXAuthenticator::authenticate: authenticated username = " +
+ authenticated_username);
+ System.out.println("TestJMXAuthenticator::authenticate: authenticated password = " +
+ authenticated_password);
+ System.out.println("TestJMXAuthenticator::authenticate: principal used for " +
+ "authorization = " + principal);
+
+ if (credentials_username.equals(authenticated_username) &&
+ credentials_password.equals(authenticated_password)) {
+ System.out.println("TestJMXAuthenticator::authenticate: " +
+ "Authenticator should succeed");
+ } else {
+ System.out.println("TestJMXAuthenticator::authenticate: " +
+ "Authenticator should reject");
+ throw new SecurityException("TestJMXAuthenticator throws EXCEPTION");
+ }
+
+ // At this point, authentication has succeeded
+ // (no SecurityException thrown).
+ //
+ // If no authorization is required, the returned subject (empty or not)
+ // is useless.
+ // Otherwise, the returned subject must define a principal
+ // and authorization will be performed against this principal.
+ //
+ // Note that this custom JMXAuthenticator is used for test purpose and
+ // the username used to perform authentication may be different from the
+ // username used to perform authorization.
+ //
+ Subject subject = new Subject();
+
+ if (principal != null) {
+ System.out.println("TestJMXAuthenticator::authenticate: " +
+ "Add " + principal + " principal to the returned subject");
+ subject.getPrincipals().add(new JMXPrincipal(principal));
+ }
+
+ return subject;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/TestSampleLoginModule.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+
+public final class TestSampleLoginModule implements LoginModule {
+
+ private Subject subject;
+ private CallbackHandler callbackHandler;
+ private Map<String, ?> sharedState;
+ private Map<String, ?> options;
+
+ public TestSampleLoginModule() {
+ }
+
+ public void initialize(Subject subject,
+ CallbackHandler callbackHandler,
+ Map<String,?> sharedState,
+ Map<String,?> options) {
+
+ this.subject = subject;
+ this.callbackHandler = callbackHandler;
+ this.sharedState = sharedState;
+ this.options = options;
+ }
+
+ /*
+ * Authenticate the user by comparing the values of the java properties
+ * (username and password) against the values of the credentials.
+ * */
+ public boolean login() throws LoginException {
+
+ String credentials_username = null;
+ String credentials_password = null;
+ String authenticated_username = System.getProperty("susername");
+ String authenticated_password = System.getProperty("spassword");
+
+ System.out.println("TestSampleLoginModule::login: Start");
+
+ // First retreive the credentials {username, password} from
+ // the callback handler
+ Callback[] callbacks = new Callback[2];
+ callbacks[0] = new NameCallback("username");
+ callbacks[1] = new PasswordCallback("password", false);
+ try {
+ callbackHandler.handle(callbacks);
+ credentials_username = ((NameCallback)callbacks[0]).getName();
+ credentials_password = new String(((PasswordCallback)callbacks[1]).
+ getPassword());
+ } catch (Exception e) {
+ throw new LoginException(e.toString());
+ }
+
+ System.out.println("TestSampleLoginModule::login: credentials username = " +
+ credentials_username);
+ System.out.println("TestSampleLoginModule::login: credentials password = " +
+ credentials_password);
+ System.out.println("TestSampleLoginModule::login: authenticated username = " +
+ authenticated_username);
+ System.out.println("TestSampleLoginModule::login: authenticated password = " +
+ authenticated_password);
+
+ if (credentials_username.equals(authenticated_username) &&
+ credentials_password.equals(authenticated_password)) {
+ System.out.println("TestSampleLoginModule::login: " +
+ "Authentication should succeed");
+ return true;
+ } else {
+ System.out.println("TestSampleLoginModule::login: " +
+ "Authentication should reject");
+ throw new LoginException("TestSampleLoginModule throws EXCEPTION");
+ }
+ }
+
+ public boolean commit() throws LoginException {
+ return true;
+ }
+
+ public boolean abort() throws LoginException {
+ return true;
+ }
+
+ public boolean logout() throws LoginException {
+ return true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/Utils.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.lang.reflect.Method;
+import javax.management.remote.JMXConnectorServerMBean;
+
+// utility class for MXBean* tests coming from JMX Tonga test suite
+class Utils {
+
+ private static final String SERVER_SIDE_NAME = "-server";
+ private static final String CLIENT_SIDE_NAME = "-client";
+
+ // DEBUG is printed depending on the DEBUG and DEBUG_LEVEL JAVA property
+ private static final String DEBUG_HEADER = "[debug] ";
+
+ // DEBUG levels
+ private static int selectedDebugLevel = 0;
+ static final int DEBUG_STANDARD = 1;
+ static final int DEBUG_VERBOSE = 2; // Mainly used for stress tests
+ static final int DEBUG_ALL = DEBUG_STANDARD | DEBUG_VERBOSE;
+
+ static void parseDebugProperties() {
+ int level = 0;
+ Properties p = System.getProperties();
+
+ // get selected levels
+ if (p.getProperty("DEBUG_STANDARD") != null) {
+ level |= DEBUG_STANDARD;
+ }
+
+ if (p.getProperty("DEBUG_VERBOSE") != null) {
+ level |= DEBUG_VERBOSE;
+ }
+
+ if (p.getProperty("DEBUG_ALL") != null) {
+ level |= DEBUG_ALL;
+ }
+
+ selectedDebugLevel = level;
+ }
+
+ /**
+ * Reproduces the original parsing and collection of test parameters
+ * from the DTonga JMX test suite.
+ *
+ * Collects passed args and returns them in a map(argname, value) structure,
+ * which will be then propagated as necessary to various called methods.
+ */
+ static Map<String, Object> parseParameters(String args[])
+ throws Exception {
+ Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Start");
+ HashMap<String, Object> map = new HashMap<>();
+
+ for ( int i = 0; i < args.length; i++ ) {
+ if ( args[i].trim().startsWith("-") ) {
+ if ((i+1) < args.length && !args[i+1].startsWith("-") ) {
+ Utils.debug(DEBUG_STANDARD,
+ "TestRoot::parseParameters: added in map = " +
+ args[i] +
+ " with value " +
+ args[i+1]) ;
+ map.put(args[i].trim(), args[i+1].trim()) ;
+ } else if ((i+1) < args.length && args[i+1].startsWith("-") ||
+ (i+1) == args.length ) {
+ Utils.debug(DEBUG_STANDARD,
+ "TestRoot::parseParameters: added in map = " +
+ args[i] +
+ " with null value") ;
+ map.put(args[i].trim(), null) ;
+ } else {
+ System.out.println(
+ "TestRoot::parseParameters: (WARNING) not added in map = " +
+ args[i]) ;
+ }
+ }
+ }
+
+ Utils.debug(DEBUG_STANDARD, "TestRoot::parseParameters: Done") ;
+ return map ;
+ }
+
+ // Parse server parameters and put them in passed serverMap
+ static int parseServerParameters(String args[],
+ String serverSideName,
+ Map<String, Object> serverMap )
+ throws Exception {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ serverSideName + "::parseServerParameters: Start");
+
+ int nextIndex = 0;
+ boolean seenServerFlag = false;
+
+ for ( int i = 0; i < args.length; i++ ) {
+ // Case of reaching "-server" flag parameter
+ if (args[i].equals(SERVER_SIDE_NAME)) {
+ if (!seenServerFlag) {
+ seenServerFlag = true;
+ continue;
+ } else {
+ // Already parsing server params, invalid params list
+ Utils.debug(Utils.DEBUG_STANDARD,
+ serverSideName + "::parseParameters: Invalid " +
+ args[i] + " parameter detected in " +
+ SERVER_SIDE_NAME + " parameters list");
+ nextIndex = -1;
+ throw new RuntimeException("Invalid Parameter list");
+ }
+ }
+
+ // Case of reaching "-client" flag parameter
+ if (args[i].equals(CLIENT_SIDE_NAME)) {
+ // While parsing server parameters, then parsing is done.
+ Utils.debug(Utils.DEBUG_STANDARD,
+ serverSideName + "::parseServerParameters: Parsing of " +
+ SERVER_SIDE_NAME + " parameters done.");
+ return i;
+ }
+
+ i = parseParamAtIndex(args, i, serverMap);
+ nextIndex = i;
+ }
+
+ Utils.debug(Utils.DEBUG_STANDARD,
+ serverSideName + "::parseServerParameters: Parsing of parameters done");
+
+ return nextIndex;
+ }
+
+ // Parse client parameters and put them in passed clientMap
+ static void parseClientParameters(String args[],
+ String clientSideName,
+ Map<String, Object> clientMap )
+ throws Exception {
+
+ Utils.debug(Utils.DEBUG_STANDARD,
+ clientSideName + "::parseClientParameters: Start");
+
+ boolean seenClientFlag = false;
+
+ for ( int i = 0; i < args.length; i++ ) {
+ // Case of reaching "-client" flag parameter
+ if (args[i].equals(CLIENT_SIDE_NAME)) {
+ if (!seenClientFlag) {
+ seenClientFlag = true;
+ continue;
+ } else {
+ // Already parsing client params, invalid params list
+ Utils.debug(Utils.DEBUG_STANDARD,
+ clientSideName + "::parseClientParameters: Invalid " +
+ CLIENT_SIDE_NAME + " parameter detected in " +
+ CLIENT_SIDE_NAME + " parameters list.");
+ throw new RuntimeException("Invalid parameter in " +
+ clientSideName + " parameter list");
+ }
+ }
+
+ // Case of reaching "-server" flag parameter
+ if (args[i].equals(SERVER_SIDE_NAME)) {
+ // While parsing client parameters, invalid parameter list.
+ Utils.debug(Utils.DEBUG_STANDARD,
+ clientSideName + "::parseClientParameters: Invalid " +
+ SERVER_SIDE_NAME + " parameter inside " +
+ CLIENT_SIDE_NAME + " parameters list.");
+ throw new RuntimeException("Invalid parameter in " +
+ clientSideName + " parameter list");
+ }
+
+ i = parseParamAtIndex(args, i, clientMap);
+ }
+
+ Utils.debug(Utils.DEBUG_STANDARD,
+ clientSideName + "::parseClientParameters: Parsing of parameters done.");
+ }
+
+ // Add param found at index to passed map
+ // We only accept either "-param value" or "-param" form.
+ // The "value" form is invalid but just ignored.
+ private static int parseParamAtIndex(String args[],
+ int index,
+ Map<String, Object> map) {
+
+ if (args[index].trim().startsWith("-") ) {
+ // Case of a "-param value" form
+ if ((index+1) < args.length && !args[index+1].startsWith("-") ) {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "TestRoot::parseParamAtIndex: added in map = "
+ + args[index]
+ + " with value "
+ + args[index+1]) ;
+ // adding ("param", value) to the passed map
+ map.put(args[index].trim(), args[index+1].trim()) ;
+ // value should not be parsed a second time
+ return index+1;
+ }
+ // Case of a "-param" form (flag parameter)
+ else if (((index+1) < args.length && args[index+1].startsWith("-")) ||
+ (index+1) == args.length ) {
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "TestRoot::parseParamAtIndex: added in map = "
+ + args[index]
+ + " with null value") ;
+ // adding ("param", null) to passed map
+ map.put(args[index].trim(), null) ;
+ }
+ } else {
+ // Unsupported "value" alone parameter
+ Utils.debug(Utils.DEBUG_STANDARD,
+ "TestRoot::parseParamAtIndex: invalid " +
+ " value-alone \"" + args[index] + "\" parameter." +
+ " Parameter ignored.");
+ }
+
+ return index;
+ }
+
+ /**
+ * This method is to be used in all tests to print anything
+ * that is temporary.
+ * Printing is done only when debug is activated by the property DEBUG.
+ * Printing depends also on the DEBUG_LEVEL property.
+ * Here it encapsulates a System.out.println.
+ */
+ static void debug(int level, String line) {
+ if ((selectedDebugLevel & level) != 0) {
+ System.out.println(DEBUG_HEADER + line);
+ }
+ }
+
+ /**
+ * Do print stack trace when withStack is true.
+ * Does try to call getTargetException() and getTargetError() then
+ * print embedded stacks in the case of an Exception wrapping
+ * another Exception or an Error. Recurse until no more wrapping
+ * is found.
+ */
+ static void printThrowable(Throwable theThro, boolean withStack) {
+ try {
+ if (withStack) {
+ theThro.printStackTrace(System.out);
+ }
+ if (theThro instanceof Exception) {
+ Exception t = (Exception) theThro;
+ Method target = null;
+ String blank = " ";
+ try {
+ target = t.getClass().getMethod("getTargetException",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetException method could be there or not
+ }
+ System.out.println(blank + t.getClass() + "==>" + t.getMessage());
+ while (target != null) {
+ try {
+ t = (Exception) target.invoke(t,
+ (java.lang.Object[]) null);
+ } catch (Exception ee) {
+ t = null;
+ }
+ try {
+ if (t != null) {
+ blank = blank + " ";
+ System.out.println(blank + t.getClass() + "==>" +
+ t.getMessage());
+ try {
+ target =
+ t.getClass().getMethod("getTargetException",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetException method could be there or not }
+ }
+ } else {
+ target = null;
+ }
+ } catch (Exception ee) {
+ target = null;
+ }
+ }
+
+ // We may have exceptions wrapping an Error then it is
+ // getTargetError that is likely to be called
+ try {
+ target = ((Exception) theThro).getClass().getMethod("getTargetError",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetError method could be there or not
+ }
+ Throwable err = theThro;
+ while (target != null) {
+ try {
+ err = (Error) target.invoke(err,
+ (java.lang.Object[]) null);
+ } catch (Exception ee) {
+ err = null;
+ }
+ try {
+ if (err != null) {
+ blank = blank + " ";
+ System.out.println(blank + err.getClass() + "==>" +
+ err.getMessage());
+ if (withStack) {
+ err.printStackTrace(System.out);
+ }
+ try {
+ target = err.getClass().getMethod("getTargetError",
+ (java.lang.Class<?>[]) null);
+ } catch (Exception ee) {
+ // OK: getTargetError method could be there or not
+ }
+ } else {
+ target = null;
+ }
+ } catch (Exception ee) {
+ target = null;
+ }
+ }
+ } else {
+ System.out.println("Throwable is : " + theThro);
+ }
+ } catch (Throwable x) {
+ System.out.println("Exception : raised in printException : " + x);
+ }
+ }
+
+ /**
+ * Wait up to maxTimeInSeconds second(s) the given JMX connector server
+ * comes up (which means isActive returns true).
+ * If it fails to do so we throw a RunTime exception.
+ */
+ static void waitReady(JMXConnectorServerMBean server,
+ int maxTimeInSeconds) throws Exception {
+ int elapsed = 0;
+
+ while (!server.isActive() && elapsed < maxTimeInSeconds) {
+ Thread.sleep(1000);
+ elapsed++;
+ }
+
+ if (server.isActive()) {
+ String message = "Utils::waitReady: JMX connector server came up";
+ if ( elapsed == 0) {
+ message += " immediately";
+ } else {
+ message += " after " + elapsed + " seconds";
+ }
+ message += " [" + server.getAddress() + "]";
+ Utils.debug(DEBUG_STANDARD, message);
+ } else {
+ String message = "Utils::waitReady: (ERROR) JMX connector" +
+ " server didn't come up after " + elapsed + " seconds [" +
+ server.getAddress() + "]";
+ System.out.println(message);
+ throw new RuntimeException(message);
+ }
+ }
+
+ /**
+ * This method is used to compare the specified Throwable and possibly
+ * the derived causes to the specified String argument.
+ * The expected String argument format is :
+ * throwable_1;throwable_2;...;throwable_N
+ * where throwable_i can be :
+ * - either a throwable class name
+ * - or the "*" character meaning several unknown throwable class names
+ * This character must be followed by a throwable class name
+ */
+ static boolean compareThrowable(
+ Throwable t,
+ String expectedThrowable) {
+
+ // First parse the expectedThrowable String
+ StringTokenizer tokenizer = new StringTokenizer(expectedThrowable, ";");
+ String token = null;
+
+ try {
+ while (tokenizer.hasMoreTokens()) {
+ token = tokenizer.nextToken();
+ if (!token.equals("*")) {
+ if (!Class.forName(token).isInstance(t)) {
+ return false;
+ }
+ } else {
+ token = tokenizer.nextToken();
+ while (!Class.forName(token).isInstance(t)) {
+ t = t.getCause();
+ if (t == null) {
+ return false;
+ }
+ }
+ }
+ t = t.getCause();
+ }
+ } catch (ClassNotFoundException cnfe) {
+ String msg = "Expected throwable class(es) " + expectedThrowable +
+ " cannot be located";
+ System.out.println(msg);
+ throw new IllegalArgumentException(msg);
+ }
+ return true;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/access.properties Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,11 @@
+# Access control file for SQE tests.
+
+# Default username
+SQE_username readwrite create Simple
+
+# Functional authorization tests
+username1 readwrite create Simple
+username2 readonly
+username3 readonly
+username4 readwrite create Simple
+username5 readwrite create Simple
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/java.policy.authorization Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,98 @@
+// Standard extensions get all permissions by default
+
+grant codeBase "file:${java.home}/lib/ext/*" {
+ permission java.security.AllPermission;
+};
+
+// default permissions granted to all domains
+grant {
+ // Allows any thread to stop itself using the java.lang.Thread.stop()
+ // method that takes no argument.
+ // Note that this permission is granted by default only to remain
+ // backwards compatible.
+ // It is strongly recommended that you either remove this permission
+ // from this policy file or further restrict it to code sources
+ // that you specify, because Thread.stop() is potentially unsafe.
+ // See "http://java.sun.com/notes" for more information.
+ permission java.lang.RuntimePermission "stopThread";
+
+ // allows anyone to listen on un-privileged ports
+ permission java.net.SocketPermission "localhost:1024-", "listen";
+
+ // "standard" properies that can be read by anyone
+
+ permission java.util.PropertyPermission "java.version", "read";
+ permission java.util.PropertyPermission "java.vendor", "read";
+ permission java.util.PropertyPermission "java.vendor.url", "read";
+ permission java.util.PropertyPermission "java.class.version", "read";
+ permission java.util.PropertyPermission "os.name", "read";
+ permission java.util.PropertyPermission "os.version", "read";
+ permission java.util.PropertyPermission "os.arch", "read";
+ permission java.util.PropertyPermission "file.separator", "read";
+ permission java.util.PropertyPermission "path.separator", "read";
+ permission java.util.PropertyPermission "line.separator", "read";
+
+ permission java.util.PropertyPermission "java.specification.version", "read";
+ permission java.util.PropertyPermission "java.specification.vendor", "read";
+ permission java.util.PropertyPermission "java.specification.name", "read";
+
+ permission java.util.PropertyPermission "java.vm.specification.version", "read";
+ permission java.util.PropertyPermission "java.vm.specification.vendor", "read";
+ permission java.util.PropertyPermission "java.vm.specification.name", "read";
+ permission java.util.PropertyPermission "java.vm.version", "read";
+ permission java.util.PropertyPermission "java.vm.vendor", "read";
+ permission java.util.PropertyPermission "java.vm.name", "read";
+
+ permission java.io.FilePermission "*","read,write";
+
+};
+
+grant codeBase "file:/-" {
+ permission java.security.AllPermission;
+ permission java.io.FilePermission "*","read,write";
+};
+
+grant principal javax.management.remote.JMXPrincipal "SQE_username" {
+ permission javax.management.MBeanServerPermission "*";
+ permission javax.management.MBeanPermission "Simple", "instantiate";
+ permission javax.management.MBeanPermission "Simple", "registerMBean";
+};
+
+grant principal javax.management.remote.JMXPrincipal "username1" {
+ //
+ // JMXPrincipals "username1" has all permissions.
+ //
+ permission java.security.AllPermission;
+};
+
+grant principal javax.management.remote.JMXPrincipal "username2" {
+ //
+ // JMXPrincipals "username2" has all permissions.
+ //
+ permission java.security.AllPermission;
+};
+
+grant principal javax.management.remote.JMXPrincipal "username3" {
+ //
+ // JMXPrincipals "username3" has some permissions.
+ //
+ permission javax.management.MBeanPermission "Simple", "instantiate";
+ permission javax.management.MBeanPermission "Simple", "registerMBean";
+ permission javax.management.MBeanPermission "Simple", "setAttribute";
+ permission javax.management.MBeanPermission "Simple", "invoke";
+};
+
+grant principal javax.management.remote.JMXPrincipal "username4" {
+ //
+ // JMXPrincipals "username4" has all permissions.
+ //
+ permission javax.management.MBeanPermission "Simple", "instantiate";
+ permission javax.management.MBeanPermission "Simple", "registerMBean";
+ permission javax.management.MBeanPermission "Simple", "invoke";
+};
+
+grant principal javax.management.remote.JMXPrincipal "username5" {
+ //
+ // JMXPrincipals "username5" has no permissions.
+ //
+};
Binary file jdk/test/javax/management/security/keystoreAgent has changed
Binary file jdk/test/javax/management/security/keystoreClient has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/login.config Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,8 @@
+PasswordFileAuthentication {
+ com.sun.jmx.remote.security.FileLoginModule required
+ passwordFile="${password.file}";
+};
+
+SampleLoginModule {
+ TestSampleLoginModule required;
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/management/security/password.properties Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,12 @@
+# Password file for default SQE username.
+SQE_username SQE_password
+
+# Functional authorization tests
+username1 password1
+username2 password2
+username3 password3
+username4 password4
+username5 password5
+username6 password6
+
+usernameFileLoginModule passwordFileLoginModule
Binary file jdk/test/javax/management/security/truststoreAgent has changed
Binary file jdk/test/javax/management/security/truststoreClient has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/TLSv12/SignatureAlgorithms.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,595 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+//
+// SunJSSE does not support dynamic system properties, no way to re-use
+// system properties in samevm/agentvm mode.
+//
+
+/*
+ * @test
+ * @bug 8049321
+ * @summary Support SHA256WithDSA in JSSE
+ * @modules java.base/sun.misc
+ * @run main/othervm SignatureAlgorithms PKIX "SHA-224,SHA-256"
+ * TLS_DHE_DSS_WITH_AES_128_CBC_SHA
+ * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-224"
+ * TLS_DHE_DSS_WITH_AES_128_CBC_SHA
+ * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-256"
+ * TLS_DHE_DSS_WITH_AES_128_CBC_SHA
+ * @run main/othervm SignatureAlgorithms PKIX "SHA-224,SHA-256"
+ * TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
+ * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-224"
+ * TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
+ * @run main/othervm SignatureAlgorithms PKIX "SHA-1,SHA-256"
+ * TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
+ */
+
+import java.net.*;
+import java.util.*;
+import java.io.*;
+import javax.net.ssl.*;
+import java.security.Security;
+import java.security.KeyStore;
+import java.security.KeyFactory;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.spec.*;
+import java.security.interfaces.*;
+import sun.misc.BASE64Decoder;
+
+
+public class SignatureAlgorithms {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ static boolean separateServerThread = true;
+
+ /*
+ * Where do we find the keystores?
+ */
+ // Certificates and key (DSA) used in the test.
+ static String trustedCertStr =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDYTCCAyGgAwIBAgIJAK8/gw6zg/DPMAkGByqGSM44BAMwOzELMAkGA1UEBhMC\n" +
+ "VVMxDTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNl\n" +
+ "MB4XDTE1MTIwMzEzNTIyNVoXDTM2MTExMjEzNTIyNVowOzELMAkGA1UEBhMCVVMx\n" +
+ "DTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNlMIIB\n" +
+ "uDCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG4X+uxu5V\n" +
+ "b3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2eBfUv/hJ\n" +
+ "cLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNAqAB9PO5Y\n" +
+ "zKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAaor8iURd82\n" +
+ "b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I81drnN0Y\n" +
+ "lyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVXW+SEGADC\n" +
+ "J1+z8uqP3lIB6ltdgOiV/GQDgYUAAoGBAOXRppuJSGdt6AiZkb81P1DCUgIUlZFI\n" +
+ "J9GxWrjbbHDmGllMwPNhK6dU7LJKJJuYVPW+95rUGlSJEjRqSlHuyHkNb6e3e7qx\n" +
+ "tmx1/oIyq+oLult50hBS7uBvLLR0JbIKjBzzkudL8Rjze4G/Wq7KDM2T1JOP49tW\n" +
+ "eocCvaC8h8uQo4GtMIGqMB0GA1UdDgQWBBT17HcqLllsqnZzP+kElcGcBGmubjBr\n" +
+ "BgNVHSMEZDBigBT17HcqLllsqnZzP+kElcGcBGmubqE/pD0wOzELMAkGA1UEBhMC\n" +
+ "VVMxDTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNl\n" +
+ "ggkArz+DDrOD8M8wDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwCQYHKoZI\n" +
+ "zjgEAwMvADAsAhQ6Y1I6LtIEBMqNo8o6GIe4LLEJuwIUbVQUKi8tvtWyRoxm8AFV\n" +
+ "0axJYUU=\n" +
+ "-----END CERTIFICATE-----";
+
+ static String[] targetCertStr = {
+ // DSA-SHA1
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDKTCCAumgAwIBAgIJAOy5c0b+8stFMAkGByqGSM44BAMwOzELMAkGA1UEBhMC\n" +
+ "VVMxDTALBgNVBAoTBEphdmExHTAbBgNVBAsTFFN1bkpTU0UgVGVzdCBTZXJpdmNl\n" +
+ "MB4XDTE1MTIwMzEzNTIyNVoXDTM1MDgyMDEzNTIyNVowTzELMAkGA1UEBhMCVVMx\n" +
+ "DTALBgNVBAoMBEphdmExHTAbBgNVBAsMFFN1bkpTU0UgVGVzdCBTZXJpdmNlMRIw\n" +
+ "EAYDVQQDDAlsb2NhbGhvc3QwggG3MIIBLAYHKoZIzjgEATCCAR8CgYEA8f5v4ZIx\n" +
+ "fopLuNcNF6/NzjrgMUbhf67G7lVvdTjKGxTwDxy4fiDCKfov8gOSZlDs3TMLSNGy\n" +
+ "IZVIywURNOrXQXf2kfZ4F9S/+Elwt8GciMES1WpX90TErzwe6XaxniZCKFDiKyw+\n" +
+ "XuGyuhL0RZiJydfSg0CoAH087ljMpt+kvtMCFQDXdZbjyuWfZQ/8toCC2fMqGpw2\n" +
+ "0wKBgQDdG/QaKtNsBqivyJRF3zZvkWUNN684JO6rtkil9lVXfSd5LtwFg163qb/f\n" +
+ "/hx9SVtuuqFuognYj4jzV2uc3RiXI3gqS7ERwHo5PB9aQhSPqu89oJCsEfxDbXds\n" +
+ "Orcce1g1o/w6h5BTJVdb5IQYAMInX7Py6o/eUgHqW12A6JX8ZAOBhAACgYB+zYqn\n" +
+ "jJwG4GZpBIN/6qhzbp0flChsV+Trlu0SL0agAQzb6XdI/4JnO87Pgbxaxh3VNAj3\n" +
+ "3+Ghr1NLBuBfTKzJ4j9msWT3EpLupkMyNtXvBYM0iyMrll67lSjMdv++wLEw35Af\n" +
+ "/bzVcjGyA5Q0i0cuEzDmHTVfi0OydynbwSLxtKNjMGEwCwYDVR0PBAQDAgPoMB0G\n" +
+ "A1UdDgQWBBQXJI8AxM0qsYCbbkIMuI5zJ+nMEDAfBgNVHSMEGDAWgBT17HcqLlls\n" +
+ "qnZzP+kElcGcBGmubjASBgNVHREBAf8ECDAGhwR/AAABMAkGByqGSM44BAMDLwAw\n" +
+ "LAIUXgyJ0xll4FrZAKXi8bj7Kiz+SA4CFH9WCSZIBYA9lmJkiTgRS7iM/6IC\n" +
+ "-----END CERTIFICATE-----",
+
+ // DSA-SHA224
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDLzCCAuugAwIBAgIJAOy5c0b+8stGMAsGCWCGSAFlAwQDATA7MQswCQYDVQQG\n" +
+ "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" +
+ "Y2UwHhcNMTUxMjAzMTU0NDM5WhcNMzUwODIwMTU0NDM5WjBPMQswCQYDVQQGEwJV\n" +
+ "UzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2Y2Ux\n" +
+ "EjAQBgNVBAMMCWxvY2FsaG9zdDCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQDx/m/h\n" +
+ "kjF+iku41w0Xr83OOuAxRuF/rsbuVW91OMobFPAPHLh+IMIp+i/yA5JmUOzdMwtI\n" +
+ "0bIhlUjLBRE06tdBd/aR9ngX1L/4SXC3wZyIwRLValf3RMSvPB7pdrGeJkIoUOIr\n" +
+ "LD5e4bK6EvRFmInJ19KDQKgAfTzuWMym36S+0wIVANd1luPK5Z9lD/y2gILZ8yoa\n" +
+ "nDbTAoGBAN0b9Boq02wGqK/IlEXfNm+RZQ03rzgk7qu2SKX2VVd9J3ku3AWDXrep\n" +
+ "v9/+HH1JW266oW6iCdiPiPNXa5zdGJcjeCpLsRHAejk8H1pCFI+q7z2gkKwR/ENt\n" +
+ "d2w6txx7WDWj/DqHkFMlV1vkhBgAwidfs/Lqj95SAepbXYDolfxkA4GEAAKBgA81\n" +
+ "CJKEv+pwiqYgxtw/9rkQ9748WP3mKrEC06kjUG+94/Z9dQloNFFfj6LiO1bymc5l\n" +
+ "6QIR8XCi4Po3N80K3+WxhBGFhY+RkVWTh43JV8epb41aH2qiWErarBwBGEh8LyGT\n" +
+ "i30db+Nkz2gfvyz9H/9T0jmYgfLEOlMCusali1qHo2MwYTALBgNVHQ8EBAMCA+gw\n" +
+ "HQYDVR0OBBYEFBqSP0S4+X+zOCTEnlp2hbAjV/W5MB8GA1UdIwQYMBaAFPXsdyou\n" +
+ "WWyqdnM/6QSVwZwEaa5uMBIGA1UdEQEB/wQIMAaHBH8AAAEwCwYJYIZIAWUDBAMB\n" +
+ "AzEAMC4CFQChiRaOnAnsCSJFwdpK22jSxU/mhQIVALgLbj/G39+1Ej8UuSWnEQyU\n" +
+ "4DA+\n" +
+ "-----END CERTIFICATE-----",
+
+ // DSA-SHA256
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDLTCCAuugAwIBAgIJAOy5c0b+8stHMAsGCWCGSAFlAwQDAjA7MQswCQYDVQQG\n" +
+ "EwJVUzENMAsGA1UEChMESmF2YTEdMBsGA1UECxMUU3VuSlNTRSBUZXN0IFNlcml2\n" +
+ "Y2UwHhcNMTUxMjAzMTU0NjUxWhcNMzUwODIwMTU0NjUxWjBPMQswCQYDVQQGEwJV\n" +
+ "UzENMAsGA1UECgwESmF2YTEdMBsGA1UECwwUU3VuSlNTRSBUZXN0IFNlcml2Y2Ux\n" +
+ "EjAQBgNVBAMMCWxvY2FsaG9zdDCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQDx/m/h\n" +
+ "kjF+iku41w0Xr83OOuAxRuF/rsbuVW91OMobFPAPHLh+IMIp+i/yA5JmUOzdMwtI\n" +
+ "0bIhlUjLBRE06tdBd/aR9ngX1L/4SXC3wZyIwRLValf3RMSvPB7pdrGeJkIoUOIr\n" +
+ "LD5e4bK6EvRFmInJ19KDQKgAfTzuWMym36S+0wIVANd1luPK5Z9lD/y2gILZ8yoa\n" +
+ "nDbTAoGBAN0b9Boq02wGqK/IlEXfNm+RZQ03rzgk7qu2SKX2VVd9J3ku3AWDXrep\n" +
+ "v9/+HH1JW266oW6iCdiPiPNXa5zdGJcjeCpLsRHAejk8H1pCFI+q7z2gkKwR/ENt\n" +
+ "d2w6txx7WDWj/DqHkFMlV1vkhBgAwidfs/Lqj95SAepbXYDolfxkA4GEAAKBgEF7\n" +
+ "2qiYxGrjX4KCOy0k5nK/RYlgLy4gYDChihQpiaa+fbA5JOBOxPWsh7rdtmJuDrEJ\n" +
+ "keacU223+DIhOKC49fa+EvhLNqo6U1oPn8n/yvBsvvnWkcynw5KfNzaLlaPmzugh\n" +
+ "v9xl/GhyZNAXc1QUcW3C+ceHVNrKnkfbTKZz5eRSo2MwYTALBgNVHQ8EBAMCA+gw\n" +
+ "HQYDVR0OBBYEFNMkPrt40oO9Dpy+bcbQdEvOlNlyMB8GA1UdIwQYMBaAFPXsdyou\n" +
+ "WWyqdnM/6QSVwZwEaa5uMBIGA1UdEQEB/wQIMAaHBH8AAAEwCwYJYIZIAWUDBAMC\n" +
+ "Ay8AMCwCFCvA2QiKSe/n+6GqSYQwgQ/zL5M9AhQfSiuWdMJKWpgPJKakvzhBUbMb\n" +
+ "vA==\n" +
+ "-----END CERTIFICATE-----"};
+
+ // Private key in the format of PKCS#8, key size is 1024 bits.
+ static String[] targetPrivateKey = {
+ // For cert DSA-SHA1
+ "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG\n" +
+ "4X+uxu5Vb3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2\n" +
+ "eBfUv/hJcLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNA\n" +
+ "qAB9PO5YzKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAao\n" +
+ "r8iURd82b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I\n" +
+ "81drnN0YlyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVX\n" +
+ "W+SEGADCJ1+z8uqP3lIB6ltdgOiV/GQEFgIUOiB7J/lrFrNduQ8nDNTe8VspoAI=",
+
+ // For cert DSA-SHA224
+ "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG\n" +
+ "4X+uxu5Vb3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2\n" +
+ "eBfUv/hJcLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNA\n" +
+ "qAB9PO5YzKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAao\n" +
+ "r8iURd82b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I\n" +
+ "81drnN0YlyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVX\n" +
+ "W+SEGADCJ1+z8uqP3lIB6ltdgOiV/GQEFgIUOj9F5mxWd9W1tiLSdsOAt8BUBzE=",
+
+ // For cert DSA-SHA256
+ "MIIBSwIBADCCASwGByqGSM44BAEwggEfAoGBAPH+b+GSMX6KS7jXDRevzc464DFG\n" +
+ "4X+uxu5Vb3U4yhsU8A8cuH4gwin6L/IDkmZQ7N0zC0jRsiGVSMsFETTq10F39pH2\n" +
+ "eBfUv/hJcLfBnIjBEtVqV/dExK88Hul2sZ4mQihQ4issPl7hsroS9EWYicnX0oNA\n" +
+ "qAB9PO5YzKbfpL7TAhUA13WW48rln2UP/LaAgtnzKhqcNtMCgYEA3Rv0GirTbAao\n" +
+ "r8iURd82b5FlDTevOCTuq7ZIpfZVV30neS7cBYNet6m/3/4cfUlbbrqhbqIJ2I+I\n" +
+ "81drnN0YlyN4KkuxEcB6OTwfWkIUj6rvPaCQrBH8Q213bDq3HHtYNaP8OoeQUyVX\n" +
+ "W+SEGADCJ1+z8uqP3lIB6ltdgOiV/GQEFgIUQ2WGgg+OO39Aujj0e4lM4pP4/9g="};
+
+
+ static char passphrase[] = "passphrase".toCharArray();
+
+ /*
+ * Turn on SSL debugging?
+ */
+ static boolean debug = false;
+
+ /*
+ * Is the server ready to serve?
+ */
+ volatile boolean serverReady = false;
+
+ /*
+ * Define the server side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doServerSide() throws Exception {
+
+ SSLContext context = generateSSLContext(
+ null, targetCertStr, targetPrivateKey);
+ SSLServerSocketFactory sslssf = context.getServerSocketFactory();
+ try (SSLServerSocket sslServerSocket =
+ (SSLServerSocket)sslssf.createServerSocket(serverPort)) {
+
+ serverPort = sslServerSocket.getLocalPort();
+
+ /*
+ * Signal Client, we're ready for his connect.
+ */
+ serverReady = true;
+
+ try (SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept()) {
+ sslSocket.setEnabledCipherSuites(
+ sslSocket.getSupportedCipherSuites());
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write('A');
+ sslOS.flush();
+
+ dumpSignatureAlgorithms(sslSocket);
+ }
+ }
+ }
+
+ /*
+ * Define the client side of the test.
+ *
+ * If the server prematurely exits, serverReady will be set to true
+ * to avoid infinite hangs.
+ */
+ void doClientSide() throws Exception {
+
+ /*
+ * Wait for server to get started.
+ */
+ while (!serverReady) {
+ Thread.sleep(50);
+ }
+
+ SSLContext context = generateSSLContext(trustedCertStr, null, null);
+ SSLSocketFactory sslsf = context.getSocketFactory();
+
+ try (SSLSocket sslSocket =
+ (SSLSocket)sslsf.createSocket("localhost", serverPort)) {
+
+ // enable TLSv1.2 only
+ sslSocket.setEnabledProtocols(new String[] {"TLSv1.2"});
+
+ // enable a block cipher
+ sslSocket.setEnabledCipherSuites(new String[] {cipherSuite});
+
+ InputStream sslIS = sslSocket.getInputStream();
+ OutputStream sslOS = sslSocket.getOutputStream();
+
+ sslOS.write('B');
+ sslOS.flush();
+ sslIS.read();
+
+ dumpSignatureAlgorithms(sslSocket);
+ }
+ }
+
+ static void dumpSignatureAlgorithms(SSLSocket sslSocket) throws Exception {
+
+ boolean isClient = sslSocket.getUseClientMode();
+ String mode = "[" + (isClient ? "Client" : "Server") + "]";
+ ExtendedSSLSession session =
+ (ExtendedSSLSession)sslSocket.getSession();
+ String[] signAlgs = session.getLocalSupportedSignatureAlgorithms();
+ System.out.println(
+ mode + " local supported signature algorithms: " +
+ Arrays.asList(signAlgs));
+
+ if (!isClient) {
+ signAlgs = session.getPeerSupportedSignatureAlgorithms();
+ System.out.println(
+ mode + " peer supported signature algorithms: " +
+ Arrays.asList(signAlgs));
+ } else {
+ Certificate[] serverCerts = session.getPeerCertificates();
+
+ // server should always send the authentication cert.
+ String sigAlg = ((X509Certificate)serverCerts[0]).getSigAlgName();
+ System.out.println(
+ mode + " the signature algorithm of server certificate: " +
+ sigAlg);
+ if (sigAlg.contains("SHA1")) {
+ if (disabledAlgorithms.contains("SHA-1")) {
+ throw new Exception(
+ "Not the expected server certificate. " +
+ "SHA-1 should be disabled");
+ }
+ } else if (sigAlg.contains("SHA224")) {
+ if (disabledAlgorithms.contains("SHA-224")) {
+ throw new Exception(
+ "Not the expected server certificate. " +
+ "SHA-224 should be disabled");
+ }
+ } else { // SHA-256
+ if (disabledAlgorithms.contains("SHA-256")) {
+ throw new Exception(
+ "Not the expected server certificate. " +
+ "SHA-256 should be disabled");
+ }
+ }
+ }
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+ private static String tmAlgorithm; // trust manager
+ private static String disabledAlgorithms; // disabled algorithms
+ private static String cipherSuite; // cipher suite
+
+ private static void parseArguments(String[] args) {
+ tmAlgorithm = args[0];
+ disabledAlgorithms = args[1];
+ cipherSuite = args[2];
+ }
+
+ private static SSLContext generateSSLContext(String trustedCertStr,
+ String[] keyCertStrs, String[] keySpecStrs) throws Exception {
+
+ // generate certificate from cert string
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+
+ // create a key store
+ KeyStore ks = KeyStore.getInstance("JKS");
+ ks.load(null, null);
+
+ // import the trused cert
+ Certificate trusedCert = null;
+ ByteArrayInputStream is = null;
+ if (trustedCertStr != null) {
+ is = new ByteArrayInputStream(trustedCertStr.getBytes());
+ trusedCert = cf.generateCertificate(is);
+ is.close();
+
+ ks.setCertificateEntry("DSA Signer", trusedCert);
+ }
+
+ if (keyCertStrs != null && keyCertStrs.length != 0) {
+ for (int i = 0; i < keyCertStrs.length; i++) {
+ String keyCertStr = keyCertStrs[i];
+ String keySpecStr = keySpecStrs[i];
+
+ // generate the private key.
+ PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(
+ new BASE64Decoder().decodeBuffer(keySpecStr));
+ KeyFactory kf = KeyFactory.getInstance("DSA");
+ DSAPrivateKey priKey =
+ (DSAPrivateKey)kf.generatePrivate(priKeySpec);
+
+ // generate certificate chain
+ is = new ByteArrayInputStream(keyCertStr.getBytes());
+ Certificate keyCert = cf.generateCertificate(is);
+ is.close();
+
+ Certificate[] chain = null;
+ if (trusedCert != null) {
+ chain = new Certificate[2];
+ chain[0] = keyCert;
+ chain[1] = trusedCert;
+ } else {
+ chain = new Certificate[1];
+ chain[0] = keyCert;
+ }
+
+ // import the key entry.
+ ks.setKeyEntry("DSA Entry " + i, priKey, passphrase, chain);
+ }
+ }
+
+ // create SSL context
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmAlgorithm);
+ tmf.init(ks);
+
+ SSLContext ctx = SSLContext.getInstance("TLS");
+ if (keyCertStrs != null && keyCertStrs.length != 0) {
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("NewSunX509");
+ kmf.init(ks, passphrase);
+
+ ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+ ks = null;
+ } else {
+ ctx.init(null, tmf.getTrustManagers(), null);
+ }
+
+ return ctx;
+ }
+
+
+ // use any free port by default
+ volatile int serverPort = 0;
+
+ volatile Exception serverException = null;
+ volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+ /*
+ * debug option
+ */
+ if (debug) {
+ System.setProperty("javax.net.debug", "all");
+ }
+
+ /*
+ * Get the customized arguments.
+ */
+ parseArguments(args);
+
+
+ /*
+ * Ignore testing on Windows if only SHA-224 is available.
+ */
+ if ((Security.getProvider("SunMSCAPI") != null) &&
+ (disabledAlgorithms.contains("SHA-1")) &&
+ (disabledAlgorithms.contains("SHA-256"))) {
+
+ System.out.println(
+ "Windows system does not support SHA-224 algorithms yet. " +
+ "Ignore the testing");
+
+ return;
+ }
+
+ /*
+ * Expose the target algorithms by diabling unexpected algorithms.
+ */
+ Security.setProperty(
+ "jdk.certpath.disabledAlgorithms", disabledAlgorithms);
+
+ /*
+ * Reset the security property to make sure that the algorithms
+ * and keys used in this test are not disabled by default.
+ */
+ Security.setProperty( "jdk.tls.disabledAlgorithms", "");
+
+ /*
+ * Start the tests.
+ */
+ new SignatureAlgorithms();
+ }
+
+ Thread clientThread = null;
+ Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SignatureAlgorithms() throws Exception {
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ // swallow for now. Show later
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ serverThread.join();
+ } else {
+ clientThread.join();
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+ String whichRemote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ whichRemote = "server";
+ } else {
+ remote = clientException;
+ local = serverException;
+ whichRemote = "client";
+ }
+
+ /*
+ * If both failed, return the curthread's exception, but also
+ * print the remote side Exception
+ */
+ if ((local != null) && (remote != null)) {
+ System.out.println(whichRemote + " also threw:");
+ remote.printStackTrace();
+ System.out.println();
+ throw local;
+ }
+
+ if (remote != null) {
+ throw remote;
+ }
+
+ if (local != null) {
+ throw local;
+ }
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.err.println("Server died..." + e);
+ serverReady = true;
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ serverException = e;
+ } finally {
+ serverReady = true;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.err.println("Client died..." + e);
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ clientException = e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/print/attribute/Services_getDocFl.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.print.DocFlavor;
+import javax.print.PrintService;
+import javax.print.PrintServiceLookup;
+import javax.print.attribute.HashPrintRequestAttributeSet;
+
+/*
+ * @test
+ * @bug 4901243 8040139
+ * @summary JPG, GIF, and PNG DocFlavors (URL) should be supported if Postscript is supported.
+ * @run main Services_getDocFl
+*/
+
+
+public class Services_getDocFl {
+ public static void main (String [] args) {
+
+ HashPrintRequestAttributeSet prSet = null;
+ boolean psSupported = false,
+ pngImagesSupported = false,
+ gifImagesSupported = false,
+ jpgImagesSupported = false;
+ String mimeType;
+
+ PrintService[] serv = PrintServiceLookup.lookupPrintServices(null, null);
+ if (serv.length==0) {
+ System.out.println("no PrintService found");
+ } else {
+ System.out.println("number of Services "+serv.length);
+ }
+
+
+ for (int i = 0; i<serv.length ;i++) {
+ System.out.println(" PRINT SERVICE: "+ i+" "+serv[i]);
+ DocFlavor[] flavors = serv[i].getSupportedDocFlavors();
+ pngImagesSupported = false;
+ gifImagesSupported = false;
+ jpgImagesSupported = false;
+ for (int j=0; j<flavors.length; j++) {
+ System.out.println(flavors[j]);
+
+ if (flavors[j].equals(DocFlavor.URL.PNG)) {
+ pngImagesSupported = true;
+ } else if (flavors[j].equals(DocFlavor.URL.GIF)) {
+ gifImagesSupported = true;
+ } else if (flavors[j].equals(DocFlavor.URL.JPEG)) {
+ jpgImagesSupported = true;
+ } else if (flavors[j].getMimeType().indexOf("postscript") != -1) {
+ psSupported = true;
+ }
+ }
+
+ if (psSupported && !(pngImagesSupported && gifImagesSupported &&
+ jpgImagesSupported)) {
+
+ throw new RuntimeException("Error: URL image DocFlavors are not reported as supported");
+ }
+ }
+
+ }
+}
+
--- a/jdk/test/javax/sound/midi/MidiDeviceProvider/FakeInfo.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-import java.util.Collection;
-import java.util.HashSet;
-
-import javax.sound.midi.MidiDevice;
-import javax.sound.midi.MidiDevice.Info;
-import javax.sound.midi.MidiSystem;
-import javax.sound.midi.MidiUnavailableException;
-import javax.sound.midi.spi.MidiDeviceProvider;
-
-import static java.util.ServiceLoader.load;
-
-/**
- * @test
- * @bug 8059743
- * @summary MidiDeviceProvider shouldn't returns incorrect results in case of
- * some unknown MidiDevice.Info
- * @author Sergey Bylokhov
- */
-public final class FakeInfo {
-
- private static final class Fake extends Info {
-
- Fake() {
- super("a", "b", "c", "d");
- }
- }
-
- public static void main(final String[] args) {
- final Info fake = new Fake();
- // MidiSystem API
- try {
- MidiSystem.getMidiDevice(fake);
- throw new RuntimeException("IllegalArgumentException expected");
- } catch (final MidiUnavailableException e) {
- throw new RuntimeException("IllegalArgumentException expected", e);
- } catch (final IllegalArgumentException ignored) {
- // expected
- }
- // MidiDeviceProvider API
- final Collection<String> errors = new HashSet<>();
- for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) {
- try {
- if (mdp.isDeviceSupported(fake)) {
- throw new RuntimeException("fake is supported");
- }
- final MidiDevice device = mdp.getDevice(fake);
- System.err.println("MidiDevice: " + device);
- throw new RuntimeException("IllegalArgumentException expected");
- } catch (final IllegalArgumentException e) {
- errors.add(e.getMessage());
- }
- }
- if (errors.size() != 1) {
- throw new RuntimeException("Wrong number of messages:" + errors);
- }
- }
-}
--- a/jdk/test/javax/sound/midi/MidiDeviceProvider/NullInfo.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-import java.util.Collection;
-import java.util.HashSet;
-
-import javax.sound.midi.MidiDevice;
-import javax.sound.midi.MidiSystem;
-import javax.sound.midi.MidiUnavailableException;
-import javax.sound.midi.spi.MidiDeviceProvider;
-
-import static java.util.ServiceLoader.load;
-
-/**
- * @test
- * @bug 8058115
- * @summary MidiDeviceProvider shouldn't returns incorrect results or throw NPE
- * in case of null MidiDevice.Info
- * @author Sergey Bylokhov
- */
-public final class NullInfo {
-
- public static void main(final String[] args) {
- // MidiSystem API
- try {
- MidiSystem.getMidiDevice(null);
- throw new RuntimeException("IllegalArgumentException expected");
- } catch (final MidiUnavailableException e) {
- throw new RuntimeException("IllegalArgumentException expected", e);
- } catch (final IllegalArgumentException ignored) {
- // expected
- }
- // MidiDeviceProvider API
- final Collection<String> errors = new HashSet<>();
- for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) {
- try {
- if (mdp.isDeviceSupported(null)) {
- throw new RuntimeException("null is supported");
- }
- final MidiDevice device = mdp.getDevice(null);
- System.err.println("MidiDevice: " + device);
- throw new RuntimeException("IllegalArgumentException expected");
- } catch (final IllegalArgumentException e) {
- errors.add(e.getMessage());
- }
- }
- if (errors.size() != 1) {
- throw new RuntimeException("Wrong number of messages:" + errors);
- }
- }
-}
--- a/jdk/test/javax/sound/midi/MidiDeviceProvider/UnsupportedInfo.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-import javax.sound.midi.MidiDevice;
-import javax.sound.midi.MidiSystem;
-import javax.sound.midi.spi.MidiDeviceProvider;
-
-import static java.util.ServiceLoader.load;
-
-/**
- * @test
- * @bug 8058115
- * @summary MidiDeviceProvider shouldn't returns incorrect results in case of
- * unsupported MidiDevice.Info
- * @author Sergey Bylokhov
- */
-public final class UnsupportedInfo {
-
- public static void main(final String[] args) {
- final MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
- for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) {
- for (final MidiDevice.Info info : infos) {
- if (mdp.isDeviceSupported(info)) {
- if (mdp.getDevice(info) == null) {
- throw new RuntimeException("MidiDevice is null");
- }
- } else {
- try {
- mdp.getDevice(info);
- throw new RuntimeException(
- "IllegalArgumentException expected");
- } catch (final IllegalArgumentException ignored) {
- // expected
- }
- }
- }
- }
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/spi/MidiDeviceProvider/ExpectedNPEOnNull.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Objects;
+
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.spi.MidiDeviceProvider;
+
+import static java.util.ServiceLoader.load;
+
+/**
+ * @test
+ * @bug 8143909
+ * @author Sergey Bylokhov
+ */
+public final class ExpectedNPEOnNull {
+
+ public static void main(final String[] args) throws Exception {
+ testMS();
+ for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) {
+ testMDP(mdp);
+ }
+ testMDP(customMDP);
+ }
+
+ /**
+ * Tests the part of MidiSystem API, which implemented via
+ * MidiDeviceProvider.
+ */
+ private static void testMS() throws Exception {
+ // MidiSystem#getMidiDevice(MidiDevice.Info)
+ try {
+ MidiSystem.getMidiDevice(null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+
+ /**
+ * Tests the MidiDeviceProvider API directly.
+ */
+ private static void testMDP(final MidiDeviceProvider mdp) throws Exception {
+ // MidiDeviceProvider#isDeviceSupported(Info)
+ try {
+ mdp.isDeviceSupported(null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiDeviceProvider#getDevice(Info)
+ try {
+ mdp.getDevice(null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+
+ /**
+ * Tests some default implementation of MidiDeviceProvider API, using the
+ * custom {@code MidiDeviceProvider}, which support nothing.
+ */
+ static MidiDeviceProvider customMDP = new MidiDeviceProvider() {
+ @Override
+ public MidiDevice.Info[] getDeviceInfo() {
+ return new MidiDevice.Info[0];
+ }
+
+ @Override
+ public MidiDevice getDevice(MidiDevice.Info info) {
+ Objects.requireNonNull(info);
+ return null;
+ }
+ };
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/spi/MidiDeviceProvider/FakeInfo.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiDevice.Info;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.spi.MidiDeviceProvider;
+
+import static java.util.ServiceLoader.load;
+
+/**
+ * @test
+ * @bug 8059743
+ * @summary MidiDeviceProvider shouldn't returns incorrect results in case of
+ * some unknown MidiDevice.Info
+ * @author Sergey Bylokhov
+ */
+public final class FakeInfo {
+
+ private static final class Fake extends Info {
+
+ Fake() {
+ super("a", "b", "c", "d");
+ }
+ }
+
+ public static void main(final String[] args) {
+ final Info fake = new Fake();
+ // MidiSystem API
+ try {
+ MidiSystem.getMidiDevice(fake);
+ throw new RuntimeException("IllegalArgumentException expected");
+ } catch (final MidiUnavailableException e) {
+ throw new RuntimeException("IllegalArgumentException expected", e);
+ } catch (final IllegalArgumentException ignored) {
+ // expected
+ }
+ // MidiDeviceProvider API
+ final Collection<String> errors = new HashSet<>();
+ for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) {
+ try {
+ if (mdp.isDeviceSupported(fake)) {
+ throw new RuntimeException("fake is supported");
+ }
+ final MidiDevice device = mdp.getDevice(fake);
+ System.err.println("MidiDevice: " + device);
+ throw new RuntimeException("IllegalArgumentException expected");
+ } catch (final IllegalArgumentException e) {
+ errors.add(e.getMessage());
+ }
+ }
+ if (errors.size() != 1) {
+ throw new RuntimeException("Wrong number of messages:" + errors);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/spi/MidiDeviceProvider/UnsupportedInfo.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.spi.MidiDeviceProvider;
+
+import static java.util.ServiceLoader.load;
+
+/**
+ * @test
+ * @bug 8058115
+ * @summary MidiDeviceProvider shouldn't returns incorrect results in case of
+ * unsupported MidiDevice.Info
+ * @author Sergey Bylokhov
+ */
+public final class UnsupportedInfo {
+
+ public static void main(final String[] args) {
+ final MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
+ for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) {
+ for (final MidiDevice.Info info : infos) {
+ if (mdp.isDeviceSupported(info)) {
+ if (mdp.getDevice(info) == null) {
+ throw new RuntimeException("MidiDevice is null");
+ }
+ } else {
+ try {
+ mdp.getDevice(info);
+ throw new RuntimeException(
+ "IllegalArgumentException expected");
+ } catch (final IllegalArgumentException ignored) {
+ // expected
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/spi/MidiFileReader/ExpectedNPEOnNull.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.spi.MidiFileReader;
+
+import static java.util.ServiceLoader.load;
+
+/**
+ * @test
+ * @bug 8143909
+ * @author Sergey Bylokhov
+ */
+public final class ExpectedNPEOnNull {
+
+ public static void main(final String[] args) throws Exception {
+ testMS();
+ for (final MidiFileReader mfr : load(MidiFileReader.class)) {
+ testMFR(mfr);
+ }
+ }
+
+ /**
+ * Tests the part of MidiSystem API, which implemented via MidiFileReader.
+ */
+ private static void testMS() throws Exception {
+ // MidiSystem#getMidiFileFormat(InputStream)
+ try {
+ MidiSystem.getMidiFileFormat((InputStream) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiSystem#getMidiFileFormat(URL)
+ try {
+ MidiSystem.getMidiFileFormat((URL) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiSystem#getMidiFileFormat(File)
+ try {
+ MidiSystem.getMidiFileFormat((File) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiSystem#getSequence(InputStream)
+ try {
+ MidiSystem.getSequence((InputStream) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiSystem#getSequence(URL)
+ try {
+ MidiSystem.getSequence((URL) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiSystem#getSequence(File)
+ try {
+ MidiSystem.getSequence((File) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+
+ /**
+ * Tests the MidiFileReader API directly.
+ */
+ private static void testMFR(final MidiFileReader mfr) throws Exception {
+ // MidiFileReader#getMidiFileFormat(InputStream)
+ try {
+ mfr.getMidiFileFormat((InputStream) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiFileReader#getMidiFileFormat(URL)
+ try {
+ mfr.getMidiFileFormat((URL) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiFileReader#getMidiFileFormat(File)
+ try {
+ mfr.getMidiFileFormat((File) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiFileReader#getSequence(InputStream)
+ try {
+ mfr.getSequence((InputStream) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiFileReader#getSequence(URL)
+ try {
+ mfr.getSequence((URL) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiFileReader#getSequence(File)
+ try {
+ mfr.getSequence((File) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/spi/MidiFileWriter/ExpectedNPEOnNull.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.OutputStream;
+import java.util.Objects;
+
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.Sequence;
+import javax.sound.midi.spi.MidiFileWriter;
+
+import static java.util.ServiceLoader.load;
+
+/**
+ * @test
+ * @bug 8143909
+ * @author Sergey Bylokhov
+ */
+public final class ExpectedNPEOnNull {
+
+ public static void main(final String[] args) throws Exception {
+ testMS();
+ for (final MidiFileWriter mfw : load(MidiFileWriter.class)) {
+ testMFW(mfw);
+ }
+ testMFW(customMFW);
+ }
+
+ /**
+ * Tests the part of MidiSystem API, which implemented via MidiFileWriter.
+ */
+ private static void testMS() throws Exception {
+ // MidiSystem#getMidiFileTypes(Sequence)
+ try {
+ MidiSystem.getMidiFileTypes(null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+
+ // MidiSystem#isFileTypeSupported(int, Sequence)
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ MidiSystem.isFileTypeSupported(type, null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ // MidiSystem#write(Sequence, int, OutputStream)
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ MidiSystem.write(null, type, new NullOutputStream());
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ MidiSystem.write(new Sequence(0, 0), type, (OutputStream) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ MidiSystem.write(null, type, (OutputStream) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ // MidiSystem#write(Sequence, int, File)
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ MidiSystem.write(null, type, new File(""));
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ MidiSystem.write(new Sequence(0, 0), type, (File) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ MidiSystem.write(null, type, (File) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Tests the MidiFileWriter API directly.
+ */
+ private static void testMFW(final MidiFileWriter mfw) throws Exception {
+ // MidiFileWriter#getMidiFileTypes(Sequence)
+ try {
+ mfw.getMidiFileTypes(null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiFileWriter#isFileTypeSupported(int, Sequence)
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ mfw.isFileTypeSupported(type, null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ // MidiFileWriter#write(Sequence, int, OutputStream)
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ mfw.write(null, type, new NullOutputStream());
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ mfw.write(new Sequence(0, 0), type, (OutputStream) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ mfw.write(null, type, (OutputStream) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ // MidiFileWriter#write(Sequence, int, File)
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ mfw.write(null, type, new File(""));
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ mfw.write(new Sequence(0, 0), type, (File) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ for (final int type : MidiSystem.getMidiFileTypes()) {
+ try {
+ mfw.write(null, type, (File) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+ }
+ /**
+ * Tests some default implementation of MidiFileWriter API, using the custom
+ * {@code MidiFileWriter}, which support nothing.
+ */
+ static MidiFileWriter customMFW = new MidiFileWriter() {
+ @Override
+ public int[] getMidiFileTypes() {
+ return new int[0];
+ }
+
+ @Override
+ public int[] getMidiFileTypes(Sequence sequence) {
+ Objects.requireNonNull(sequence);
+ return new int[0];
+ }
+
+ @Override
+ public int write(Sequence in, int fileType, OutputStream out) {
+ Objects.requireNonNull(in);
+ Objects.requireNonNull(out);
+ return 0;
+ }
+
+ @Override
+ public int write(Sequence in, int fileType, File out) {
+ Objects.requireNonNull(in);
+ Objects.requireNonNull(out);
+ return 0;
+ }
+ };
+
+ private static final class NullOutputStream extends OutputStream {
+
+ @Override
+ public void write(final int b) {
+ //do nothing
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/spi/SoundbankReader/ExpectedNPEOnNull.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.spi.SoundbankReader;
+
+import static java.util.ServiceLoader.load;
+
+/**
+ * @test
+ * @bug 8143909
+ * @author Sergey Bylokhov
+ */
+public final class ExpectedNPEOnNull {
+
+ public static void main(final String[] args) throws Exception {
+ testMS();
+ for (final SoundbankReader sbr : load(SoundbankReader.class)) {
+ testSBR(sbr);
+ }
+ }
+
+ /**
+ * Tests the part of MidiSystem API, which implemented via SoundbankReader.
+ */
+ private static void testMS() throws Exception {
+ // MidiSystem#getSoundbank(InputStream)
+ try {
+ MidiSystem.getSoundbank((InputStream) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiSystem#getSoundbank(URL)
+ try {
+ MidiSystem.getSoundbank((URL) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // MidiSystem#getSoundbank(File)
+ try {
+ MidiSystem.getSoundbank((File) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+
+ /**
+ * Tests the SoundbankReader API directly.
+ */
+ private static void testSBR(final SoundbankReader sbr) throws Exception {
+ // SoundbankReader#getSoundbank(InputStream)
+ try {
+ sbr.getSoundbank((InputStream) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // SoundbankReader#getSoundbank(URL)
+ try {
+ sbr.getSoundbank((URL) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ // SoundbankReader#getSoundbank(File)
+ try {
+ sbr.getSoundbank((File) null);
+ throw new RuntimeException("NPE is expected");
+ } catch (final NullPointerException ignored) {
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JFileChooser/8067660/FileChooserTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8067660
+ * @summary JFileChooser create new folder fails silently
+ * @requires (os.family == "windows")
+ * @run main/manual FileChooserTest
+ */
+import java.awt.Panel;
+import java.awt.TextArea;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.SwingUtilities;
+
+public class FileChooserTest {
+
+ private static boolean theTestPassed;
+ private static boolean testGeneratedInterrupt;
+ private static Thread mainThread;
+ private static int sleepTime = 30000;
+ public static JFileChooser fileChooser;
+
+ private static void init() throws Exception {
+
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ String[] instructions
+ = {
+ "1) Create a folder with read only permissions",
+ "2) Click on run test button.It will open a open dialog"
+ + " Navigate to the newly created read only folder",
+ "3) Click on the create new folder button in open dialog",
+ "4) If an error message does not pops up"
+ + "test failed otherwise passed.",
+ "5) Pressing Pass/Fail button will mark test as "
+ + "pass/fail and will shutdown JVM"};
+
+ Sysout.createDialogWithInstructions(instructions);
+ Sysout.printInstructions(instructions);
+ }
+ });
+ }
+
+ /**
+ * ***************************************************
+ * Standard Test Machinery Section DO NOT modify anything in this section --
+ * it's a standard chunk of code which has all of the synchronisation
+ * necessary for the test harness. By keeping it the same in all tests, it
+ * is easier to read and understand someone else's test, as well as insuring
+ * that all tests behave correctly with the test harness. There is a section
+ * following this for test-defined classes
+ */
+ public static void main(String args[]) throws Exception {
+
+ mainThread = Thread.currentThread();
+ try {
+ init();
+ } catch (Exception ex) {
+ return;
+ }
+ try {
+ mainThread.sleep(sleepTime);
+ } catch (InterruptedException ex) {
+ Sysout.dispose();
+ if (!theTestPassed && testGeneratedInterrupt) {
+ throw new RuntimeException("Test Failed");
+ }
+ }
+ if (!testGeneratedInterrupt) {
+ Sysout.dispose();
+ throw new RuntimeException("Test Failed");
+ }
+ }
+
+ public static synchronized void pass() {
+ theTestPassed = true;
+ testGeneratedInterrupt = true;
+ mainThread.interrupt();
+ }
+
+ public static synchronized void fail() {
+ theTestPassed = false;
+ testGeneratedInterrupt = true;
+ mainThread.interrupt();
+ }
+}
+
+/**
+ * This is part of the standard test machinery. It creates a dialog (with the
+ * instructions), and is the interface for sending text messages to the user. To
+ * print the instructions, send an array of strings to Sysout.createDialog
+ * WithInstructions method. Put one line of instructions per array entry. To
+ * display a message for the tester to see, simply call Sysout.println with the
+ * string to be displayed. This mimics System.out.println but works within the
+ * test harness as well as standalone.
+ */
+class Sysout {
+
+ private static TestDialog dialog;
+ private static JFrame frame;
+
+ public static void createDialogWithInstructions(String[] instructions) {
+ frame = new JFrame();
+ dialog = new TestDialog(frame, "Instructions");
+ dialog.printInstructions(instructions);
+ dialog.setVisible(true);
+ println("Any messages for the tester will display here.");
+ }
+
+ public static void printInstructions(String[] instructions) {
+ dialog.printInstructions(instructions);
+ }
+
+ public static void println(String messageIn) {
+ dialog.displayMessage(messageIn);
+ }
+
+ public static void dispose() {
+ Sysout.println("Shutting down the Java process..");
+ if(FileChooserTest.fileChooser != null) {
+ FileChooserTest.fileChooser.cancelSelection();
+ }
+ frame.dispose();
+ dialog.dispose();
+ }
+}
+
+/**
+ * This is part of the standard test machinery. It provides a place for the test
+ * instructions to be displayed, and a place for interactive messages to the
+ * user to be displayed. To have the test instructions displayed, see Sysout. To
+ * have a message to the user be displayed, see Sysout. Do not call anything in
+ * this dialog directly.
+ */
+class TestDialog extends JDialog {
+
+ private TextArea instructionsText;
+ private TextArea messageText;
+ private int maxStringLength = 80;
+ private Panel buttonP = new Panel();
+ private JButton run = new JButton("Run");
+ private JButton passB = new JButton("Pass");
+ private JButton failB = new JButton("Fail");
+
+ public TestDialog(JFrame frame, String name) {
+ super(frame, name);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ int scrollBoth = TextArea.SCROLLBARS_BOTH;
+ instructionsText = new TextArea("", 15, maxStringLength, scrollBoth);
+ add("North", instructionsText);
+
+ messageText = new TextArea("", 5, maxStringLength, scrollBoth);
+ add("Center", messageText);
+
+ buttonP.add("East", run);
+ buttonP.add("East", passB);
+ buttonP.add("West", failB);
+ passB.setEnabled(false);
+ failB.setEnabled(false);
+ add("South", buttonP);
+
+ run.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent ae) {
+ FileChooserTest.fileChooser = new JFileChooser();
+ FileChooserTest.fileChooser.showOpenDialog(null);
+ passB.setEnabled(true);
+ failB.setEnabled(true);
+ }
+ });
+
+ passB.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent ae) {
+ FileChooserTest.pass();
+ }
+ });
+
+ failB.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent ae) {
+ FileChooserTest.fail();
+ }
+ });
+ pack();
+
+ setVisible(true);
+ }
+
+ public void printInstructions(String[] instructions) {
+ instructionsText.setText("");
+
+ String printStr, remainingStr;
+ for (String instruction : instructions) {
+ remainingStr = instruction;
+ while (remainingStr.length() > 0) {
+ if (remainingStr.length() >= maxStringLength) {
+ int posOfSpace = remainingStr.
+ lastIndexOf(' ', maxStringLength - 1);
+
+ if (posOfSpace <= 0) {
+ posOfSpace = maxStringLength - 1;
+ }
+
+ printStr = remainingStr.substring(0, posOfSpace + 1);
+ remainingStr = remainingStr.substring(posOfSpace + 1);
+ } else {
+ printStr = remainingStr;
+ remainingStr = "";
+ }
+ instructionsText.append(printStr + "\n");
+ }
+ }
+
+ }
+
+ public void displayMessage(String messageIn) {
+ messageText.append(messageIn + "\n");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/JMenuItem/8139169/ScreenMenuBarInputTwice.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8139169
+ * @summary verifies if TextArea gets input twice due to Apple's Screen Menubar
+ * @requires (os.family=="mac")
+ * @library ../../regtesthelpers
+ * @build Util
+ * @run main ScreenMenuBarInputTwice
+ */
+import java.awt.BorderLayout;
+import java.awt.Point;
+import java.awt.Robot;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import static java.awt.event.KeyEvent.VK_COMMA;
+import static java.awt.event.KeyEvent.VK_META;
+import static java.awt.event.KeyEvent.VK_SHIFT;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+import javax.swing.text.BadLocationException;
+
+public class ScreenMenuBarInputTwice {
+
+ public static final String TEST_STRING = "Check string";
+
+ private static Robot robot;
+ private static JFrame frame;
+ private static JPanel content;
+ private static JTextArea textArea;
+ private static JMenuBar menuBar;
+ private static JMenu menu;
+ private static JMenuItem menuItem;
+
+ public static void main(String[] args) throws Exception {
+ robot = new Robot();
+ createUIWithSeperateMenuBar();
+ robot.delay(2000);
+ shortcutTestCase();
+ robot.delay(2000);
+ cleanUp();
+ createUIWithIntegratedMenuBar();
+ robot.delay(2000);
+ menuTestCase();
+ robot.delay(2000);
+ cleanUp();
+ }
+
+ private static void createUIWithSeperateMenuBar() throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+
+ @Override
+ public void run() {
+ System.setProperty(
+ "com.apple.mrj.application.apple.menu.about.name",
+ "A test frame");
+ System.setProperty("apple.laf.useScreenMenuBar", "true");
+ frame = new JFrame("Text input twice check");
+ content = new JPanel(new BorderLayout());
+ textArea = new JTextArea();
+ content.add(new JScrollPane(textArea,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED),
+ BorderLayout.CENTER);
+ menuBar = new JMenuBar();
+ frame.setJMenuBar(menuBar);
+ Action a = new AbstractAction("Insert some text") {
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ try {
+
+ textArea.getDocument()
+ .insertString(0, TEST_STRING, null);
+ } catch (BadLocationException e) {
+ frame.dispose();
+ throw new RuntimeException("Bad location: ", e);
+ }
+ }
+ };
+ KeyStroke keyStroke = KeyStroke.getKeyStroke(
+ "meta shift COMMA");
+ a.putValue(Action.ACCELERATOR_KEY, keyStroke);
+ textArea.getInputMap().put(keyStroke, "myAction");
+ textArea.getActionMap().put("myAction", a);
+ menu = new JMenu("The Menu");
+ menuItem = new JMenuItem(a);
+ menuItem.setAccelerator((KeyStroke) a.getValue(
+ Action.ACCELERATOR_KEY));
+ menu.add(menuItem);
+ menuBar.add(menu);
+ frame.getContentPane().add(content);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setLocationRelativeTo(null);
+ frame.setSize(500, 500);
+ frame.setVisible(true);
+ frame.toFront();
+ }
+ });
+ }
+
+ private static void createUIWithIntegratedMenuBar() throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+
+ @Override
+ public void run() {
+ System.setProperty(
+ "com.apple.mrj.application.apple.menu.about.name",
+ "A test frame");
+ System.setProperty("apple.laf.useScreenMenuBar", "false");
+ frame = new JFrame("Text input twice check");
+ content = new JPanel(new BorderLayout());
+ textArea = new JTextArea();
+ content.add(new JScrollPane(textArea,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED),
+ BorderLayout.CENTER);
+ menuBar = new JMenuBar();
+ frame.setJMenuBar(menuBar);
+ Action a = new AbstractAction("Insert some text") {
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ try {
+
+ textArea.getDocument()
+ .insertString(0, TEST_STRING, null);
+ } catch (BadLocationException e) {
+ frame.dispose();
+ throw new RuntimeException("Bad location: ", e);
+ }
+ }
+ };
+ KeyStroke keyStroke = KeyStroke.getKeyStroke(
+ "meta shift COMMA");
+ a.putValue(Action.ACCELERATOR_KEY, keyStroke);
+ textArea.getInputMap().put(keyStroke, "myAction");
+ textArea.getActionMap().put("myAction", a);
+ menu = new JMenu("The Menu");
+ menuItem = new JMenuItem(a);
+ menuItem.setAccelerator((KeyStroke) a.getValue(
+ Action.ACCELERATOR_KEY));
+ menu.add(menuItem);
+ menuBar.add(menu);
+ frame.getContentPane().add(content);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setSize(500, 500);
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+ frame.toFront();
+ }
+ });
+ }
+
+ private static void shortcutTestCase() throws Exception {
+ robot.keyPress(KeyEvent.VK_META);
+ robot.keyPress(KeyEvent.VK_SHIFT);
+ robot.keyPress(KeyEvent.VK_COMMA);
+ robot.keyRelease(VK_COMMA);
+ robot.keyRelease(VK_SHIFT);
+ robot.keyRelease(VK_META);
+ robot.delay(2000);
+ checkText(textArea.getText());
+ }
+
+ private static void menuTestCase() throws Exception {
+ Point mousePoint;
+ mousePoint = Util.getCenterPoint(menu);
+ robot.mouseMove(mousePoint.x, mousePoint.y);
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+ robot.delay(2000);
+ mousePoint = Util.getCenterPoint(menuItem);
+ robot.mouseMove(mousePoint.x, mousePoint.y);
+ robot.delay(2000);
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+ robot.delay(2000);
+ checkText(textArea.getText());
+ }
+
+ private static void checkText(String text) throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ if (TEST_STRING.equals(text)) {
+ textArea.setText("");
+ } else {
+ frame.dispose();
+ throw new RuntimeException("Failed. "
+ + " Menu item shortcut invoked twice");
+ }
+ }
+ });
+ }
+
+ private static void cleanUp() throws Exception {
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ frame.dispose();
+ }
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/undo/UndoManager/AbstractDocumentUndoConcurrentTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ @bug 8030702
+ @summary Deadlock between subclass of AbstractDocument and UndoManager
+ @author Semyon Sadetsky
+ */
+
+import javax.swing.text.PlainDocument;
+import javax.swing.text.StringContent;
+import javax.swing.undo.UndoManager;
+import java.text.DecimalFormat;
+import java.text.Format;
+import java.util.concurrent.CyclicBarrier;
+
+public class AbstractDocumentUndoConcurrentTest {
+ static CyclicBarrier barrier = new CyclicBarrier(3);
+
+ private static PlainDocument doc1;
+ private static PlainDocument doc2;
+ private static Format format1 = new DecimalFormat("<Test1 0000>");
+ private static Format format2 = new DecimalFormat("<Test22 0000>");
+
+ public static void main(String[] args) throws Exception {
+ test();
+ System.out.println(doc1.getText(0, doc1.getLength()));
+ System.out.println(doc2.getText(0, doc2.getLength()));
+ System.out.println("ok");
+ }
+
+ private static void test() throws Exception {
+ doc1 = new PlainDocument(new StringContent());
+ final UndoManager undoManager = new UndoManager();
+
+ doc1.addUndoableEditListener(undoManager);
+ doc1.insertString(0, "<Test1 XXXX>", null);
+
+ doc2 = new PlainDocument(new StringContent());
+
+ doc2.addUndoableEditListener(undoManager);
+ doc2.insertString(0, "<Test22 XXXX>", null);
+
+ Thread t1 = new Thread("Thread doc1") {
+ @Override
+ public void run() {
+ try {
+ barrier.await();
+ for (int i = 0; i < 1000; i++) {
+ doc1.insertString(0, format1.format(i), null);
+ if(doc1.getLength() > 100) doc1.remove(0, 12);
+ }
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ System.out.println("t1 done");
+ }
+ };
+
+ Thread t2 = new Thread("Thread doc2") {
+ @Override
+ public void run() {
+ try {
+ barrier.await();
+ for (int i = 0; i < 1000; i++) {
+ doc2.insertString(0, format2.format(i), null);
+ if(doc2.getLength() > 100) doc2.remove(0, 13);
+ }
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ System.out.println("t2 done");
+ }
+ };
+
+ Thread t3 = new Thread("Undo/Redo Thread") {
+ @Override
+ public void run() {
+ try {
+ barrier.await();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ for (int i = 0; i < 1000; i++) {
+ undoManager.undoOrRedo();
+ undoManager.undo();
+ }
+ System.out.println("t3 done");
+ }
+ };
+
+ t1.start();
+ t2.start();
+ t3.start();
+
+ t1.join();
+ t2.join();
+ t3.join();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/jdk/internal/math/FloatingDecimal/OldFDBigIntForTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+//package jdk.internal.math;
+
+/*
+ * A really, really simple bigint package
+ * tailored to the needs of floating base conversion.
+ */
+class OldFDBigIntForTest {
+ int nWords; // number of words used
+ int data[]; // value: data[0] is least significant
+
+
+ public OldFDBigIntForTest( int v ){
+ nWords = 1;
+ data = new int[1];
+ data[0] = v;
+ }
+
+ public OldFDBigIntForTest( long v ){
+ data = new int[2];
+ data[0] = (int)v;
+ data[1] = (int)(v>>>32);
+ nWords = (data[1]==0) ? 1 : 2;
+ }
+
+ public OldFDBigIntForTest( OldFDBigIntForTest other ){
+ data = new int[nWords = other.nWords];
+ System.arraycopy( other.data, 0, data, 0, nWords );
+ }
+
+ private OldFDBigIntForTest( int [] d, int n ){
+ data = d;
+ nWords = n;
+ }
+
+ public OldFDBigIntForTest( long seed, char digit[], int nd0, int nd ){
+ int n= (nd+8)/9; // estimate size needed.
+ if ( n < 2 ) n = 2;
+ data = new int[n]; // allocate enough space
+ data[0] = (int)seed; // starting value
+ data[1] = (int)(seed>>>32);
+ nWords = (data[1]==0) ? 1 : 2;
+ int i = nd0;
+ int limit = nd-5; // slurp digits 5 at a time.
+ int v;
+ while ( i < limit ){
+ int ilim = i+5;
+ v = (int)digit[i++]-(int)'0';
+ while( i <ilim ){
+ v = 10*v + (int)digit[i++]-(int)'0';
+ }
+ multaddMe( 100000, v); // ... where 100000 is 10^5.
+ }
+ int factor = 1;
+ v = 0;
+ while ( i < nd ){
+ v = 10*v + (int)digit[i++]-(int)'0';
+ factor *= 10;
+ }
+ if ( factor != 1 ){
+ multaddMe( factor, v );
+ }
+ }
+
+ /*
+ * Left shift by c bits.
+ * Shifts this in place.
+ */
+ public void
+ lshiftMe( int c )throws IllegalArgumentException {
+ if ( c <= 0 ){
+ if ( c == 0 )
+ return; // silly.
+ else
+ throw new IllegalArgumentException("negative shift count");
+ }
+ int wordcount = c>>5;
+ int bitcount = c & 0x1f;
+ int anticount = 32-bitcount;
+ int t[] = data;
+ int s[] = data;
+ if ( nWords+wordcount+1 > t.length ){
+ // reallocate.
+ t = new int[ nWords+wordcount+1 ];
+ }
+ int target = nWords+wordcount;
+ int src = nWords-1;
+ if ( bitcount == 0 ){
+ // special hack, since an anticount of 32 won't go!
+ System.arraycopy( s, 0, t, wordcount, nWords );
+ target = wordcount-1;
+ } else {
+ t[target--] = s[src]>>>anticount;
+ while ( src >= 1 ){
+ t[target--] = (s[src]<<bitcount) | (s[--src]>>>anticount);
+ }
+ t[target--] = s[src]<<bitcount;
+ }
+ while( target >= 0 ){
+ t[target--] = 0;
+ }
+ data = t;
+ nWords += wordcount + 1;
+ // may have constructed high-order word of 0.
+ // if so, trim it
+ while ( nWords > 1 && data[nWords-1] == 0 )
+ nWords--;
+ }
+
+ /*
+ * normalize this number by shifting until
+ * the MSB of the number is at 0x08000000.
+ * This is in preparation for quoRemIteration, below.
+ * The idea is that, to make division easier, we want the
+ * divisor to be "normalized" -- usually this means shifting
+ * the MSB into the high words sign bit. But because we know that
+ * the quotient will be 0 < q < 10, we would like to arrange that
+ * the dividend not span up into another word of precision.
+ * (This needs to be explained more clearly!)
+ */
+ public int
+ normalizeMe() throws IllegalArgumentException {
+ int src;
+ int wordcount = 0;
+ int bitcount = 0;
+ int v = 0;
+ for ( src= nWords-1 ; src >= 0 && (v=data[src]) == 0 ; src--){
+ wordcount += 1;
+ }
+ if ( src < 0 ){
+ // oops. Value is zero. Cannot normalize it!
+ throw new IllegalArgumentException("zero value");
+ }
+ /*
+ * In most cases, we assume that wordcount is zero. This only
+ * makes sense, as we try not to maintain any high-order
+ * words full of zeros. In fact, if there are zeros, we will
+ * simply SHORTEN our number at this point. Watch closely...
+ */
+ nWords -= wordcount;
+ /*
+ * Compute how far left we have to shift v s.t. its highest-
+ * order bit is in the right place. Then call lshiftMe to
+ * do the work.
+ */
+ if ( (v & 0xf0000000) != 0 ){
+ // will have to shift up into the next word.
+ // too bad.
+ for( bitcount = 32 ; (v & 0xf0000000) != 0 ; bitcount-- )
+ v >>>= 1;
+ } else {
+ while ( v <= 0x000fffff ){
+ // hack: byte-at-a-time shifting
+ v <<= 8;
+ bitcount += 8;
+ }
+ while ( v <= 0x07ffffff ){
+ v <<= 1;
+ bitcount += 1;
+ }
+ }
+ if ( bitcount != 0 )
+ lshiftMe( bitcount );
+ return bitcount;
+ }
+
+ /*
+ * Multiply a OldFDBigIntForTest by an int.
+ * Result is a new OldFDBigIntForTest.
+ */
+ public OldFDBigIntForTest
+ mult( int iv ) {
+ long v = iv;
+ int r[];
+ long p;
+
+ // guess adequate size of r.
+ r = new int[ ( v * ((long)data[nWords-1]&0xffffffffL) > 0xfffffffL ) ? nWords+1 : nWords ];
+ p = 0L;
+ for( int i=0; i < nWords; i++ ) {
+ p += v * ((long)data[i]&0xffffffffL);
+ r[i] = (int)p;
+ p >>>= 32;
+ }
+ if ( p == 0L){
+ return new OldFDBigIntForTest( r, nWords );
+ } else {
+ r[nWords] = (int)p;
+ return new OldFDBigIntForTest( r, nWords+1 );
+ }
+ }
+
+ /*
+ * Multiply a OldFDBigIntForTest by an int and add another int.
+ * Result is computed in place.
+ * Hope it fits!
+ */
+ public void
+ multaddMe( int iv, int addend ) {
+ long v = iv;
+ long p;
+
+ // unroll 0th iteration, doing addition.
+ p = v * ((long)data[0]&0xffffffffL) + ((long)addend&0xffffffffL);
+ data[0] = (int)p;
+ p >>>= 32;
+ for( int i=1; i < nWords; i++ ) {
+ p += v * ((long)data[i]&0xffffffffL);
+ data[i] = (int)p;
+ p >>>= 32;
+ }
+ if ( p != 0L){
+ data[nWords] = (int)p; // will fail noisily if illegal!
+ nWords++;
+ }
+ }
+
+ /*
+ * Multiply a OldFDBigIntForTest by another OldFDBigIntForTest.
+ * Result is a new OldFDBigIntForTest.
+ */
+ public OldFDBigIntForTest
+ mult( OldFDBigIntForTest other ){
+ // crudely guess adequate size for r
+ int r[] = new int[ nWords + other.nWords ];
+ int i;
+ // I think I am promised zeros...
+
+ for( i = 0; i < this.nWords; i++ ){
+ long v = (long)this.data[i] & 0xffffffffL; // UNSIGNED CONVERSION
+ long p = 0L;
+ int j;
+ for( j = 0; j < other.nWords; j++ ){
+ p += ((long)r[i+j]&0xffffffffL) + v*((long)other.data[j]&0xffffffffL); // UNSIGNED CONVERSIONS ALL 'ROUND.
+ r[i+j] = (int)p;
+ p >>>= 32;
+ }
+ r[i+j] = (int)p;
+ }
+ // compute how much of r we actually needed for all that.
+ for ( i = r.length-1; i> 0; i--)
+ if ( r[i] != 0 )
+ break;
+ return new OldFDBigIntForTest( r, i+1 );
+ }
+
+ /*
+ * Add one OldFDBigIntForTest to another. Return a OldFDBigIntForTest
+ */
+ public OldFDBigIntForTest
+ add( OldFDBigIntForTest other ){
+ int i;
+ int a[], b[];
+ int n, m;
+ long c = 0L;
+ // arrange such that a.nWords >= b.nWords;
+ // n = a.nWords, m = b.nWords
+ if ( this.nWords >= other.nWords ){
+ a = this.data;
+ n = this.nWords;
+ b = other.data;
+ m = other.nWords;
+ } else {
+ a = other.data;
+ n = other.nWords;
+ b = this.data;
+ m = this.nWords;
+ }
+ int r[] = new int[ n ];
+ for ( i = 0; i < n; i++ ){
+ c += (long)a[i] & 0xffffffffL;
+ if ( i < m ){
+ c += (long)b[i] & 0xffffffffL;
+ }
+ r[i] = (int) c;
+ c >>= 32; // signed shift.
+ }
+ if ( c != 0L ){
+ // oops -- carry out -- need longer result.
+ int s[] = new int[ r.length+1 ];
+ System.arraycopy( r, 0, s, 0, r.length );
+ s[i++] = (int)c;
+ return new OldFDBigIntForTest( s, i );
+ }
+ return new OldFDBigIntForTest( r, i );
+ }
+
+ /*
+ * Subtract one OldFDBigIntForTest from another. Return a OldFDBigIntForTest
+ * Assert that the result is positive.
+ */
+ public OldFDBigIntForTest
+ sub( OldFDBigIntForTest other ){
+ int r[] = new int[ this.nWords ];
+ int i;
+ int n = this.nWords;
+ int m = other.nWords;
+ int nzeros = 0;
+ long c = 0L;
+ for ( i = 0; i < n; i++ ){
+ c += (long)this.data[i] & 0xffffffffL;
+ if ( i < m ){
+ c -= (long)other.data[i] & 0xffffffffL;
+ }
+ if ( ( r[i] = (int) c ) == 0 )
+ nzeros++;
+ else
+ nzeros = 0;
+ c >>= 32; // signed shift
+ }
+ assert c == 0L : c; // borrow out of subtract
+ assert dataInRangeIsZero(i, m, other); // negative result of subtract
+ return new OldFDBigIntForTest( r, n-nzeros );
+ }
+
+ private static boolean dataInRangeIsZero(int i, int m, OldFDBigIntForTest other) {
+ while ( i < m )
+ if (other.data[i++] != 0)
+ return false;
+ return true;
+ }
+
+ /*
+ * Compare OldFDBigIntForTest with another OldFDBigIntForTest. Return an integer
+ * >0: this > other
+ * 0: this == other
+ * <0: this < other
+ */
+ public int
+ cmp( OldFDBigIntForTest other ){
+ int i;
+ if ( this.nWords > other.nWords ){
+ // if any of my high-order words is non-zero,
+ // then the answer is evident
+ int j = other.nWords-1;
+ for ( i = this.nWords-1; i > j ; i-- )
+ if ( this.data[i] != 0 ) return 1;
+ }else if ( this.nWords < other.nWords ){
+ // if any of other's high-order words is non-zero,
+ // then the answer is evident
+ int j = this.nWords-1;
+ for ( i = other.nWords-1; i > j ; i-- )
+ if ( other.data[i] != 0 ) return -1;
+ } else{
+ i = this.nWords-1;
+ }
+ for ( ; i > 0 ; i-- )
+ if ( this.data[i] != other.data[i] )
+ break;
+ // careful! want unsigned compare!
+ // use brute force here.
+ int a = this.data[i];
+ int b = other.data[i];
+ if ( a < 0 ){
+ // a is really big, unsigned
+ if ( b < 0 ){
+ return a-b; // both big, negative
+ } else {
+ return 1; // b not big, answer is obvious;
+ }
+ } else {
+ // a is not really big
+ if ( b < 0 ) {
+ // but b is really big
+ return -1;
+ } else {
+ return a - b;
+ }
+ }
+ }
+
+ /*
+ * Compute
+ * q = (int)( this / S )
+ * this = 10 * ( this mod S )
+ * Return q.
+ * This is the iteration step of digit development for output.
+ * We assume that S has been normalized, as above, and that
+ * "this" has been lshift'ed accordingly.
+ * Also assume, of course, that the result, q, can be expressed
+ * as an integer, 0 <= q < 10.
+ */
+ public int
+ quoRemIteration( OldFDBigIntForTest S )throws IllegalArgumentException {
+ // ensure that this and S have the same number of
+ // digits. If S is properly normalized and q < 10 then
+ // this must be so.
+ if ( nWords != S.nWords ){
+ throw new IllegalArgumentException("disparate values");
+ }
+ // estimate q the obvious way. We will usually be
+ // right. If not, then we're only off by a little and
+ // will re-add.
+ int n = nWords-1;
+ long q = ((long)data[n]&0xffffffffL) / (long)S.data[n];
+ long diff = 0L;
+ for ( int i = 0; i <= n ; i++ ){
+ diff += ((long)data[i]&0xffffffffL) - q*((long)S.data[i]&0xffffffffL);
+ data[i] = (int)diff;
+ diff >>= 32; // N.B. SIGNED shift.
+ }
+ if ( diff != 0L ) {
+ // damn, damn, damn. q is too big.
+ // add S back in until this turns +. This should
+ // not be very many times!
+ long sum = 0L;
+ while ( sum == 0L ){
+ sum = 0L;
+ for ( int i = 0; i <= n; i++ ){
+ sum += ((long)data[i]&0xffffffffL) + ((long)S.data[i]&0xffffffffL);
+ data[i] = (int) sum;
+ sum >>= 32; // Signed or unsigned, answer is 0 or 1
+ }
+ /*
+ * Originally the following line read
+ * "if ( sum !=0 && sum != -1 )"
+ * but that would be wrong, because of the
+ * treatment of the two values as entirely unsigned,
+ * it would be impossible for a carry-out to be interpreted
+ * as -1 -- it would have to be a single-bit carry-out, or
+ * +1.
+ */
+ assert sum == 0 || sum == 1 : sum; // carry out of division correction
+ q -= 1;
+ }
+ }
+ // finally, we can multiply this by 10.
+ // it cannot overflow, right, as the high-order word has
+ // at least 4 high-order zeros!
+ long p = 0L;
+ for ( int i = 0; i <= n; i++ ){
+ p += 10*((long)data[i]&0xffffffffL);
+ data[i] = (int)p;
+ p >>= 32; // SIGNED shift.
+ }
+ assert p == 0L : p; // Carry out of *10
+ return (int)q;
+ }
+
+ public long
+ longValue(){
+ // if this can be represented as a long, return the value
+ assert this.nWords > 0 : this.nWords; // longValue confused
+
+ if (this.nWords == 1)
+ return ((long)data[0]&0xffffffffL);
+
+ assert dataInRangeIsZero(2, this.nWords, this); // value too big
+ assert data[1] >= 0; // value too big
+ return ((long)(data[1]) << 32) | ((long)data[0]&0xffffffffL);
+ }
+
+ public String
+ toString() {
+ StringBuffer r = new StringBuffer(30);
+ r.append('[');
+ int i = Math.min( nWords-1, data.length-1) ;
+ if ( nWords > data.length ){
+ r.append( "("+data.length+"<"+nWords+"!)" );
+ }
+ for( ; i> 0 ; i-- ){
+ r.append( Integer.toHexString( data[i] ) );
+ r.append(' ');
+ }
+ r.append( Integer.toHexString( data[0] ) );
+ r.append(']');
+ return new String( r );
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/jdk/internal/math/FloatingDecimal/OldFloatingDecimalForTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,2434 @@
+/*
+ * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+//package jdk.internal.math;
+
+import java.util.regex.*;
+
+public class OldFloatingDecimalForTest{
+ boolean isExceptional;
+ boolean isNegative;
+ int decExponent;
+ char digits[];
+ int nDigits;
+ int bigIntExp;
+ int bigIntNBits;
+ boolean mustSetRoundDir = false;
+ boolean fromHex = false;
+ int roundDir = 0; // set by doubleValue
+
+ /*
+ * The fields below provides additional information about the result of
+ * the binary to decimal digits conversion done in dtoa() and roundup()
+ * methods. They are changed if needed by those two methods.
+ */
+
+ // True if the dtoa() binary to decimal conversion was exact.
+ boolean exactDecimalConversion = false;
+
+ // True if the result of the binary to decimal conversion was rounded-up
+ // at the end of the conversion process, i.e. roundUp() method was called.
+ boolean decimalDigitsRoundedUp = false;
+
+ private OldFloatingDecimalForTest( boolean negSign, int decExponent, char []digits, int n, boolean e )
+ {
+ isNegative = negSign;
+ isExceptional = e;
+ this.decExponent = decExponent;
+ this.digits = digits;
+ this.nDigits = n;
+ }
+
+ /*
+ * Constants of the implementation
+ * Most are IEEE-754 related.
+ * (There are more really boring constants at the end.)
+ */
+ static final long signMask = 0x8000000000000000L;
+ static final long expMask = 0x7ff0000000000000L;
+ static final long fractMask= ~(signMask|expMask);
+ static final int expShift = 52;
+ static final int expBias = 1023;
+ static final long fractHOB = ( 1L<<expShift ); // assumed High-Order bit
+ static final long expOne = ((long)expBias)<<expShift; // exponent of 1.0
+ static final int maxSmallBinExp = 62;
+ static final int minSmallBinExp = -( 63 / 3 );
+ static final int maxDecimalDigits = 15;
+ static final int maxDecimalExponent = 308;
+ static final int minDecimalExponent = -324;
+ static final int bigDecimalExponent = 324; // i.e. abs(minDecimalExponent)
+
+ static final long highbyte = 0xff00000000000000L;
+ static final long highbit = 0x8000000000000000L;
+ static final long lowbytes = ~highbyte;
+
+ static final int singleSignMask = 0x80000000;
+ static final int singleExpMask = 0x7f800000;
+ static final int singleFractMask = ~(singleSignMask|singleExpMask);
+ static final int singleExpShift = 23;
+ static final int singleFractHOB = 1<<singleExpShift;
+ static final int singleExpBias = 127;
+ static final int singleMaxDecimalDigits = 7;
+ static final int singleMaxDecimalExponent = 38;
+ static final int singleMinDecimalExponent = -45;
+
+ static final int intDecimalDigits = 9;
+
+
+ /*
+ * count number of bits from high-order 1 bit to low-order 1 bit,
+ * inclusive.
+ */
+ private static int
+ countBits( long v ){
+ //
+ // the strategy is to shift until we get a non-zero sign bit
+ // then shift until we have no bits left, counting the difference.
+ // we do byte shifting as a hack. Hope it helps.
+ //
+ if ( v == 0L ) return 0;
+
+ while ( ( v & highbyte ) == 0L ){
+ v <<= 8;
+ }
+ while ( v > 0L ) { // i.e. while ((v&highbit) == 0L )
+ v <<= 1;
+ }
+
+ int n = 0;
+ while (( v & lowbytes ) != 0L ){
+ v <<= 8;
+ n += 8;
+ }
+ while ( v != 0L ){
+ v <<= 1;
+ n += 1;
+ }
+ return n;
+ }
+
+ /*
+ * Keep big powers of 5 handy for future reference.
+ */
+ private static OldFDBigIntForTest b5p[];
+
+ private static synchronized OldFDBigIntForTest
+ big5pow( int p ){
+ assert p >= 0 : p; // negative power of 5
+ if ( b5p == null ){
+ b5p = new OldFDBigIntForTest[ p+1 ];
+ }else if (b5p.length <= p ){
+ OldFDBigIntForTest t[] = new OldFDBigIntForTest[ p+1 ];
+ System.arraycopy( b5p, 0, t, 0, b5p.length );
+ b5p = t;
+ }
+ if ( b5p[p] != null )
+ return b5p[p];
+ else if ( p < small5pow.length )
+ return b5p[p] = new OldFDBigIntForTest( small5pow[p] );
+ else if ( p < long5pow.length )
+ return b5p[p] = new OldFDBigIntForTest( long5pow[p] );
+ else {
+ // construct the value.
+ // recursively.
+ int q, r;
+ // in order to compute 5^p,
+ // compute its square root, 5^(p/2) and square.
+ // or, let q = p / 2, r = p -q, then
+ // 5^p = 5^(q+r) = 5^q * 5^r
+ q = p >> 1;
+ r = p - q;
+ OldFDBigIntForTest bigq = b5p[q];
+ if ( bigq == null )
+ bigq = big5pow ( q );
+ if ( r < small5pow.length ){
+ return (b5p[p] = bigq.mult( small5pow[r] ) );
+ }else{
+ OldFDBigIntForTest bigr = b5p[ r ];
+ if ( bigr == null )
+ bigr = big5pow( r );
+ return (b5p[p] = bigq.mult( bigr ) );
+ }
+ }
+ }
+
+ //
+ // a common operation
+ //
+ private static OldFDBigIntForTest
+ multPow52( OldFDBigIntForTest v, int p5, int p2 ){
+ if ( p5 != 0 ){
+ if ( p5 < small5pow.length ){
+ v = v.mult( small5pow[p5] );
+ } else {
+ v = v.mult( big5pow( p5 ) );
+ }
+ }
+ if ( p2 != 0 ){
+ v.lshiftMe( p2 );
+ }
+ return v;
+ }
+
+ //
+ // another common operation
+ //
+ private static OldFDBigIntForTest
+ constructPow52( int p5, int p2 ){
+ OldFDBigIntForTest v = new OldFDBigIntForTest( big5pow( p5 ) );
+ if ( p2 != 0 ){
+ v.lshiftMe( p2 );
+ }
+ return v;
+ }
+
+ /*
+ * Make a floating double into a OldFDBigIntForTest.
+ * This could also be structured as a OldFDBigIntForTest
+ * constructor, but we'd have to build a lot of knowledge
+ * about floating-point representation into it, and we don't want to.
+ *
+ * AS A SIDE EFFECT, THIS METHOD WILL SET THE INSTANCE VARIABLES
+ * bigIntExp and bigIntNBits
+ *
+ */
+ private OldFDBigIntForTest
+ doubleToBigInt( double dval ){
+ long lbits = Double.doubleToLongBits( dval ) & ~signMask;
+ int binexp = (int)(lbits >>> expShift);
+ lbits &= fractMask;
+ if ( binexp > 0 ){
+ lbits |= fractHOB;
+ } else {
+ assert lbits != 0L : lbits; // doubleToBigInt(0.0)
+ binexp +=1;
+ while ( (lbits & fractHOB ) == 0L){
+ lbits <<= 1;
+ binexp -= 1;
+ }
+ }
+ binexp -= expBias;
+ int nbits = countBits( lbits );
+ /*
+ * We now know where the high-order 1 bit is,
+ * and we know how many there are.
+ */
+ int lowOrderZeros = expShift+1-nbits;
+ lbits >>>= lowOrderZeros;
+
+ bigIntExp = binexp+1-nbits;
+ bigIntNBits = nbits;
+ return new OldFDBigIntForTest( lbits );
+ }
+
+ /*
+ * Compute a number that is the ULP of the given value,
+ * for purposes of addition/subtraction. Generally easy.
+ * More difficult if subtracting and the argument
+ * is a normalized a power of 2, as the ULP changes at these points.
+ */
+ private static double ulp( double dval, boolean subtracting ){
+ long lbits = Double.doubleToLongBits( dval ) & ~signMask;
+ int binexp = (int)(lbits >>> expShift);
+ double ulpval;
+ if ( subtracting && ( binexp >= expShift ) && ((lbits&fractMask) == 0L) ){
+ // for subtraction from normalized, powers of 2,
+ // use next-smaller exponent
+ binexp -= 1;
+ }
+ if ( binexp > expShift ){
+ ulpval = Double.longBitsToDouble( ((long)(binexp-expShift))<<expShift );
+ } else if ( binexp == 0 ){
+ ulpval = Double.MIN_VALUE;
+ } else {
+ ulpval = Double.longBitsToDouble( 1L<<(binexp-1) );
+ }
+ if ( subtracting ) ulpval = - ulpval;
+
+ return ulpval;
+ }
+
+ /*
+ * Round a double to a float.
+ * In addition to the fraction bits of the double,
+ * look at the class instance variable roundDir,
+ * which should help us avoid double-rounding error.
+ * roundDir was set in hardValueOf if the estimate was
+ * close enough, but not exact. It tells us which direction
+ * of rounding is preferred.
+ */
+ float
+ stickyRound( double dval ){
+ long lbits = Double.doubleToLongBits( dval );
+ long binexp = lbits & expMask;
+ if ( binexp == 0L || binexp == expMask ){
+ // what we have here is special.
+ // don't worry, the right thing will happen.
+ return (float) dval;
+ }
+ lbits += (long)roundDir; // hack-o-matic.
+ return (float)Double.longBitsToDouble( lbits );
+ }
+
+
+ /*
+ * This is the easy subcase --
+ * all the significant bits, after scaling, are held in lvalue.
+ * negSign and decExponent tell us what processing and scaling
+ * has already been done. Exceptional cases have already been
+ * stripped out.
+ * In particular:
+ * lvalue is a finite number (not Inf, nor NaN)
+ * lvalue > 0L (not zero, nor negative).
+ *
+ * The only reason that we develop the digits here, rather than
+ * calling on Long.toString() is that we can do it a little faster,
+ * and besides want to treat trailing 0s specially. If Long.toString
+ * changes, we should re-evaluate this strategy!
+ */
+ private void
+ developLongDigits( int decExponent, long lvalue, long insignificant ){
+ char digits[];
+ int ndigits;
+ int digitno;
+ int c;
+ //
+ // Discard non-significant low-order bits, while rounding,
+ // up to insignificant value.
+ int i;
+ for ( i = 0; insignificant >= 10L; i++ )
+ insignificant /= 10L;
+ if ( i != 0 ){
+ long pow10 = long5pow[i] << i; // 10^i == 5^i * 2^i;
+ long residue = lvalue % pow10;
+ lvalue /= pow10;
+ decExponent += i;
+ if ( residue >= (pow10>>1) ){
+ // round up based on the low-order bits we're discarding
+ lvalue++;
+ }
+ }
+ if ( lvalue <= Integer.MAX_VALUE ){
+ assert lvalue > 0L : lvalue; // lvalue <= 0
+ // even easier subcase!
+ // can do int arithmetic rather than long!
+ int ivalue = (int)lvalue;
+ ndigits = 10;
+ digits = perThreadBuffer.get();
+ digitno = ndigits-1;
+ c = ivalue%10;
+ ivalue /= 10;
+ while ( c == 0 ){
+ decExponent++;
+ c = ivalue%10;
+ ivalue /= 10;
+ }
+ while ( ivalue != 0){
+ digits[digitno--] = (char)(c+'0');
+ decExponent++;
+ c = ivalue%10;
+ ivalue /= 10;
+ }
+ digits[digitno] = (char)(c+'0');
+ } else {
+ // same algorithm as above (same bugs, too )
+ // but using long arithmetic.
+ ndigits = 20;
+ digits = perThreadBuffer.get();
+ digitno = ndigits-1;
+ c = (int)(lvalue%10L);
+ lvalue /= 10L;
+ while ( c == 0 ){
+ decExponent++;
+ c = (int)(lvalue%10L);
+ lvalue /= 10L;
+ }
+ while ( lvalue != 0L ){
+ digits[digitno--] = (char)(c+'0');
+ decExponent++;
+ c = (int)(lvalue%10L);
+ lvalue /= 10;
+ }
+ digits[digitno] = (char)(c+'0');
+ }
+ char result [];
+ ndigits -= digitno;
+ result = new char[ ndigits ];
+ System.arraycopy( digits, digitno, result, 0, ndigits );
+ this.digits = result;
+ this.decExponent = decExponent+1;
+ this.nDigits = ndigits;
+ }
+
+ //
+ // add one to the least significant digit.
+ // in the unlikely event there is a carry out,
+ // deal with it.
+ // assert that this will only happen where there
+ // is only one digit, e.g. (float)1e-44 seems to do it.
+ //
+ private void
+ roundup(){
+ int i;
+ int q = digits[ i = (nDigits-1)];
+ if ( q == '9' ){
+ while ( q == '9' && i > 0 ){
+ digits[i] = '0';
+ q = digits[--i];
+ }
+ if ( q == '9' ){
+ // carryout! High-order 1, rest 0s, larger exp.
+ decExponent += 1;
+ digits[0] = '1';
+ return;
+ }
+ // else fall through.
+ }
+ digits[i] = (char)(q+1);
+ decimalDigitsRoundedUp = true;
+ }
+
+ public boolean digitsRoundedUp() {
+ return decimalDigitsRoundedUp;
+ }
+
+ /*
+ * FIRST IMPORTANT CONSTRUCTOR: DOUBLE
+ */
+ public OldFloatingDecimalForTest( double d )
+ {
+ long dBits = Double.doubleToLongBits( d );
+ long fractBits;
+ int binExp;
+ int nSignificantBits;
+
+ // discover and delete sign
+ if ( (dBits&signMask) != 0 ){
+ isNegative = true;
+ dBits ^= signMask;
+ } else {
+ isNegative = false;
+ }
+ // Begin to unpack
+ // Discover obvious special cases of NaN and Infinity.
+ binExp = (int)( (dBits&expMask) >> expShift );
+ fractBits = dBits&fractMask;
+ if ( binExp == (int)(expMask>>expShift) ) {
+ isExceptional = true;
+ if ( fractBits == 0L ){
+ digits = infinity;
+ } else {
+ digits = notANumber;
+ isNegative = false; // NaN has no sign!
+ }
+ nDigits = digits.length;
+ return;
+ }
+ isExceptional = false;
+ // Finish unpacking
+ // Normalize denormalized numbers.
+ // Insert assumed high-order bit for normalized numbers.
+ // Subtract exponent bias.
+ if ( binExp == 0 ){
+ if ( fractBits == 0L ){
+ // not a denorm, just a 0!
+ decExponent = 0;
+ digits = zero;
+ nDigits = 1;
+ return;
+ }
+ while ( (fractBits&fractHOB) == 0L ){
+ fractBits <<= 1;
+ binExp -= 1;
+ }
+ nSignificantBits = expShift + binExp +1; // recall binExp is - shift count.
+ binExp += 1;
+ } else {
+ fractBits |= fractHOB;
+ nSignificantBits = expShift+1;
+ }
+ binExp -= expBias;
+ // call the routine that actually does all the hard work.
+ dtoa( binExp, fractBits, nSignificantBits );
+ }
+
+ /*
+ * SECOND IMPORTANT CONSTRUCTOR: SINGLE
+ */
+ public OldFloatingDecimalForTest( float f )
+ {
+ int fBits = Float.floatToIntBits( f );
+ int fractBits;
+ int binExp;
+ int nSignificantBits;
+
+ // discover and delete sign
+ if ( (fBits&singleSignMask) != 0 ){
+ isNegative = true;
+ fBits ^= singleSignMask;
+ } else {
+ isNegative = false;
+ }
+ // Begin to unpack
+ // Discover obvious special cases of NaN and Infinity.
+ binExp = (fBits&singleExpMask) >> singleExpShift;
+ fractBits = fBits&singleFractMask;
+ if ( binExp == (singleExpMask>>singleExpShift) ) {
+ isExceptional = true;
+ if ( fractBits == 0L ){
+ digits = infinity;
+ } else {
+ digits = notANumber;
+ isNegative = false; // NaN has no sign!
+ }
+ nDigits = digits.length;
+ return;
+ }
+ isExceptional = false;
+ // Finish unpacking
+ // Normalize denormalized numbers.
+ // Insert assumed high-order bit for normalized numbers.
+ // Subtract exponent bias.
+ if ( binExp == 0 ){
+ if ( fractBits == 0 ){
+ // not a denorm, just a 0!
+ decExponent = 0;
+ digits = zero;
+ nDigits = 1;
+ return;
+ }
+ while ( (fractBits&singleFractHOB) == 0 ){
+ fractBits <<= 1;
+ binExp -= 1;
+ }
+ nSignificantBits = singleExpShift + binExp +1; // recall binExp is - shift count.
+ binExp += 1;
+ } else {
+ fractBits |= singleFractHOB;
+ nSignificantBits = singleExpShift+1;
+ }
+ binExp -= singleExpBias;
+ // call the routine that actually does all the hard work.
+ dtoa( binExp, ((long)fractBits)<<(expShift-singleExpShift), nSignificantBits );
+ }
+
+ private void
+ dtoa( int binExp, long fractBits, int nSignificantBits )
+ {
+ int nFractBits; // number of significant bits of fractBits;
+ int nTinyBits; // number of these to the right of the point.
+ int decExp;
+
+ // Examine number. Determine if it is an easy case,
+ // which we can do pretty trivially using float/long conversion,
+ // or whether we must do real work.
+ nFractBits = countBits( fractBits );
+ nTinyBits = Math.max( 0, nFractBits - binExp - 1 );
+ if ( binExp <= maxSmallBinExp && binExp >= minSmallBinExp ){
+ // Look more closely at the number to decide if,
+ // with scaling by 10^nTinyBits, the result will fit in
+ // a long.
+ if ( (nTinyBits < long5pow.length) && ((nFractBits + n5bits[nTinyBits]) < 64 ) ){
+ /*
+ * We can do this:
+ * take the fraction bits, which are normalized.
+ * (a) nTinyBits == 0: Shift left or right appropriately
+ * to align the binary point at the extreme right, i.e.
+ * where a long int point is expected to be. The integer
+ * result is easily converted to a string.
+ * (b) nTinyBits > 0: Shift right by expShift-nFractBits,
+ * which effectively converts to long and scales by
+ * 2^nTinyBits. Then multiply by 5^nTinyBits to
+ * complete the scaling. We know this won't overflow
+ * because we just counted the number of bits necessary
+ * in the result. The integer you get from this can
+ * then be converted to a string pretty easily.
+ */
+ long halfULP;
+ if ( nTinyBits == 0 ) {
+ if ( binExp > nSignificantBits ){
+ halfULP = 1L << ( binExp-nSignificantBits-1);
+ } else {
+ halfULP = 0L;
+ }
+ if ( binExp >= expShift ){
+ fractBits <<= (binExp-expShift);
+ } else {
+ fractBits >>>= (expShift-binExp) ;
+ }
+ developLongDigits( 0, fractBits, halfULP );
+ return;
+ }
+ /*
+ * The following causes excess digits to be printed
+ * out in the single-float case. Our manipulation of
+ * halfULP here is apparently not correct. If we
+ * better understand how this works, perhaps we can
+ * use this special case again. But for the time being,
+ * we do not.
+ * else {
+ * fractBits >>>= expShift+1-nFractBits;
+ * fractBits *= long5pow[ nTinyBits ];
+ * halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits);
+ * developLongDigits( -nTinyBits, fractBits, halfULP );
+ * return;
+ * }
+ */
+ }
+ }
+ /*
+ * This is the hard case. We are going to compute large positive
+ * integers B and S and integer decExp, s.t.
+ * d = ( B / S ) * 10^decExp
+ * 1 <= B / S < 10
+ * Obvious choices are:
+ * decExp = floor( log10(d) )
+ * B = d * 2^nTinyBits * 10^max( 0, -decExp )
+ * S = 10^max( 0, decExp) * 2^nTinyBits
+ * (noting that nTinyBits has already been forced to non-negative)
+ * I am also going to compute a large positive integer
+ * M = (1/2^nSignificantBits) * 2^nTinyBits * 10^max( 0, -decExp )
+ * i.e. M is (1/2) of the ULP of d, scaled like B.
+ * When we iterate through dividing B/S and picking off the
+ * quotient bits, we will know when to stop when the remainder
+ * is <= M.
+ *
+ * We keep track of powers of 2 and powers of 5.
+ */
+
+ /*
+ * Estimate decimal exponent. (If it is small-ish,
+ * we could double-check.)
+ *
+ * First, scale the mantissa bits such that 1 <= d2 < 2.
+ * We are then going to estimate
+ * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5)
+ * and so we can estimate
+ * log10(d) ~=~ log10(d2) + binExp * log10(2)
+ * take the floor and call it decExp.
+ * FIXME -- use more precise constants here. It costs no more.
+ */
+ double d2 = Double.longBitsToDouble(
+ expOne | ( fractBits &~ fractHOB ) );
+ decExp = (int)Math.floor(
+ (d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981 );
+ int B2, B5; // powers of 2 and powers of 5, respectively, in B
+ int S2, S5; // powers of 2 and powers of 5, respectively, in S
+ int M2, M5; // powers of 2 and powers of 5, respectively, in M
+ int Bbits; // binary digits needed to represent B, approx.
+ int tenSbits; // binary digits needed to represent 10*S, approx.
+ OldFDBigIntForTest Sval, Bval, Mval;
+
+ B5 = Math.max( 0, -decExp );
+ B2 = B5 + nTinyBits + binExp;
+
+ S5 = Math.max( 0, decExp );
+ S2 = S5 + nTinyBits;
+
+ M5 = B5;
+ M2 = B2 - nSignificantBits;
+
+ /*
+ * the long integer fractBits contains the (nFractBits) interesting
+ * bits from the mantissa of d ( hidden 1 added if necessary) followed
+ * by (expShift+1-nFractBits) zeros. In the interest of compactness,
+ * I will shift out those zeros before turning fractBits into a
+ * OldFDBigIntForTest. The resulting whole number will be
+ * d * 2^(nFractBits-1-binExp).
+ */
+ fractBits >>>= (expShift+1-nFractBits);
+ B2 -= nFractBits-1;
+ int common2factor = Math.min( B2, S2 );
+ B2 -= common2factor;
+ S2 -= common2factor;
+ M2 -= common2factor;
+
+ /*
+ * HACK!! For exact powers of two, the next smallest number
+ * is only half as far away as we think (because the meaning of
+ * ULP changes at power-of-two bounds) for this reason, we
+ * hack M2. Hope this works.
+ */
+ if ( nFractBits == 1 )
+ M2 -= 1;
+
+ if ( M2 < 0 ){
+ // oops.
+ // since we cannot scale M down far enough,
+ // we must scale the other values up.
+ B2 -= M2;
+ S2 -= M2;
+ M2 = 0;
+ }
+ /*
+ * Construct, Scale, iterate.
+ * Some day, we'll write a stopping test that takes
+ * account of the asymmetry of the spacing of floating-point
+ * numbers below perfect powers of 2
+ * 26 Sept 96 is not that day.
+ * So we use a symmetric test.
+ */
+ char digits[] = this.digits = new char[18];
+ int ndigit = 0;
+ boolean low, high;
+ long lowDigitDifference;
+ int q;
+
+ /*
+ * Detect the special cases where all the numbers we are about
+ * to compute will fit in int or long integers.
+ * In these cases, we will avoid doing OldFDBigIntForTest arithmetic.
+ * We use the same algorithms, except that we "normalize"
+ * our OldFDBigIntForTests before iterating. This is to make division easier,
+ * as it makes our fist guess (quotient of high-order words)
+ * more accurate!
+ *
+ * Some day, we'll write a stopping test that takes
+ * account of the asymmetry of the spacing of floating-point
+ * numbers below perfect powers of 2
+ * 26 Sept 96 is not that day.
+ * So we use a symmetric test.
+ */
+ Bbits = nFractBits + B2 + (( B5 < n5bits.length )? n5bits[B5] : ( B5*3 ));
+ tenSbits = S2+1 + (( (S5+1) < n5bits.length )? n5bits[(S5+1)] : ( (S5+1)*3 ));
+ if ( Bbits < 64 && tenSbits < 64){
+ if ( Bbits < 32 && tenSbits < 32){
+ // wa-hoo! They're all ints!
+ int b = ((int)fractBits * small5pow[B5] ) << B2;
+ int s = small5pow[S5] << S2;
+ int m = small5pow[M5] << M2;
+ int tens = s * 10;
+ /*
+ * Unroll the first iteration. If our decExp estimate
+ * was too high, our first quotient will be zero. In this
+ * case, we discard it and decrement decExp.
+ */
+ ndigit = 0;
+ q = b / s;
+ b = 10 * ( b % s );
+ m *= 10;
+ low = (b < m );
+ high = (b+m > tens );
+ assert q < 10 : q; // excessively large digit
+ if ( (q == 0) && ! high ){
+ // oops. Usually ignore leading zero.
+ decExp--;
+ } else {
+ digits[ndigit++] = (char)('0' + q);
+ }
+ /*
+ * HACK! Java spec sez that we always have at least
+ * one digit after the . in either F- or E-form output.
+ * Thus we will need more than one digit if we're using
+ * E-form
+ */
+ if ( decExp < -3 || decExp >= 8 ){
+ high = low = false;
+ }
+ while( ! low && ! high ){
+ q = b / s;
+ b = 10 * ( b % s );
+ m *= 10;
+ assert q < 10 : q; // excessively large digit
+ if ( m > 0L ){
+ low = (b < m );
+ high = (b+m > tens );
+ } else {
+ // hack -- m might overflow!
+ // in this case, it is certainly > b,
+ // which won't
+ // and b+m > tens, too, since that has overflowed
+ // either!
+ low = true;
+ high = true;
+ }
+ digits[ndigit++] = (char)('0' + q);
+ }
+ lowDigitDifference = (b<<1) - tens;
+ exactDecimalConversion = (b == 0);
+ } else {
+ // still good! they're all longs!
+ long b = (fractBits * long5pow[B5] ) << B2;
+ long s = long5pow[S5] << S2;
+ long m = long5pow[M5] << M2;
+ long tens = s * 10L;
+ /*
+ * Unroll the first iteration. If our decExp estimate
+ * was too high, our first quotient will be zero. In this
+ * case, we discard it and decrement decExp.
+ */
+ ndigit = 0;
+ q = (int) ( b / s );
+ b = 10L * ( b % s );
+ m *= 10L;
+ low = (b < m );
+ high = (b+m > tens );
+ assert q < 10 : q; // excessively large digit
+ if ( (q == 0) && ! high ){
+ // oops. Usually ignore leading zero.
+ decExp--;
+ } else {
+ digits[ndigit++] = (char)('0' + q);
+ }
+ /*
+ * HACK! Java spec sez that we always have at least
+ * one digit after the . in either F- or E-form output.
+ * Thus we will need more than one digit if we're using
+ * E-form
+ */
+ if ( decExp < -3 || decExp >= 8 ){
+ high = low = false;
+ }
+ while( ! low && ! high ){
+ q = (int) ( b / s );
+ b = 10 * ( b % s );
+ m *= 10;
+ assert q < 10 : q; // excessively large digit
+ if ( m > 0L ){
+ low = (b < m );
+ high = (b+m > tens );
+ } else {
+ // hack -- m might overflow!
+ // in this case, it is certainly > b,
+ // which won't
+ // and b+m > tens, too, since that has overflowed
+ // either!
+ low = true;
+ high = true;
+ }
+ digits[ndigit++] = (char)('0' + q);
+ }
+ lowDigitDifference = (b<<1) - tens;
+ exactDecimalConversion = (b == 0);
+ }
+ } else {
+ OldFDBigIntForTest ZeroVal = new OldFDBigIntForTest(0);
+ OldFDBigIntForTest tenSval;
+ int shiftBias;
+
+ /*
+ * We really must do OldFDBigIntForTest arithmetic.
+ * Fist, construct our OldFDBigIntForTest initial values.
+ */
+ Bval = multPow52( new OldFDBigIntForTest( fractBits ), B5, B2 );
+ Sval = constructPow52( S5, S2 );
+ Mval = constructPow52( M5, M2 );
+
+
+ // normalize so that division works better
+ Bval.lshiftMe( shiftBias = Sval.normalizeMe() );
+ Mval.lshiftMe( shiftBias );
+ tenSval = Sval.mult( 10 );
+ /*
+ * Unroll the first iteration. If our decExp estimate
+ * was too high, our first quotient will be zero. In this
+ * case, we discard it and decrement decExp.
+ */
+ ndigit = 0;
+ q = Bval.quoRemIteration( Sval );
+ Mval = Mval.mult( 10 );
+ low = (Bval.cmp( Mval ) < 0);
+ high = (Bval.add( Mval ).cmp( tenSval ) > 0 );
+ assert q < 10 : q; // excessively large digit
+ if ( (q == 0) && ! high ){
+ // oops. Usually ignore leading zero.
+ decExp--;
+ } else {
+ digits[ndigit++] = (char)('0' + q);
+ }
+ /*
+ * HACK! Java spec sez that we always have at least
+ * one digit after the . in either F- or E-form output.
+ * Thus we will need more than one digit if we're using
+ * E-form
+ */
+ if ( decExp < -3 || decExp >= 8 ){
+ high = low = false;
+ }
+ while( ! low && ! high ){
+ q = Bval.quoRemIteration( Sval );
+ Mval = Mval.mult( 10 );
+ assert q < 10 : q; // excessively large digit
+ low = (Bval.cmp( Mval ) < 0);
+ high = (Bval.add( Mval ).cmp( tenSval ) > 0 );
+ digits[ndigit++] = (char)('0' + q);
+ }
+ if ( high && low ){
+ Bval.lshiftMe(1);
+ lowDigitDifference = Bval.cmp(tenSval);
+ } else {
+ lowDigitDifference = 0L; // this here only for flow analysis!
+ }
+ exactDecimalConversion = (Bval.cmp( ZeroVal ) == 0);
+ }
+ this.decExponent = decExp+1;
+ this.digits = digits;
+ this.nDigits = ndigit;
+ /*
+ * Last digit gets rounded based on stopping condition.
+ */
+ if ( high ){
+ if ( low ){
+ if ( lowDigitDifference == 0L ){
+ // it's a tie!
+ // choose based on which digits we like.
+ if ( (digits[nDigits-1]&1) != 0 ) roundup();
+ } else if ( lowDigitDifference > 0 ){
+ roundup();
+ }
+ } else {
+ roundup();
+ }
+ }
+ }
+
+ public boolean decimalDigitsExact() {
+ return exactDecimalConversion;
+ }
+
+ public String
+ toString(){
+ // most brain-dead version
+ StringBuffer result = new StringBuffer( nDigits+8 );
+ if ( isNegative ){ result.append( '-' ); }
+ if ( isExceptional ){
+ result.append( digits, 0, nDigits );
+ } else {
+ result.append( "0.");
+ result.append( digits, 0, nDigits );
+ result.append('e');
+ result.append( decExponent );
+ }
+ return new String(result);
+ }
+
+ public String toJavaFormatString() {
+ char result[] = perThreadBuffer.get();
+ int i = getChars(result);
+ return new String(result, 0, i);
+ }
+
+ private int getChars(char[] result) {
+ assert nDigits <= 19 : nDigits; // generous bound on size of nDigits
+ int i = 0;
+ if (isNegative) { result[0] = '-'; i = 1; }
+ if (isExceptional) {
+ System.arraycopy(digits, 0, result, i, nDigits);
+ i += nDigits;
+ } else {
+ if (decExponent > 0 && decExponent < 8) {
+ // print digits.digits.
+ int charLength = Math.min(nDigits, decExponent);
+ System.arraycopy(digits, 0, result, i, charLength);
+ i += charLength;
+ if (charLength < decExponent) {
+ charLength = decExponent-charLength;
+ System.arraycopy(zero, 0, result, i, charLength);
+ i += charLength;
+ result[i++] = '.';
+ result[i++] = '0';
+ } else {
+ result[i++] = '.';
+ if (charLength < nDigits) {
+ int t = nDigits - charLength;
+ System.arraycopy(digits, charLength, result, i, t);
+ i += t;
+ } else {
+ result[i++] = '0';
+ }
+ }
+ } else if (decExponent <=0 && decExponent > -3) {
+ result[i++] = '0';
+ result[i++] = '.';
+ if (decExponent != 0) {
+ System.arraycopy(zero, 0, result, i, -decExponent);
+ i -= decExponent;
+ }
+ System.arraycopy(digits, 0, result, i, nDigits);
+ i += nDigits;
+ } else {
+ result[i++] = digits[0];
+ result[i++] = '.';
+ if (nDigits > 1) {
+ System.arraycopy(digits, 1, result, i, nDigits-1);
+ i += nDigits-1;
+ } else {
+ result[i++] = '0';
+ }
+ result[i++] = 'E';
+ int e;
+ if (decExponent <= 0) {
+ result[i++] = '-';
+ e = -decExponent+1;
+ } else {
+ e = decExponent-1;
+ }
+ // decExponent has 1, 2, or 3, digits
+ if (e <= 9) {
+ result[i++] = (char)(e+'0');
+ } else if (e <= 99) {
+ result[i++] = (char)(e/10 +'0');
+ result[i++] = (char)(e%10 + '0');
+ } else {
+ result[i++] = (char)(e/100+'0');
+ e %= 100;
+ result[i++] = (char)(e/10+'0');
+ result[i++] = (char)(e%10 + '0');
+ }
+ }
+ }
+ return i;
+ }
+
+ // Per-thread buffer for string/stringbuffer conversion
+ private static ThreadLocal<char[]> perThreadBuffer = new ThreadLocal<char[]>() {
+ protected synchronized char[] initialValue() {
+ return new char[26];
+ }
+ };
+
+ public void appendTo(Appendable buf) {
+ char result[] = perThreadBuffer.get();
+ int i = getChars(result);
+ if (buf instanceof StringBuilder)
+ ((StringBuilder) buf).append(result, 0, i);
+ else if (buf instanceof StringBuffer)
+ ((StringBuffer) buf).append(result, 0, i);
+ else
+ assert false;
+ }
+
+ @SuppressWarnings("fallthrough")
+ public static OldFloatingDecimalForTest
+ readJavaFormatString( String in ) throws NumberFormatException {
+ boolean isNegative = false;
+ boolean signSeen = false;
+ int decExp;
+ char c;
+
+ parseNumber:
+ try{
+ in = in.trim(); // don't fool around with white space.
+ // throws NullPointerException if null
+ int l = in.length();
+ if ( l == 0 ) throw new NumberFormatException("empty String");
+ int i = 0;
+ switch ( c = in.charAt( i ) ){
+ case '-':
+ isNegative = true;
+ //FALLTHROUGH
+ case '+':
+ i++;
+ signSeen = true;
+ }
+
+ // Check for NaN and Infinity strings
+ c = in.charAt(i);
+ if(c == 'N' || c == 'I') { // possible NaN or infinity
+ boolean potentialNaN = false;
+ char targetChars[] = null; // char array of "NaN" or "Infinity"
+
+ if(c == 'N') {
+ targetChars = notANumber;
+ potentialNaN = true;
+ } else {
+ targetChars = infinity;
+ }
+
+ // compare Input string to "NaN" or "Infinity"
+ int j = 0;
+ while(i < l && j < targetChars.length) {
+ if(in.charAt(i) == targetChars[j]) {
+ i++; j++;
+ }
+ else // something is amiss, throw exception
+ break parseNumber;
+ }
+
+ // For the candidate string to be a NaN or infinity,
+ // all characters in input string and target char[]
+ // must be matched ==> j must equal targetChars.length
+ // and i must equal l
+ if( (j == targetChars.length) && (i == l) ) { // return NaN or infinity
+ return (potentialNaN ? new OldFloatingDecimalForTest(Double.NaN) // NaN has no sign
+ : new OldFloatingDecimalForTest(isNegative?
+ Double.NEGATIVE_INFINITY:
+ Double.POSITIVE_INFINITY)) ;
+ }
+ else { // something went wrong, throw exception
+ break parseNumber;
+ }
+
+ } else if (c == '0') { // check for hexadecimal floating-point number
+ if (l > i+1 ) {
+ char ch = in.charAt(i+1);
+ if (ch == 'x' || ch == 'X' ) // possible hex string
+ return parseHexString(in);
+ }
+ } // look for and process decimal floating-point string
+
+ char[] digits = new char[ l ];
+ int nDigits= 0;
+ boolean decSeen = false;
+ int decPt = 0;
+ int nLeadZero = 0;
+ int nTrailZero= 0;
+ digitLoop:
+ while ( i < l ){
+ switch ( c = in.charAt( i ) ){
+ case '0':
+ if ( nDigits > 0 ){
+ nTrailZero += 1;
+ } else {
+ nLeadZero += 1;
+ }
+ break; // out of switch.
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ while ( nTrailZero > 0 ){
+ digits[nDigits++] = '0';
+ nTrailZero -= 1;
+ }
+ digits[nDigits++] = c;
+ break; // out of switch.
+ case '.':
+ if ( decSeen ){
+ // already saw one ., this is the 2nd.
+ throw new NumberFormatException("multiple points");
+ }
+ decPt = i;
+ if ( signSeen ){
+ decPt -= 1;
+ }
+ decSeen = true;
+ break; // out of switch.
+ default:
+ break digitLoop;
+ }
+ i++;
+ }
+ /*
+ * At this point, we've scanned all the digits and decimal
+ * point we're going to see. Trim off leading and trailing
+ * zeros, which will just confuse us later, and adjust
+ * our initial decimal exponent accordingly.
+ * To review:
+ * we have seen i total characters.
+ * nLeadZero of them were zeros before any other digits.
+ * nTrailZero of them were zeros after any other digits.
+ * if ( decSeen ), then a . was seen after decPt characters
+ * ( including leading zeros which have been discarded )
+ * nDigits characters were neither lead nor trailing
+ * zeros, nor point
+ */
+ /*
+ * special hack: if we saw no non-zero digits, then the
+ * answer is zero!
+ * Unfortunately, we feel honor-bound to keep parsing!
+ */
+ if ( nDigits == 0 ){
+ digits = zero;
+ nDigits = 1;
+ if ( nLeadZero == 0 ){
+ // we saw NO DIGITS AT ALL,
+ // not even a crummy 0!
+ // this is not allowed.
+ break parseNumber; // go throw exception
+ }
+
+ }
+
+ /* Our initial exponent is decPt, adjusted by the number of
+ * discarded zeros. Or, if there was no decPt,
+ * then its just nDigits adjusted by discarded trailing zeros.
+ */
+ if ( decSeen ){
+ decExp = decPt - nLeadZero;
+ } else {
+ decExp = nDigits+nTrailZero;
+ }
+
+ /*
+ * Look for 'e' or 'E' and an optionally signed integer.
+ */
+ if ( (i < l) && (((c = in.charAt(i) )=='e') || (c == 'E') ) ){
+ int expSign = 1;
+ int expVal = 0;
+ int reallyBig = Integer.MAX_VALUE / 10;
+ boolean expOverflow = false;
+ switch( in.charAt(++i) ){
+ case '-':
+ expSign = -1;
+ //FALLTHROUGH
+ case '+':
+ i++;
+ }
+ int expAt = i;
+ expLoop:
+ while ( i < l ){
+ if ( expVal >= reallyBig ){
+ // the next character will cause integer
+ // overflow.
+ expOverflow = true;
+ }
+ switch ( c = in.charAt(i++) ){
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ expVal = expVal*10 + ( (int)c - (int)'0' );
+ continue;
+ default:
+ i--; // back up.
+ break expLoop; // stop parsing exponent.
+ }
+ }
+ int expLimit = bigDecimalExponent+nDigits+nTrailZero;
+ if ( expOverflow || ( expVal > expLimit ) ){
+ //
+ // The intent here is to end up with
+ // infinity or zero, as appropriate.
+ // The reason for yielding such a small decExponent,
+ // rather than something intuitive such as
+ // expSign*Integer.MAX_VALUE, is that this value
+ // is subject to further manipulation in
+ // doubleValue() and floatValue(), and I don't want
+ // it to be able to cause overflow there!
+ // (The only way we can get into trouble here is for
+ // really outrageous nDigits+nTrailZero, such as 2 billion. )
+ //
+ decExp = expSign*expLimit;
+ } else {
+ // this should not overflow, since we tested
+ // for expVal > (MAX+N), where N >= abs(decExp)
+ decExp = decExp + expSign*expVal;
+ }
+
+ // if we saw something not a digit ( or end of string )
+ // after the [Ee][+-], without seeing any digits at all
+ // this is certainly an error. If we saw some digits,
+ // but then some trailing garbage, that might be ok.
+ // so we just fall through in that case.
+ // HUMBUG
+ if ( i == expAt )
+ break parseNumber; // certainly bad
+ }
+ /*
+ * We parsed everything we could.
+ * If there are leftovers, then this is not good input!
+ */
+ if ( i < l &&
+ ((i != l - 1) ||
+ (in.charAt(i) != 'f' &&
+ in.charAt(i) != 'F' &&
+ in.charAt(i) != 'd' &&
+ in.charAt(i) != 'D'))) {
+ break parseNumber; // go throw exception
+ }
+
+ return new OldFloatingDecimalForTest( isNegative, decExp, digits, nDigits, false );
+ } catch ( StringIndexOutOfBoundsException e ){ }
+ throw new NumberFormatException("For input string: \"" + in + "\"");
+ }
+
+ /*
+ * Take a FloatingDecimal, which we presumably just scanned in,
+ * and find out what its value is, as a double.
+ *
+ * AS A SIDE EFFECT, SET roundDir TO INDICATE PREFERRED
+ * ROUNDING DIRECTION in case the result is really destined
+ * for a single-precision float.
+ */
+
+ public strictfp double doubleValue(){
+ int kDigits = Math.min( nDigits, maxDecimalDigits+1 );
+ long lValue;
+ double dValue;
+ double rValue, tValue;
+
+ // First, check for NaN and Infinity values
+ if(digits == infinity || digits == notANumber) {
+ if(digits == notANumber)
+ return Double.NaN;
+ else
+ return (isNegative?Double.NEGATIVE_INFINITY:Double.POSITIVE_INFINITY);
+ }
+ else {
+ if (mustSetRoundDir) {
+ roundDir = 0;
+ }
+ /*
+ * convert the lead kDigits to a long integer.
+ */
+ // (special performance hack: start to do it using int)
+ int iValue = (int)digits[0]-(int)'0';
+ int iDigits = Math.min( kDigits, intDecimalDigits );
+ for ( int i=1; i < iDigits; i++ ){
+ iValue = iValue*10 + (int)digits[i]-(int)'0';
+ }
+ lValue = (long)iValue;
+ for ( int i=iDigits; i < kDigits; i++ ){
+ lValue = lValue*10L + (long)((int)digits[i]-(int)'0');
+ }
+ dValue = (double)lValue;
+ int exp = decExponent-kDigits;
+ /*
+ * lValue now contains a long integer with the value of
+ * the first kDigits digits of the number.
+ * dValue contains the (double) of the same.
+ */
+
+ if ( nDigits <= maxDecimalDigits ){
+ /*
+ * possibly an easy case.
+ * We know that the digits can be represented
+ * exactly. And if the exponent isn't too outrageous,
+ * the whole thing can be done with one operation,
+ * thus one rounding error.
+ * Note that all our constructors trim all leading and
+ * trailing zeros, so simple values (including zero)
+ * will always end up here
+ */
+ if (exp == 0 || dValue == 0.0)
+ return (isNegative)? -dValue : dValue; // small floating integer
+ else if ( exp >= 0 ){
+ if ( exp <= maxSmallTen ){
+ /*
+ * Can get the answer with one operation,
+ * thus one roundoff.
+ */
+ rValue = dValue * small10pow[exp];
+ if ( mustSetRoundDir ){
+ tValue = rValue / small10pow[exp];
+ roundDir = ( tValue == dValue ) ? 0
+ :( tValue < dValue ) ? 1
+ : -1;
+ }
+ return (isNegative)? -rValue : rValue;
+ }
+ int slop = maxDecimalDigits - kDigits;
+ if ( exp <= maxSmallTen+slop ){
+ /*
+ * We can multiply dValue by 10^(slop)
+ * and it is still "small" and exact.
+ * Then we can multiply by 10^(exp-slop)
+ * with one rounding.
+ */
+ dValue *= small10pow[slop];
+ rValue = dValue * small10pow[exp-slop];
+
+ if ( mustSetRoundDir ){
+ tValue = rValue / small10pow[exp-slop];
+ roundDir = ( tValue == dValue ) ? 0
+ :( tValue < dValue ) ? 1
+ : -1;
+ }
+ return (isNegative)? -rValue : rValue;
+ }
+ /*
+ * Else we have a hard case with a positive exp.
+ */
+ } else {
+ if ( exp >= -maxSmallTen ){
+ /*
+ * Can get the answer in one division.
+ */
+ rValue = dValue / small10pow[-exp];
+ tValue = rValue * small10pow[-exp];
+ if ( mustSetRoundDir ){
+ roundDir = ( tValue == dValue ) ? 0
+ :( tValue < dValue ) ? 1
+ : -1;
+ }
+ return (isNegative)? -rValue : rValue;
+ }
+ /*
+ * Else we have a hard case with a negative exp.
+ */
+ }
+ }
+
+ /*
+ * Harder cases:
+ * The sum of digits plus exponent is greater than
+ * what we think we can do with one error.
+ *
+ * Start by approximating the right answer by,
+ * naively, scaling by powers of 10.
+ */
+ if ( exp > 0 ){
+ if ( decExponent > maxDecimalExponent+1 ){
+ /*
+ * Lets face it. This is going to be
+ * Infinity. Cut to the chase.
+ */
+ return (isNegative)? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+ }
+ if ( (exp&15) != 0 ){
+ dValue *= small10pow[exp&15];
+ }
+ if ( (exp>>=4) != 0 ){
+ int j;
+ for( j = 0; exp > 1; j++, exp>>=1 ){
+ if ( (exp&1)!=0)
+ dValue *= big10pow[j];
+ }
+ /*
+ * The reason for the weird exp > 1 condition
+ * in the above loop was so that the last multiply
+ * would get unrolled. We handle it here.
+ * It could overflow.
+ */
+ double t = dValue * big10pow[j];
+ if ( Double.isInfinite( t ) ){
+ /*
+ * It did overflow.
+ * Look more closely at the result.
+ * If the exponent is just one too large,
+ * then use the maximum finite as our estimate
+ * value. Else call the result infinity
+ * and punt it.
+ * ( I presume this could happen because
+ * rounding forces the result here to be
+ * an ULP or two larger than
+ * Double.MAX_VALUE ).
+ */
+ t = dValue / 2.0;
+ t *= big10pow[j];
+ if ( Double.isInfinite( t ) ){
+ return (isNegative)? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
+ }
+ t = Double.MAX_VALUE;
+ }
+ dValue = t;
+ }
+ } else if ( exp < 0 ){
+ exp = -exp;
+ if ( decExponent < minDecimalExponent-1 ){
+ /*
+ * Lets face it. This is going to be
+ * zero. Cut to the chase.
+ */
+ return (isNegative)? -0.0 : 0.0;
+ }
+ if ( (exp&15) != 0 ){
+ dValue /= small10pow[exp&15];
+ }
+ if ( (exp>>=4) != 0 ){
+ int j;
+ for( j = 0; exp > 1; j++, exp>>=1 ){
+ if ( (exp&1)!=0)
+ dValue *= tiny10pow[j];
+ }
+ /*
+ * The reason for the weird exp > 1 condition
+ * in the above loop was so that the last multiply
+ * would get unrolled. We handle it here.
+ * It could underflow.
+ */
+ double t = dValue * tiny10pow[j];
+ if ( t == 0.0 ){
+ /*
+ * It did underflow.
+ * Look more closely at the result.
+ * If the exponent is just one too small,
+ * then use the minimum finite as our estimate
+ * value. Else call the result 0.0
+ * and punt it.
+ * ( I presume this could happen because
+ * rounding forces the result here to be
+ * an ULP or two less than
+ * Double.MIN_VALUE ).
+ */
+ t = dValue * 2.0;
+ t *= tiny10pow[j];
+ if ( t == 0.0 ){
+ return (isNegative)? -0.0 : 0.0;
+ }
+ t = Double.MIN_VALUE;
+ }
+ dValue = t;
+ }
+ }
+
+ /*
+ * dValue is now approximately the result.
+ * The hard part is adjusting it, by comparison
+ * with OldFDBigIntForTest arithmetic.
+ * Formulate the EXACT big-number result as
+ * bigD0 * 10^exp
+ */
+ OldFDBigIntForTest bigD0 = new OldFDBigIntForTest( lValue, digits, kDigits, nDigits );
+ exp = decExponent - nDigits;
+
+ correctionLoop:
+ while(true){
+ /* AS A SIDE EFFECT, THIS METHOD WILL SET THE INSTANCE VARIABLES
+ * bigIntExp and bigIntNBits
+ */
+ OldFDBigIntForTest bigB = doubleToBigInt( dValue );
+
+ /*
+ * Scale bigD, bigB appropriately for
+ * big-integer operations.
+ * Naively, we multiply by powers of ten
+ * and powers of two. What we actually do
+ * is keep track of the powers of 5 and
+ * powers of 2 we would use, then factor out
+ * common divisors before doing the work.
+ */
+ int B2, B5; // powers of 2, 5 in bigB
+ int D2, D5; // powers of 2, 5 in bigD
+ int Ulp2; // powers of 2 in halfUlp.
+ if ( exp >= 0 ){
+ B2 = B5 = 0;
+ D2 = D5 = exp;
+ } else {
+ B2 = B5 = -exp;
+ D2 = D5 = 0;
+ }
+ if ( bigIntExp >= 0 ){
+ B2 += bigIntExp;
+ } else {
+ D2 -= bigIntExp;
+ }
+ Ulp2 = B2;
+ // shift bigB and bigD left by a number s. t.
+ // halfUlp is still an integer.
+ int hulpbias;
+ if ( bigIntExp+bigIntNBits <= -expBias+1 ){
+ // This is going to be a denormalized number
+ // (if not actually zero).
+ // half an ULP is at 2^-(expBias+expShift+1)
+ hulpbias = bigIntExp+ expBias + expShift;
+ } else {
+ hulpbias = expShift + 2 - bigIntNBits;
+ }
+ B2 += hulpbias;
+ D2 += hulpbias;
+ // if there are common factors of 2, we might just as well
+ // factor them out, as they add nothing useful.
+ int common2 = Math.min( B2, Math.min( D2, Ulp2 ) );
+ B2 -= common2;
+ D2 -= common2;
+ Ulp2 -= common2;
+ // do multiplications by powers of 5 and 2
+ bigB = multPow52( bigB, B5, B2 );
+ OldFDBigIntForTest bigD = multPow52( new OldFDBigIntForTest( bigD0 ), D5, D2 );
+ //
+ // to recap:
+ // bigB is the scaled-big-int version of our floating-point
+ // candidate.
+ // bigD is the scaled-big-int version of the exact value
+ // as we understand it.
+ // halfUlp is 1/2 an ulp of bigB, except for special cases
+ // of exact powers of 2
+ //
+ // the plan is to compare bigB with bigD, and if the difference
+ // is less than halfUlp, then we're satisfied. Otherwise,
+ // use the ratio of difference to halfUlp to calculate a fudge
+ // factor to add to the floating value, then go 'round again.
+ //
+ OldFDBigIntForTest diff;
+ int cmpResult;
+ boolean overvalue;
+ if ( (cmpResult = bigB.cmp( bigD ) ) > 0 ){
+ overvalue = true; // our candidate is too big.
+ diff = bigB.sub( bigD );
+ if ( (bigIntNBits == 1) && (bigIntExp > -expBias+1) ){
+ // candidate is a normalized exact power of 2 and
+ // is too big. We will be subtracting.
+ // For our purposes, ulp is the ulp of the
+ // next smaller range.
+ Ulp2 -= 1;
+ if ( Ulp2 < 0 ){
+ // rats. Cannot de-scale ulp this far.
+ // must scale diff in other direction.
+ Ulp2 = 0;
+ diff.lshiftMe( 1 );
+ }
+ }
+ } else if ( cmpResult < 0 ){
+ overvalue = false; // our candidate is too small.
+ diff = bigD.sub( bigB );
+ } else {
+ // the candidate is exactly right!
+ // this happens with surprising frequency
+ break correctionLoop;
+ }
+ OldFDBigIntForTest halfUlp = constructPow52( B5, Ulp2 );
+ if ( (cmpResult = diff.cmp( halfUlp ) ) < 0 ){
+ // difference is small.
+ // this is close enough
+ if (mustSetRoundDir) {
+ roundDir = overvalue ? -1 : 1;
+ }
+ break correctionLoop;
+ } else if ( cmpResult == 0 ){
+ // difference is exactly half an ULP
+ // round to some other value maybe, then finish
+ dValue += 0.5*ulp( dValue, overvalue );
+ // should check for bigIntNBits == 1 here??
+ if (mustSetRoundDir) {
+ roundDir = overvalue ? -1 : 1;
+ }
+ break correctionLoop;
+ } else {
+ // difference is non-trivial.
+ // could scale addend by ratio of difference to
+ // halfUlp here, if we bothered to compute that difference.
+ // Most of the time ( I hope ) it is about 1 anyway.
+ dValue += ulp( dValue, overvalue );
+ if ( dValue == 0.0 || dValue == Double.POSITIVE_INFINITY )
+ break correctionLoop; // oops. Fell off end of range.
+ continue; // try again.
+ }
+
+ }
+ return (isNegative)? -dValue : dValue;
+ }
+ }
+
+ /*
+ * Take a FloatingDecimal, which we presumably just scanned in,
+ * and find out what its value is, as a float.
+ * This is distinct from doubleValue() to avoid the extremely
+ * unlikely case of a double rounding error, wherein the conversion
+ * to double has one rounding error, and the conversion of that double
+ * to a float has another rounding error, IN THE WRONG DIRECTION,
+ * ( because of the preference to a zero low-order bit ).
+ */
+
+ public strictfp float floatValue(){
+ int kDigits = Math.min( nDigits, singleMaxDecimalDigits+1 );
+ int iValue;
+ float fValue;
+
+ // First, check for NaN and Infinity values
+ if(digits == infinity || digits == notANumber) {
+ if(digits == notANumber)
+ return Float.NaN;
+ else
+ return (isNegative?Float.NEGATIVE_INFINITY:Float.POSITIVE_INFINITY);
+ }
+ else {
+ /*
+ * convert the lead kDigits to an integer.
+ */
+ iValue = (int)digits[0]-(int)'0';
+ for ( int i=1; i < kDigits; i++ ){
+ iValue = iValue*10 + (int)digits[i]-(int)'0';
+ }
+ fValue = (float)iValue;
+ int exp = decExponent-kDigits;
+ /*
+ * iValue now contains an integer with the value of
+ * the first kDigits digits of the number.
+ * fValue contains the (float) of the same.
+ */
+
+ if ( nDigits <= singleMaxDecimalDigits ){
+ /*
+ * possibly an easy case.
+ * We know that the digits can be represented
+ * exactly. And if the exponent isn't too outrageous,
+ * the whole thing can be done with one operation,
+ * thus one rounding error.
+ * Note that all our constructors trim all leading and
+ * trailing zeros, so simple values (including zero)
+ * will always end up here.
+ */
+ if (exp == 0 || fValue == 0.0f)
+ return (isNegative)? -fValue : fValue; // small floating integer
+ else if ( exp >= 0 ){
+ if ( exp <= singleMaxSmallTen ){
+ /*
+ * Can get the answer with one operation,
+ * thus one roundoff.
+ */
+ fValue *= singleSmall10pow[exp];
+ return (isNegative)? -fValue : fValue;
+ }
+ int slop = singleMaxDecimalDigits - kDigits;
+ if ( exp <= singleMaxSmallTen+slop ){
+ /*
+ * We can multiply dValue by 10^(slop)
+ * and it is still "small" and exact.
+ * Then we can multiply by 10^(exp-slop)
+ * with one rounding.
+ */
+ fValue *= singleSmall10pow[slop];
+ fValue *= singleSmall10pow[exp-slop];
+ return (isNegative)? -fValue : fValue;
+ }
+ /*
+ * Else we have a hard case with a positive exp.
+ */
+ } else {
+ if ( exp >= -singleMaxSmallTen ){
+ /*
+ * Can get the answer in one division.
+ */
+ fValue /= singleSmall10pow[-exp];
+ return (isNegative)? -fValue : fValue;
+ }
+ /*
+ * Else we have a hard case with a negative exp.
+ */
+ }
+ } else if ( (decExponent >= nDigits) && (nDigits+decExponent <= maxDecimalDigits) ){
+ /*
+ * In double-precision, this is an exact floating integer.
+ * So we can compute to double, then shorten to float
+ * with one round, and get the right answer.
+ *
+ * First, finish accumulating digits.
+ * Then convert that integer to a double, multiply
+ * by the appropriate power of ten, and convert to float.
+ */
+ long lValue = (long)iValue;
+ for ( int i=kDigits; i < nDigits; i++ ){
+ lValue = lValue*10L + (long)((int)digits[i]-(int)'0');
+ }
+ double dValue = (double)lValue;
+ exp = decExponent-nDigits;
+ dValue *= small10pow[exp];
+ fValue = (float)dValue;
+ return (isNegative)? -fValue : fValue;
+
+ }
+ /*
+ * Harder cases:
+ * The sum of digits plus exponent is greater than
+ * what we think we can do with one error.
+ *
+ * Start by weeding out obviously out-of-range
+ * results, then convert to double and go to
+ * common hard-case code.
+ */
+ if ( decExponent > singleMaxDecimalExponent+1 ){
+ /*
+ * Lets face it. This is going to be
+ * Infinity. Cut to the chase.
+ */
+ return (isNegative)? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
+ } else if ( decExponent < singleMinDecimalExponent-1 ){
+ /*
+ * Lets face it. This is going to be
+ * zero. Cut to the chase.
+ */
+ return (isNegative)? -0.0f : 0.0f;
+ }
+
+ /*
+ * Here, we do 'way too much work, but throwing away
+ * our partial results, and going and doing the whole
+ * thing as double, then throwing away half the bits that computes
+ * when we convert back to float.
+ *
+ * The alternative is to reproduce the whole multiple-precision
+ * algorithm for float precision, or to try to parameterize it
+ * for common usage. The former will take about 400 lines of code,
+ * and the latter I tried without success. Thus the semi-hack
+ * answer here.
+ */
+ mustSetRoundDir = !fromHex;
+ double dValue = doubleValue();
+ return stickyRound( dValue );
+ }
+ }
+
+
+ /*
+ * All the positive powers of 10 that can be
+ * represented exactly in double/float.
+ */
+ private static final double small10pow[] = {
+ 1.0e0,
+ 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5,
+ 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10,
+ 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15,
+ 1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20,
+ 1.0e21, 1.0e22
+ };
+
+ private static final float singleSmall10pow[] = {
+ 1.0e0f,
+ 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
+ 1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
+ };
+
+ private static final double big10pow[] = {
+ 1e16, 1e32, 1e64, 1e128, 1e256 };
+ private static final double tiny10pow[] = {
+ 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 };
+
+ private static final int maxSmallTen = small10pow.length-1;
+ private static final int singleMaxSmallTen = singleSmall10pow.length-1;
+
+ private static final int small5pow[] = {
+ 1,
+ 5,
+ 5*5,
+ 5*5*5,
+ 5*5*5*5,
+ 5*5*5*5*5,
+ 5*5*5*5*5*5,
+ 5*5*5*5*5*5*5,
+ 5*5*5*5*5*5*5*5,
+ 5*5*5*5*5*5*5*5*5,
+ 5*5*5*5*5*5*5*5*5*5,
+ 5*5*5*5*5*5*5*5*5*5*5,
+ 5*5*5*5*5*5*5*5*5*5*5*5,
+ 5*5*5*5*5*5*5*5*5*5*5*5*5
+ };
+
+
+ private static final long long5pow[] = {
+ 1L,
+ 5L,
+ 5L*5,
+ 5L*5*5,
+ 5L*5*5*5,
+ 5L*5*5*5*5,
+ 5L*5*5*5*5*5,
+ 5L*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
+ };
+
+ // approximately ceil( log2( long5pow[i] ) )
+ private static final int n5bits[] = {
+ 0,
+ 3,
+ 5,
+ 7,
+ 10,
+ 12,
+ 14,
+ 17,
+ 19,
+ 21,
+ 24,
+ 26,
+ 28,
+ 31,
+ 33,
+ 35,
+ 38,
+ 40,
+ 42,
+ 45,
+ 47,
+ 49,
+ 52,
+ 54,
+ 56,
+ 59,
+ 61,
+ };
+
+ private static final char infinity[] = { 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y' };
+ private static final char notANumber[] = { 'N', 'a', 'N' };
+ private static final char zero[] = { '0', '0', '0', '0', '0', '0', '0', '0' };
+
+
+ /*
+ * Grammar is compatible with hexadecimal floating-point constants
+ * described in section 6.4.4.2 of the C99 specification.
+ */
+ private static Pattern hexFloatPattern = null;
+ private static synchronized Pattern getHexFloatPattern() {
+ if (hexFloatPattern == null) {
+ hexFloatPattern = Pattern.compile(
+ //1 234 56 7 8 9
+ "([-+])?0[xX](((\\p{XDigit}+)\\.?)|((\\p{XDigit}*)\\.(\\p{XDigit}+)))[pP]([-+])?(\\p{Digit}+)[fFdD]?"
+ );
+ }
+ return hexFloatPattern;
+ }
+
+ /*
+ * Convert string s to a suitable floating decimal; uses the
+ * double constructor and set the roundDir variable appropriately
+ * in case the value is later converted to a float.
+ */
+ static OldFloatingDecimalForTest parseHexString(String s) {
+ // Verify string is a member of the hexadecimal floating-point
+ // string language.
+ Matcher m = getHexFloatPattern().matcher(s);
+ boolean validInput = m.matches();
+
+ if (!validInput) {
+ // Input does not match pattern
+ throw new NumberFormatException("For input string: \"" + s + "\"");
+ } else { // validInput
+ /*
+ * We must isolate the sign, significand, and exponent
+ * fields. The sign value is straightforward. Since
+ * floating-point numbers are stored with a normalized
+ * representation, the significand and exponent are
+ * interrelated.
+ *
+ * After extracting the sign, we normalized the
+ * significand as a hexadecimal value, calculating an
+ * exponent adjust for any shifts made during
+ * normalization. If the significand is zero, the
+ * exponent doesn't need to be examined since the output
+ * will be zero.
+ *
+ * Next the exponent in the input string is extracted.
+ * Afterwards, the significand is normalized as a *binary*
+ * value and the input value's normalized exponent can be
+ * computed. The significand bits are copied into a
+ * double significand; if the string has more logical bits
+ * than can fit in a double, the extra bits affect the
+ * round and sticky bits which are used to round the final
+ * value.
+ */
+
+ // Extract significand sign
+ String group1 = m.group(1);
+ double sign = (( group1 == null ) || group1.equals("+"))? 1.0 : -1.0;
+
+
+ // Extract Significand magnitude
+ /*
+ * Based on the form of the significand, calculate how the
+ * binary exponent needs to be adjusted to create a
+ * normalized *hexadecimal* floating-point number; that
+ * is, a number where there is one nonzero hex digit to
+ * the left of the (hexa)decimal point. Since we are
+ * adjusting a binary, not hexadecimal exponent, the
+ * exponent is adjusted by a multiple of 4.
+ *
+ * There are a number of significand scenarios to consider;
+ * letters are used in indicate nonzero digits:
+ *
+ * 1. 000xxxx => x.xxx normalized
+ * increase exponent by (number of x's - 1)*4
+ *
+ * 2. 000xxx.yyyy => x.xxyyyy normalized
+ * increase exponent by (number of x's - 1)*4
+ *
+ * 3. .000yyy => y.yy normalized
+ * decrease exponent by (number of zeros + 1)*4
+ *
+ * 4. 000.00000yyy => y.yy normalized
+ * decrease exponent by (number of zeros to right of point + 1)*4
+ *
+ * If the significand is exactly zero, return a properly
+ * signed zero.
+ */
+
+ String significandString =null;
+ int signifLength = 0;
+ int exponentAdjust = 0;
+ {
+ int leftDigits = 0; // number of meaningful digits to
+ // left of "decimal" point
+ // (leading zeros stripped)
+ int rightDigits = 0; // number of digits to right of
+ // "decimal" point; leading zeros
+ // must always be accounted for
+ /*
+ * The significand is made up of either
+ *
+ * 1. group 4 entirely (integer portion only)
+ *
+ * OR
+ *
+ * 2. the fractional portion from group 7 plus any
+ * (optional) integer portions from group 6.
+ */
+ String group4;
+ if( (group4 = m.group(4)) != null) { // Integer-only significand
+ // Leading zeros never matter on the integer portion
+ significandString = stripLeadingZeros(group4);
+ leftDigits = significandString.length();
+ }
+ else {
+ // Group 6 is the optional integer; leading zeros
+ // never matter on the integer portion
+ String group6 = stripLeadingZeros(m.group(6));
+ leftDigits = group6.length();
+
+ // fraction
+ String group7 = m.group(7);
+ rightDigits = group7.length();
+
+ // Turn "integer.fraction" into "integer"+"fraction"
+ significandString =
+ ((group6 == null)?"":group6) + // is the null
+ // check necessary?
+ group7;
+ }
+
+ significandString = stripLeadingZeros(significandString);
+ signifLength = significandString.length();
+
+ /*
+ * Adjust exponent as described above
+ */
+ if (leftDigits >= 1) { // Cases 1 and 2
+ exponentAdjust = 4*(leftDigits - 1);
+ } else { // Cases 3 and 4
+ exponentAdjust = -4*( rightDigits - signifLength + 1);
+ }
+
+ // If the significand is zero, the exponent doesn't
+ // matter; return a properly signed zero.
+
+ if (signifLength == 0) { // Only zeros in input
+ return new OldFloatingDecimalForTest(sign * 0.0);
+ }
+ }
+
+ // Extract Exponent
+ /*
+ * Use an int to read in the exponent value; this should
+ * provide more than sufficient range for non-contrived
+ * inputs. If reading the exponent in as an int does
+ * overflow, examine the sign of the exponent and
+ * significand to determine what to do.
+ */
+ String group8 = m.group(8);
+ boolean positiveExponent = ( group8 == null ) || group8.equals("+");
+ long unsignedRawExponent;
+ try {
+ unsignedRawExponent = Integer.parseInt(m.group(9));
+ }
+ catch (NumberFormatException e) {
+ // At this point, we know the exponent is
+ // syntactically well-formed as a sequence of
+ // digits. Therefore, if an NumberFormatException
+ // is thrown, it must be due to overflowing int's
+ // range. Also, at this point, we have already
+ // checked for a zero significand. Thus the signs
+ // of the exponent and significand determine the
+ // final result:
+ //
+ // significand
+ // + -
+ // exponent + +infinity -infinity
+ // - +0.0 -0.0
+ return new OldFloatingDecimalForTest(sign * (positiveExponent ?
+ Double.POSITIVE_INFINITY : 0.0));
+ }
+
+ long rawExponent =
+ (positiveExponent ? 1L : -1L) * // exponent sign
+ unsignedRawExponent; // exponent magnitude
+
+ // Calculate partially adjusted exponent
+ long exponent = rawExponent + exponentAdjust ;
+
+ // Starting copying non-zero bits into proper position in
+ // a long; copy explicit bit too; this will be masked
+ // later for normal values.
+
+ boolean round = false;
+ boolean sticky = false;
+ int bitsCopied=0;
+ int nextShift=0;
+ long significand=0L;
+ // First iteration is different, since we only copy
+ // from the leading significand bit; one more exponent
+ // adjust will be needed...
+
+ // IMPORTANT: make leadingDigit a long to avoid
+ // surprising shift semantics!
+ long leadingDigit = getHexDigit(significandString, 0);
+
+ /*
+ * Left shift the leading digit (53 - (bit position of
+ * leading 1 in digit)); this sets the top bit of the
+ * significand to 1. The nextShift value is adjusted
+ * to take into account the number of bit positions of
+ * the leadingDigit actually used. Finally, the
+ * exponent is adjusted to normalize the significand
+ * as a binary value, not just a hex value.
+ */
+ if (leadingDigit == 1) {
+ significand |= leadingDigit << 52;
+ nextShift = 52 - 4;
+ /* exponent += 0 */ }
+ else if (leadingDigit <= 3) { // [2, 3]
+ significand |= leadingDigit << 51;
+ nextShift = 52 - 5;
+ exponent += 1;
+ }
+ else if (leadingDigit <= 7) { // [4, 7]
+ significand |= leadingDigit << 50;
+ nextShift = 52 - 6;
+ exponent += 2;
+ }
+ else if (leadingDigit <= 15) { // [8, f]
+ significand |= leadingDigit << 49;
+ nextShift = 52 - 7;
+ exponent += 3;
+ } else {
+ throw new AssertionError("Result from digit conversion too large!");
+ }
+ // The preceding if-else could be replaced by a single
+ // code block based on the high-order bit set in
+ // leadingDigit. Given leadingOnePosition,
+
+ // significand |= leadingDigit << (SIGNIFICAND_WIDTH - leadingOnePosition);
+ // nextShift = 52 - (3 + leadingOnePosition);
+ // exponent += (leadingOnePosition-1);
+
+
+ /*
+ * Now the exponent variable is equal to the normalized
+ * binary exponent. Code below will make representation
+ * adjustments if the exponent is incremented after
+ * rounding (includes overflows to infinity) or if the
+ * result is subnormal.
+ */
+
+ // Copy digit into significand until the significand can't
+ // hold another full hex digit or there are no more input
+ // hex digits.
+ int i = 0;
+ for(i = 1;
+ i < signifLength && nextShift >= 0;
+ i++) {
+ long currentDigit = getHexDigit(significandString, i);
+ significand |= (currentDigit << nextShift);
+ nextShift-=4;
+ }
+
+ // After the above loop, the bulk of the string is copied.
+ // Now, we must copy any partial hex digits into the
+ // significand AND compute the round bit and start computing
+ // sticky bit.
+
+ if ( i < signifLength ) { // at least one hex input digit exists
+ long currentDigit = getHexDigit(significandString, i);
+
+ // from nextShift, figure out how many bits need
+ // to be copied, if any
+ switch(nextShift) { // must be negative
+ case -1:
+ // three bits need to be copied in; can
+ // set round bit
+ significand |= ((currentDigit & 0xEL) >> 1);
+ round = (currentDigit & 0x1L) != 0L;
+ break;
+
+ case -2:
+ // two bits need to be copied in; can
+ // set round and start sticky
+ significand |= ((currentDigit & 0xCL) >> 2);
+ round = (currentDigit &0x2L) != 0L;
+ sticky = (currentDigit & 0x1L) != 0;
+ break;
+
+ case -3:
+ // one bit needs to be copied in
+ significand |= ((currentDigit & 0x8L)>>3);
+ // Now set round and start sticky, if possible
+ round = (currentDigit &0x4L) != 0L;
+ sticky = (currentDigit & 0x3L) != 0;
+ break;
+
+ case -4:
+ // all bits copied into significand; set
+ // round and start sticky
+ round = ((currentDigit & 0x8L) != 0); // is top bit set?
+ // nonzeros in three low order bits?
+ sticky = (currentDigit & 0x7L) != 0;
+ break;
+
+ default:
+ throw new AssertionError("Unexpected shift distance remainder.");
+ // break;
+ }
+
+ // Round is set; sticky might be set.
+
+ // For the sticky bit, it suffices to check the
+ // current digit and test for any nonzero digits in
+ // the remaining unprocessed input.
+ i++;
+ while(i < signifLength && !sticky) {
+ currentDigit = getHexDigit(significandString,i);
+ sticky = sticky || (currentDigit != 0);
+ i++;
+ }
+
+ }
+ // else all of string was seen, round and sticky are
+ // correct as false.
+
+
+ // Check for overflow and update exponent accordingly.
+
+ if (exponent > Double.MAX_EXPONENT) { // Infinite result
+ // overflow to properly signed infinity
+ return new OldFloatingDecimalForTest(sign * Double.POSITIVE_INFINITY);
+ } else { // Finite return value
+ if (exponent <= Double.MAX_EXPONENT && // (Usually) normal result
+ exponent >= Double.MIN_EXPONENT) {
+
+ // The result returned in this block cannot be a
+ // zero or subnormal; however after the
+ // significand is adjusted from rounding, we could
+ // still overflow in infinity.
+
+ // AND exponent bits into significand; if the
+ // significand is incremented and overflows from
+ // rounding, this combination will update the
+ // exponent correctly, even in the case of
+ // Double.MAX_VALUE overflowing to infinity.
+
+ significand = (( (exponent +
+ (long)DoubleConsts.EXP_BIAS) <<
+ (DoubleConsts.SIGNIFICAND_WIDTH-1))
+ & DoubleConsts.EXP_BIT_MASK) |
+ (DoubleConsts.SIGNIF_BIT_MASK & significand);
+
+ } else { // Subnormal or zero
+ // (exponent < Double.MIN_EXPONENT)
+
+ if (exponent < (DoubleConsts.MIN_SUB_EXPONENT -1 )) {
+ // No way to round back to nonzero value
+ // regardless of significand if the exponent is
+ // less than -1075.
+ return new OldFloatingDecimalForTest(sign * 0.0);
+ } else { // -1075 <= exponent <= MIN_EXPONENT -1 = -1023
+ /*
+ * Find bit position to round to; recompute
+ * round and sticky bits, and shift
+ * significand right appropriately.
+ */
+
+ sticky = sticky || round;
+ round = false;
+
+ // Number of bits of significand to preserve is
+ // exponent - abs_min_exp +1
+ // check:
+ // -1075 +1074 + 1 = 0
+ // -1023 +1074 + 1 = 52
+
+ int bitsDiscarded = 53 -
+ ((int)exponent - DoubleConsts.MIN_SUB_EXPONENT + 1);
+ assert bitsDiscarded >= 1 && bitsDiscarded <= 53;
+
+ // What to do here:
+ // First, isolate the new round bit
+ round = (significand & (1L << (bitsDiscarded -1))) != 0L;
+ if (bitsDiscarded > 1) {
+ // create mask to update sticky bits; low
+ // order bitsDiscarded bits should be 1
+ long mask = ~((~0L) << (bitsDiscarded -1));
+ sticky = sticky || ((significand & mask) != 0L ) ;
+ }
+
+ // Now, discard the bits
+ significand = significand >> bitsDiscarded;
+
+ significand = (( ((long)(Double.MIN_EXPONENT -1) + // subnorm exp.
+ (long)DoubleConsts.EXP_BIAS) <<
+ (DoubleConsts.SIGNIFICAND_WIDTH-1))
+ & DoubleConsts.EXP_BIT_MASK) |
+ (DoubleConsts.SIGNIF_BIT_MASK & significand);
+ }
+ }
+
+ // The significand variable now contains the currently
+ // appropriate exponent bits too.
+
+ /*
+ * Determine if significand should be incremented;
+ * making this determination depends on the least
+ * significant bit and the round and sticky bits.
+ *
+ * Round to nearest even rounding table, adapted from
+ * table 4.7 in "Computer Arithmetic" by IsraelKoren.
+ * The digit to the left of the "decimal" point is the
+ * least significant bit, the digits to the right of
+ * the point are the round and sticky bits
+ *
+ * Number Round(x)
+ * x0.00 x0.
+ * x0.01 x0.
+ * x0.10 x0.
+ * x0.11 x1. = x0. +1
+ * x1.00 x1.
+ * x1.01 x1.
+ * x1.10 x1. + 1
+ * x1.11 x1. + 1
+ */
+ boolean incremented = false;
+ boolean leastZero = ((significand & 1L) == 0L);
+ if( ( leastZero && round && sticky ) ||
+ ((!leastZero) && round )) {
+ incremented = true;
+ significand++;
+ }
+
+ OldFloatingDecimalForTest fd = new OldFloatingDecimalForTest(Math.copySign(
+ Double.longBitsToDouble(significand),
+ sign));
+
+ /*
+ * Set roundingDir variable field of fd properly so
+ * that the input string can be properly rounded to a
+ * float value. There are two cases to consider:
+ *
+ * 1. rounding to double discards sticky bit
+ * information that would change the result of a float
+ * rounding (near halfway case between two floats)
+ *
+ * 2. rounding to double rounds up when rounding up
+ * would not occur when rounding to float.
+ *
+ * For former case only needs to be considered when
+ * the bits rounded away when casting to float are all
+ * zero; otherwise, float round bit is properly set
+ * and sticky will already be true.
+ *
+ * The lower exponent bound for the code below is the
+ * minimum (normalized) subnormal exponent - 1 since a
+ * value with that exponent can round up to the
+ * minimum subnormal value and the sticky bit
+ * information must be preserved (i.e. case 1).
+ */
+ if ((exponent >= FloatConsts.MIN_SUB_EXPONENT-1) &&
+ (exponent <= Float.MAX_EXPONENT ) ){
+ // Outside above exponent range, the float value
+ // will be zero or infinity.
+
+ /*
+ * If the low-order 28 bits of a rounded double
+ * significand are 0, the double could be a
+ * half-way case for a rounding to float. If the
+ * double value is a half-way case, the double
+ * significand may have to be modified to round
+ * the the right float value (see the stickyRound
+ * method). If the rounding to double has lost
+ * what would be float sticky bit information, the
+ * double significand must be incremented. If the
+ * double value's significand was itself
+ * incremented, the float value may end up too
+ * large so the increment should be undone.
+ */
+ if ((significand & 0xfffffffL) == 0x0L) {
+ // For negative values, the sign of the
+ // roundDir is the same as for positive values
+ // since adding 1 increasing the significand's
+ // magnitude and subtracting 1 decreases the
+ // significand's magnitude. If neither round
+ // nor sticky is true, the double value is
+ // exact and no adjustment is required for a
+ // proper float rounding.
+ if( round || sticky) {
+ if (leastZero) { // prerounding lsb is 0
+ // If round and sticky were both true,
+ // and the least significant
+ // significand bit were 0, the rounded
+ // significand would not have its
+ // low-order bits be zero. Therefore,
+ // we only need to adjust the
+ // significand if round XOR sticky is
+ // true.
+ if (round ^ sticky) {
+ fd.roundDir = 1;
+ }
+ }
+ else { // prerounding lsb is 1
+ // If the prerounding lsb is 1 and the
+ // resulting significand has its
+ // low-order bits zero, the significand
+ // was incremented. Here, we undo the
+ // increment, which will ensure the
+ // right guard and sticky bits for the
+ // float rounding.
+ if (round)
+ fd.roundDir = -1;
+ }
+ }
+ }
+ }
+
+ fd.fromHex = true;
+ return fd;
+ }
+ }
+ }
+
+ /**
+ * Return <code>s</code> with any leading zeros removed.
+ */
+ static String stripLeadingZeros(String s) {
+ return s.replaceFirst("^0+", "");
+ }
+
+ /**
+ * Extract a hexadecimal digit from position <code>position</code>
+ * of string <code>s</code>.
+ */
+ static int getHexDigit(String s, int position) {
+ int value = Character.digit(s.charAt(position), 16);
+ if (value <= -1 || value >= 16) {
+ throw new AssertionError("Unexpected failure of digit conversion of " +
+ s.charAt(position));
+ }
+ return value;
+ }
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/jdk/internal/math/FloatingDecimal/TestFDBigInteger.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,435 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.math.BigInteger;
+import java.util.Random;
+import jdk.internal.math.FDBigInteger;
+
+/**
+ * @test
+ * @bug 7032154
+ * @summary unit testys of FDBigInteger
+ * @modules java.base/jdk.internal.math
+ * @author Dmitry Nadezhin
+ */
+public class TestFDBigInteger {
+
+ private static final int MAX_P5 = 413;
+ private static final int MAX_P2 = 65;
+ private static final long LONG_SIGN_MASK = (1L << 63);
+ private static final BigInteger FIVE = BigInteger.valueOf(5);
+ private static final FDBigInteger MUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0));
+ private static final FDBigInteger IMMUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0));
+ private static final FDBigInteger IMMUTABLE_MILLION = genMillion1();
+ private static final FDBigInteger IMMUTABLE_BILLION = genBillion1();
+ private static final FDBigInteger IMMUTABLE_TEN18 = genTen18();
+
+ static {
+ IMMUTABLE_ZERO.makeImmutable();
+ IMMUTABLE_MILLION.makeImmutable();
+ IMMUTABLE_BILLION.makeImmutable();
+ IMMUTABLE_TEN18.makeImmutable();
+ }
+
+ private static FDBigInteger mutable(String hex, int offset) {
+ char[] chars = new BigInteger(hex, 16).toString().toCharArray();
+ return new FDBigInteger(0, chars, 0, chars.length).multByPow52(0, offset * 32);
+ }
+
+ private static FDBigInteger immutable(String hex, int offset) {
+ FDBigInteger fd = mutable(hex, offset);
+ fd.makeImmutable();
+ return fd;
+ }
+
+ private static BigInteger biPow52(int p5, int p2) {
+ return FIVE.pow(p5).shiftLeft(p2);
+ }
+
+ // data.length == 1, nWords == 1, offset == 0
+ private static FDBigInteger genMillion1() {
+ return FDBigInteger.valueOfPow52(6, 0).leftShift(6);
+ }
+
+ // data.length == 2, nWords == 1, offset == 0
+ private static FDBigInteger genMillion2() {
+ return FDBigInteger.valueOfMulPow52(1000000L, 0, 0);
+ }
+
+ // data.length == 1, nWords == 1, offset == 0
+ private static FDBigInteger genBillion1() {
+ return FDBigInteger.valueOfPow52(9, 0).leftShift(9);
+ }
+
+ // data.length == 2, nWords == 2, offset == 0
+ private static FDBigInteger genTen18() {
+ return FDBigInteger.valueOfPow52(18, 0).leftShift(18);
+ }
+
+ private static void check(BigInteger expected, FDBigInteger actual, String message) throws Exception {
+ if (!expected.equals(actual.toBigInteger())) {
+ throw new Exception(message + " result " + actual.toHexString() + " expected " + expected.toString(16));
+ }
+ }
+
+ private static void testValueOfPow52(int p5, int p2) throws Exception {
+ check(biPow52(p5, p2), FDBigInteger.valueOfPow52(p5, p2),
+ "valueOfPow52(" + p5 + "," + p2 + ")");
+ }
+
+ private static void testValueOfPow52() throws Exception {
+ for (int p5 = 0; p5 <= MAX_P5; p5++) {
+ for (int p2 = 0; p2 <= MAX_P2; p2++) {
+ testValueOfPow52(p5, p2);
+ }
+ }
+ }
+
+ private static void testValueOfMulPow52(long value, int p5, int p2) throws Exception {
+ BigInteger bi = BigInteger.valueOf(value & ~LONG_SIGN_MASK);
+ if (value < 0) {
+ bi = bi.setBit(63);
+ }
+ check(biPow52(p5, p2).multiply(bi), FDBigInteger.valueOfMulPow52(value, p5, p2),
+ "valueOfMulPow52(" + Long.toHexString(value) + "." + p5 + "," + p2 + ")");
+ }
+
+ private static void testValueOfMulPow52(long value, int p5) throws Exception {
+ testValueOfMulPow52(value, p5, 0);
+ testValueOfMulPow52(value, p5, 1);
+ testValueOfMulPow52(value, p5, 30);
+ testValueOfMulPow52(value, p5, 31);
+ testValueOfMulPow52(value, p5, 33);
+ testValueOfMulPow52(value, p5, 63);
+ }
+
+ private static void testValueOfMulPow52() throws Exception {
+ for (int p5 = 0; p5 <= MAX_P5; p5++) {
+ testValueOfMulPow52(0xFFFFFFFFL, p5);
+ testValueOfMulPow52(0x123456789AL, p5);
+ testValueOfMulPow52(0x7FFFFFFFFFFFFFFFL, p5);
+ testValueOfMulPow52(0xFFFFFFFFFFF54321L, p5);
+ }
+ }
+
+ private static void testLeftShift(FDBigInteger t, int shift, boolean isImmutable) throws Exception {
+ BigInteger bt = t.toBigInteger();
+ FDBigInteger r = t.leftShift(shift);
+ if ((bt.signum() == 0 || shift == 0 || !isImmutable) && r != t) {
+ throw new Exception("leftShift doesn't reuse its argument");
+ }
+ if (isImmutable) {
+ check(bt, t, "leftShift corrupts its argument");
+ }
+ check(bt.shiftLeft(shift), r, "leftShift returns wrong result");
+ }
+
+ private static void testLeftShift() throws Exception {
+ testLeftShift(IMMUTABLE_ZERO, 0, true);
+ testLeftShift(IMMUTABLE_ZERO, 10, true);
+ testLeftShift(MUTABLE_ZERO, 0, false);
+ testLeftShift(MUTABLE_ZERO, 10, false);
+
+ testLeftShift(IMMUTABLE_MILLION, 0, true);
+ testLeftShift(IMMUTABLE_MILLION, 1, true);
+ testLeftShift(IMMUTABLE_MILLION, 12, true);
+ testLeftShift(IMMUTABLE_MILLION, 13, true);
+ testLeftShift(IMMUTABLE_MILLION, 32, true);
+ testLeftShift(IMMUTABLE_MILLION, 33, true);
+ testLeftShift(IMMUTABLE_MILLION, 44, true);
+ testLeftShift(IMMUTABLE_MILLION, 45, true);
+
+ testLeftShift(genMillion1(), 0, false);
+ testLeftShift(genMillion1(), 1, false);
+ testLeftShift(genMillion1(), 12, false);
+ testLeftShift(genMillion1(), 13, false);
+ testLeftShift(genMillion1(), 25, false);
+ testLeftShift(genMillion1(), 26, false);
+ testLeftShift(genMillion1(), 32, false);
+ testLeftShift(genMillion1(), 33, false);
+ testLeftShift(genMillion1(), 44, false);
+ testLeftShift(genMillion1(), 45, false);
+
+ testLeftShift(genMillion2(), 0, false);
+ testLeftShift(genMillion2(), 1, false);
+ testLeftShift(genMillion2(), 12, false);
+ testLeftShift(genMillion2(), 13, false);
+ testLeftShift(genMillion2(), 25, false);
+ testLeftShift(genMillion2(), 26, false);
+ testLeftShift(genMillion2(), 32, false);
+ testLeftShift(genMillion2(), 33, false);
+ testLeftShift(genMillion2(), 44, false);
+ testLeftShift(genMillion2(), 45, false);
+ }
+
+ private static void testQuoRemIteration(FDBigInteger t, FDBigInteger s) throws Exception {
+ BigInteger bt = t.toBigInteger();
+ BigInteger bs = s.toBigInteger();
+ int q = t.quoRemIteration(s);
+ BigInteger[] qr = bt.divideAndRemainder(bs);
+ if (!BigInteger.valueOf(q).equals(qr[0])) {
+ throw new Exception("quoRemIteration returns incorrect quo");
+ }
+ check(qr[1].multiply(BigInteger.TEN), t, "quoRemIteration returns incorrect rem");
+ }
+
+ private static void testQuoRemIteration() throws Exception {
+ // IMMUTABLE_TEN18 == 0de0b6b3a7640000
+ // q = 0
+ testQuoRemIteration(mutable("00000001", 0), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("00000001", 1), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("0de0b6b2", 1), IMMUTABLE_TEN18);
+ // q = 1 -> q = 0
+ testQuoRemIteration(mutable("0de0b6b3", 1), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("0de0b6b3a763FFFF", 0), IMMUTABLE_TEN18);
+ // q = 1
+ testQuoRemIteration(mutable("0de0b6b3a7640000", 0), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("0de0b6b3FFFFFFFF", 0), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("8ac72304", 1), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("0de0b6b400000000", 0), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("8ac72305", 1), IMMUTABLE_TEN18);
+ // q = 18
+ testQuoRemIteration(mutable("FFFFFFFF", 1), IMMUTABLE_TEN18);
+ }
+
+ private static void testCmp(FDBigInteger t, FDBigInteger o) throws Exception {
+ BigInteger bt = t.toBigInteger();
+ BigInteger bo = o.toBigInteger();
+ int cmp = t.cmp(o);
+ int bcmp = bt.compareTo(bo);
+ if (bcmp != cmp) {
+ throw new Exception("cmp returns " + cmp + " expected " + bcmp);
+ }
+ check(bt, t, "cmp corrupts this");
+ check(bo, o, "cmp corrupts other");
+ if (o.cmp(t) != -cmp) {
+ throw new Exception("asymmetrical cmp");
+ }
+ check(bt, t, "cmp corrupts this");
+ check(bo, o, "cmp corrupts other");
+ }
+
+ private static void testCmp() throws Exception {
+ testCmp(mutable("FFFFFFFF", 0), mutable("100000000", 0));
+ testCmp(mutable("FFFFFFFF", 0), mutable("1", 1));
+ testCmp(mutable("5", 0), mutable("6", 0));
+ testCmp(mutable("5", 0), mutable("5", 0));
+ testCmp(mutable("5000000001", 0), mutable("500000001", 0));
+ testCmp(mutable("5000000001", 0), mutable("6", 1));
+ testCmp(mutable("5000000001", 0), mutable("5", 1));
+ testCmp(mutable("5000000000", 0), mutable("5", 1));
+ }
+
+ private static void testCmpPow52(FDBigInteger t, int p5, int p2) throws Exception {
+ FDBigInteger o = FDBigInteger.valueOfPow52(p5, p2);
+ BigInteger bt = t.toBigInteger();
+ BigInteger bo = biPow52(p5, p2);
+ int cmp = t.cmp(o);
+ int bcmp = bt.compareTo(bo);
+ if (bcmp != cmp) {
+ throw new Exception("cmpPow52 returns " + cmp + " expected " + bcmp);
+ }
+ check(bt, t, "cmpPow52 corrupts this");
+ check(bo, o, "cmpPow5 corrupts other");
+ }
+
+ private static void testCmpPow52() throws Exception {
+ testCmpPow52(mutable("00000002", 1), 0, 31);
+ testCmpPow52(mutable("00000002", 1), 0, 32);
+ testCmpPow52(mutable("00000002", 1), 0, 33);
+ testCmpPow52(mutable("00000002", 1), 0, 34);
+ testCmpPow52(mutable("00000002", 1), 0, 64);
+ testCmpPow52(mutable("00000003", 1), 0, 32);
+ testCmpPow52(mutable("00000003", 1), 0, 33);
+ testCmpPow52(mutable("00000003", 1), 0, 34);
+ }
+
+ private static void testAddAndCmp(FDBigInteger t, FDBigInteger x, FDBigInteger y) throws Exception {
+ BigInteger bt = t.toBigInteger();
+ BigInteger bx = x.toBigInteger();
+ BigInteger by = y.toBigInteger();
+ int cmp = t.addAndCmp(x, y);
+ int bcmp = bt.compareTo(bx.add(by));
+ if (bcmp != cmp) {
+ throw new Exception("addAndCmp returns " + cmp + " expected " + bcmp);
+ }
+ check(bt, t, "addAndCmp corrupts this");
+ check(bx, x, "addAndCmp corrupts x");
+ check(by, y, "addAndCmp corrupts y");
+ }
+
+ private static void testAddAndCmp() throws Exception {
+ testAddAndCmp(MUTABLE_ZERO, MUTABLE_ZERO, MUTABLE_ZERO);
+ testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, MUTABLE_ZERO);
+ testAddAndCmp(mutable("00000001", 0), mutable("00000001", 0), MUTABLE_ZERO);
+ testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000001", 0));
+ testAddAndCmp(mutable("00000001", 0), mutable("00000002", 0), MUTABLE_ZERO);
+ testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000002", 0));
+ testAddAndCmp(mutable("00000001", 2), mutable("FFFFFFFF", 0), mutable("FFFFFFFF", 0));
+ testAddAndCmp(mutable("00000001", 0), mutable("00000001", 1), mutable("00000001", 0));
+
+ testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0F80000000", 1), mutable("F0F0F0F080000000", 1));
+ testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0E80000000", 1), mutable("F0F0F0F080000000", 1));
+
+ testAddAndCmp(mutable("00000002", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
+ testAddAndCmp(mutable("00000003", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
+ testAddAndCmp(mutable("00000004", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
+ testAddAndCmp(mutable("00000005", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
+
+ testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
+ testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000001", 0));
+ testAddAndCmp(mutable("00000002", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
+ testAddAndCmp(mutable("00000003", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
+ }
+
+ private static void testMultBy10(FDBigInteger t, boolean isImmutable) throws Exception {
+ BigInteger bt = t.toBigInteger();
+ FDBigInteger r = t.multBy10();
+ if ((bt.signum() == 0 || !isImmutable) && r != t) {
+ throw new Exception("multBy10 of doesn't reuse its argument");
+ }
+ if (isImmutable) {
+ check(bt, t, "multBy10 corrupts its argument");
+ }
+ check(bt.multiply(BigInteger.TEN), r, "multBy10 returns wrong result");
+ }
+
+ private static void testMultBy10() throws Exception {
+ for (int p5 = 0; p5 <= MAX_P5; p5++) {
+ for (int p2 = 0; p2 <= MAX_P2; p2++) {
+ // This strange way of creating a value ensures that it is mutable.
+ FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
+ testMultBy10(value, false);
+ value.makeImmutable();
+ testMultBy10(value, true);
+ }
+ }
+ }
+
+ private static void testMultByPow52(FDBigInteger t, int p5, int p2) throws Exception {
+ BigInteger bt = t.toBigInteger();
+ FDBigInteger r = t.multByPow52(p5, p2);
+ if (bt.signum() == 0 && r != t) {
+ throw new Exception("multByPow52 of doesn't reuse its argument");
+ }
+ check(bt.multiply(biPow52(p5, p2)), r, "multByPow52 returns wrong result");
+ }
+
+ private static void testMultByPow52() throws Exception {
+ for (int p5 = 0; p5 <= MAX_P5; p5++) {
+ for (int p2 = 0; p2 <= MAX_P2; p2++) {
+ // This strange way of creating a value ensures that it is mutable.
+ FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
+ testMultByPow52(value, p5, p2);
+ }
+ }
+ }
+
+ private static void testLeftInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception {
+ BigInteger biLeft = left.toBigInteger();
+ BigInteger biRight = right.toBigInteger();
+ FDBigInteger diff = left.leftInplaceSub(right);
+ if (!isImmutable && diff != left) {
+ throw new Exception("leftInplaceSub of doesn't reuse its argument");
+ }
+ if (isImmutable) {
+ check(biLeft, left, "leftInplaceSub corrupts its left immutable argument");
+ }
+ check(biRight, right, "leftInplaceSub corrupts its right argument");
+ check(biLeft.subtract(biRight), diff, "leftInplaceSub returns wrong result");
+ }
+
+ private static void testLeftInplaceSub() throws Exception {
+ for (int p5 = 0; p5 <= MAX_P5; p5++) {
+ for (int p2 = 0; p2 <= MAX_P2; p2++) {
+// for (int p5r = 0; p5r <= p5; p5r += 10) {
+// for (int p2r = 0; p2r <= p2; p2r += 10) {
+ for (int p5r = 0; p5r <= p5; p5r++) {
+ for (int p2r = 0; p2r <= p2; p2r++) {
+ // This strange way of creating a value ensures that it is mutable.
+ FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
+ FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
+ testLeftInplaceSub(left, right, false);
+ left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
+ left.makeImmutable();
+ testLeftInplaceSub(left, right, true);
+ }
+ }
+ }
+ }
+ }
+
+ private static void testRightInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception {
+ BigInteger biLeft = left.toBigInteger();
+ BigInteger biRight = right.toBigInteger();
+ FDBigInteger diff = left.rightInplaceSub(right);
+ if (!isImmutable && diff != right) {
+ throw new Exception("rightInplaceSub of doesn't reuse its argument");
+ }
+ check(biLeft, left, "leftInplaceSub corrupts its left argument");
+ if (isImmutable) {
+ check(biRight, right, "leftInplaceSub corrupts its right immutable argument");
+ }
+ try {
+ check(biLeft.subtract(biRight), diff, "rightInplaceSub returns wrong result");
+ } catch (Exception e) {
+ System.out.println(biLeft+" - "+biRight+" = "+biLeft.subtract(biRight));
+ throw e;
+ }
+ }
+
+ private static void testRightInplaceSub() throws Exception {
+ for (int p5 = 0; p5 <= MAX_P5; p5++) {
+ for (int p2 = 0; p2 <= MAX_P2; p2++) {
+// for (int p5r = 0; p5r <= p5; p5r += 10) {
+// for (int p2r = 0; p2r <= p2; p2r += 10) {
+ for (int p5r = 0; p5r <= p5; p5r++) {
+ for (int p2r = 0; p2r <= p2; p2r++) {
+ // This strange way of creating a value ensures that it is mutable.
+ FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
+ FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
+ testRightInplaceSub(left, right, false);
+ right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
+ right.makeImmutable();
+ testRightInplaceSub(left, right, true);
+ }
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ testValueOfPow52();
+ testValueOfMulPow52();
+ testLeftShift();
+ testQuoRemIteration();
+ testCmp();
+ testCmpPow52();
+ testAddAndCmp();
+ // Uncomment the following for more comprehensize but slow testing.
+ // testLeftInplaceSub();
+ // testMultBy10();
+ // testMultByPow52();
+ // testRightInplaceSub();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/jdk/internal/math/FloatingDecimal/TestFloatingDecimal.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Random;
+import jdk.internal.math.FloatingDecimal;
+
+/*
+OldFloatingDecimalForTest
+
+public class OldFloatingDecimalForTest {
+ public boolean digitsRoundedUp();
+ public OldFloatingDecimalForTest(double);
+ public OldFloatingDecimalForTest(float);
+ public boolean decimalDigitsExact();
+ public java.lang.String toString();
+ public java.lang.String toJavaFormatString();
+ public void appendTo(java.lang.Appendable);
+ public static OldFloatingDecimalForTest readJavaFormatString(java.lang.String) throws java.lang.NumberFormatException;
+ public strictfp double doubleValue();
+ public strictfp float floatValue();
+}
+
+jdk.internal.math.FloatingDecimal
+
+public class jdk.internal.math.FloatingDecimal {
+ public jdk.internal.math.FloatingDecimal();
+ public static java.lang.String toJavaFormatString(double);
+ public static java.lang.String toJavaFormatString(float);
+ public static void appendTo(double, java.lang.Appendable);
+ public static void appendTo(float, java.lang.Appendable);
+ public static double parseDouble(java.lang.String) throws java.lang.NumberFormatException;
+ public static float parseFloat(java.lang.String) throws java.lang.NumberFormatException;
+ public static jdk.internal.math.FloatingDecimal$AbstractD2ABuffer getD2ABuffer(double);
+}
+*/
+
+/**
+ * @test
+ * @bug 7032154
+ * @summary unit tests of FloatingDecimal
+ * @modules java.base/jdk.internal.math
+ * @library /java/lang/Math
+ * @build DoubleConsts FloatConsts
+ * @run main TestFloatingDecimal
+ * @author Brian Burkhalter
+ * @key randomness
+ */
+public class TestFloatingDecimal {
+ private static enum ResultType {
+ RESULT_EXCEPTION,
+ RESULT_PRINT
+ }
+
+ private static final ResultType RESULT_TYPE = ResultType.RESULT_PRINT;
+ private static final int NUM_RANDOM_TESTS = 100000;
+
+ private static final Random RANDOM = new Random();
+
+ private static void result(String message) {
+ switch (RESULT_TYPE) {
+ case RESULT_EXCEPTION:
+ throw new RuntimeException(message);
+ case RESULT_PRINT:
+ System.err.println(message);
+ break;
+ default:
+ assert false;
+ }
+ }
+
+ private static int check(String test, Object expected, Object actual) {
+ int failures = 0;
+ if(!actual.equals(expected)) {
+ failures++;
+ result("Test "+test+" expected "+expected+" but obtained "+actual);
+ }
+ return failures;
+ }
+
+ private static int testAppendToDouble() {
+ System.out.println(" testAppendToDouble");
+ int failures = 0;
+
+ for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
+ double[] d = new double[] {
+ RANDOM.nextLong(),
+ RANDOM.nextGaussian(),
+ RANDOM.nextDouble()*Double.MAX_VALUE
+ };
+ for(int j = 0; j < d.length; j++) {
+ OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]);
+ StringBuilder sb = new StringBuilder();
+ ofd.appendTo(sb);
+ String oldString = sb.toString();
+ sb = new StringBuilder();
+ FloatingDecimal.appendTo(d[j], sb);
+ String newString = sb.toString();
+ failures += check("testAppendToDouble", oldString, newString);
+ }
+ }
+
+ return failures;
+ }
+
+ private static int testAppendToFloat() {
+ System.out.println(" testAppendToFloat");
+ int failures = 0;
+
+ for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
+ float[] f = new float[] {
+ RANDOM.nextLong(),
+ (float)RANDOM.nextGaussian(),
+ RANDOM.nextFloat()*Float.MAX_VALUE
+ };
+ for(int j = 0; j < f.length; j++) {
+ OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]);
+ StringBuilder sb = new StringBuilder();
+ ofd.appendTo(sb);
+ String oldString = sb.toString();
+ sb = new StringBuilder();
+ FloatingDecimal.appendTo(f[j], sb);
+ String newString = sb.toString();
+ failures += check("testAppendToFloat", oldString, newString);
+ }
+ }
+
+ return failures;
+ }
+
+ private static int testAppendTo() {
+ System.out.println("testAppendTo");
+ int failures = 0;
+
+ failures += testAppendToDouble();
+ failures += testAppendToFloat();
+
+ return failures;
+ }
+
+ private static int testParseDouble() {
+ System.out.println(" testParseDouble");
+ int failures = 0;
+
+ for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
+ double[] d = new double[] {
+ RANDOM.nextLong(),
+ RANDOM.nextGaussian(),
+ RANDOM.nextDouble()*Double.MAX_VALUE
+ };
+ for(int j = 0; j < d.length; j++) {
+ OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]);
+ String javaFormatString = ofd.toJavaFormatString();
+ ofd = OldFloatingDecimalForTest.readJavaFormatString(javaFormatString);
+ double oldDouble = ofd.doubleValue();
+ double newDouble = FloatingDecimal.parseDouble(javaFormatString);
+ failures += check("testParseDouble", oldDouble, newDouble);
+ }
+ }
+
+ return failures;
+ }
+
+ private static int testParseFloat() {
+ System.out.println(" testParseFloat");
+ int failures = 0;
+
+ for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
+ float[] f = new float[] {
+ RANDOM.nextInt(),
+ (float)RANDOM.nextGaussian(),
+ RANDOM.nextFloat()*Float.MAX_VALUE
+ };
+ for(int j = 0; j < f.length; j++) {
+ OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]);
+ String javaFormatString = ofd.toJavaFormatString();
+ ofd = OldFloatingDecimalForTest.readJavaFormatString(javaFormatString);
+ float oldFloat = ofd.floatValue();
+ float newFloat = FloatingDecimal.parseFloat(javaFormatString);
+ failures += check("testParseFloat", oldFloat, newFloat);
+ }
+ }
+
+ return failures;
+ }
+
+ private static int testParse() {
+ System.out.println("testParse");
+ int failures = 0;
+
+ failures += testParseDouble();
+ failures += testParseFloat();
+
+ return failures;
+ }
+
+ private static int testToJavaFormatStringDoubleFixed() {
+ System.out.println(" testToJavaFormatStringDoubleFixed");
+ int failures = 0;
+
+ double[] d = new double [] {
+ -5.9522650387500933e18, // dtoa() fast path
+ 0.872989018674569, // dtoa() fast iterative - long
+ 1.1317400099603851e308 // dtoa() slow iterative
+ };
+
+ for(int i = 0; i < d.length; i++) {
+ OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[i]);
+ failures += check("testToJavaFormatStringDoubleFixed", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(d[i]));
+ }
+
+ return failures;
+ }
+
+ private static int testToJavaFormatStringDoubleRandom() {
+ System.out.println(" testToJavaFormatStringDoubleRandom");
+ int failures = 0;
+
+ for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
+ double[] d = new double[] {
+ RANDOM.nextLong(),
+ RANDOM.nextGaussian(),
+ RANDOM.nextDouble()*Double.MAX_VALUE
+ };
+ for(int j = 0; j < d.length; j++) {
+ OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]);
+ failures += check("testToJavaFormatStringDoubleRandom", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(d[j]));
+ }
+ }
+
+ return failures;
+ }
+
+ private static int testToJavaFormatStringDouble() {
+ System.out.println(" testToJavaFormatStringDouble");
+ int failures = 0;
+ failures += testToJavaFormatStringDoubleFixed();
+ failures += testToJavaFormatStringDoubleRandom();
+ return failures;
+ }
+
+ private static int testToJavaFormatStringFloatFixed() {
+ System.out.println(" testToJavaFormatStringFloatFixed");
+ int failures = 0;
+
+ float[] f = new float[] {
+ -9.8784166e8f, // dtoa() fast path
+ 0.70443946f, // dtoa() fast iterative - int
+ 1.8254228e37f // dtoa() slow iterative
+ };
+
+ for(int i = 0; i < f.length; i++) {
+ OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[i]);
+ failures += check("testToJavaFormatStringFloatFixed", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(f[i]));
+ }
+
+ return failures;
+ }
+
+ private static int testToJavaFormatStringFloatRandom() {
+ System.out.println(" testToJavaFormatStringFloatRandom");
+ int failures = 0;
+
+ for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
+ float[] f = new float[] {
+ RANDOM.nextInt(),
+ (float)RANDOM.nextGaussian(),
+ RANDOM.nextFloat()*Float.MAX_VALUE
+ };
+ for(int j = 0; j < f.length; j++) {
+ OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]);
+ failures += check("testToJavaFormatStringFloatRandom", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(f[j]));
+ }
+ }
+
+ return failures;
+ }
+
+ private static int testToJavaFormatStringFloat() {
+ System.out.println(" testToJavaFormatStringFloat");
+ int failures = 0;
+
+ failures += testToJavaFormatStringFloatFixed();
+ failures += testToJavaFormatStringFloatRandom();
+
+ return failures;
+ }
+
+ private static int testToJavaFormatString() {
+ System.out.println("testToJavaFormatString");
+ int failures = 0;
+
+ failures += testToJavaFormatStringDouble();
+ failures += testToJavaFormatStringFloat();
+
+ return failures;
+ }
+
+ public static void main(String[] args) {
+ int failures = 0;
+
+ failures += testAppendTo();
+ failures += testParse();
+ failures += testToJavaFormatString();
+
+ if (failures != 0) {
+ throw new RuntimeException("" + failures + " failures while testing FloatingDecimal");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/java2d/marlin/ArrayCacheSizeTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import sun.java2d.marlin.ArrayCache;
+
+/**
+ * @test
+ * @bug 8144445
+ * @summary Check the ArrayCache getNewLargeSize() method
+ * @run main ArrayCacheSizeTest
+ */
+public class ArrayCacheSizeTest {
+
+ public static void main(String[] args) {
+ testNewSize();
+ testNewLargeSize();
+ }
+
+ private static void testNewSize() {
+ testNewSize(0, 1);
+ testNewSize(0, 100000);
+
+ testNewSize(4096, 4097);
+ testNewSize(4096 * 16, 4096 * 16 + 1);
+
+ testNewSize(4096 * 4096 * 4, 4096 * 4096 * 4 + 1);
+
+ testNewSize(4096 * 4096 * 4, Integer.MAX_VALUE);
+
+ testNewSize(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE);
+
+ testNewSizeExpectAIOB(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE + 1);
+ testNewSizeExpectAIOB(1, -1);
+ testNewSizeExpectAIOB(Integer.MAX_VALUE, -1);
+ }
+
+ private static void testNewSizeExpectAIOB(final int curSize,
+ final int needSize) {
+ try {
+ testNewSize(curSize, needSize);
+ throw new RuntimeException("ArrayIndexOutOfBoundsException not thrown");
+ } catch (ArrayIndexOutOfBoundsException aiobe) {
+ System.out.println("ArrayIndexOutOfBoundsException expected.");
+ } catch (RuntimeException re) {
+ throw re;
+ } catch (Throwable th) {
+ throw new RuntimeException("Unexpected exception", th);
+ }
+ }
+
+ private static void testNewSize(final int curSize,
+ final int needSize) {
+
+ int size = ArrayCache.getNewSize(curSize, needSize);
+
+ System.out.println("getNewSize(" + curSize + ", " + needSize
+ + ") = " + size);
+
+ if (size < 0 || size < needSize) {
+ throw new IllegalStateException("Invalid getNewSize("
+ + curSize + ", " + needSize + ") = " + size + " !");
+ }
+ }
+
+ private static void testNewLargeSize() {
+ testNewLargeSize(0, 1);
+ testNewLargeSize(0, 100000);
+
+ testNewLargeSize(4096, 4097);
+ testNewLargeSize(4096 * 16, 4096 * 16 + 1);
+
+ testNewLargeSize(4096 * 4096 * 4, 4096 * 4096 * 4 + 1);
+
+ testNewLargeSize(4096 * 4096 * 4, Integer.MAX_VALUE);
+
+ testNewLargeSize(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE);
+
+ testNewLargeSizeExpectAIOB(Integer.MAX_VALUE - 1000, Integer.MAX_VALUE + 1L);
+ testNewLargeSizeExpectAIOB(1, -1L);
+ testNewLargeSizeExpectAIOB(Integer.MAX_VALUE, -1L);
+ }
+
+ private static void testNewLargeSizeExpectAIOB(final long curSize,
+ final long needSize) {
+ try {
+ testNewLargeSize(curSize, needSize);
+ throw new RuntimeException("ArrayIndexOutOfBoundsException not thrown");
+ } catch (ArrayIndexOutOfBoundsException aiobe) {
+ System.out.println("ArrayIndexOutOfBoundsException expected.");
+ } catch (RuntimeException re) {
+ throw re;
+ } catch (Throwable th) {
+ throw new RuntimeException("Unexpected exception", th);
+ }
+ }
+
+ private static void testNewLargeSize(final long curSize,
+ final long needSize) {
+
+ long size = ArrayCache.getNewLargeSize(curSize, needSize);
+
+ System.out.println("getNewLargeSize(" + curSize + ", " + needSize
+ + ") = " + size);
+
+ if (size < 0 || size < needSize || size > Integer.MAX_VALUE) {
+ throw new IllegalStateException("Invalid getNewLargeSize("
+ + curSize + ", " + needSize + ") = " + size + " !");
+ }
+ }
+
+}
--- a/jdk/test/sun/java2d/marlin/CrashTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/sun/java2d/marlin/CrashTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -31,31 +31,44 @@
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
-import sun.java2d.pipe.RenderingEngine;
/**
- * Simple crash rendering test using huge GeneralPaths with marlin renderer
- *
- * run it with large heap (2g):
- * java -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine marlin.CrashTest
- *
- * @author bourgesl
- */
+ * @test
+ * @summary Simple crash rendering test using huge GeneralPaths with the Marlin renderer
+ * @run main/othervm -mx512m CrashTest
+ * @ignore tests that take a long time and consumes 5Gb memory
+ * @run main/othervm -ms4g -mx4g CrashTest -slow
+*/
public class CrashTest {
static final boolean SAVE_IMAGE = false;
static boolean USE_ROUND_CAPS_AND_JOINS = true;
public static void main(String[] args) {
+ boolean runSlowTests = (args.length != 0 && "-slow".equals(args[0]));
+
+ // First display which renderer is tested:
+ System.setProperty("sun.java2d.renderer.verbose", "true");
+
// try insane image sizes:
// subpixel coords may overflow:
-// testHugeImage((Integer.MAX_VALUE >> 3) + 1, 6);
+ // check MAX_VALUE / (8 * 2); overflow may happen due to orientation flag
+ // But as it is impossible to allocate an image larger than 2Gb (byte) then
+ // it is also impossible to have rowAAChunk larger than 2Gb !
+
+ // Disabled test as it consumes 4GB heap + offheap (2Gb) ie > 6Gb !
+ if (runSlowTests) {
+ testHugeImage((Integer.MAX_VALUE >> 4) - 100, 16);
+ }
+
// larger than 23 bits: (RLE)
testHugeImage(8388608 + 1, 10);
- test(0.1f, false, 0);
- test(0.1f, true, 7f);
+ if (runSlowTests) {
+ test(0.1f, false, 0);
+ test(0.1f, true, 7f);
+ }
// Exceed 2Gb OffHeap buffer for edges:
try {
@@ -67,17 +80,15 @@
if (th instanceof ArrayIndexOutOfBoundsException) {
System.out.println("ArrayIndexOutOfBoundsException expected.");
} else {
- System.out.println("Exception occured:");
- th.printStackTrace();
+ throw new RuntimeException("Unexpected exception", th);
}
}
-
}
private static void test(final float lineStroke,
final boolean useDashes,
final float dashMinLen)
- throws ArrayIndexOutOfBoundsException
+ throws ArrayIndexOutOfBoundsException
{
System.out.println("---\n" + "test: "
+ "lineStroke=" + lineStroke
@@ -85,9 +96,6 @@
+", dashMinLen=" + dashMinLen
);
- final String renderer = RenderingEngine.getInstance().getClass().getSimpleName();
- System.out.println("Testing renderer = " + renderer);
-
final BasicStroke stroke = createStroke(lineStroke, useDashes, dashMinLen);
// TODO: test Dasher.firstSegmentsBuffer resizing ?
@@ -135,7 +143,7 @@
if (SAVE_IMAGE) {
try {
- final File file = new File("CrashTest-" + renderer + "-dash-" + useDashes + ".bmp");
+ final File file = new File("CrashTest-dash-" + useDashes + ".bmp");
System.out.println("Writing file: " + file.getAbsolutePath());
ImageIO.write(image, "BMP", file);
@@ -150,15 +158,10 @@
}
private static void testHugeImage(final int width, final int height)
- throws ArrayIndexOutOfBoundsException
+ throws ArrayIndexOutOfBoundsException
{
System.out.println("---\n" + "testHugeImage: "
- + "width=" + width
- + ", height=" + height
- );
-
- final String renderer = RenderingEngine.getInstance().getClass().getSimpleName();
- System.out.println("Testing renderer = " + renderer);
+ + "width=" + width + ", height=" + height);
final BasicStroke stroke = createStroke(2.5f, false, 0);
@@ -195,8 +198,8 @@
if (SAVE_IMAGE) {
try {
- final File file = new File("CrashTest-" + renderer +
- "-huge-" + width + "x" +height + ".bmp");
+ final File file = new File("CrashTest-huge-"
+ + width + "x" +height + ".bmp");
System.out.println("Writing file: " + file.getAbsolutePath());
ImageIO.write(image, "BMP", file);
--- a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.java Thu Dec 24 10:34:31 2015 -0800
@@ -58,7 +58,7 @@
* @run main/othervm/timeout=600 -XX:+UsePerfData JMXStartStopTest
* @summary Makes sure that enabling/disabling the management agent through JCMD
* achieves the desired results
- * @key randomness
+ * @key randomness intermittent
*/
public class JMXStartStopTest {
private static final String TEST_APP_NAME = "TestApp";
--- a/jdk/test/sun/misc/FloatingDecimal/OldFDBigIntForTest.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,491 +0,0 @@
-/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-//package sun.misc;
-
-/*
- * A really, really simple bigint package
- * tailored to the needs of floating base conversion.
- */
-class OldFDBigIntForTest {
- int nWords; // number of words used
- int data[]; // value: data[0] is least significant
-
-
- public OldFDBigIntForTest( int v ){
- nWords = 1;
- data = new int[1];
- data[0] = v;
- }
-
- public OldFDBigIntForTest( long v ){
- data = new int[2];
- data[0] = (int)v;
- data[1] = (int)(v>>>32);
- nWords = (data[1]==0) ? 1 : 2;
- }
-
- public OldFDBigIntForTest( OldFDBigIntForTest other ){
- data = new int[nWords = other.nWords];
- System.arraycopy( other.data, 0, data, 0, nWords );
- }
-
- private OldFDBigIntForTest( int [] d, int n ){
- data = d;
- nWords = n;
- }
-
- public OldFDBigIntForTest( long seed, char digit[], int nd0, int nd ){
- int n= (nd+8)/9; // estimate size needed.
- if ( n < 2 ) n = 2;
- data = new int[n]; // allocate enough space
- data[0] = (int)seed; // starting value
- data[1] = (int)(seed>>>32);
- nWords = (data[1]==0) ? 1 : 2;
- int i = nd0;
- int limit = nd-5; // slurp digits 5 at a time.
- int v;
- while ( i < limit ){
- int ilim = i+5;
- v = (int)digit[i++]-(int)'0';
- while( i <ilim ){
- v = 10*v + (int)digit[i++]-(int)'0';
- }
- multaddMe( 100000, v); // ... where 100000 is 10^5.
- }
- int factor = 1;
- v = 0;
- while ( i < nd ){
- v = 10*v + (int)digit[i++]-(int)'0';
- factor *= 10;
- }
- if ( factor != 1 ){
- multaddMe( factor, v );
- }
- }
-
- /*
- * Left shift by c bits.
- * Shifts this in place.
- */
- public void
- lshiftMe( int c )throws IllegalArgumentException {
- if ( c <= 0 ){
- if ( c == 0 )
- return; // silly.
- else
- throw new IllegalArgumentException("negative shift count");
- }
- int wordcount = c>>5;
- int bitcount = c & 0x1f;
- int anticount = 32-bitcount;
- int t[] = data;
- int s[] = data;
- if ( nWords+wordcount+1 > t.length ){
- // reallocate.
- t = new int[ nWords+wordcount+1 ];
- }
- int target = nWords+wordcount;
- int src = nWords-1;
- if ( bitcount == 0 ){
- // special hack, since an anticount of 32 won't go!
- System.arraycopy( s, 0, t, wordcount, nWords );
- target = wordcount-1;
- } else {
- t[target--] = s[src]>>>anticount;
- while ( src >= 1 ){
- t[target--] = (s[src]<<bitcount) | (s[--src]>>>anticount);
- }
- t[target--] = s[src]<<bitcount;
- }
- while( target >= 0 ){
- t[target--] = 0;
- }
- data = t;
- nWords += wordcount + 1;
- // may have constructed high-order word of 0.
- // if so, trim it
- while ( nWords > 1 && data[nWords-1] == 0 )
- nWords--;
- }
-
- /*
- * normalize this number by shifting until
- * the MSB of the number is at 0x08000000.
- * This is in preparation for quoRemIteration, below.
- * The idea is that, to make division easier, we want the
- * divisor to be "normalized" -- usually this means shifting
- * the MSB into the high words sign bit. But because we know that
- * the quotient will be 0 < q < 10, we would like to arrange that
- * the dividend not span up into another word of precision.
- * (This needs to be explained more clearly!)
- */
- public int
- normalizeMe() throws IllegalArgumentException {
- int src;
- int wordcount = 0;
- int bitcount = 0;
- int v = 0;
- for ( src= nWords-1 ; src >= 0 && (v=data[src]) == 0 ; src--){
- wordcount += 1;
- }
- if ( src < 0 ){
- // oops. Value is zero. Cannot normalize it!
- throw new IllegalArgumentException("zero value");
- }
- /*
- * In most cases, we assume that wordcount is zero. This only
- * makes sense, as we try not to maintain any high-order
- * words full of zeros. In fact, if there are zeros, we will
- * simply SHORTEN our number at this point. Watch closely...
- */
- nWords -= wordcount;
- /*
- * Compute how far left we have to shift v s.t. its highest-
- * order bit is in the right place. Then call lshiftMe to
- * do the work.
- */
- if ( (v & 0xf0000000) != 0 ){
- // will have to shift up into the next word.
- // too bad.
- for( bitcount = 32 ; (v & 0xf0000000) != 0 ; bitcount-- )
- v >>>= 1;
- } else {
- while ( v <= 0x000fffff ){
- // hack: byte-at-a-time shifting
- v <<= 8;
- bitcount += 8;
- }
- while ( v <= 0x07ffffff ){
- v <<= 1;
- bitcount += 1;
- }
- }
- if ( bitcount != 0 )
- lshiftMe( bitcount );
- return bitcount;
- }
-
- /*
- * Multiply a OldFDBigIntForTest by an int.
- * Result is a new OldFDBigIntForTest.
- */
- public OldFDBigIntForTest
- mult( int iv ) {
- long v = iv;
- int r[];
- long p;
-
- // guess adequate size of r.
- r = new int[ ( v * ((long)data[nWords-1]&0xffffffffL) > 0xfffffffL ) ? nWords+1 : nWords ];
- p = 0L;
- for( int i=0; i < nWords; i++ ) {
- p += v * ((long)data[i]&0xffffffffL);
- r[i] = (int)p;
- p >>>= 32;
- }
- if ( p == 0L){
- return new OldFDBigIntForTest( r, nWords );
- } else {
- r[nWords] = (int)p;
- return new OldFDBigIntForTest( r, nWords+1 );
- }
- }
-
- /*
- * Multiply a OldFDBigIntForTest by an int and add another int.
- * Result is computed in place.
- * Hope it fits!
- */
- public void
- multaddMe( int iv, int addend ) {
- long v = iv;
- long p;
-
- // unroll 0th iteration, doing addition.
- p = v * ((long)data[0]&0xffffffffL) + ((long)addend&0xffffffffL);
- data[0] = (int)p;
- p >>>= 32;
- for( int i=1; i < nWords; i++ ) {
- p += v * ((long)data[i]&0xffffffffL);
- data[i] = (int)p;
- p >>>= 32;
- }
- if ( p != 0L){
- data[nWords] = (int)p; // will fail noisily if illegal!
- nWords++;
- }
- }
-
- /*
- * Multiply a OldFDBigIntForTest by another OldFDBigIntForTest.
- * Result is a new OldFDBigIntForTest.
- */
- public OldFDBigIntForTest
- mult( OldFDBigIntForTest other ){
- // crudely guess adequate size for r
- int r[] = new int[ nWords + other.nWords ];
- int i;
- // I think I am promised zeros...
-
- for( i = 0; i < this.nWords; i++ ){
- long v = (long)this.data[i] & 0xffffffffL; // UNSIGNED CONVERSION
- long p = 0L;
- int j;
- for( j = 0; j < other.nWords; j++ ){
- p += ((long)r[i+j]&0xffffffffL) + v*((long)other.data[j]&0xffffffffL); // UNSIGNED CONVERSIONS ALL 'ROUND.
- r[i+j] = (int)p;
- p >>>= 32;
- }
- r[i+j] = (int)p;
- }
- // compute how much of r we actually needed for all that.
- for ( i = r.length-1; i> 0; i--)
- if ( r[i] != 0 )
- break;
- return new OldFDBigIntForTest( r, i+1 );
- }
-
- /*
- * Add one OldFDBigIntForTest to another. Return a OldFDBigIntForTest
- */
- public OldFDBigIntForTest
- add( OldFDBigIntForTest other ){
- int i;
- int a[], b[];
- int n, m;
- long c = 0L;
- // arrange such that a.nWords >= b.nWords;
- // n = a.nWords, m = b.nWords
- if ( this.nWords >= other.nWords ){
- a = this.data;
- n = this.nWords;
- b = other.data;
- m = other.nWords;
- } else {
- a = other.data;
- n = other.nWords;
- b = this.data;
- m = this.nWords;
- }
- int r[] = new int[ n ];
- for ( i = 0; i < n; i++ ){
- c += (long)a[i] & 0xffffffffL;
- if ( i < m ){
- c += (long)b[i] & 0xffffffffL;
- }
- r[i] = (int) c;
- c >>= 32; // signed shift.
- }
- if ( c != 0L ){
- // oops -- carry out -- need longer result.
- int s[] = new int[ r.length+1 ];
- System.arraycopy( r, 0, s, 0, r.length );
- s[i++] = (int)c;
- return new OldFDBigIntForTest( s, i );
- }
- return new OldFDBigIntForTest( r, i );
- }
-
- /*
- * Subtract one OldFDBigIntForTest from another. Return a OldFDBigIntForTest
- * Assert that the result is positive.
- */
- public OldFDBigIntForTest
- sub( OldFDBigIntForTest other ){
- int r[] = new int[ this.nWords ];
- int i;
- int n = this.nWords;
- int m = other.nWords;
- int nzeros = 0;
- long c = 0L;
- for ( i = 0; i < n; i++ ){
- c += (long)this.data[i] & 0xffffffffL;
- if ( i < m ){
- c -= (long)other.data[i] & 0xffffffffL;
- }
- if ( ( r[i] = (int) c ) == 0 )
- nzeros++;
- else
- nzeros = 0;
- c >>= 32; // signed shift
- }
- assert c == 0L : c; // borrow out of subtract
- assert dataInRangeIsZero(i, m, other); // negative result of subtract
- return new OldFDBigIntForTest( r, n-nzeros );
- }
-
- private static boolean dataInRangeIsZero(int i, int m, OldFDBigIntForTest other) {
- while ( i < m )
- if (other.data[i++] != 0)
- return false;
- return true;
- }
-
- /*
- * Compare OldFDBigIntForTest with another OldFDBigIntForTest. Return an integer
- * >0: this > other
- * 0: this == other
- * <0: this < other
- */
- public int
- cmp( OldFDBigIntForTest other ){
- int i;
- if ( this.nWords > other.nWords ){
- // if any of my high-order words is non-zero,
- // then the answer is evident
- int j = other.nWords-1;
- for ( i = this.nWords-1; i > j ; i-- )
- if ( this.data[i] != 0 ) return 1;
- }else if ( this.nWords < other.nWords ){
- // if any of other's high-order words is non-zero,
- // then the answer is evident
- int j = this.nWords-1;
- for ( i = other.nWords-1; i > j ; i-- )
- if ( other.data[i] != 0 ) return -1;
- } else{
- i = this.nWords-1;
- }
- for ( ; i > 0 ; i-- )
- if ( this.data[i] != other.data[i] )
- break;
- // careful! want unsigned compare!
- // use brute force here.
- int a = this.data[i];
- int b = other.data[i];
- if ( a < 0 ){
- // a is really big, unsigned
- if ( b < 0 ){
- return a-b; // both big, negative
- } else {
- return 1; // b not big, answer is obvious;
- }
- } else {
- // a is not really big
- if ( b < 0 ) {
- // but b is really big
- return -1;
- } else {
- return a - b;
- }
- }
- }
-
- /*
- * Compute
- * q = (int)( this / S )
- * this = 10 * ( this mod S )
- * Return q.
- * This is the iteration step of digit development for output.
- * We assume that S has been normalized, as above, and that
- * "this" has been lshift'ed accordingly.
- * Also assume, of course, that the result, q, can be expressed
- * as an integer, 0 <= q < 10.
- */
- public int
- quoRemIteration( OldFDBigIntForTest S )throws IllegalArgumentException {
- // ensure that this and S have the same number of
- // digits. If S is properly normalized and q < 10 then
- // this must be so.
- if ( nWords != S.nWords ){
- throw new IllegalArgumentException("disparate values");
- }
- // estimate q the obvious way. We will usually be
- // right. If not, then we're only off by a little and
- // will re-add.
- int n = nWords-1;
- long q = ((long)data[n]&0xffffffffL) / (long)S.data[n];
- long diff = 0L;
- for ( int i = 0; i <= n ; i++ ){
- diff += ((long)data[i]&0xffffffffL) - q*((long)S.data[i]&0xffffffffL);
- data[i] = (int)diff;
- diff >>= 32; // N.B. SIGNED shift.
- }
- if ( diff != 0L ) {
- // damn, damn, damn. q is too big.
- // add S back in until this turns +. This should
- // not be very many times!
- long sum = 0L;
- while ( sum == 0L ){
- sum = 0L;
- for ( int i = 0; i <= n; i++ ){
- sum += ((long)data[i]&0xffffffffL) + ((long)S.data[i]&0xffffffffL);
- data[i] = (int) sum;
- sum >>= 32; // Signed or unsigned, answer is 0 or 1
- }
- /*
- * Originally the following line read
- * "if ( sum !=0 && sum != -1 )"
- * but that would be wrong, because of the
- * treatment of the two values as entirely unsigned,
- * it would be impossible for a carry-out to be interpreted
- * as -1 -- it would have to be a single-bit carry-out, or
- * +1.
- */
- assert sum == 0 || sum == 1 : sum; // carry out of division correction
- q -= 1;
- }
- }
- // finally, we can multiply this by 10.
- // it cannot overflow, right, as the high-order word has
- // at least 4 high-order zeros!
- long p = 0L;
- for ( int i = 0; i <= n; i++ ){
- p += 10*((long)data[i]&0xffffffffL);
- data[i] = (int)p;
- p >>= 32; // SIGNED shift.
- }
- assert p == 0L : p; // Carry out of *10
- return (int)q;
- }
-
- public long
- longValue(){
- // if this can be represented as a long, return the value
- assert this.nWords > 0 : this.nWords; // longValue confused
-
- if (this.nWords == 1)
- return ((long)data[0]&0xffffffffL);
-
- assert dataInRangeIsZero(2, this.nWords, this); // value too big
- assert data[1] >= 0; // value too big
- return ((long)(data[1]) << 32) | ((long)data[0]&0xffffffffL);
- }
-
- public String
- toString() {
- StringBuffer r = new StringBuffer(30);
- r.append('[');
- int i = Math.min( nWords-1, data.length-1) ;
- if ( nWords > data.length ){
- r.append( "("+data.length+"<"+nWords+"!)" );
- }
- for( ; i> 0 ; i-- ){
- r.append( Integer.toHexString( data[i] ) );
- r.append(' ');
- }
- r.append( Integer.toHexString( data[0] ) );
- r.append(']');
- return new String( r );
- }
-}
--- a/jdk/test/sun/misc/FloatingDecimal/OldFloatingDecimalForTest.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2434 +0,0 @@
-/*
- * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-//package sun.misc;
-
-import java.util.regex.*;
-
-public class OldFloatingDecimalForTest{
- boolean isExceptional;
- boolean isNegative;
- int decExponent;
- char digits[];
- int nDigits;
- int bigIntExp;
- int bigIntNBits;
- boolean mustSetRoundDir = false;
- boolean fromHex = false;
- int roundDir = 0; // set by doubleValue
-
- /*
- * The fields below provides additional information about the result of
- * the binary to decimal digits conversion done in dtoa() and roundup()
- * methods. They are changed if needed by those two methods.
- */
-
- // True if the dtoa() binary to decimal conversion was exact.
- boolean exactDecimalConversion = false;
-
- // True if the result of the binary to decimal conversion was rounded-up
- // at the end of the conversion process, i.e. roundUp() method was called.
- boolean decimalDigitsRoundedUp = false;
-
- private OldFloatingDecimalForTest( boolean negSign, int decExponent, char []digits, int n, boolean e )
- {
- isNegative = negSign;
- isExceptional = e;
- this.decExponent = decExponent;
- this.digits = digits;
- this.nDigits = n;
- }
-
- /*
- * Constants of the implementation
- * Most are IEEE-754 related.
- * (There are more really boring constants at the end.)
- */
- static final long signMask = 0x8000000000000000L;
- static final long expMask = 0x7ff0000000000000L;
- static final long fractMask= ~(signMask|expMask);
- static final int expShift = 52;
- static final int expBias = 1023;
- static final long fractHOB = ( 1L<<expShift ); // assumed High-Order bit
- static final long expOne = ((long)expBias)<<expShift; // exponent of 1.0
- static final int maxSmallBinExp = 62;
- static final int minSmallBinExp = -( 63 / 3 );
- static final int maxDecimalDigits = 15;
- static final int maxDecimalExponent = 308;
- static final int minDecimalExponent = -324;
- static final int bigDecimalExponent = 324; // i.e. abs(minDecimalExponent)
-
- static final long highbyte = 0xff00000000000000L;
- static final long highbit = 0x8000000000000000L;
- static final long lowbytes = ~highbyte;
-
- static final int singleSignMask = 0x80000000;
- static final int singleExpMask = 0x7f800000;
- static final int singleFractMask = ~(singleSignMask|singleExpMask);
- static final int singleExpShift = 23;
- static final int singleFractHOB = 1<<singleExpShift;
- static final int singleExpBias = 127;
- static final int singleMaxDecimalDigits = 7;
- static final int singleMaxDecimalExponent = 38;
- static final int singleMinDecimalExponent = -45;
-
- static final int intDecimalDigits = 9;
-
-
- /*
- * count number of bits from high-order 1 bit to low-order 1 bit,
- * inclusive.
- */
- private static int
- countBits( long v ){
- //
- // the strategy is to shift until we get a non-zero sign bit
- // then shift until we have no bits left, counting the difference.
- // we do byte shifting as a hack. Hope it helps.
- //
- if ( v == 0L ) return 0;
-
- while ( ( v & highbyte ) == 0L ){
- v <<= 8;
- }
- while ( v > 0L ) { // i.e. while ((v&highbit) == 0L )
- v <<= 1;
- }
-
- int n = 0;
- while (( v & lowbytes ) != 0L ){
- v <<= 8;
- n += 8;
- }
- while ( v != 0L ){
- v <<= 1;
- n += 1;
- }
- return n;
- }
-
- /*
- * Keep big powers of 5 handy for future reference.
- */
- private static OldFDBigIntForTest b5p[];
-
- private static synchronized OldFDBigIntForTest
- big5pow( int p ){
- assert p >= 0 : p; // negative power of 5
- if ( b5p == null ){
- b5p = new OldFDBigIntForTest[ p+1 ];
- }else if (b5p.length <= p ){
- OldFDBigIntForTest t[] = new OldFDBigIntForTest[ p+1 ];
- System.arraycopy( b5p, 0, t, 0, b5p.length );
- b5p = t;
- }
- if ( b5p[p] != null )
- return b5p[p];
- else if ( p < small5pow.length )
- return b5p[p] = new OldFDBigIntForTest( small5pow[p] );
- else if ( p < long5pow.length )
- return b5p[p] = new OldFDBigIntForTest( long5pow[p] );
- else {
- // construct the value.
- // recursively.
- int q, r;
- // in order to compute 5^p,
- // compute its square root, 5^(p/2) and square.
- // or, let q = p / 2, r = p -q, then
- // 5^p = 5^(q+r) = 5^q * 5^r
- q = p >> 1;
- r = p - q;
- OldFDBigIntForTest bigq = b5p[q];
- if ( bigq == null )
- bigq = big5pow ( q );
- if ( r < small5pow.length ){
- return (b5p[p] = bigq.mult( small5pow[r] ) );
- }else{
- OldFDBigIntForTest bigr = b5p[ r ];
- if ( bigr == null )
- bigr = big5pow( r );
- return (b5p[p] = bigq.mult( bigr ) );
- }
- }
- }
-
- //
- // a common operation
- //
- private static OldFDBigIntForTest
- multPow52( OldFDBigIntForTest v, int p5, int p2 ){
- if ( p5 != 0 ){
- if ( p5 < small5pow.length ){
- v = v.mult( small5pow[p5] );
- } else {
- v = v.mult( big5pow( p5 ) );
- }
- }
- if ( p2 != 0 ){
- v.lshiftMe( p2 );
- }
- return v;
- }
-
- //
- // another common operation
- //
- private static OldFDBigIntForTest
- constructPow52( int p5, int p2 ){
- OldFDBigIntForTest v = new OldFDBigIntForTest( big5pow( p5 ) );
- if ( p2 != 0 ){
- v.lshiftMe( p2 );
- }
- return v;
- }
-
- /*
- * Make a floating double into a OldFDBigIntForTest.
- * This could also be structured as a OldFDBigIntForTest
- * constructor, but we'd have to build a lot of knowledge
- * about floating-point representation into it, and we don't want to.
- *
- * AS A SIDE EFFECT, THIS METHOD WILL SET THE INSTANCE VARIABLES
- * bigIntExp and bigIntNBits
- *
- */
- private OldFDBigIntForTest
- doubleToBigInt( double dval ){
- long lbits = Double.doubleToLongBits( dval ) & ~signMask;
- int binexp = (int)(lbits >>> expShift);
- lbits &= fractMask;
- if ( binexp > 0 ){
- lbits |= fractHOB;
- } else {
- assert lbits != 0L : lbits; // doubleToBigInt(0.0)
- binexp +=1;
- while ( (lbits & fractHOB ) == 0L){
- lbits <<= 1;
- binexp -= 1;
- }
- }
- binexp -= expBias;
- int nbits = countBits( lbits );
- /*
- * We now know where the high-order 1 bit is,
- * and we know how many there are.
- */
- int lowOrderZeros = expShift+1-nbits;
- lbits >>>= lowOrderZeros;
-
- bigIntExp = binexp+1-nbits;
- bigIntNBits = nbits;
- return new OldFDBigIntForTest( lbits );
- }
-
- /*
- * Compute a number that is the ULP of the given value,
- * for purposes of addition/subtraction. Generally easy.
- * More difficult if subtracting and the argument
- * is a normalized a power of 2, as the ULP changes at these points.
- */
- private static double ulp( double dval, boolean subtracting ){
- long lbits = Double.doubleToLongBits( dval ) & ~signMask;
- int binexp = (int)(lbits >>> expShift);
- double ulpval;
- if ( subtracting && ( binexp >= expShift ) && ((lbits&fractMask) == 0L) ){
- // for subtraction from normalized, powers of 2,
- // use next-smaller exponent
- binexp -= 1;
- }
- if ( binexp > expShift ){
- ulpval = Double.longBitsToDouble( ((long)(binexp-expShift))<<expShift );
- } else if ( binexp == 0 ){
- ulpval = Double.MIN_VALUE;
- } else {
- ulpval = Double.longBitsToDouble( 1L<<(binexp-1) );
- }
- if ( subtracting ) ulpval = - ulpval;
-
- return ulpval;
- }
-
- /*
- * Round a double to a float.
- * In addition to the fraction bits of the double,
- * look at the class instance variable roundDir,
- * which should help us avoid double-rounding error.
- * roundDir was set in hardValueOf if the estimate was
- * close enough, but not exact. It tells us which direction
- * of rounding is preferred.
- */
- float
- stickyRound( double dval ){
- long lbits = Double.doubleToLongBits( dval );
- long binexp = lbits & expMask;
- if ( binexp == 0L || binexp == expMask ){
- // what we have here is special.
- // don't worry, the right thing will happen.
- return (float) dval;
- }
- lbits += (long)roundDir; // hack-o-matic.
- return (float)Double.longBitsToDouble( lbits );
- }
-
-
- /*
- * This is the easy subcase --
- * all the significant bits, after scaling, are held in lvalue.
- * negSign and decExponent tell us what processing and scaling
- * has already been done. Exceptional cases have already been
- * stripped out.
- * In particular:
- * lvalue is a finite number (not Inf, nor NaN)
- * lvalue > 0L (not zero, nor negative).
- *
- * The only reason that we develop the digits here, rather than
- * calling on Long.toString() is that we can do it a little faster,
- * and besides want to treat trailing 0s specially. If Long.toString
- * changes, we should re-evaluate this strategy!
- */
- private void
- developLongDigits( int decExponent, long lvalue, long insignificant ){
- char digits[];
- int ndigits;
- int digitno;
- int c;
- //
- // Discard non-significant low-order bits, while rounding,
- // up to insignificant value.
- int i;
- for ( i = 0; insignificant >= 10L; i++ )
- insignificant /= 10L;
- if ( i != 0 ){
- long pow10 = long5pow[i] << i; // 10^i == 5^i * 2^i;
- long residue = lvalue % pow10;
- lvalue /= pow10;
- decExponent += i;
- if ( residue >= (pow10>>1) ){
- // round up based on the low-order bits we're discarding
- lvalue++;
- }
- }
- if ( lvalue <= Integer.MAX_VALUE ){
- assert lvalue > 0L : lvalue; // lvalue <= 0
- // even easier subcase!
- // can do int arithmetic rather than long!
- int ivalue = (int)lvalue;
- ndigits = 10;
- digits = perThreadBuffer.get();
- digitno = ndigits-1;
- c = ivalue%10;
- ivalue /= 10;
- while ( c == 0 ){
- decExponent++;
- c = ivalue%10;
- ivalue /= 10;
- }
- while ( ivalue != 0){
- digits[digitno--] = (char)(c+'0');
- decExponent++;
- c = ivalue%10;
- ivalue /= 10;
- }
- digits[digitno] = (char)(c+'0');
- } else {
- // same algorithm as above (same bugs, too )
- // but using long arithmetic.
- ndigits = 20;
- digits = perThreadBuffer.get();
- digitno = ndigits-1;
- c = (int)(lvalue%10L);
- lvalue /= 10L;
- while ( c == 0 ){
- decExponent++;
- c = (int)(lvalue%10L);
- lvalue /= 10L;
- }
- while ( lvalue != 0L ){
- digits[digitno--] = (char)(c+'0');
- decExponent++;
- c = (int)(lvalue%10L);
- lvalue /= 10;
- }
- digits[digitno] = (char)(c+'0');
- }
- char result [];
- ndigits -= digitno;
- result = new char[ ndigits ];
- System.arraycopy( digits, digitno, result, 0, ndigits );
- this.digits = result;
- this.decExponent = decExponent+1;
- this.nDigits = ndigits;
- }
-
- //
- // add one to the least significant digit.
- // in the unlikely event there is a carry out,
- // deal with it.
- // assert that this will only happen where there
- // is only one digit, e.g. (float)1e-44 seems to do it.
- //
- private void
- roundup(){
- int i;
- int q = digits[ i = (nDigits-1)];
- if ( q == '9' ){
- while ( q == '9' && i > 0 ){
- digits[i] = '0';
- q = digits[--i];
- }
- if ( q == '9' ){
- // carryout! High-order 1, rest 0s, larger exp.
- decExponent += 1;
- digits[0] = '1';
- return;
- }
- // else fall through.
- }
- digits[i] = (char)(q+1);
- decimalDigitsRoundedUp = true;
- }
-
- public boolean digitsRoundedUp() {
- return decimalDigitsRoundedUp;
- }
-
- /*
- * FIRST IMPORTANT CONSTRUCTOR: DOUBLE
- */
- public OldFloatingDecimalForTest( double d )
- {
- long dBits = Double.doubleToLongBits( d );
- long fractBits;
- int binExp;
- int nSignificantBits;
-
- // discover and delete sign
- if ( (dBits&signMask) != 0 ){
- isNegative = true;
- dBits ^= signMask;
- } else {
- isNegative = false;
- }
- // Begin to unpack
- // Discover obvious special cases of NaN and Infinity.
- binExp = (int)( (dBits&expMask) >> expShift );
- fractBits = dBits&fractMask;
- if ( binExp == (int)(expMask>>expShift) ) {
- isExceptional = true;
- if ( fractBits == 0L ){
- digits = infinity;
- } else {
- digits = notANumber;
- isNegative = false; // NaN has no sign!
- }
- nDigits = digits.length;
- return;
- }
- isExceptional = false;
- // Finish unpacking
- // Normalize denormalized numbers.
- // Insert assumed high-order bit for normalized numbers.
- // Subtract exponent bias.
- if ( binExp == 0 ){
- if ( fractBits == 0L ){
- // not a denorm, just a 0!
- decExponent = 0;
- digits = zero;
- nDigits = 1;
- return;
- }
- while ( (fractBits&fractHOB) == 0L ){
- fractBits <<= 1;
- binExp -= 1;
- }
- nSignificantBits = expShift + binExp +1; // recall binExp is - shift count.
- binExp += 1;
- } else {
- fractBits |= fractHOB;
- nSignificantBits = expShift+1;
- }
- binExp -= expBias;
- // call the routine that actually does all the hard work.
- dtoa( binExp, fractBits, nSignificantBits );
- }
-
- /*
- * SECOND IMPORTANT CONSTRUCTOR: SINGLE
- */
- public OldFloatingDecimalForTest( float f )
- {
- int fBits = Float.floatToIntBits( f );
- int fractBits;
- int binExp;
- int nSignificantBits;
-
- // discover and delete sign
- if ( (fBits&singleSignMask) != 0 ){
- isNegative = true;
- fBits ^= singleSignMask;
- } else {
- isNegative = false;
- }
- // Begin to unpack
- // Discover obvious special cases of NaN and Infinity.
- binExp = (fBits&singleExpMask) >> singleExpShift;
- fractBits = fBits&singleFractMask;
- if ( binExp == (singleExpMask>>singleExpShift) ) {
- isExceptional = true;
- if ( fractBits == 0L ){
- digits = infinity;
- } else {
- digits = notANumber;
- isNegative = false; // NaN has no sign!
- }
- nDigits = digits.length;
- return;
- }
- isExceptional = false;
- // Finish unpacking
- // Normalize denormalized numbers.
- // Insert assumed high-order bit for normalized numbers.
- // Subtract exponent bias.
- if ( binExp == 0 ){
- if ( fractBits == 0 ){
- // not a denorm, just a 0!
- decExponent = 0;
- digits = zero;
- nDigits = 1;
- return;
- }
- while ( (fractBits&singleFractHOB) == 0 ){
- fractBits <<= 1;
- binExp -= 1;
- }
- nSignificantBits = singleExpShift + binExp +1; // recall binExp is - shift count.
- binExp += 1;
- } else {
- fractBits |= singleFractHOB;
- nSignificantBits = singleExpShift+1;
- }
- binExp -= singleExpBias;
- // call the routine that actually does all the hard work.
- dtoa( binExp, ((long)fractBits)<<(expShift-singleExpShift), nSignificantBits );
- }
-
- private void
- dtoa( int binExp, long fractBits, int nSignificantBits )
- {
- int nFractBits; // number of significant bits of fractBits;
- int nTinyBits; // number of these to the right of the point.
- int decExp;
-
- // Examine number. Determine if it is an easy case,
- // which we can do pretty trivially using float/long conversion,
- // or whether we must do real work.
- nFractBits = countBits( fractBits );
- nTinyBits = Math.max( 0, nFractBits - binExp - 1 );
- if ( binExp <= maxSmallBinExp && binExp >= minSmallBinExp ){
- // Look more closely at the number to decide if,
- // with scaling by 10^nTinyBits, the result will fit in
- // a long.
- if ( (nTinyBits < long5pow.length) && ((nFractBits + n5bits[nTinyBits]) < 64 ) ){
- /*
- * We can do this:
- * take the fraction bits, which are normalized.
- * (a) nTinyBits == 0: Shift left or right appropriately
- * to align the binary point at the extreme right, i.e.
- * where a long int point is expected to be. The integer
- * result is easily converted to a string.
- * (b) nTinyBits > 0: Shift right by expShift-nFractBits,
- * which effectively converts to long and scales by
- * 2^nTinyBits. Then multiply by 5^nTinyBits to
- * complete the scaling. We know this won't overflow
- * because we just counted the number of bits necessary
- * in the result. The integer you get from this can
- * then be converted to a string pretty easily.
- */
- long halfULP;
- if ( nTinyBits == 0 ) {
- if ( binExp > nSignificantBits ){
- halfULP = 1L << ( binExp-nSignificantBits-1);
- } else {
- halfULP = 0L;
- }
- if ( binExp >= expShift ){
- fractBits <<= (binExp-expShift);
- } else {
- fractBits >>>= (expShift-binExp) ;
- }
- developLongDigits( 0, fractBits, halfULP );
- return;
- }
- /*
- * The following causes excess digits to be printed
- * out in the single-float case. Our manipulation of
- * halfULP here is apparently not correct. If we
- * better understand how this works, perhaps we can
- * use this special case again. But for the time being,
- * we do not.
- * else {
- * fractBits >>>= expShift+1-nFractBits;
- * fractBits *= long5pow[ nTinyBits ];
- * halfULP = long5pow[ nTinyBits ] >> (1+nSignificantBits-nFractBits);
- * developLongDigits( -nTinyBits, fractBits, halfULP );
- * return;
- * }
- */
- }
- }
- /*
- * This is the hard case. We are going to compute large positive
- * integers B and S and integer decExp, s.t.
- * d = ( B / S ) * 10^decExp
- * 1 <= B / S < 10
- * Obvious choices are:
- * decExp = floor( log10(d) )
- * B = d * 2^nTinyBits * 10^max( 0, -decExp )
- * S = 10^max( 0, decExp) * 2^nTinyBits
- * (noting that nTinyBits has already been forced to non-negative)
- * I am also going to compute a large positive integer
- * M = (1/2^nSignificantBits) * 2^nTinyBits * 10^max( 0, -decExp )
- * i.e. M is (1/2) of the ULP of d, scaled like B.
- * When we iterate through dividing B/S and picking off the
- * quotient bits, we will know when to stop when the remainder
- * is <= M.
- *
- * We keep track of powers of 2 and powers of 5.
- */
-
- /*
- * Estimate decimal exponent. (If it is small-ish,
- * we could double-check.)
- *
- * First, scale the mantissa bits such that 1 <= d2 < 2.
- * We are then going to estimate
- * log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5)
- * and so we can estimate
- * log10(d) ~=~ log10(d2) + binExp * log10(2)
- * take the floor and call it decExp.
- * FIXME -- use more precise constants here. It costs no more.
- */
- double d2 = Double.longBitsToDouble(
- expOne | ( fractBits &~ fractHOB ) );
- decExp = (int)Math.floor(
- (d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981 );
- int B2, B5; // powers of 2 and powers of 5, respectively, in B
- int S2, S5; // powers of 2 and powers of 5, respectively, in S
- int M2, M5; // powers of 2 and powers of 5, respectively, in M
- int Bbits; // binary digits needed to represent B, approx.
- int tenSbits; // binary digits needed to represent 10*S, approx.
- OldFDBigIntForTest Sval, Bval, Mval;
-
- B5 = Math.max( 0, -decExp );
- B2 = B5 + nTinyBits + binExp;
-
- S5 = Math.max( 0, decExp );
- S2 = S5 + nTinyBits;
-
- M5 = B5;
- M2 = B2 - nSignificantBits;
-
- /*
- * the long integer fractBits contains the (nFractBits) interesting
- * bits from the mantissa of d ( hidden 1 added if necessary) followed
- * by (expShift+1-nFractBits) zeros. In the interest of compactness,
- * I will shift out those zeros before turning fractBits into a
- * OldFDBigIntForTest. The resulting whole number will be
- * d * 2^(nFractBits-1-binExp).
- */
- fractBits >>>= (expShift+1-nFractBits);
- B2 -= nFractBits-1;
- int common2factor = Math.min( B2, S2 );
- B2 -= common2factor;
- S2 -= common2factor;
- M2 -= common2factor;
-
- /*
- * HACK!! For exact powers of two, the next smallest number
- * is only half as far away as we think (because the meaning of
- * ULP changes at power-of-two bounds) for this reason, we
- * hack M2. Hope this works.
- */
- if ( nFractBits == 1 )
- M2 -= 1;
-
- if ( M2 < 0 ){
- // oops.
- // since we cannot scale M down far enough,
- // we must scale the other values up.
- B2 -= M2;
- S2 -= M2;
- M2 = 0;
- }
- /*
- * Construct, Scale, iterate.
- * Some day, we'll write a stopping test that takes
- * account of the asymmetry of the spacing of floating-point
- * numbers below perfect powers of 2
- * 26 Sept 96 is not that day.
- * So we use a symmetric test.
- */
- char digits[] = this.digits = new char[18];
- int ndigit = 0;
- boolean low, high;
- long lowDigitDifference;
- int q;
-
- /*
- * Detect the special cases where all the numbers we are about
- * to compute will fit in int or long integers.
- * In these cases, we will avoid doing OldFDBigIntForTest arithmetic.
- * We use the same algorithms, except that we "normalize"
- * our OldFDBigIntForTests before iterating. This is to make division easier,
- * as it makes our fist guess (quotient of high-order words)
- * more accurate!
- *
- * Some day, we'll write a stopping test that takes
- * account of the asymmetry of the spacing of floating-point
- * numbers below perfect powers of 2
- * 26 Sept 96 is not that day.
- * So we use a symmetric test.
- */
- Bbits = nFractBits + B2 + (( B5 < n5bits.length )? n5bits[B5] : ( B5*3 ));
- tenSbits = S2+1 + (( (S5+1) < n5bits.length )? n5bits[(S5+1)] : ( (S5+1)*3 ));
- if ( Bbits < 64 && tenSbits < 64){
- if ( Bbits < 32 && tenSbits < 32){
- // wa-hoo! They're all ints!
- int b = ((int)fractBits * small5pow[B5] ) << B2;
- int s = small5pow[S5] << S2;
- int m = small5pow[M5] << M2;
- int tens = s * 10;
- /*
- * Unroll the first iteration. If our decExp estimate
- * was too high, our first quotient will be zero. In this
- * case, we discard it and decrement decExp.
- */
- ndigit = 0;
- q = b / s;
- b = 10 * ( b % s );
- m *= 10;
- low = (b < m );
- high = (b+m > tens );
- assert q < 10 : q; // excessively large digit
- if ( (q == 0) && ! high ){
- // oops. Usually ignore leading zero.
- decExp--;
- } else {
- digits[ndigit++] = (char)('0' + q);
- }
- /*
- * HACK! Java spec sez that we always have at least
- * one digit after the . in either F- or E-form output.
- * Thus we will need more than one digit if we're using
- * E-form
- */
- if ( decExp < -3 || decExp >= 8 ){
- high = low = false;
- }
- while( ! low && ! high ){
- q = b / s;
- b = 10 * ( b % s );
- m *= 10;
- assert q < 10 : q; // excessively large digit
- if ( m > 0L ){
- low = (b < m );
- high = (b+m > tens );
- } else {
- // hack -- m might overflow!
- // in this case, it is certainly > b,
- // which won't
- // and b+m > tens, too, since that has overflowed
- // either!
- low = true;
- high = true;
- }
- digits[ndigit++] = (char)('0' + q);
- }
- lowDigitDifference = (b<<1) - tens;
- exactDecimalConversion = (b == 0);
- } else {
- // still good! they're all longs!
- long b = (fractBits * long5pow[B5] ) << B2;
- long s = long5pow[S5] << S2;
- long m = long5pow[M5] << M2;
- long tens = s * 10L;
- /*
- * Unroll the first iteration. If our decExp estimate
- * was too high, our first quotient will be zero. In this
- * case, we discard it and decrement decExp.
- */
- ndigit = 0;
- q = (int) ( b / s );
- b = 10L * ( b % s );
- m *= 10L;
- low = (b < m );
- high = (b+m > tens );
- assert q < 10 : q; // excessively large digit
- if ( (q == 0) && ! high ){
- // oops. Usually ignore leading zero.
- decExp--;
- } else {
- digits[ndigit++] = (char)('0' + q);
- }
- /*
- * HACK! Java spec sez that we always have at least
- * one digit after the . in either F- or E-form output.
- * Thus we will need more than one digit if we're using
- * E-form
- */
- if ( decExp < -3 || decExp >= 8 ){
- high = low = false;
- }
- while( ! low && ! high ){
- q = (int) ( b / s );
- b = 10 * ( b % s );
- m *= 10;
- assert q < 10 : q; // excessively large digit
- if ( m > 0L ){
- low = (b < m );
- high = (b+m > tens );
- } else {
- // hack -- m might overflow!
- // in this case, it is certainly > b,
- // which won't
- // and b+m > tens, too, since that has overflowed
- // either!
- low = true;
- high = true;
- }
- digits[ndigit++] = (char)('0' + q);
- }
- lowDigitDifference = (b<<1) - tens;
- exactDecimalConversion = (b == 0);
- }
- } else {
- OldFDBigIntForTest ZeroVal = new OldFDBigIntForTest(0);
- OldFDBigIntForTest tenSval;
- int shiftBias;
-
- /*
- * We really must do OldFDBigIntForTest arithmetic.
- * Fist, construct our OldFDBigIntForTest initial values.
- */
- Bval = multPow52( new OldFDBigIntForTest( fractBits ), B5, B2 );
- Sval = constructPow52( S5, S2 );
- Mval = constructPow52( M5, M2 );
-
-
- // normalize so that division works better
- Bval.lshiftMe( shiftBias = Sval.normalizeMe() );
- Mval.lshiftMe( shiftBias );
- tenSval = Sval.mult( 10 );
- /*
- * Unroll the first iteration. If our decExp estimate
- * was too high, our first quotient will be zero. In this
- * case, we discard it and decrement decExp.
- */
- ndigit = 0;
- q = Bval.quoRemIteration( Sval );
- Mval = Mval.mult( 10 );
- low = (Bval.cmp( Mval ) < 0);
- high = (Bval.add( Mval ).cmp( tenSval ) > 0 );
- assert q < 10 : q; // excessively large digit
- if ( (q == 0) && ! high ){
- // oops. Usually ignore leading zero.
- decExp--;
- } else {
- digits[ndigit++] = (char)('0' + q);
- }
- /*
- * HACK! Java spec sez that we always have at least
- * one digit after the . in either F- or E-form output.
- * Thus we will need more than one digit if we're using
- * E-form
- */
- if ( decExp < -3 || decExp >= 8 ){
- high = low = false;
- }
- while( ! low && ! high ){
- q = Bval.quoRemIteration( Sval );
- Mval = Mval.mult( 10 );
- assert q < 10 : q; // excessively large digit
- low = (Bval.cmp( Mval ) < 0);
- high = (Bval.add( Mval ).cmp( tenSval ) > 0 );
- digits[ndigit++] = (char)('0' + q);
- }
- if ( high && low ){
- Bval.lshiftMe(1);
- lowDigitDifference = Bval.cmp(tenSval);
- } else {
- lowDigitDifference = 0L; // this here only for flow analysis!
- }
- exactDecimalConversion = (Bval.cmp( ZeroVal ) == 0);
- }
- this.decExponent = decExp+1;
- this.digits = digits;
- this.nDigits = ndigit;
- /*
- * Last digit gets rounded based on stopping condition.
- */
- if ( high ){
- if ( low ){
- if ( lowDigitDifference == 0L ){
- // it's a tie!
- // choose based on which digits we like.
- if ( (digits[nDigits-1]&1) != 0 ) roundup();
- } else if ( lowDigitDifference > 0 ){
- roundup();
- }
- } else {
- roundup();
- }
- }
- }
-
- public boolean decimalDigitsExact() {
- return exactDecimalConversion;
- }
-
- public String
- toString(){
- // most brain-dead version
- StringBuffer result = new StringBuffer( nDigits+8 );
- if ( isNegative ){ result.append( '-' ); }
- if ( isExceptional ){
- result.append( digits, 0, nDigits );
- } else {
- result.append( "0.");
- result.append( digits, 0, nDigits );
- result.append('e');
- result.append( decExponent );
- }
- return new String(result);
- }
-
- public String toJavaFormatString() {
- char result[] = perThreadBuffer.get();
- int i = getChars(result);
- return new String(result, 0, i);
- }
-
- private int getChars(char[] result) {
- assert nDigits <= 19 : nDigits; // generous bound on size of nDigits
- int i = 0;
- if (isNegative) { result[0] = '-'; i = 1; }
- if (isExceptional) {
- System.arraycopy(digits, 0, result, i, nDigits);
- i += nDigits;
- } else {
- if (decExponent > 0 && decExponent < 8) {
- // print digits.digits.
- int charLength = Math.min(nDigits, decExponent);
- System.arraycopy(digits, 0, result, i, charLength);
- i += charLength;
- if (charLength < decExponent) {
- charLength = decExponent-charLength;
- System.arraycopy(zero, 0, result, i, charLength);
- i += charLength;
- result[i++] = '.';
- result[i++] = '0';
- } else {
- result[i++] = '.';
- if (charLength < nDigits) {
- int t = nDigits - charLength;
- System.arraycopy(digits, charLength, result, i, t);
- i += t;
- } else {
- result[i++] = '0';
- }
- }
- } else if (decExponent <=0 && decExponent > -3) {
- result[i++] = '0';
- result[i++] = '.';
- if (decExponent != 0) {
- System.arraycopy(zero, 0, result, i, -decExponent);
- i -= decExponent;
- }
- System.arraycopy(digits, 0, result, i, nDigits);
- i += nDigits;
- } else {
- result[i++] = digits[0];
- result[i++] = '.';
- if (nDigits > 1) {
- System.arraycopy(digits, 1, result, i, nDigits-1);
- i += nDigits-1;
- } else {
- result[i++] = '0';
- }
- result[i++] = 'E';
- int e;
- if (decExponent <= 0) {
- result[i++] = '-';
- e = -decExponent+1;
- } else {
- e = decExponent-1;
- }
- // decExponent has 1, 2, or 3, digits
- if (e <= 9) {
- result[i++] = (char)(e+'0');
- } else if (e <= 99) {
- result[i++] = (char)(e/10 +'0');
- result[i++] = (char)(e%10 + '0');
- } else {
- result[i++] = (char)(e/100+'0');
- e %= 100;
- result[i++] = (char)(e/10+'0');
- result[i++] = (char)(e%10 + '0');
- }
- }
- }
- return i;
- }
-
- // Per-thread buffer for string/stringbuffer conversion
- private static ThreadLocal<char[]> perThreadBuffer = new ThreadLocal<char[]>() {
- protected synchronized char[] initialValue() {
- return new char[26];
- }
- };
-
- public void appendTo(Appendable buf) {
- char result[] = perThreadBuffer.get();
- int i = getChars(result);
- if (buf instanceof StringBuilder)
- ((StringBuilder) buf).append(result, 0, i);
- else if (buf instanceof StringBuffer)
- ((StringBuffer) buf).append(result, 0, i);
- else
- assert false;
- }
-
- @SuppressWarnings("fallthrough")
- public static OldFloatingDecimalForTest
- readJavaFormatString( String in ) throws NumberFormatException {
- boolean isNegative = false;
- boolean signSeen = false;
- int decExp;
- char c;
-
- parseNumber:
- try{
- in = in.trim(); // don't fool around with white space.
- // throws NullPointerException if null
- int l = in.length();
- if ( l == 0 ) throw new NumberFormatException("empty String");
- int i = 0;
- switch ( c = in.charAt( i ) ){
- case '-':
- isNegative = true;
- //FALLTHROUGH
- case '+':
- i++;
- signSeen = true;
- }
-
- // Check for NaN and Infinity strings
- c = in.charAt(i);
- if(c == 'N' || c == 'I') { // possible NaN or infinity
- boolean potentialNaN = false;
- char targetChars[] = null; // char array of "NaN" or "Infinity"
-
- if(c == 'N') {
- targetChars = notANumber;
- potentialNaN = true;
- } else {
- targetChars = infinity;
- }
-
- // compare Input string to "NaN" or "Infinity"
- int j = 0;
- while(i < l && j < targetChars.length) {
- if(in.charAt(i) == targetChars[j]) {
- i++; j++;
- }
- else // something is amiss, throw exception
- break parseNumber;
- }
-
- // For the candidate string to be a NaN or infinity,
- // all characters in input string and target char[]
- // must be matched ==> j must equal targetChars.length
- // and i must equal l
- if( (j == targetChars.length) && (i == l) ) { // return NaN or infinity
- return (potentialNaN ? new OldFloatingDecimalForTest(Double.NaN) // NaN has no sign
- : new OldFloatingDecimalForTest(isNegative?
- Double.NEGATIVE_INFINITY:
- Double.POSITIVE_INFINITY)) ;
- }
- else { // something went wrong, throw exception
- break parseNumber;
- }
-
- } else if (c == '0') { // check for hexadecimal floating-point number
- if (l > i+1 ) {
- char ch = in.charAt(i+1);
- if (ch == 'x' || ch == 'X' ) // possible hex string
- return parseHexString(in);
- }
- } // look for and process decimal floating-point string
-
- char[] digits = new char[ l ];
- int nDigits= 0;
- boolean decSeen = false;
- int decPt = 0;
- int nLeadZero = 0;
- int nTrailZero= 0;
- digitLoop:
- while ( i < l ){
- switch ( c = in.charAt( i ) ){
- case '0':
- if ( nDigits > 0 ){
- nTrailZero += 1;
- } else {
- nLeadZero += 1;
- }
- break; // out of switch.
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- while ( nTrailZero > 0 ){
- digits[nDigits++] = '0';
- nTrailZero -= 1;
- }
- digits[nDigits++] = c;
- break; // out of switch.
- case '.':
- if ( decSeen ){
- // already saw one ., this is the 2nd.
- throw new NumberFormatException("multiple points");
- }
- decPt = i;
- if ( signSeen ){
- decPt -= 1;
- }
- decSeen = true;
- break; // out of switch.
- default:
- break digitLoop;
- }
- i++;
- }
- /*
- * At this point, we've scanned all the digits and decimal
- * point we're going to see. Trim off leading and trailing
- * zeros, which will just confuse us later, and adjust
- * our initial decimal exponent accordingly.
- * To review:
- * we have seen i total characters.
- * nLeadZero of them were zeros before any other digits.
- * nTrailZero of them were zeros after any other digits.
- * if ( decSeen ), then a . was seen after decPt characters
- * ( including leading zeros which have been discarded )
- * nDigits characters were neither lead nor trailing
- * zeros, nor point
- */
- /*
- * special hack: if we saw no non-zero digits, then the
- * answer is zero!
- * Unfortunately, we feel honor-bound to keep parsing!
- */
- if ( nDigits == 0 ){
- digits = zero;
- nDigits = 1;
- if ( nLeadZero == 0 ){
- // we saw NO DIGITS AT ALL,
- // not even a crummy 0!
- // this is not allowed.
- break parseNumber; // go throw exception
- }
-
- }
-
- /* Our initial exponent is decPt, adjusted by the number of
- * discarded zeros. Or, if there was no decPt,
- * then its just nDigits adjusted by discarded trailing zeros.
- */
- if ( decSeen ){
- decExp = decPt - nLeadZero;
- } else {
- decExp = nDigits+nTrailZero;
- }
-
- /*
- * Look for 'e' or 'E' and an optionally signed integer.
- */
- if ( (i < l) && (((c = in.charAt(i) )=='e') || (c == 'E') ) ){
- int expSign = 1;
- int expVal = 0;
- int reallyBig = Integer.MAX_VALUE / 10;
- boolean expOverflow = false;
- switch( in.charAt(++i) ){
- case '-':
- expSign = -1;
- //FALLTHROUGH
- case '+':
- i++;
- }
- int expAt = i;
- expLoop:
- while ( i < l ){
- if ( expVal >= reallyBig ){
- // the next character will cause integer
- // overflow.
- expOverflow = true;
- }
- switch ( c = in.charAt(i++) ){
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- expVal = expVal*10 + ( (int)c - (int)'0' );
- continue;
- default:
- i--; // back up.
- break expLoop; // stop parsing exponent.
- }
- }
- int expLimit = bigDecimalExponent+nDigits+nTrailZero;
- if ( expOverflow || ( expVal > expLimit ) ){
- //
- // The intent here is to end up with
- // infinity or zero, as appropriate.
- // The reason for yielding such a small decExponent,
- // rather than something intuitive such as
- // expSign*Integer.MAX_VALUE, is that this value
- // is subject to further manipulation in
- // doubleValue() and floatValue(), and I don't want
- // it to be able to cause overflow there!
- // (The only way we can get into trouble here is for
- // really outrageous nDigits+nTrailZero, such as 2 billion. )
- //
- decExp = expSign*expLimit;
- } else {
- // this should not overflow, since we tested
- // for expVal > (MAX+N), where N >= abs(decExp)
- decExp = decExp + expSign*expVal;
- }
-
- // if we saw something not a digit ( or end of string )
- // after the [Ee][+-], without seeing any digits at all
- // this is certainly an error. If we saw some digits,
- // but then some trailing garbage, that might be ok.
- // so we just fall through in that case.
- // HUMBUG
- if ( i == expAt )
- break parseNumber; // certainly bad
- }
- /*
- * We parsed everything we could.
- * If there are leftovers, then this is not good input!
- */
- if ( i < l &&
- ((i != l - 1) ||
- (in.charAt(i) != 'f' &&
- in.charAt(i) != 'F' &&
- in.charAt(i) != 'd' &&
- in.charAt(i) != 'D'))) {
- break parseNumber; // go throw exception
- }
-
- return new OldFloatingDecimalForTest( isNegative, decExp, digits, nDigits, false );
- } catch ( StringIndexOutOfBoundsException e ){ }
- throw new NumberFormatException("For input string: \"" + in + "\"");
- }
-
- /*
- * Take a FloatingDecimal, which we presumably just scanned in,
- * and find out what its value is, as a double.
- *
- * AS A SIDE EFFECT, SET roundDir TO INDICATE PREFERRED
- * ROUNDING DIRECTION in case the result is really destined
- * for a single-precision float.
- */
-
- public strictfp double doubleValue(){
- int kDigits = Math.min( nDigits, maxDecimalDigits+1 );
- long lValue;
- double dValue;
- double rValue, tValue;
-
- // First, check for NaN and Infinity values
- if(digits == infinity || digits == notANumber) {
- if(digits == notANumber)
- return Double.NaN;
- else
- return (isNegative?Double.NEGATIVE_INFINITY:Double.POSITIVE_INFINITY);
- }
- else {
- if (mustSetRoundDir) {
- roundDir = 0;
- }
- /*
- * convert the lead kDigits to a long integer.
- */
- // (special performance hack: start to do it using int)
- int iValue = (int)digits[0]-(int)'0';
- int iDigits = Math.min( kDigits, intDecimalDigits );
- for ( int i=1; i < iDigits; i++ ){
- iValue = iValue*10 + (int)digits[i]-(int)'0';
- }
- lValue = (long)iValue;
- for ( int i=iDigits; i < kDigits; i++ ){
- lValue = lValue*10L + (long)((int)digits[i]-(int)'0');
- }
- dValue = (double)lValue;
- int exp = decExponent-kDigits;
- /*
- * lValue now contains a long integer with the value of
- * the first kDigits digits of the number.
- * dValue contains the (double) of the same.
- */
-
- if ( nDigits <= maxDecimalDigits ){
- /*
- * possibly an easy case.
- * We know that the digits can be represented
- * exactly. And if the exponent isn't too outrageous,
- * the whole thing can be done with one operation,
- * thus one rounding error.
- * Note that all our constructors trim all leading and
- * trailing zeros, so simple values (including zero)
- * will always end up here
- */
- if (exp == 0 || dValue == 0.0)
- return (isNegative)? -dValue : dValue; // small floating integer
- else if ( exp >= 0 ){
- if ( exp <= maxSmallTen ){
- /*
- * Can get the answer with one operation,
- * thus one roundoff.
- */
- rValue = dValue * small10pow[exp];
- if ( mustSetRoundDir ){
- tValue = rValue / small10pow[exp];
- roundDir = ( tValue == dValue ) ? 0
- :( tValue < dValue ) ? 1
- : -1;
- }
- return (isNegative)? -rValue : rValue;
- }
- int slop = maxDecimalDigits - kDigits;
- if ( exp <= maxSmallTen+slop ){
- /*
- * We can multiply dValue by 10^(slop)
- * and it is still "small" and exact.
- * Then we can multiply by 10^(exp-slop)
- * with one rounding.
- */
- dValue *= small10pow[slop];
- rValue = dValue * small10pow[exp-slop];
-
- if ( mustSetRoundDir ){
- tValue = rValue / small10pow[exp-slop];
- roundDir = ( tValue == dValue ) ? 0
- :( tValue < dValue ) ? 1
- : -1;
- }
- return (isNegative)? -rValue : rValue;
- }
- /*
- * Else we have a hard case with a positive exp.
- */
- } else {
- if ( exp >= -maxSmallTen ){
- /*
- * Can get the answer in one division.
- */
- rValue = dValue / small10pow[-exp];
- tValue = rValue * small10pow[-exp];
- if ( mustSetRoundDir ){
- roundDir = ( tValue == dValue ) ? 0
- :( tValue < dValue ) ? 1
- : -1;
- }
- return (isNegative)? -rValue : rValue;
- }
- /*
- * Else we have a hard case with a negative exp.
- */
- }
- }
-
- /*
- * Harder cases:
- * The sum of digits plus exponent is greater than
- * what we think we can do with one error.
- *
- * Start by approximating the right answer by,
- * naively, scaling by powers of 10.
- */
- if ( exp > 0 ){
- if ( decExponent > maxDecimalExponent+1 ){
- /*
- * Lets face it. This is going to be
- * Infinity. Cut to the chase.
- */
- return (isNegative)? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
- }
- if ( (exp&15) != 0 ){
- dValue *= small10pow[exp&15];
- }
- if ( (exp>>=4) != 0 ){
- int j;
- for( j = 0; exp > 1; j++, exp>>=1 ){
- if ( (exp&1)!=0)
- dValue *= big10pow[j];
- }
- /*
- * The reason for the weird exp > 1 condition
- * in the above loop was so that the last multiply
- * would get unrolled. We handle it here.
- * It could overflow.
- */
- double t = dValue * big10pow[j];
- if ( Double.isInfinite( t ) ){
- /*
- * It did overflow.
- * Look more closely at the result.
- * If the exponent is just one too large,
- * then use the maximum finite as our estimate
- * value. Else call the result infinity
- * and punt it.
- * ( I presume this could happen because
- * rounding forces the result here to be
- * an ULP or two larger than
- * Double.MAX_VALUE ).
- */
- t = dValue / 2.0;
- t *= big10pow[j];
- if ( Double.isInfinite( t ) ){
- return (isNegative)? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
- }
- t = Double.MAX_VALUE;
- }
- dValue = t;
- }
- } else if ( exp < 0 ){
- exp = -exp;
- if ( decExponent < minDecimalExponent-1 ){
- /*
- * Lets face it. This is going to be
- * zero. Cut to the chase.
- */
- return (isNegative)? -0.0 : 0.0;
- }
- if ( (exp&15) != 0 ){
- dValue /= small10pow[exp&15];
- }
- if ( (exp>>=4) != 0 ){
- int j;
- for( j = 0; exp > 1; j++, exp>>=1 ){
- if ( (exp&1)!=0)
- dValue *= tiny10pow[j];
- }
- /*
- * The reason for the weird exp > 1 condition
- * in the above loop was so that the last multiply
- * would get unrolled. We handle it here.
- * It could underflow.
- */
- double t = dValue * tiny10pow[j];
- if ( t == 0.0 ){
- /*
- * It did underflow.
- * Look more closely at the result.
- * If the exponent is just one too small,
- * then use the minimum finite as our estimate
- * value. Else call the result 0.0
- * and punt it.
- * ( I presume this could happen because
- * rounding forces the result here to be
- * an ULP or two less than
- * Double.MIN_VALUE ).
- */
- t = dValue * 2.0;
- t *= tiny10pow[j];
- if ( t == 0.0 ){
- return (isNegative)? -0.0 : 0.0;
- }
- t = Double.MIN_VALUE;
- }
- dValue = t;
- }
- }
-
- /*
- * dValue is now approximately the result.
- * The hard part is adjusting it, by comparison
- * with OldFDBigIntForTest arithmetic.
- * Formulate the EXACT big-number result as
- * bigD0 * 10^exp
- */
- OldFDBigIntForTest bigD0 = new OldFDBigIntForTest( lValue, digits, kDigits, nDigits );
- exp = decExponent - nDigits;
-
- correctionLoop:
- while(true){
- /* AS A SIDE EFFECT, THIS METHOD WILL SET THE INSTANCE VARIABLES
- * bigIntExp and bigIntNBits
- */
- OldFDBigIntForTest bigB = doubleToBigInt( dValue );
-
- /*
- * Scale bigD, bigB appropriately for
- * big-integer operations.
- * Naively, we multiply by powers of ten
- * and powers of two. What we actually do
- * is keep track of the powers of 5 and
- * powers of 2 we would use, then factor out
- * common divisors before doing the work.
- */
- int B2, B5; // powers of 2, 5 in bigB
- int D2, D5; // powers of 2, 5 in bigD
- int Ulp2; // powers of 2 in halfUlp.
- if ( exp >= 0 ){
- B2 = B5 = 0;
- D2 = D5 = exp;
- } else {
- B2 = B5 = -exp;
- D2 = D5 = 0;
- }
- if ( bigIntExp >= 0 ){
- B2 += bigIntExp;
- } else {
- D2 -= bigIntExp;
- }
- Ulp2 = B2;
- // shift bigB and bigD left by a number s. t.
- // halfUlp is still an integer.
- int hulpbias;
- if ( bigIntExp+bigIntNBits <= -expBias+1 ){
- // This is going to be a denormalized number
- // (if not actually zero).
- // half an ULP is at 2^-(expBias+expShift+1)
- hulpbias = bigIntExp+ expBias + expShift;
- } else {
- hulpbias = expShift + 2 - bigIntNBits;
- }
- B2 += hulpbias;
- D2 += hulpbias;
- // if there are common factors of 2, we might just as well
- // factor them out, as they add nothing useful.
- int common2 = Math.min( B2, Math.min( D2, Ulp2 ) );
- B2 -= common2;
- D2 -= common2;
- Ulp2 -= common2;
- // do multiplications by powers of 5 and 2
- bigB = multPow52( bigB, B5, B2 );
- OldFDBigIntForTest bigD = multPow52( new OldFDBigIntForTest( bigD0 ), D5, D2 );
- //
- // to recap:
- // bigB is the scaled-big-int version of our floating-point
- // candidate.
- // bigD is the scaled-big-int version of the exact value
- // as we understand it.
- // halfUlp is 1/2 an ulp of bigB, except for special cases
- // of exact powers of 2
- //
- // the plan is to compare bigB with bigD, and if the difference
- // is less than halfUlp, then we're satisfied. Otherwise,
- // use the ratio of difference to halfUlp to calculate a fudge
- // factor to add to the floating value, then go 'round again.
- //
- OldFDBigIntForTest diff;
- int cmpResult;
- boolean overvalue;
- if ( (cmpResult = bigB.cmp( bigD ) ) > 0 ){
- overvalue = true; // our candidate is too big.
- diff = bigB.sub( bigD );
- if ( (bigIntNBits == 1) && (bigIntExp > -expBias+1) ){
- // candidate is a normalized exact power of 2 and
- // is too big. We will be subtracting.
- // For our purposes, ulp is the ulp of the
- // next smaller range.
- Ulp2 -= 1;
- if ( Ulp2 < 0 ){
- // rats. Cannot de-scale ulp this far.
- // must scale diff in other direction.
- Ulp2 = 0;
- diff.lshiftMe( 1 );
- }
- }
- } else if ( cmpResult < 0 ){
- overvalue = false; // our candidate is too small.
- diff = bigD.sub( bigB );
- } else {
- // the candidate is exactly right!
- // this happens with surprising frequency
- break correctionLoop;
- }
- OldFDBigIntForTest halfUlp = constructPow52( B5, Ulp2 );
- if ( (cmpResult = diff.cmp( halfUlp ) ) < 0 ){
- // difference is small.
- // this is close enough
- if (mustSetRoundDir) {
- roundDir = overvalue ? -1 : 1;
- }
- break correctionLoop;
- } else if ( cmpResult == 0 ){
- // difference is exactly half an ULP
- // round to some other value maybe, then finish
- dValue += 0.5*ulp( dValue, overvalue );
- // should check for bigIntNBits == 1 here??
- if (mustSetRoundDir) {
- roundDir = overvalue ? -1 : 1;
- }
- break correctionLoop;
- } else {
- // difference is non-trivial.
- // could scale addend by ratio of difference to
- // halfUlp here, if we bothered to compute that difference.
- // Most of the time ( I hope ) it is about 1 anyway.
- dValue += ulp( dValue, overvalue );
- if ( dValue == 0.0 || dValue == Double.POSITIVE_INFINITY )
- break correctionLoop; // oops. Fell off end of range.
- continue; // try again.
- }
-
- }
- return (isNegative)? -dValue : dValue;
- }
- }
-
- /*
- * Take a FloatingDecimal, which we presumably just scanned in,
- * and find out what its value is, as a float.
- * This is distinct from doubleValue() to avoid the extremely
- * unlikely case of a double rounding error, wherein the conversion
- * to double has one rounding error, and the conversion of that double
- * to a float has another rounding error, IN THE WRONG DIRECTION,
- * ( because of the preference to a zero low-order bit ).
- */
-
- public strictfp float floatValue(){
- int kDigits = Math.min( nDigits, singleMaxDecimalDigits+1 );
- int iValue;
- float fValue;
-
- // First, check for NaN and Infinity values
- if(digits == infinity || digits == notANumber) {
- if(digits == notANumber)
- return Float.NaN;
- else
- return (isNegative?Float.NEGATIVE_INFINITY:Float.POSITIVE_INFINITY);
- }
- else {
- /*
- * convert the lead kDigits to an integer.
- */
- iValue = (int)digits[0]-(int)'0';
- for ( int i=1; i < kDigits; i++ ){
- iValue = iValue*10 + (int)digits[i]-(int)'0';
- }
- fValue = (float)iValue;
- int exp = decExponent-kDigits;
- /*
- * iValue now contains an integer with the value of
- * the first kDigits digits of the number.
- * fValue contains the (float) of the same.
- */
-
- if ( nDigits <= singleMaxDecimalDigits ){
- /*
- * possibly an easy case.
- * We know that the digits can be represented
- * exactly. And if the exponent isn't too outrageous,
- * the whole thing can be done with one operation,
- * thus one rounding error.
- * Note that all our constructors trim all leading and
- * trailing zeros, so simple values (including zero)
- * will always end up here.
- */
- if (exp == 0 || fValue == 0.0f)
- return (isNegative)? -fValue : fValue; // small floating integer
- else if ( exp >= 0 ){
- if ( exp <= singleMaxSmallTen ){
- /*
- * Can get the answer with one operation,
- * thus one roundoff.
- */
- fValue *= singleSmall10pow[exp];
- return (isNegative)? -fValue : fValue;
- }
- int slop = singleMaxDecimalDigits - kDigits;
- if ( exp <= singleMaxSmallTen+slop ){
- /*
- * We can multiply dValue by 10^(slop)
- * and it is still "small" and exact.
- * Then we can multiply by 10^(exp-slop)
- * with one rounding.
- */
- fValue *= singleSmall10pow[slop];
- fValue *= singleSmall10pow[exp-slop];
- return (isNegative)? -fValue : fValue;
- }
- /*
- * Else we have a hard case with a positive exp.
- */
- } else {
- if ( exp >= -singleMaxSmallTen ){
- /*
- * Can get the answer in one division.
- */
- fValue /= singleSmall10pow[-exp];
- return (isNegative)? -fValue : fValue;
- }
- /*
- * Else we have a hard case with a negative exp.
- */
- }
- } else if ( (decExponent >= nDigits) && (nDigits+decExponent <= maxDecimalDigits) ){
- /*
- * In double-precision, this is an exact floating integer.
- * So we can compute to double, then shorten to float
- * with one round, and get the right answer.
- *
- * First, finish accumulating digits.
- * Then convert that integer to a double, multiply
- * by the appropriate power of ten, and convert to float.
- */
- long lValue = (long)iValue;
- for ( int i=kDigits; i < nDigits; i++ ){
- lValue = lValue*10L + (long)((int)digits[i]-(int)'0');
- }
- double dValue = (double)lValue;
- exp = decExponent-nDigits;
- dValue *= small10pow[exp];
- fValue = (float)dValue;
- return (isNegative)? -fValue : fValue;
-
- }
- /*
- * Harder cases:
- * The sum of digits plus exponent is greater than
- * what we think we can do with one error.
- *
- * Start by weeding out obviously out-of-range
- * results, then convert to double and go to
- * common hard-case code.
- */
- if ( decExponent > singleMaxDecimalExponent+1 ){
- /*
- * Lets face it. This is going to be
- * Infinity. Cut to the chase.
- */
- return (isNegative)? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
- } else if ( decExponent < singleMinDecimalExponent-1 ){
- /*
- * Lets face it. This is going to be
- * zero. Cut to the chase.
- */
- return (isNegative)? -0.0f : 0.0f;
- }
-
- /*
- * Here, we do 'way too much work, but throwing away
- * our partial results, and going and doing the whole
- * thing as double, then throwing away half the bits that computes
- * when we convert back to float.
- *
- * The alternative is to reproduce the whole multiple-precision
- * algorithm for float precision, or to try to parameterize it
- * for common usage. The former will take about 400 lines of code,
- * and the latter I tried without success. Thus the semi-hack
- * answer here.
- */
- mustSetRoundDir = !fromHex;
- double dValue = doubleValue();
- return stickyRound( dValue );
- }
- }
-
-
- /*
- * All the positive powers of 10 that can be
- * represented exactly in double/float.
- */
- private static final double small10pow[] = {
- 1.0e0,
- 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5,
- 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10,
- 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15,
- 1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20,
- 1.0e21, 1.0e22
- };
-
- private static final float singleSmall10pow[] = {
- 1.0e0f,
- 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
- 1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
- };
-
- private static final double big10pow[] = {
- 1e16, 1e32, 1e64, 1e128, 1e256 };
- private static final double tiny10pow[] = {
- 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 };
-
- private static final int maxSmallTen = small10pow.length-1;
- private static final int singleMaxSmallTen = singleSmall10pow.length-1;
-
- private static final int small5pow[] = {
- 1,
- 5,
- 5*5,
- 5*5*5,
- 5*5*5*5,
- 5*5*5*5*5,
- 5*5*5*5*5*5,
- 5*5*5*5*5*5*5,
- 5*5*5*5*5*5*5*5,
- 5*5*5*5*5*5*5*5*5,
- 5*5*5*5*5*5*5*5*5*5,
- 5*5*5*5*5*5*5*5*5*5*5,
- 5*5*5*5*5*5*5*5*5*5*5*5,
- 5*5*5*5*5*5*5*5*5*5*5*5*5
- };
-
-
- private static final long long5pow[] = {
- 1L,
- 5L,
- 5L*5,
- 5L*5*5,
- 5L*5*5*5,
- 5L*5*5*5*5,
- 5L*5*5*5*5*5,
- 5L*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- 5L*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5*5,
- };
-
- // approximately ceil( log2( long5pow[i] ) )
- private static final int n5bits[] = {
- 0,
- 3,
- 5,
- 7,
- 10,
- 12,
- 14,
- 17,
- 19,
- 21,
- 24,
- 26,
- 28,
- 31,
- 33,
- 35,
- 38,
- 40,
- 42,
- 45,
- 47,
- 49,
- 52,
- 54,
- 56,
- 59,
- 61,
- };
-
- private static final char infinity[] = { 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y' };
- private static final char notANumber[] = { 'N', 'a', 'N' };
- private static final char zero[] = { '0', '0', '0', '0', '0', '0', '0', '0' };
-
-
- /*
- * Grammar is compatible with hexadecimal floating-point constants
- * described in section 6.4.4.2 of the C99 specification.
- */
- private static Pattern hexFloatPattern = null;
- private static synchronized Pattern getHexFloatPattern() {
- if (hexFloatPattern == null) {
- hexFloatPattern = Pattern.compile(
- //1 234 56 7 8 9
- "([-+])?0[xX](((\\p{XDigit}+)\\.?)|((\\p{XDigit}*)\\.(\\p{XDigit}+)))[pP]([-+])?(\\p{Digit}+)[fFdD]?"
- );
- }
- return hexFloatPattern;
- }
-
- /*
- * Convert string s to a suitable floating decimal; uses the
- * double constructor and set the roundDir variable appropriately
- * in case the value is later converted to a float.
- */
- static OldFloatingDecimalForTest parseHexString(String s) {
- // Verify string is a member of the hexadecimal floating-point
- // string language.
- Matcher m = getHexFloatPattern().matcher(s);
- boolean validInput = m.matches();
-
- if (!validInput) {
- // Input does not match pattern
- throw new NumberFormatException("For input string: \"" + s + "\"");
- } else { // validInput
- /*
- * We must isolate the sign, significand, and exponent
- * fields. The sign value is straightforward. Since
- * floating-point numbers are stored with a normalized
- * representation, the significand and exponent are
- * interrelated.
- *
- * After extracting the sign, we normalized the
- * significand as a hexadecimal value, calculating an
- * exponent adjust for any shifts made during
- * normalization. If the significand is zero, the
- * exponent doesn't need to be examined since the output
- * will be zero.
- *
- * Next the exponent in the input string is extracted.
- * Afterwards, the significand is normalized as a *binary*
- * value and the input value's normalized exponent can be
- * computed. The significand bits are copied into a
- * double significand; if the string has more logical bits
- * than can fit in a double, the extra bits affect the
- * round and sticky bits which are used to round the final
- * value.
- */
-
- // Extract significand sign
- String group1 = m.group(1);
- double sign = (( group1 == null ) || group1.equals("+"))? 1.0 : -1.0;
-
-
- // Extract Significand magnitude
- /*
- * Based on the form of the significand, calculate how the
- * binary exponent needs to be adjusted to create a
- * normalized *hexadecimal* floating-point number; that
- * is, a number where there is one nonzero hex digit to
- * the left of the (hexa)decimal point. Since we are
- * adjusting a binary, not hexadecimal exponent, the
- * exponent is adjusted by a multiple of 4.
- *
- * There are a number of significand scenarios to consider;
- * letters are used in indicate nonzero digits:
- *
- * 1. 000xxxx => x.xxx normalized
- * increase exponent by (number of x's - 1)*4
- *
- * 2. 000xxx.yyyy => x.xxyyyy normalized
- * increase exponent by (number of x's - 1)*4
- *
- * 3. .000yyy => y.yy normalized
- * decrease exponent by (number of zeros + 1)*4
- *
- * 4. 000.00000yyy => y.yy normalized
- * decrease exponent by (number of zeros to right of point + 1)*4
- *
- * If the significand is exactly zero, return a properly
- * signed zero.
- */
-
- String significandString =null;
- int signifLength = 0;
- int exponentAdjust = 0;
- {
- int leftDigits = 0; // number of meaningful digits to
- // left of "decimal" point
- // (leading zeros stripped)
- int rightDigits = 0; // number of digits to right of
- // "decimal" point; leading zeros
- // must always be accounted for
- /*
- * The significand is made up of either
- *
- * 1. group 4 entirely (integer portion only)
- *
- * OR
- *
- * 2. the fractional portion from group 7 plus any
- * (optional) integer portions from group 6.
- */
- String group4;
- if( (group4 = m.group(4)) != null) { // Integer-only significand
- // Leading zeros never matter on the integer portion
- significandString = stripLeadingZeros(group4);
- leftDigits = significandString.length();
- }
- else {
- // Group 6 is the optional integer; leading zeros
- // never matter on the integer portion
- String group6 = stripLeadingZeros(m.group(6));
- leftDigits = group6.length();
-
- // fraction
- String group7 = m.group(7);
- rightDigits = group7.length();
-
- // Turn "integer.fraction" into "integer"+"fraction"
- significandString =
- ((group6 == null)?"":group6) + // is the null
- // check necessary?
- group7;
- }
-
- significandString = stripLeadingZeros(significandString);
- signifLength = significandString.length();
-
- /*
- * Adjust exponent as described above
- */
- if (leftDigits >= 1) { // Cases 1 and 2
- exponentAdjust = 4*(leftDigits - 1);
- } else { // Cases 3 and 4
- exponentAdjust = -4*( rightDigits - signifLength + 1);
- }
-
- // If the significand is zero, the exponent doesn't
- // matter; return a properly signed zero.
-
- if (signifLength == 0) { // Only zeros in input
- return new OldFloatingDecimalForTest(sign * 0.0);
- }
- }
-
- // Extract Exponent
- /*
- * Use an int to read in the exponent value; this should
- * provide more than sufficient range for non-contrived
- * inputs. If reading the exponent in as an int does
- * overflow, examine the sign of the exponent and
- * significand to determine what to do.
- */
- String group8 = m.group(8);
- boolean positiveExponent = ( group8 == null ) || group8.equals("+");
- long unsignedRawExponent;
- try {
- unsignedRawExponent = Integer.parseInt(m.group(9));
- }
- catch (NumberFormatException e) {
- // At this point, we know the exponent is
- // syntactically well-formed as a sequence of
- // digits. Therefore, if an NumberFormatException
- // is thrown, it must be due to overflowing int's
- // range. Also, at this point, we have already
- // checked for a zero significand. Thus the signs
- // of the exponent and significand determine the
- // final result:
- //
- // significand
- // + -
- // exponent + +infinity -infinity
- // - +0.0 -0.0
- return new OldFloatingDecimalForTest(sign * (positiveExponent ?
- Double.POSITIVE_INFINITY : 0.0));
- }
-
- long rawExponent =
- (positiveExponent ? 1L : -1L) * // exponent sign
- unsignedRawExponent; // exponent magnitude
-
- // Calculate partially adjusted exponent
- long exponent = rawExponent + exponentAdjust ;
-
- // Starting copying non-zero bits into proper position in
- // a long; copy explicit bit too; this will be masked
- // later for normal values.
-
- boolean round = false;
- boolean sticky = false;
- int bitsCopied=0;
- int nextShift=0;
- long significand=0L;
- // First iteration is different, since we only copy
- // from the leading significand bit; one more exponent
- // adjust will be needed...
-
- // IMPORTANT: make leadingDigit a long to avoid
- // surprising shift semantics!
- long leadingDigit = getHexDigit(significandString, 0);
-
- /*
- * Left shift the leading digit (53 - (bit position of
- * leading 1 in digit)); this sets the top bit of the
- * significand to 1. The nextShift value is adjusted
- * to take into account the number of bit positions of
- * the leadingDigit actually used. Finally, the
- * exponent is adjusted to normalize the significand
- * as a binary value, not just a hex value.
- */
- if (leadingDigit == 1) {
- significand |= leadingDigit << 52;
- nextShift = 52 - 4;
- /* exponent += 0 */ }
- else if (leadingDigit <= 3) { // [2, 3]
- significand |= leadingDigit << 51;
- nextShift = 52 - 5;
- exponent += 1;
- }
- else if (leadingDigit <= 7) { // [4, 7]
- significand |= leadingDigit << 50;
- nextShift = 52 - 6;
- exponent += 2;
- }
- else if (leadingDigit <= 15) { // [8, f]
- significand |= leadingDigit << 49;
- nextShift = 52 - 7;
- exponent += 3;
- } else {
- throw new AssertionError("Result from digit conversion too large!");
- }
- // The preceding if-else could be replaced by a single
- // code block based on the high-order bit set in
- // leadingDigit. Given leadingOnePosition,
-
- // significand |= leadingDigit << (SIGNIFICAND_WIDTH - leadingOnePosition);
- // nextShift = 52 - (3 + leadingOnePosition);
- // exponent += (leadingOnePosition-1);
-
-
- /*
- * Now the exponent variable is equal to the normalized
- * binary exponent. Code below will make representation
- * adjustments if the exponent is incremented after
- * rounding (includes overflows to infinity) or if the
- * result is subnormal.
- */
-
- // Copy digit into significand until the significand can't
- // hold another full hex digit or there are no more input
- // hex digits.
- int i = 0;
- for(i = 1;
- i < signifLength && nextShift >= 0;
- i++) {
- long currentDigit = getHexDigit(significandString, i);
- significand |= (currentDigit << nextShift);
- nextShift-=4;
- }
-
- // After the above loop, the bulk of the string is copied.
- // Now, we must copy any partial hex digits into the
- // significand AND compute the round bit and start computing
- // sticky bit.
-
- if ( i < signifLength ) { // at least one hex input digit exists
- long currentDigit = getHexDigit(significandString, i);
-
- // from nextShift, figure out how many bits need
- // to be copied, if any
- switch(nextShift) { // must be negative
- case -1:
- // three bits need to be copied in; can
- // set round bit
- significand |= ((currentDigit & 0xEL) >> 1);
- round = (currentDigit & 0x1L) != 0L;
- break;
-
- case -2:
- // two bits need to be copied in; can
- // set round and start sticky
- significand |= ((currentDigit & 0xCL) >> 2);
- round = (currentDigit &0x2L) != 0L;
- sticky = (currentDigit & 0x1L) != 0;
- break;
-
- case -3:
- // one bit needs to be copied in
- significand |= ((currentDigit & 0x8L)>>3);
- // Now set round and start sticky, if possible
- round = (currentDigit &0x4L) != 0L;
- sticky = (currentDigit & 0x3L) != 0;
- break;
-
- case -4:
- // all bits copied into significand; set
- // round and start sticky
- round = ((currentDigit & 0x8L) != 0); // is top bit set?
- // nonzeros in three low order bits?
- sticky = (currentDigit & 0x7L) != 0;
- break;
-
- default:
- throw new AssertionError("Unexpected shift distance remainder.");
- // break;
- }
-
- // Round is set; sticky might be set.
-
- // For the sticky bit, it suffices to check the
- // current digit and test for any nonzero digits in
- // the remaining unprocessed input.
- i++;
- while(i < signifLength && !sticky) {
- currentDigit = getHexDigit(significandString,i);
- sticky = sticky || (currentDigit != 0);
- i++;
- }
-
- }
- // else all of string was seen, round and sticky are
- // correct as false.
-
-
- // Check for overflow and update exponent accordingly.
-
- if (exponent > Double.MAX_EXPONENT) { // Infinite result
- // overflow to properly signed infinity
- return new OldFloatingDecimalForTest(sign * Double.POSITIVE_INFINITY);
- } else { // Finite return value
- if (exponent <= Double.MAX_EXPONENT && // (Usually) normal result
- exponent >= Double.MIN_EXPONENT) {
-
- // The result returned in this block cannot be a
- // zero or subnormal; however after the
- // significand is adjusted from rounding, we could
- // still overflow in infinity.
-
- // AND exponent bits into significand; if the
- // significand is incremented and overflows from
- // rounding, this combination will update the
- // exponent correctly, even in the case of
- // Double.MAX_VALUE overflowing to infinity.
-
- significand = (( (exponent +
- (long)DoubleConsts.EXP_BIAS) <<
- (DoubleConsts.SIGNIFICAND_WIDTH-1))
- & DoubleConsts.EXP_BIT_MASK) |
- (DoubleConsts.SIGNIF_BIT_MASK & significand);
-
- } else { // Subnormal or zero
- // (exponent < Double.MIN_EXPONENT)
-
- if (exponent < (DoubleConsts.MIN_SUB_EXPONENT -1 )) {
- // No way to round back to nonzero value
- // regardless of significand if the exponent is
- // less than -1075.
- return new OldFloatingDecimalForTest(sign * 0.0);
- } else { // -1075 <= exponent <= MIN_EXPONENT -1 = -1023
- /*
- * Find bit position to round to; recompute
- * round and sticky bits, and shift
- * significand right appropriately.
- */
-
- sticky = sticky || round;
- round = false;
-
- // Number of bits of significand to preserve is
- // exponent - abs_min_exp +1
- // check:
- // -1075 +1074 + 1 = 0
- // -1023 +1074 + 1 = 52
-
- int bitsDiscarded = 53 -
- ((int)exponent - DoubleConsts.MIN_SUB_EXPONENT + 1);
- assert bitsDiscarded >= 1 && bitsDiscarded <= 53;
-
- // What to do here:
- // First, isolate the new round bit
- round = (significand & (1L << (bitsDiscarded -1))) != 0L;
- if (bitsDiscarded > 1) {
- // create mask to update sticky bits; low
- // order bitsDiscarded bits should be 1
- long mask = ~((~0L) << (bitsDiscarded -1));
- sticky = sticky || ((significand & mask) != 0L ) ;
- }
-
- // Now, discard the bits
- significand = significand >> bitsDiscarded;
-
- significand = (( ((long)(Double.MIN_EXPONENT -1) + // subnorm exp.
- (long)DoubleConsts.EXP_BIAS) <<
- (DoubleConsts.SIGNIFICAND_WIDTH-1))
- & DoubleConsts.EXP_BIT_MASK) |
- (DoubleConsts.SIGNIF_BIT_MASK & significand);
- }
- }
-
- // The significand variable now contains the currently
- // appropriate exponent bits too.
-
- /*
- * Determine if significand should be incremented;
- * making this determination depends on the least
- * significant bit and the round and sticky bits.
- *
- * Round to nearest even rounding table, adapted from
- * table 4.7 in "Computer Arithmetic" by IsraelKoren.
- * The digit to the left of the "decimal" point is the
- * least significant bit, the digits to the right of
- * the point are the round and sticky bits
- *
- * Number Round(x)
- * x0.00 x0.
- * x0.01 x0.
- * x0.10 x0.
- * x0.11 x1. = x0. +1
- * x1.00 x1.
- * x1.01 x1.
- * x1.10 x1. + 1
- * x1.11 x1. + 1
- */
- boolean incremented = false;
- boolean leastZero = ((significand & 1L) == 0L);
- if( ( leastZero && round && sticky ) ||
- ((!leastZero) && round )) {
- incremented = true;
- significand++;
- }
-
- OldFloatingDecimalForTest fd = new OldFloatingDecimalForTest(Math.copySign(
- Double.longBitsToDouble(significand),
- sign));
-
- /*
- * Set roundingDir variable field of fd properly so
- * that the input string can be properly rounded to a
- * float value. There are two cases to consider:
- *
- * 1. rounding to double discards sticky bit
- * information that would change the result of a float
- * rounding (near halfway case between two floats)
- *
- * 2. rounding to double rounds up when rounding up
- * would not occur when rounding to float.
- *
- * For former case only needs to be considered when
- * the bits rounded away when casting to float are all
- * zero; otherwise, float round bit is properly set
- * and sticky will already be true.
- *
- * The lower exponent bound for the code below is the
- * minimum (normalized) subnormal exponent - 1 since a
- * value with that exponent can round up to the
- * minimum subnormal value and the sticky bit
- * information must be preserved (i.e. case 1).
- */
- if ((exponent >= FloatConsts.MIN_SUB_EXPONENT-1) &&
- (exponent <= Float.MAX_EXPONENT ) ){
- // Outside above exponent range, the float value
- // will be zero or infinity.
-
- /*
- * If the low-order 28 bits of a rounded double
- * significand are 0, the double could be a
- * half-way case for a rounding to float. If the
- * double value is a half-way case, the double
- * significand may have to be modified to round
- * the the right float value (see the stickyRound
- * method). If the rounding to double has lost
- * what would be float sticky bit information, the
- * double significand must be incremented. If the
- * double value's significand was itself
- * incremented, the float value may end up too
- * large so the increment should be undone.
- */
- if ((significand & 0xfffffffL) == 0x0L) {
- // For negative values, the sign of the
- // roundDir is the same as for positive values
- // since adding 1 increasing the significand's
- // magnitude and subtracting 1 decreases the
- // significand's magnitude. If neither round
- // nor sticky is true, the double value is
- // exact and no adjustment is required for a
- // proper float rounding.
- if( round || sticky) {
- if (leastZero) { // prerounding lsb is 0
- // If round and sticky were both true,
- // and the least significant
- // significand bit were 0, the rounded
- // significand would not have its
- // low-order bits be zero. Therefore,
- // we only need to adjust the
- // significand if round XOR sticky is
- // true.
- if (round ^ sticky) {
- fd.roundDir = 1;
- }
- }
- else { // prerounding lsb is 1
- // If the prerounding lsb is 1 and the
- // resulting significand has its
- // low-order bits zero, the significand
- // was incremented. Here, we undo the
- // increment, which will ensure the
- // right guard and sticky bits for the
- // float rounding.
- if (round)
- fd.roundDir = -1;
- }
- }
- }
- }
-
- fd.fromHex = true;
- return fd;
- }
- }
- }
-
- /**
- * Return <code>s</code> with any leading zeros removed.
- */
- static String stripLeadingZeros(String s) {
- return s.replaceFirst("^0+", "");
- }
-
- /**
- * Extract a hexadecimal digit from position <code>position</code>
- * of string <code>s</code>.
- */
- static int getHexDigit(String s, int position) {
- int value = Character.digit(s.charAt(position), 16);
- if (value <= -1 || value >= 16) {
- throw new AssertionError("Unexpected failure of digit conversion of " +
- s.charAt(position));
- }
- return value;
- }
-
-
-}
--- a/jdk/test/sun/misc/FloatingDecimal/TestFDBigInteger.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,435 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-import java.math.BigInteger;
-import java.util.Random;
-import sun.misc.FDBigInteger;
-
-/**
- * @test
- * @bug 7032154
- * @summary unit testys of sun.misc.FDBigInteger
- * @modules java.base/sun.misc
- * @author Dmitry Nadezhin
- */
-public class TestFDBigInteger {
-
- private static final int MAX_P5 = 413;
- private static final int MAX_P2 = 65;
- private static final long LONG_SIGN_MASK = (1L << 63);
- private static final BigInteger FIVE = BigInteger.valueOf(5);
- private static final FDBigInteger MUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0));
- private static final FDBigInteger IMMUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0));
- private static final FDBigInteger IMMUTABLE_MILLION = genMillion1();
- private static final FDBigInteger IMMUTABLE_BILLION = genBillion1();
- private static final FDBigInteger IMMUTABLE_TEN18 = genTen18();
-
- static {
- IMMUTABLE_ZERO.makeImmutable();
- IMMUTABLE_MILLION.makeImmutable();
- IMMUTABLE_BILLION.makeImmutable();
- IMMUTABLE_TEN18.makeImmutable();
- }
-
- private static FDBigInteger mutable(String hex, int offset) {
- char[] chars = new BigInteger(hex, 16).toString().toCharArray();
- return new FDBigInteger(0, chars, 0, chars.length).multByPow52(0, offset * 32);
- }
-
- private static FDBigInteger immutable(String hex, int offset) {
- FDBigInteger fd = mutable(hex, offset);
- fd.makeImmutable();
- return fd;
- }
-
- private static BigInteger biPow52(int p5, int p2) {
- return FIVE.pow(p5).shiftLeft(p2);
- }
-
- // data.length == 1, nWords == 1, offset == 0
- private static FDBigInteger genMillion1() {
- return FDBigInteger.valueOfPow52(6, 0).leftShift(6);
- }
-
- // data.length == 2, nWords == 1, offset == 0
- private static FDBigInteger genMillion2() {
- return FDBigInteger.valueOfMulPow52(1000000L, 0, 0);
- }
-
- // data.length == 1, nWords == 1, offset == 0
- private static FDBigInteger genBillion1() {
- return FDBigInteger.valueOfPow52(9, 0).leftShift(9);
- }
-
- // data.length == 2, nWords == 2, offset == 0
- private static FDBigInteger genTen18() {
- return FDBigInteger.valueOfPow52(18, 0).leftShift(18);
- }
-
- private static void check(BigInteger expected, FDBigInteger actual, String message) throws Exception {
- if (!expected.equals(actual.toBigInteger())) {
- throw new Exception(message + " result " + actual.toHexString() + " expected " + expected.toString(16));
- }
- }
-
- private static void testValueOfPow52(int p5, int p2) throws Exception {
- check(biPow52(p5, p2), FDBigInteger.valueOfPow52(p5, p2),
- "valueOfPow52(" + p5 + "," + p2 + ")");
- }
-
- private static void testValueOfPow52() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- for (int p2 = 0; p2 <= MAX_P2; p2++) {
- testValueOfPow52(p5, p2);
- }
- }
- }
-
- private static void testValueOfMulPow52(long value, int p5, int p2) throws Exception {
- BigInteger bi = BigInteger.valueOf(value & ~LONG_SIGN_MASK);
- if (value < 0) {
- bi = bi.setBit(63);
- }
- check(biPow52(p5, p2).multiply(bi), FDBigInteger.valueOfMulPow52(value, p5, p2),
- "valueOfMulPow52(" + Long.toHexString(value) + "." + p5 + "," + p2 + ")");
- }
-
- private static void testValueOfMulPow52(long value, int p5) throws Exception {
- testValueOfMulPow52(value, p5, 0);
- testValueOfMulPow52(value, p5, 1);
- testValueOfMulPow52(value, p5, 30);
- testValueOfMulPow52(value, p5, 31);
- testValueOfMulPow52(value, p5, 33);
- testValueOfMulPow52(value, p5, 63);
- }
-
- private static void testValueOfMulPow52() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- testValueOfMulPow52(0xFFFFFFFFL, p5);
- testValueOfMulPow52(0x123456789AL, p5);
- testValueOfMulPow52(0x7FFFFFFFFFFFFFFFL, p5);
- testValueOfMulPow52(0xFFFFFFFFFFF54321L, p5);
- }
- }
-
- private static void testLeftShift(FDBigInteger t, int shift, boolean isImmutable) throws Exception {
- BigInteger bt = t.toBigInteger();
- FDBigInteger r = t.leftShift(shift);
- if ((bt.signum() == 0 || shift == 0 || !isImmutable) && r != t) {
- throw new Exception("leftShift doesn't reuse its argument");
- }
- if (isImmutable) {
- check(bt, t, "leftShift corrupts its argument");
- }
- check(bt.shiftLeft(shift), r, "leftShift returns wrong result");
- }
-
- private static void testLeftShift() throws Exception {
- testLeftShift(IMMUTABLE_ZERO, 0, true);
- testLeftShift(IMMUTABLE_ZERO, 10, true);
- testLeftShift(MUTABLE_ZERO, 0, false);
- testLeftShift(MUTABLE_ZERO, 10, false);
-
- testLeftShift(IMMUTABLE_MILLION, 0, true);
- testLeftShift(IMMUTABLE_MILLION, 1, true);
- testLeftShift(IMMUTABLE_MILLION, 12, true);
- testLeftShift(IMMUTABLE_MILLION, 13, true);
- testLeftShift(IMMUTABLE_MILLION, 32, true);
- testLeftShift(IMMUTABLE_MILLION, 33, true);
- testLeftShift(IMMUTABLE_MILLION, 44, true);
- testLeftShift(IMMUTABLE_MILLION, 45, true);
-
- testLeftShift(genMillion1(), 0, false);
- testLeftShift(genMillion1(), 1, false);
- testLeftShift(genMillion1(), 12, false);
- testLeftShift(genMillion1(), 13, false);
- testLeftShift(genMillion1(), 25, false);
- testLeftShift(genMillion1(), 26, false);
- testLeftShift(genMillion1(), 32, false);
- testLeftShift(genMillion1(), 33, false);
- testLeftShift(genMillion1(), 44, false);
- testLeftShift(genMillion1(), 45, false);
-
- testLeftShift(genMillion2(), 0, false);
- testLeftShift(genMillion2(), 1, false);
- testLeftShift(genMillion2(), 12, false);
- testLeftShift(genMillion2(), 13, false);
- testLeftShift(genMillion2(), 25, false);
- testLeftShift(genMillion2(), 26, false);
- testLeftShift(genMillion2(), 32, false);
- testLeftShift(genMillion2(), 33, false);
- testLeftShift(genMillion2(), 44, false);
- testLeftShift(genMillion2(), 45, false);
- }
-
- private static void testQuoRemIteration(FDBigInteger t, FDBigInteger s) throws Exception {
- BigInteger bt = t.toBigInteger();
- BigInteger bs = s.toBigInteger();
- int q = t.quoRemIteration(s);
- BigInteger[] qr = bt.divideAndRemainder(bs);
- if (!BigInteger.valueOf(q).equals(qr[0])) {
- throw new Exception("quoRemIteration returns incorrect quo");
- }
- check(qr[1].multiply(BigInteger.TEN), t, "quoRemIteration returns incorrect rem");
- }
-
- private static void testQuoRemIteration() throws Exception {
- // IMMUTABLE_TEN18 == 0de0b6b3a7640000
- // q = 0
- testQuoRemIteration(mutable("00000001", 0), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("00000001", 1), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("0de0b6b2", 1), IMMUTABLE_TEN18);
- // q = 1 -> q = 0
- testQuoRemIteration(mutable("0de0b6b3", 1), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("0de0b6b3a763FFFF", 0), IMMUTABLE_TEN18);
- // q = 1
- testQuoRemIteration(mutable("0de0b6b3a7640000", 0), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("0de0b6b3FFFFFFFF", 0), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("8ac72304", 1), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("0de0b6b400000000", 0), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("8ac72305", 1), IMMUTABLE_TEN18);
- // q = 18
- testQuoRemIteration(mutable("FFFFFFFF", 1), IMMUTABLE_TEN18);
- }
-
- private static void testCmp(FDBigInteger t, FDBigInteger o) throws Exception {
- BigInteger bt = t.toBigInteger();
- BigInteger bo = o.toBigInteger();
- int cmp = t.cmp(o);
- int bcmp = bt.compareTo(bo);
- if (bcmp != cmp) {
- throw new Exception("cmp returns " + cmp + " expected " + bcmp);
- }
- check(bt, t, "cmp corrupts this");
- check(bo, o, "cmp corrupts other");
- if (o.cmp(t) != -cmp) {
- throw new Exception("asymmetrical cmp");
- }
- check(bt, t, "cmp corrupts this");
- check(bo, o, "cmp corrupts other");
- }
-
- private static void testCmp() throws Exception {
- testCmp(mutable("FFFFFFFF", 0), mutable("100000000", 0));
- testCmp(mutable("FFFFFFFF", 0), mutable("1", 1));
- testCmp(mutable("5", 0), mutable("6", 0));
- testCmp(mutable("5", 0), mutable("5", 0));
- testCmp(mutable("5000000001", 0), mutable("500000001", 0));
- testCmp(mutable("5000000001", 0), mutable("6", 1));
- testCmp(mutable("5000000001", 0), mutable("5", 1));
- testCmp(mutable("5000000000", 0), mutable("5", 1));
- }
-
- private static void testCmpPow52(FDBigInteger t, int p5, int p2) throws Exception {
- FDBigInteger o = FDBigInteger.valueOfPow52(p5, p2);
- BigInteger bt = t.toBigInteger();
- BigInteger bo = biPow52(p5, p2);
- int cmp = t.cmp(o);
- int bcmp = bt.compareTo(bo);
- if (bcmp != cmp) {
- throw new Exception("cmpPow52 returns " + cmp + " expected " + bcmp);
- }
- check(bt, t, "cmpPow52 corrupts this");
- check(bo, o, "cmpPow5 corrupts other");
- }
-
- private static void testCmpPow52() throws Exception {
- testCmpPow52(mutable("00000002", 1), 0, 31);
- testCmpPow52(mutable("00000002", 1), 0, 32);
- testCmpPow52(mutable("00000002", 1), 0, 33);
- testCmpPow52(mutable("00000002", 1), 0, 34);
- testCmpPow52(mutable("00000002", 1), 0, 64);
- testCmpPow52(mutable("00000003", 1), 0, 32);
- testCmpPow52(mutable("00000003", 1), 0, 33);
- testCmpPow52(mutable("00000003", 1), 0, 34);
- }
-
- private static void testAddAndCmp(FDBigInteger t, FDBigInteger x, FDBigInteger y) throws Exception {
- BigInteger bt = t.toBigInteger();
- BigInteger bx = x.toBigInteger();
- BigInteger by = y.toBigInteger();
- int cmp = t.addAndCmp(x, y);
- int bcmp = bt.compareTo(bx.add(by));
- if (bcmp != cmp) {
- throw new Exception("addAndCmp returns " + cmp + " expected " + bcmp);
- }
- check(bt, t, "addAndCmp corrupts this");
- check(bx, x, "addAndCmp corrupts x");
- check(by, y, "addAndCmp corrupts y");
- }
-
- private static void testAddAndCmp() throws Exception {
- testAddAndCmp(MUTABLE_ZERO, MUTABLE_ZERO, MUTABLE_ZERO);
- testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, MUTABLE_ZERO);
- testAddAndCmp(mutable("00000001", 0), mutable("00000001", 0), MUTABLE_ZERO);
- testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000001", 0));
- testAddAndCmp(mutable("00000001", 0), mutable("00000002", 0), MUTABLE_ZERO);
- testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000002", 0));
- testAddAndCmp(mutable("00000001", 2), mutable("FFFFFFFF", 0), mutable("FFFFFFFF", 0));
- testAddAndCmp(mutable("00000001", 0), mutable("00000001", 1), mutable("00000001", 0));
-
- testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0F80000000", 1), mutable("F0F0F0F080000000", 1));
- testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0E80000000", 1), mutable("F0F0F0F080000000", 1));
-
- testAddAndCmp(mutable("00000002", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
- testAddAndCmp(mutable("00000003", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
- testAddAndCmp(mutable("00000004", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
- testAddAndCmp(mutable("00000005", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
-
- testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
- testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000001", 0));
- testAddAndCmp(mutable("00000002", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
- testAddAndCmp(mutable("00000003", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
- }
-
- private static void testMultBy10(FDBigInteger t, boolean isImmutable) throws Exception {
- BigInteger bt = t.toBigInteger();
- FDBigInteger r = t.multBy10();
- if ((bt.signum() == 0 || !isImmutable) && r != t) {
- throw new Exception("multBy10 of doesn't reuse its argument");
- }
- if (isImmutable) {
- check(bt, t, "multBy10 corrupts its argument");
- }
- check(bt.multiply(BigInteger.TEN), r, "multBy10 returns wrong result");
- }
-
- private static void testMultBy10() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- for (int p2 = 0; p2 <= MAX_P2; p2++) {
- // This strange way of creating a value ensures that it is mutable.
- FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
- testMultBy10(value, false);
- value.makeImmutable();
- testMultBy10(value, true);
- }
- }
- }
-
- private static void testMultByPow52(FDBigInteger t, int p5, int p2) throws Exception {
- BigInteger bt = t.toBigInteger();
- FDBigInteger r = t.multByPow52(p5, p2);
- if (bt.signum() == 0 && r != t) {
- throw new Exception("multByPow52 of doesn't reuse its argument");
- }
- check(bt.multiply(biPow52(p5, p2)), r, "multByPow52 returns wrong result");
- }
-
- private static void testMultByPow52() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- for (int p2 = 0; p2 <= MAX_P2; p2++) {
- // This strange way of creating a value ensures that it is mutable.
- FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
- testMultByPow52(value, p5, p2);
- }
- }
- }
-
- private static void testLeftInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception {
- BigInteger biLeft = left.toBigInteger();
- BigInteger biRight = right.toBigInteger();
- FDBigInteger diff = left.leftInplaceSub(right);
- if (!isImmutable && diff != left) {
- throw new Exception("leftInplaceSub of doesn't reuse its argument");
- }
- if (isImmutable) {
- check(biLeft, left, "leftInplaceSub corrupts its left immutable argument");
- }
- check(biRight, right, "leftInplaceSub corrupts its right argument");
- check(biLeft.subtract(biRight), diff, "leftInplaceSub returns wrong result");
- }
-
- private static void testLeftInplaceSub() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- for (int p2 = 0; p2 <= MAX_P2; p2++) {
-// for (int p5r = 0; p5r <= p5; p5r += 10) {
-// for (int p2r = 0; p2r <= p2; p2r += 10) {
- for (int p5r = 0; p5r <= p5; p5r++) {
- for (int p2r = 0; p2r <= p2; p2r++) {
- // This strange way of creating a value ensures that it is mutable.
- FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
- FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
- testLeftInplaceSub(left, right, false);
- left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
- left.makeImmutable();
- testLeftInplaceSub(left, right, true);
- }
- }
- }
- }
- }
-
- private static void testRightInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception {
- BigInteger biLeft = left.toBigInteger();
- BigInteger biRight = right.toBigInteger();
- FDBigInteger diff = left.rightInplaceSub(right);
- if (!isImmutable && diff != right) {
- throw new Exception("rightInplaceSub of doesn't reuse its argument");
- }
- check(biLeft, left, "leftInplaceSub corrupts its left argument");
- if (isImmutable) {
- check(biRight, right, "leftInplaceSub corrupts its right immutable argument");
- }
- try {
- check(biLeft.subtract(biRight), diff, "rightInplaceSub returns wrong result");
- } catch (Exception e) {
- System.out.println(biLeft+" - "+biRight+" = "+biLeft.subtract(biRight));
- throw e;
- }
- }
-
- private static void testRightInplaceSub() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- for (int p2 = 0; p2 <= MAX_P2; p2++) {
-// for (int p5r = 0; p5r <= p5; p5r += 10) {
-// for (int p2r = 0; p2r <= p2; p2r += 10) {
- for (int p5r = 0; p5r <= p5; p5r++) {
- for (int p2r = 0; p2r <= p2; p2r++) {
- // This strange way of creating a value ensures that it is mutable.
- FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
- FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
- testRightInplaceSub(left, right, false);
- right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
- right.makeImmutable();
- testRightInplaceSub(left, right, true);
- }
- }
- }
- }
- }
-
- public static void main(String[] args) throws Exception {
- testValueOfPow52();
- testValueOfMulPow52();
- testLeftShift();
- testQuoRemIteration();
- testCmp();
- testCmpPow52();
- testAddAndCmp();
- // Uncomment the following for more comprehensize but slow testing.
- // testLeftInplaceSub();
- // testMultBy10();
- // testMultByPow52();
- // testRightInplaceSub();
- }
-}
--- a/jdk/test/sun/misc/FloatingDecimal/TestFloatingDecimal.java Wed Dec 23 15:41:55 2015 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,329 +0,0 @@
-/*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-import java.util.Random;
-import sun.misc.FloatingDecimal;
-
-/*
-OldFloatingDecimalForTest
-
-public class OldFloatingDecimalForTest {
- public boolean digitsRoundedUp();
- public OldFloatingDecimalForTest(double);
- public OldFloatingDecimalForTest(float);
- public boolean decimalDigitsExact();
- public java.lang.String toString();
- public java.lang.String toJavaFormatString();
- public void appendTo(java.lang.Appendable);
- public static OldFloatingDecimalForTest readJavaFormatString(java.lang.String) throws java.lang.NumberFormatException;
- public strictfp double doubleValue();
- public strictfp float floatValue();
-}
-
-sun.misc.FloatingDecimal
-
-public class sun.misc.FloatingDecimal {
- public sun.misc.FloatingDecimal();
- public static java.lang.String toJavaFormatString(double);
- public static java.lang.String toJavaFormatString(float);
- public static void appendTo(double, java.lang.Appendable);
- public static void appendTo(float, java.lang.Appendable);
- public static double parseDouble(java.lang.String) throws java.lang.NumberFormatException;
- public static float parseFloat(java.lang.String) throws java.lang.NumberFormatException;
- public static sun.misc.FloatingDecimal$AbstractD2ABuffer getD2ABuffer(double);
-}
-*/
-
-/**
- * @test
- * @bug 7032154
- * @summary unit tests of sun.misc.FloatingDecimal
- * @modules java.base/sun.misc
- * @library ../../../java/lang/Math
- * @build DoubleConsts FloatConsts
- * @run main TestFloatingDecimal
- * @author Brian Burkhalter
- * @key randomness
- */
-public class TestFloatingDecimal {
- private static enum ResultType {
- RESULT_EXCEPTION,
- RESULT_PRINT
- }
-
- private static final ResultType RESULT_TYPE = ResultType.RESULT_PRINT;
- private static final int NUM_RANDOM_TESTS = 100000;
-
- private static final Random RANDOM = new Random();
-
- private static void result(String message) {
- switch (RESULT_TYPE) {
- case RESULT_EXCEPTION:
- throw new RuntimeException(message);
- case RESULT_PRINT:
- System.err.println(message);
- break;
- default:
- assert false;
- }
- }
-
- private static int check(String test, Object expected, Object actual) {
- int failures = 0;
- if(!actual.equals(expected)) {
- failures++;
- result("Test "+test+" expected "+expected+" but obtained "+actual);
- }
- return failures;
- }
-
- private static int testAppendToDouble() {
- System.out.println(" testAppendToDouble");
- int failures = 0;
-
- for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
- double[] d = new double[] {
- RANDOM.nextLong(),
- RANDOM.nextGaussian(),
- RANDOM.nextDouble()*Double.MAX_VALUE
- };
- for(int j = 0; j < d.length; j++) {
- OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]);
- StringBuilder sb = new StringBuilder();
- ofd.appendTo(sb);
- String oldString = sb.toString();
- sb = new StringBuilder();
- FloatingDecimal.appendTo(d[j], sb);
- String newString = sb.toString();
- failures += check("testAppendToDouble", oldString, newString);
- }
- }
-
- return failures;
- }
-
- private static int testAppendToFloat() {
- System.out.println(" testAppendToFloat");
- int failures = 0;
-
- for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
- float[] f = new float[] {
- RANDOM.nextLong(),
- (float)RANDOM.nextGaussian(),
- RANDOM.nextFloat()*Float.MAX_VALUE
- };
- for(int j = 0; j < f.length; j++) {
- OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]);
- StringBuilder sb = new StringBuilder();
- ofd.appendTo(sb);
- String oldString = sb.toString();
- sb = new StringBuilder();
- FloatingDecimal.appendTo(f[j], sb);
- String newString = sb.toString();
- failures += check("testAppendToFloat", oldString, newString);
- }
- }
-
- return failures;
- }
-
- private static int testAppendTo() {
- System.out.println("testAppendTo");
- int failures = 0;
-
- failures += testAppendToDouble();
- failures += testAppendToFloat();
-
- return failures;
- }
-
- private static int testParseDouble() {
- System.out.println(" testParseDouble");
- int failures = 0;
-
- for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
- double[] d = new double[] {
- RANDOM.nextLong(),
- RANDOM.nextGaussian(),
- RANDOM.nextDouble()*Double.MAX_VALUE
- };
- for(int j = 0; j < d.length; j++) {
- OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]);
- String javaFormatString = ofd.toJavaFormatString();
- ofd = OldFloatingDecimalForTest.readJavaFormatString(javaFormatString);
- double oldDouble = ofd.doubleValue();
- double newDouble = FloatingDecimal.parseDouble(javaFormatString);
- failures += check("testParseDouble", oldDouble, newDouble);
- }
- }
-
- return failures;
- }
-
- private static int testParseFloat() {
- System.out.println(" testParseFloat");
- int failures = 0;
-
- for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
- float[] f = new float[] {
- RANDOM.nextInt(),
- (float)RANDOM.nextGaussian(),
- RANDOM.nextFloat()*Float.MAX_VALUE
- };
- for(int j = 0; j < f.length; j++) {
- OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]);
- String javaFormatString = ofd.toJavaFormatString();
- ofd = OldFloatingDecimalForTest.readJavaFormatString(javaFormatString);
- float oldFloat = ofd.floatValue();
- float newFloat = FloatingDecimal.parseFloat(javaFormatString);
- failures += check("testParseFloat", oldFloat, newFloat);
- }
- }
-
- return failures;
- }
-
- private static int testParse() {
- System.out.println("testParse");
- int failures = 0;
-
- failures += testParseDouble();
- failures += testParseFloat();
-
- return failures;
- }
-
- private static int testToJavaFormatStringDoubleFixed() {
- System.out.println(" testToJavaFormatStringDoubleFixed");
- int failures = 0;
-
- double[] d = new double [] {
- -5.9522650387500933e18, // dtoa() fast path
- 0.872989018674569, // dtoa() fast iterative - long
- 1.1317400099603851e308 // dtoa() slow iterative
- };
-
- for(int i = 0; i < d.length; i++) {
- OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[i]);
- failures += check("testToJavaFormatStringDoubleFixed", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(d[i]));
- }
-
- return failures;
- }
-
- private static int testToJavaFormatStringDoubleRandom() {
- System.out.println(" testToJavaFormatStringDoubleRandom");
- int failures = 0;
-
- for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
- double[] d = new double[] {
- RANDOM.nextLong(),
- RANDOM.nextGaussian(),
- RANDOM.nextDouble()*Double.MAX_VALUE
- };
- for(int j = 0; j < d.length; j++) {
- OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(d[j]);
- failures += check("testToJavaFormatStringDoubleRandom", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(d[j]));
- }
- }
-
- return failures;
- }
-
- private static int testToJavaFormatStringDouble() {
- System.out.println(" testToJavaFormatStringDouble");
- int failures = 0;
- failures += testToJavaFormatStringDoubleFixed();
- failures += testToJavaFormatStringDoubleRandom();
- return failures;
- }
-
- private static int testToJavaFormatStringFloatFixed() {
- System.out.println(" testToJavaFormatStringFloatFixed");
- int failures = 0;
-
- float[] f = new float[] {
- -9.8784166e8f, // dtoa() fast path
- 0.70443946f, // dtoa() fast iterative - int
- 1.8254228e37f // dtoa() slow iterative
- };
-
- for(int i = 0; i < f.length; i++) {
- OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[i]);
- failures += check("testToJavaFormatStringFloatFixed", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(f[i]));
- }
-
- return failures;
- }
-
- private static int testToJavaFormatStringFloatRandom() {
- System.out.println(" testToJavaFormatStringFloatRandom");
- int failures = 0;
-
- for(int i = 0; i < NUM_RANDOM_TESTS; i++) {
- float[] f = new float[] {
- RANDOM.nextInt(),
- (float)RANDOM.nextGaussian(),
- RANDOM.nextFloat()*Float.MAX_VALUE
- };
- for(int j = 0; j < f.length; j++) {
- OldFloatingDecimalForTest ofd = new OldFloatingDecimalForTest(f[j]);
- failures += check("testToJavaFormatStringFloatRandom", ofd.toJavaFormatString(), FloatingDecimal.toJavaFormatString(f[j]));
- }
- }
-
- return failures;
- }
-
- private static int testToJavaFormatStringFloat() {
- System.out.println(" testToJavaFormatStringFloat");
- int failures = 0;
-
- failures += testToJavaFormatStringFloatFixed();
- failures += testToJavaFormatStringFloatRandom();
-
- return failures;
- }
-
- private static int testToJavaFormatString() {
- System.out.println("testToJavaFormatString");
- int failures = 0;
-
- failures += testToJavaFormatStringDouble();
- failures += testToJavaFormatStringFloat();
-
- return failures;
- }
-
- public static void main(String[] args) {
- int failures = 0;
-
- failures += testAppendTo();
- failures += testParse();
- failures += testToJavaFormatString();
-
- if (failures != 0) {
- throw new RuntimeException("" + failures + " failures while testing FloatingDecimal");
- }
- }
-}
--- a/jdk/test/sun/tools/jstatd/TestJstatdServer.java Wed Dec 23 15:41:55 2015 -0800
+++ b/jdk/test/sun/tools/jstatd/TestJstatdServer.java Thu Dec 24 10:34:31 2015 -0800
@@ -24,6 +24,7 @@
/*
* @test
* @bug 4990825
+ * @key intermittent
* @library /lib/testlibrary
* @modules java.management
* @build jdk.testlibrary.* JstatdTest JstatGCUtilParser