Mercurial > hg > openjdk > lambda > jdk
changeset 9536:e233d4d68878
Merge
line wrap: on
line diff
--- a/.hgignore Tue Aug 13 10:42:37 2013 -0700 +++ b/.hgignore Wed Aug 14 15:53:13 2013 -0700 @@ -5,3 +5,19 @@ ^make/netbeans/.*/dist/ ^.hgtip .DS_Store +^test-ng/gen-separate/ +^test-ng/lib/ +^.idea/ +^combo-tests/gen/ +^combo-tests/build/ +^out/ +.*\.rej$ +.*\.orig$ +.*\.iml$ +.*~$ +^webrev/ +webrev.zip$ +^.classpath +^.project +JTreport +JTwork
--- a/.hgtags Tue Aug 13 10:42:37 2013 -0700 +++ b/.hgtags Wed Aug 14 15:53:13 2013 -0700 @@ -167,16 +167,20 @@ b3246687c3695dff6f461bb407f9db88f7d072e7 jdk8-b43 db471a7af03168e4441c245b1d9976f720a7cb77 jdk8-b44 b92353a01aa049bc508fc56f0347d5934b7c4390 jdk8-b45 +077225955d5722f0fbd780650178c5931693c07e lambda-b45 8d2ed9d58453c8049715a72a6d26b6b66b37a94c jdk8-b46 00b22b23269a57d0bb46c57753be2fe9a9d2c1a3 jdk8-b47 3e4ab821f46166fcf63e8fe5c8046216003c941f jdk8-b48 +b3b65a3d441e7f39cb68140d2db547704ea0a670 lambda-b48 51707c3b75c0f521794d9ab425f4e5b2351c70c1 jdk8-b49 e4bae5c53fca8fcb9393d47fd36a34b9e2e8d4ec jdk8-b50 +958eaa191b79bf79be82979240903199791ed9f7 lambda-b50 e865efbc71059a414b3b2dd2e0adfcb3d2ab6ff9 jdk8-b51 e8569a473cee7f4955bd9e76a9bdf6c6a07ced27 jdk8-b52 2c6933c5106b81a8578b70996fe5b735fb3adb60 jdk8-b53 70ad0ed1d6cef0e7712690d1bab21e4769708aad jdk8-b54 1f3f4b333341873f00da3dee85e4879f0e89c9bb jdk8-b55 +2e6170973d921fe4b8d2dfd6032f5aaf4150a542 lambda-b56 2e9eeef2909b33c9224a024afddb61ccb0b77f14 jdk8-b56 51594d095a4bcffac4a314bf6e148214501399e0 jdk8-b57 d94613ac03d8de375ef60493e2bb76dbd30d875d jdk8-b58
--- a/.jcheck/conf Tue Aug 13 10:42:37 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -project=jdk8
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/build.xml Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="jdk" default="test"> + + <property name="build.dir" value="../../build/combo-tests" /> + <property name="gen.dir" value="${build.dir}/gen" /> + <property name="test.classes.dir" value="${build.dir}/test-classes"/> + <property name="test.reports.dir" value="${build.dir}/test-reports"/> + <property name="test.src.dir" value="tests"/> + <property name="lib.dir" location="lib" /> + <property name="test.pattern" value="*Test" /> + <property name="lambda.metafactory" value="" /> + <property name="combo.debug" value="" /> + <property name="heap.size" value="4G" /> + + <property name="lib.testng.jar" value="${lib.dir}/testng-6.7.jar"/> + <property name="lib.tools.jar" value="${java.home}/../lib/tools.jar"/> + + <path id="test.class.path"> + <pathelement path="${sun.boot.class.path}" /> + <pathelement location="${test.classes.dir}" /> + <pathelement location="${lib.testng.jar}"/> + <pathelement location="${lib.tools.jar}"/> + </path> + + <taskdef name="testng" classpathref="test.class.path" classname="org.testng.TestNGAntTask" /> + + <target name="prepare"> + <mkdir dir="${build.dir}"/> + <mkdir dir="${lib.dir}"/> + <mkdir dir="${test.classes.dir}"/> + <mkdir dir="${test.reports.dir}"/> + </target> + + <target name="test-compile" depends="prepare"> + <javac destdir="${test.classes.dir}" debug="on" srcdir="${test.src.dir}" fork="true" + classpathref="test.class.path"> + <compilerarg value="-XDlambdaToMethod"/> + </javac> + </target> + + <target name="test" depends="test-compile" > + <delete dir="${gen.dir}" /> + <testng outputdir="${test.reports.dir}" usedefaultlisteners="false" + listeners="org.testng.reporters.FailedReporter,org.testng.reporters.XMLReporter"> + <classpath refid="test.class.path" /> + <classfileset dir="${test.classes.dir}" includes="**/${test.pattern}.class"/> + <jvmarg value="-Xms1G" /> + <jvmarg value="-Xmx${heap.size}" /> + <jvmarg value="-XX:+UseNUMA" /> + <jvmarg value="-XX:+UseG1GC" /> + <jvmarg value="-ea" /> + <jvmarg value="-esa" /> + <jvmarg value="-Xverify:all" /> + <sysproperty key="lambda.metafactory" value="${lambda.metafactory}" /> + <sysproperty key="combo.debug" value="${combo.debug}" /> + </testng> + </target> + + <!-- New target added that uses a different set of listeners to show results in Aurora --> + <target name="aurora-test" depends="test-compile" > + <testng outputdir="${test.reports.dir}" usedefaultlisteners="false" + listeners="org.testng.reporters.JUnitXMLReporter,org.testng.reporters.JUnitReportReporter, + org.testng.reporters.XMLReporter"> + <classpath refid="test.class.path" /> + <classfileset dir="${test.classes.dir}" includes="**/${test.pattern}.class"/> + <jvmarg value="-Xms1G" /> + <jvmarg value="-Xmx2G" /> + <jvmarg value="-XX:+UseNUMA" /> + <jvmarg value="-XX:+UseG1GC" /> + <jvmarg value="-ea" /> + <jvmarg value="-esa" /> + <jvmarg value="-Xverify:all" /> + <sysproperty key="lambda.metafactory" value="${lambda.metafactory}" /> + <sysproperty key="combo.debug" value="${combo.debug}" /> + </testng> + </target> + + <target name="clean"> + <delete dir="${build.dir}" /> + <delete dir="${gen.dir}" /> + </target> +</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/BridgeMethodTestCase.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static org.testng.Assert.fail; + +/** + * BridgeMethodTestCase -- used for asserting linkage to bridges under separate compilation. + * + * Example test case: + * public void test1() throws IOException, ReflectiveOperationException { + * compileSpec("C(Bc1(A))"); + * assertLinkage("C", LINKAGE_ERROR, "B1"); + * recompileSpec("C(Bc1(Ac0))", "A"); + * assertLinkage("C", "A0", "B1"); + * } + * + * This compiles A, B, and C, asserts that C.m()Object does not exist, asserts + * that C.m()Number eventually invokes B.m()Number, recompiles B, and then asserts + * that the result of calling C.m()Object now arrives at A. + * + * @author Brian Goetz + */ + +@Test +public abstract class BridgeMethodTestCase extends JavacTemplateTestBase { + + private static final String TYPE_LETTERS = "ABCDIJK"; + + private static final String BASE_INDEX_CLASS = "class C0 {\n" + + " int val;\n" + + " C0(int val) { this.val = val; }\n" + + " public int getVal() { return val; }\n" + + "}\n"; + + private static final String INDEX_CLASS_TEMPLATE = "class C#ID extends C#PREV {\n" + + " C#ID(int val) { super(val); }\n" + + "}\n"; + + + + protected static String LINKAGE_ERROR = "-1"; + + private List<File> compileDirs = new ArrayList<>(); + + /** + * Compile all the classes in a class spec, and put them on the classpath. + * + * The spec is the specification for a nest of classes, using the following notation + * A, B represent abstract classes + * C represents a concrete class + * I, J, K represent interfaces + * Lowercase 'c' following a class means that the method m() is concrete + * Lowercase 'a' following a class or interface means that the method m() is abstract + * Lowercase 'd' following an interface means that the method m() is default + * A number 0, 1, or 2 following the lowercase letter indicates the return type of that method + * 0 = Object, 1 = Number, 2 = Integer (these form an inheritance chain so bridges are generated) + * A classes supertypes follow its method spec, in parentheses + * Examples: + * C(Ia0, Jd0) -- C extends I and J, I has abstract m()Object, J has default m()Object + * Cc1(Ia0) -- C has concrete m()Number, extends I with abstract m()Object + * If a type must appear multiple times, its full spec must be in the first occurrence + * Example: + * C(I(Kd0), J(K)) + */ + protected void compileSpec(String spec) throws IOException { + compileSpec(spec, false); + } + + protected void compileSpec(String spec, boolean debug) throws IOException { + ClassModel cm = new Parser(spec).parseClassModel(); + for (int i = 0; i <= cm.maxIndex() ; i++) { + if (debug) System.out.println(indexClass(i)); + addSourceFile(String.format("C%d.java", i), new StringTemplate(indexClass(i))); + } + for (Map.Entry<String, ClassModel> e : classes(cm).entrySet()) { + if (debug) System.out.println(e.getValue().toSource()); + addSourceFile(e.getKey() + ".java", new StringTemplate(e.getValue().toSource())); + } + compileDirs.add(compile(true)); + resetSourceFiles(); + assertCompileSucceeded(); + } + + /** + * Recompile only a subset of classes in the class spec, as named by names, + * and put them on the classpath such that they shadow earlier versions of that class. + */ + protected void recompileSpec(String spec, String... names) throws IOException { + List<String> nameList = Arrays.asList(names); + ClassModel cm = new Parser(spec).parseClassModel(); + for (int i = 0; i <= cm.maxIndex() ; i++) { + addSourceFile(String.format("C%d.java", i), new StringTemplate(indexClass(i))); + } + for (Map.Entry<String, ClassModel> e : classes(cm).entrySet()) + if (nameList.contains(e.getKey())) + addSourceFile(e.getKey() + ".java", new StringTemplate(e.getValue().toSource())); + compileDirs.add(compile(Arrays.asList(classPaths()), true)); + resetSourceFiles(); + assertCompileSucceeded(); + } + + protected void assertLinkage(String name, String... expected) throws ReflectiveOperationException { + for (int i=0; i<expected.length; i++) { + String e = expected[i]; + if (e.equals(LINKAGE_ERROR)) { + try { + int actual = invoke(name, i); + fail("Expected linkage error, got" + fromNum(actual)); + } + catch (LinkageError x) { + // success + } + } + else { + if (e.length() == 1) + e += "0"; + int expectedInt = toNum(e); + int actual = invoke(name, i); + if (expectedInt != actual) + fail(String.format("Expected %s but found %s for %s.m()%d", fromNum(expectedInt), fromNum(actual), name, i)); + } + } + } + + private Map<String, ClassModel> classes(ClassModel cm) { + HashMap<String, ClassModel> m = new HashMap<>(); + classesHelper(cm, m); + return m; + } + + private String indexClass(int index) { + if (index == 0) { + return BASE_INDEX_CLASS; + } else { + return INDEX_CLASS_TEMPLATE + .replace("#ID", String.valueOf(index)) + .replace("#PREV", String.valueOf(index - 1)); + } + } + + private static String overrideName(int index) { + return "C" + index; + } + + private void classesHelper(ClassModel cm, Map<String, ClassModel> m) { + if (!m.containsKey(cm.name)) + m.put(cm.name, cm); + for (ClassModel s : cm.supertypes) + classesHelper(s, m); + } + + private static String fromNum(int num) { + return String.format("%c%d", TYPE_LETTERS.charAt(num / 10), num % 10); + } + + private static int toNum(String name, int index) { + return 10*(TYPE_LETTERS.indexOf(name.charAt(0))) + index; + } + + private static int toNum(String string) { + return 10*(TYPE_LETTERS.indexOf(string.charAt(0))) + Integer.parseInt(string.substring(1, 2)); + } + + private int invoke(String name, int index) throws ReflectiveOperationException { + File[] files = classPaths(); + Class clazz = loadClass(name, files); + Method[] ms = clazz.getMethods(); + for (Method m : ms) { + if (m.getName().equals("m") && m.getReturnType().getName().equals(overrideName(index))) { + m.setAccessible(true); + Object instance = clazz.newInstance(); + Object c0 = m.invoke(instance); + Method getVal = c0.getClass().getMethod("getVal"); + getVal.setAccessible(true); + return (int)getVal.invoke(c0); + } + } + throw new NoSuchMethodError("cannot find method m()" + index + " in class " + name); + } + + private File[] classPaths() { + File[] files = new File[compileDirs.size()]; + for (int i=0; i<files.length; i++) + files[files.length - i - 1] = compileDirs.get(i); + return files; + } + + @BeforeMethod + @Override + public void reset() { + compileDirs.clear(); + super.reset(); + } + + private static class ClassModel { + + enum MethodType { + ABSTRACT('a'), CONCRETE('c'), DEFAULT('d'); + + public final char designator; + + MethodType(char designator) { + this.designator = designator; + } + + public static MethodType find(char c) { + for (MethodType m : values()) + if (m.designator == c) + return m; + throw new IllegalArgumentException(); + } + } + + private final String name; + private final boolean isInterface; + private final List<ClassModel> supertypes; + private final MethodType methodType; + private final int methodIndex; + + private ClassModel(String name, + boolean anInterface, + List<ClassModel> supertypes, + MethodType methodType, + int methodIndex) { + this.name = name; + isInterface = anInterface; + this.supertypes = supertypes; + this.methodType = methodType; + this.methodIndex = methodIndex; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(name); + if (methodType != null) { + sb.append(methodType.designator); + sb.append(methodIndex); + } + if (!supertypes.isEmpty()) { + sb.append("("); + for (int i=0; i<supertypes.size(); i++) { + if (i > 0) + sb.append(","); + sb.append(supertypes.get(i).toString()); + } + sb.append(")"); + } + return sb.toString(); + } + + int maxIndex() { + int maxSoFar = methodIndex; + for (ClassModel cm : supertypes) { + maxSoFar = Math.max(cm.maxIndex(), maxSoFar); + } + return maxSoFar; + } + + public String toSource() { + String extendsClause = ""; + String implementsClause = ""; + String methodBody = ""; + boolean isAbstract = "AB".contains(name); + + for (ClassModel s : supertypes) { + if (!s.isInterface) { + extendsClause = String.format("extends %s", s.name); + break; + } + } + + StringJoiner sj = new StringJoiner(", "); + for (ClassModel s : supertypes) + if (s.isInterface) + sj.add(s.name); + if (sj.length() > 0) { + if (isInterface) + implementsClause = "extends " + sj.toString(); + else + implementsClause = "implements " + sj.toString(); + } + if (methodType != null) { + switch (methodType) { + case ABSTRACT: + methodBody = String.format("public abstract %s m();", overrideName(methodIndex)); + break; + case CONCRETE: + methodBody = String.format("public %s m() { return new %s(%d); };", + overrideName(methodIndex), overrideName(methodIndex), toNum(name, methodIndex)); + break; + case DEFAULT: + methodBody = String.format("public default %s m() { return new %s(%d); };", + overrideName(methodIndex), overrideName(methodIndex), toNum(name, methodIndex)); + break; + + } + } + + return String.format("public %s %s %s %s %s { %s }", isAbstract ? "abstract" : "", + isInterface ? "interface" : "class", + name, extendsClause, implementsClause, methodBody); + } + } + + private static class Parser { + private final String input; + private final char[] chars; + private int index; + + private Parser(String s) { + input = s; + chars = s.toCharArray(); + } + + private char peek() { + return index < chars.length ? chars[index] : 0; + } + + private boolean peek(String validChars) { + return validChars.indexOf(peek()) >= 0; + } + + private char advanceIf(String validChars) { + if (peek(validChars)) + return chars[index++]; + else + return 0; + } + + private char advanceIfDigit() { + return advanceIf("0123456789"); + } + + private int index() { + StringBuilder buf = new StringBuilder(); + char c = advanceIfDigit(); + while (c != 0) { + buf.append(c); + c = advanceIfDigit(); + } + return Integer.valueOf(buf.toString()); + } + + private char advance() { + return chars[index++]; + } + + private char expect(String validChars) { + char c = advanceIf(validChars); + if (c == 0) + throw new IllegalArgumentException(String.format("Expecting %s at position %d of %s", validChars, index, input)); + return c; + } + + public ClassModel parseClassModel() { + List<ClassModel> supers = new ArrayList<>(); + char name = expect(TYPE_LETTERS); + boolean isInterface = "IJK".indexOf(name) >= 0; + ClassModel.MethodType methodType = peek(isInterface ? "ad" : "ac") ? ClassModel.MethodType.find(advance()) : null; + int methodIndex = 0; + if (methodType != null) { + methodIndex = index(); + } + if (peek() == '(') { + advance(); + supers.add(parseClassModel()); + while (peek() == ',') { + advance(); + supers.add(parseClassModel()); + } + expect(")"); + } + return new ClassModel(new String(new char[]{ name }), isInterface, supers, methodType, methodIndex); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/BridgeMethodsLinearTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,1083 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import java.io.IOException; + +import org.testng.annotations.Test; + +/** + * BridgeMethodsLinearTest + * + * @author Brian Goetz + */ +@Test +public class BridgeMethodsLinearTest extends BridgeMethodTestCase { + + /* + * Cc1(A) -> Cc1(Ac0) + * + * 0*: Inherited from A + * 1: Declared in C + */ + public void test1() throws IOException, ReflectiveOperationException { + compileSpec("Cc1(A)"); + assertLinkage("C", LINKAGE_ERROR, "C1"); + recompileSpec("Cc1(Ac0)", "A"); + assertLinkage("C", "A0", "C1"); + } + + /* + * Cc1(I) -> Cc1(Id0) + * + * 0*: Inherited default from I + * 1: Declared in C + */ + public void test2() throws IOException, ReflectiveOperationException { + compileSpec("Cc1(I)"); + assertLinkage("C", LINKAGE_ERROR, "C1"); + recompileSpec("Cc1(Id0)", "I"); + assertLinkage("C", "I0", "C1"); + } + + /* + * C(Bc1(A)) -> C(Bc1(Ac0)) + * + * 0*: Inherited from A + * 1: Inherited from B + */ + public void test3() throws IOException, ReflectiveOperationException { + compileSpec("C(Bc1(A))"); + assertLinkage("C", LINKAGE_ERROR, "B1"); + recompileSpec("C(Bc1(Ac0))", "A"); + assertLinkage("C", "A0", "B1"); + } + + /* + * C(B(Ac0)) -> C(Bc1(Ac0)) + * + * 0: Inherited from B (through bridge) + * 1: Inherited from B + */ + public void test4() throws IOException, ReflectiveOperationException { + compileSpec("C(B(Ac0))"); + assertLinkage("C", "A0", LINKAGE_ERROR); + recompileSpec("C(Bc1(Ac0))", "B"); + assertLinkage("C", "B1", "B1"); + } + + /* + * C(B(A)) -> C(Bc1(Ac0)) + * + * 0: Inherited from B (through bridge) + * 1: Inherited from B + */ + public void test5() throws IOException, ReflectiveOperationException { + compileSpec("C(B(A))"); + assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR); + recompileSpec("C(Bc1(Ac0))", "A", "B"); + assertLinkage("C", "B1", "B1"); + } + + /* + * C(Ac1(I)) -> C(Ac1(Id0)) + * + * 0*: Inherited default from I + * 1: Inherited from A + */ + public void test6() throws IOException, ReflectiveOperationException { + compileSpec("C(Ac1(I))"); + assertLinkage("C", LINKAGE_ERROR, "A1"); + recompileSpec("C(Ac1(Id0))", "I"); + assertLinkage("C", "I0", "A1"); + } + + /* + * C(A(Id0)) -> C(Ac1(Id0)) + * + * 0: Inherited from A (through bridge) + * 1: Inherited from A + */ + public void test7() throws IOException, ReflectiveOperationException { + compileSpec("C(A(Id0))"); + assertLinkage("C", "I0", LINKAGE_ERROR); + recompileSpec("C(Ac1(Id0))", "A"); + assertLinkage("C", "A1", "A1"); + } + + /* + * C(A(I)) -> C(Ac1(Id0)) + * + * 0*: Inherited from A (through bridge) + * 1*: Inherited from A + */ + public void test8() throws IOException, ReflectiveOperationException { + compileSpec("C(A(I))"); + assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR); + recompileSpec("C(Ac1(Id0))", "A", "I"); + assertLinkage("C", "A1", "A1"); + } + + /* + * C(Id1(J)) -> C(Id1(Jd0)) + * + * 0*: Inherited default from J + * 1: Inherited default from I + */ + public void test9() throws IOException, ReflectiveOperationException { + compileSpec("C(Id1(J))"); + assertLinkage("C", LINKAGE_ERROR, "I1"); + recompileSpec("C(Id1(Jd0))", "J"); + assertLinkage("C", "J0", "I1"); + } + + /* + * C(I(Jd0)) -> C(Id1(Jd0)) + * + * 0: Inherited default from I (through bridge) + * 1: Inherited default from I + */ + public void test10() throws IOException, ReflectiveOperationException { + compileSpec("C(I(Jd0))"); + assertLinkage("C", "J0", LINKAGE_ERROR); + recompileSpec("C(Id1(Jd0))", "I"); + assertLinkage("C", "I1", "I1"); + } + + /* + * C(I(J)) -> C(Id1(Jd0)) + * + * 0: Inherited default from I (through bridge) + * 1: Inherited default from I + */ + public void test11() throws IOException, ReflectiveOperationException { + compileSpec("C(I(J))"); + assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR); + recompileSpec("C(Id1(Jd0))", "I", "J"); + assertLinkage("C", "I1", "I1"); + } + + /* + * Cc2(B(Ac0)) -> Cc2(Bc1(Ac0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited from B + * 2: Declared in C + */ + public void test12() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(B(Ac0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Bc1(Ac0))", "B"); + assertLinkage("C", "C2", "B1", "C2"); + } + + /* + * Cc2(B(Aa0)) -> Cc2(Bc1(Aa0)) + * + * 0: Bridge in C (through bridge) + * 1*: Inherited from B + * 2: Declared in C + */ + public void test13() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(B(Aa0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Bc1(Aa0))", "B"); + assertLinkage("C", "C2", "B1", "C2"); + } + + /* + * Cc2(Bc1(A)) -> Cc2(Bc1(Ac0)) + * + * 0*: Inherited from A + * 1: Declared in C (through bridge) + * 2: Declared in C + */ + public void test14() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(Bc1(A))"); + assertLinkage("C", LINKAGE_ERROR, "C2", "C2"); + recompileSpec("Cc2(Bc1(Ac0))", "A"); + assertLinkage("C", "A0", "C2", "C2"); + } + + /* + * Cc2(Ba1(A)) -> Cc2(Ba1(Ac0)) + * + * 0*: Inherited from A + * 1: Declared in C (through bridge) + * 2: Declared in C + */ + public void test15() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(Ba1(A))"); + assertLinkage("C", LINKAGE_ERROR, "C2", "C2"); + recompileSpec("Cc2(Ba1(Ac0))", "A"); + assertLinkage("C", "A0", "C2", "C2"); + } + + /* + * Cc2(B(A)) -> Cc2(Bc1(Ac0)) + * + * 0*: Inherited from B (through bridge) + * 1*: Inherited from B + * 2: Declared in C + */ + public void test16() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(B(A))"); + assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Bc1(Ac0))", "B", "A"); + assertLinkage("C", "B1", "B1", "C2"); + } + + /* + * Cc2(A(Id0)) -> Cc2(Ac1(Id0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited from A + * 2: Declared in C + */ + public void test17() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(A(Id0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Ac1(Id0))", "A"); + assertLinkage("C", "C2", "A1", "C2"); + } + + /* + * Cc2(A(Ia0)) -> Cc2(Ac1(Ia0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited from A + * 2: Declared in C + */ + public void test18() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(A(Ia0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Ac1(Ia0))", "A"); + assertLinkage("C", "C2", "A1", "C2"); + } + + /* + * Cc2(Ac1(I)) -> Cc2(Ac1(Id0)) + * + * 0*: Inherited from I + * 1: Declared in C (through bridge) + * 2: Declared in C + */ + public void test19() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(Ac1(I))"); + assertLinkage("C", LINKAGE_ERROR, "C2", "C2"); + recompileSpec("Cc2(Ac1(Id0))", "I"); + assertLinkage("C", "I0", "C2", "C2"); + } + + /* + * Cc2(Aa1(I)) -> Cc2(Aa1(Id0)) + * + * 0*: Inherited from I + * 1: Declared in C (through bridge) + * 2: Declared in C + */ + public void test20() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(Aa1(I))"); + assertLinkage("C", LINKAGE_ERROR, "C2", "C2"); + recompileSpec("Cc2(Aa1(Id0))", "I"); + assertLinkage("C", "I0", "C2", "C2"); + } + + /* + * Cc2(A(I)) -> Cc2(Ac1(Id0)) + * + * 0*: Inherited from A (through bridge) + * 1*: Inherited from A + * 2: Declared in C + */ + public void test21() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(A(I))"); + assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Ac1(Id0))", "A", "I"); + assertLinkage("C", "A1", "A1", "C2"); + } + + /* + * Cc2(J(Id0)) -> Cc2(Jd1(Id0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited default from J + * 2: Declared in C + */ + public void test22() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(J(Id0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Jd1(Id0))", "J"); + assertLinkage("C", "C2", "J1", "C2"); + } + + /* + * Cc2(J(Ia0)) -> Cc2(Jd1(Ia0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited default from J + * 2: Declared in C + */ + public void test23() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(J(Ia0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Jd1(Ia0))", "J"); + assertLinkage("C", "C2", "J1", "C2"); + } + + /* + * Cc2(Jd1(I)) -> Cc2(Jd1(Id0)) + * + * 0*: Inherited default from I + * 1: Declared in C (through bridge) + * 2: Declared in C + */ + public void test24() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(Jd1(I))"); + assertLinkage("C", LINKAGE_ERROR, "C2", "C2"); + recompileSpec("Cc2(Jd1(Id0))", "I"); + assertLinkage("C", "I0", "C2", "C2"); + } + + /* + * Cc2(Ja1(I)) -> Cc2(Ja1(Id0)) + * + * 0*: Inherited default from I + * 1: Declared in C (through bridge) + * 2: Declared in C + */ + public void test25() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(Ja1(I))"); + assertLinkage("C", LINKAGE_ERROR, "C2", "C2"); + recompileSpec("Cc2(Ja1(Id0))", "I"); + assertLinkage("C", "I0", "C2", "C2"); + } + + /* + * Cc2(J(I)) -> Cc2(Jd1(Id0)) + * + * 0*: Inherited default from J (through bridge) + * 1*: Inherited default from J + * 2: Declared in C + */ + public void test26() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(J(I))"); + assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Jd1(Id0))", "J", "I"); + assertLinkage("C", "J1", "J1", "C2"); + } + + /* + * C(Ac1, I) -> C(Ac1, Id0) + * + * 0*: Inherited default from I + * 1: Inherited from A + */ + public void test27() throws IOException, ReflectiveOperationException { + compileSpec("C(Ac1,I)"); + assertLinkage("C", LINKAGE_ERROR, "A1"); + recompileSpec("C(Ac1,Id0)", "I"); + assertLinkage("C", "I0", "A1"); + } + + /* + * C(A, Id0) -> C(Ac1, Id0) + * + * 0*: Inherited default from I + * 1: Inherited from A + */ + public void test28() throws IOException, ReflectiveOperationException { + compileSpec("C(A,Id0)"); + assertLinkage("C", "I0", LINKAGE_ERROR); + recompileSpec("C(Ac1,Id0)", "A"); + assertLinkage("C", "I0", "A1"); + } + + /* + * C(A, I) -> C(Ac1, Id0) + * + * 0*: Inherited default from I + * 1: Inherited from A + */ + public void test29() throws IOException, ReflectiveOperationException { + compileSpec("C(A,I)"); + assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR); + recompileSpec("C(Ac1,Id0)", "A", "I"); + assertLinkage("C", "I0", "A1"); + } + + /* + * Cc2(Ac1, I) -> Cc2(Ac1, Id0) + * + * 0*: Inherited default from I + * 1: Declared in C (through bridge) + * 2: Declared in C + */ + public void test30() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(Ac1,I)"); + assertLinkage("C", LINKAGE_ERROR, "C2", "C2"); + recompileSpec("Cc2(Ac1,Id0)", "I"); + assertLinkage("C", "I0", "C2", "C2"); + } + + /* + * Cc2(Aa1, I) -> Cc2(Aa1, Id0) + * + * 0*: Inherited default from I + * 1: Declared in C (through bridge) + * 2: Declared in C + */ + public void test31() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(Aa1,I)"); + assertLinkage("C", LINKAGE_ERROR, "C2", "C2"); + recompileSpec("Cc2(Aa1,Id0)", "I"); + assertLinkage("C", "I0", "C2", "C2"); + } + + /* + * Cc2(A, Id0) -> Cc2(Ac1, Id0) + * + * 0: Declared in C (through bridge) + * 1*: Inherited from A + * 2: Declared in C + */ + public void test32() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(A,Id0)"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Ac1,Id0)", "A"); + assertLinkage("C", "C2", "A1", "C2"); + } + + /* + * Cc2(A, Ia0) -> Cc2(Ac1, Ia0) + * + * 0: Declared in C (through bridge) + * 1*: Inherited from A + * 2: Declared in C + */ + public void test33() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(A,Ia0)"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Ac1,Ia0)", "A"); + assertLinkage("C", "C2", "A1", "C2"); + } + + /* + * Cc2(A, I) -> Cc2(Ac1, Id0) + * + * 0*: Inherited from A + * 1*: Inherited default from I + * 2: Declared in C + */ + public void test34() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(A,I)"); + assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Ac1,Id0)", "A", "I"); + assertLinkage("C", "I0", "A1", "C2"); + } + + /* + * Cc2(Id0, J) -> Cc2(Id0, Jd1) + * + * 0: Declared in C (through bridge) + * 1*: Inherited default from J + * 2: Declared in C + */ + public void test35() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(Id0,J)"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Id0,Jd1)", "J"); + assertLinkage("C", "C2", "J1", "C2"); + } + + /* + * Cc2(Ia0, J) -> Cc2(Ia0, Jd1) + * + * 0: Declared in C (through bridge) + * 1*: Inherited default from J + * 2: Declared in C + */ + public void test36() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(Ia0,J)"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Ia0,Jd1)", "J"); + assertLinkage("C", "C2", "J1", "C2"); + } + + /* + * Cc2(I, J) -> Cc2(Id0, Jd1) + * + * 0*: Inherited default from I + * 1*: Inherited default from J + * 2: Declared in C + */ + public void test37() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(I,J)"); + assertLinkage("C", LINKAGE_ERROR, LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Id0,Jd1)", "I", "J"); + assertLinkage("C", "I0", "J1", "C2"); + } + + /* + * C(A(Id0), J(Id0)) -> C(Ac1(Id0), J(Id0)) + * + * 0: Inherited default from I + * 0*: Inherited from A (through bridge) + * 1*: Inherited from A + */ + public void test38() throws IOException, ReflectiveOperationException { + compileSpec("C(A(Id0),J(Id0))"); + assertLinkage("C", "I0", LINKAGE_ERROR); + recompileSpec("C(Ac1(Id0),J(Id0))", "A"); + assertLinkage("C", "A1", "A1"); + } + + /* + * C(A(Id0), J(Id0)) -> C(A(Id0), Jd1(Id0)) + * + * 0: Inherited default from I + * 0: Inherited default from J (through bridge) + * 1*: Inherited default from J + */ + public void test39() throws IOException, ReflectiveOperationException { + compileSpec("C(A(Id0),J(Id0))"); + assertLinkage("C", "I0", LINKAGE_ERROR); + recompileSpec("C(A(Id0),Jd1(Id0))", "J"); + assertLinkage("C", "J1", "J1"); + } + + /* + * C(A(Id0), J(Id0)) -> C(Ac2(Id0), Jd1(Id0)) + * + * 0: Inherited default from I + * 0*: Inherited from A (new bridge in A beats new bridge in J) + * 1*: Inherited default from J + * 2: Inherited from A + */ + public void test40() throws IOException, ReflectiveOperationException { + compileSpec("C(A(Id0),J(Id0))"); + assertLinkage("C", "I0", LINKAGE_ERROR); + recompileSpec("C(Ac2(Id0),Jd1(Id0))", "A", "J"); + assertLinkage("C", "A2", "J1", "A2"); + } + + /* + * C(J(Id0), K(Id0)) -> C(Jd1(Id0), K(Id0)) + * + * 0: Inherited from I + * 0*: Inherited default from J (through bridge) + * 1: Inherited default from J + */ + public void test41() throws IOException, ReflectiveOperationException { + compileSpec("C(J(Id0),K(Id0))"); + assertLinkage("C", "I0", LINKAGE_ERROR); + recompileSpec("C(Jd1(Id0),K(Id0))", "J"); + assertLinkage("C", "J1", "J1"); + } + + /* + * C(Ac2(Id0), J(Id0)) -> C(Ac2(Id0), Jd1(Id0)) + * + * 0: Inherited from A (bridge in A beats new bridge in J) + * 1*: Inherited default from J + * 2: Inherited from A + */ + public void test42() throws IOException, ReflectiveOperationException { + compileSpec("C(Ac2(Id0),J(Id0))"); + assertLinkage("C", "A2", LINKAGE_ERROR, "A2"); + recompileSpec("C(Ac2(Id0),Jd1(Id0))", "J"); + assertLinkage("C", "A2", "J1", "A2"); + } + + /* + * C(Ac2(Ia0), J(Ia0)) -> C(Ac2(Ia0), Jd1(Ia0)) + * + * 0: Inherited from A (bridge in A beats new bridge in J) + * 1*: Inherited default from J + * 2: Inherited from A + */ + public void test43() throws IOException, ReflectiveOperationException { + compileSpec("C(Ac2(Ia0),J(Ia0))"); + assertLinkage("C", "A2", LINKAGE_ERROR, "A2"); + recompileSpec("C(Ac2(Ia0),Jd1(Ia0))", "J"); + assertLinkage("C", "A2", "J1", "A2"); + } + + /* + * C(A(Id0), Jd1(Id0)) -> C(Ac2(Id0), Jd1(Id0)) + * + * 0: Inherited from J + * 0*: Inherited from A (new bridge in A beats bridge in J) + * 1: Inherited default from J + * 2*: Inherited from A + */ + public void test44() throws IOException, ReflectiveOperationException { + compileSpec("C(A(Id0),Jd1(Id0))"); + assertLinkage("C", "J1", "J1", LINKAGE_ERROR); + recompileSpec("C(Ac2(Id0),Jd1(Id0))", "A"); + assertLinkage("C", "A2", "J1", "A2"); + } + + /* + * C(A(Ia0), Jd1(Ia0)) -> C(Ac2(Ia0), Jd1(Ia0)) + * + * 0: Inherited from J + * 0*: Inherited from A (new bridge in A beats bridge in J) + * 1: Inherited default from J + * 2*: Inherited from A + */ + public void test45() throws IOException, ReflectiveOperationException { + compileSpec("C(A(Ia0),Jd1(Ia0))"); + assertLinkage("C", "J1", "J1", LINKAGE_ERROR); + recompileSpec("C(Ac2(Ia0),Jd1(Ia0))", "A"); + assertLinkage("C", "A2", "J1", "A2"); + } + + /* + * Cc2(A(Id0), J(Id0)) -> Cc2(Ac1(Id0), J(Id0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited from A + * 2: Declared in C + */ + public void test46() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(A(Id0),J(Id0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Ac1(Id0),J(Id0))", "A"); + assertLinkage("C", "C2", "A1", "C2"); + } + + /* + * Cc2(A(Ia0), J(Ia0)) -> Cc2(Ac1(Ia0), J(Ia0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited from A + * 2: Declared in C + */ + public void test47() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(A(Ia0),J(Ia0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Ac1(Ia0),J(Ia0))", "A"); + assertLinkage("C", "C2", "A1", "C2"); + } + + /* + * Cc2(A(Id0), J(Id0)) -> Cc2(A(Id0), Jd1(Id0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited default from J + * 2: Declared in C + */ + public void test48() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(A(Id0),J(Id0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(A(Id0),Jd1(Id0))", "J"); + assertLinkage("C", "C2", "J1", "C2"); + } + + /* + * Cc2(A(Ia0), J(Ia0)) -> Cc2(A(Ia0), Jd1(Ia0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited default from J + * 2: Declared in C + */ + public void test49() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(A(Ia0),J(Ia0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(A(Ia0),Jd1(Ia0))", "J"); + assertLinkage("C", "C2", "J1", "C2"); + } + + + /* + * Cc3(A(Id0), J(Id0)) -> Cc3(Ac1(Id0), Jd2(Id0)) + * + * 0: Bridge in C + * 1*: Inherited from A + * 2*: Inherited default from J + * 3: Declared in C + */ + public void test50() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(A(Id0),J(Id0))"); + assertLinkage("C", "C3", LINKAGE_ERROR, LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Ac1(Id0),Jd2(Id0))", "A", "J"); + assertLinkage("C", "C3", "A1", "J2", "C3"); + } + + /* + * Cc3(A(Ia0), J(Ia0)) -> Cc3(Ac1(Ia0), Jd2(Ia0)) + * + * 0: Bridge in C + * 1*: Inherited from A + * 2*: Inherited default from J + * 3: Declared in C + */ + public void test51() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(A(Ia0),J(Ia0))"); + assertLinkage("C", "C3", LINKAGE_ERROR, LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Ac1(Ia0),Jd2(Ia0))", "A", "J"); + assertLinkage("C", "C3", "A1", "J2", "C3"); + } + + /* + * Cc2(J(Id0), K(Id0)) -> Cc2(Jd1(Id0), K(Id0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited default from J + * 2: Declared in C + */ + public void test52() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(J(Id0),K(Id0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Jd1(Id0),K(Id0))", "J"); + assertLinkage("C", "C2", "J1", "C2"); + } + + /* + * Cc2(J(Ia0), K(Ia0)) -> Cc2(Jd1(Ia0), K(Ia0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited default from J + * 2: Declared in C + */ + public void test53() throws IOException, ReflectiveOperationException { + compileSpec("Cc2(J(Ia0),K(Ia0))"); + assertLinkage("C", "C2", LINKAGE_ERROR, "C2"); + recompileSpec("Cc2(Jd1(Ia0),K(Ia0))", "J"); + assertLinkage("C", "C2", "J1", "C2"); + } + + /* + * Cc3(J(Id0), K(Id0)) -> Cc3(Jd1(Id0), Kd2(Id0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited default from J + * 2*: Inherited default from K + * 3: Declared in C + */ + public void test54() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(J(Id0),K(Id0))"); + assertLinkage("C", "C3", LINKAGE_ERROR, LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Jd1(Id0),Kd2(Id0))", "J", "K"); + assertLinkage("C", "C3", "J1", "K2", "C3"); + } + + /* + * Cc3(J(Ia0), K(Ia0)) -> Cc3(Jd1(Ia0), Kd2(Ia0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited default from J + * 2*: Inherited default from K + * 3: Declared in C + */ + public void test55() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(J(Ia0),K(Ia0))"); + assertLinkage("C", "C3", LINKAGE_ERROR, LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Jd1(Ia0),Kd2(Ia0))", "J", "K"); + assertLinkage("C", "C3", "J1", "K2", "C3"); + } + + /* + * Cc3(Ac1(Id0), J(Id0)) -> Cc3(Ac1(Id0), Jd2(Id0)) + * + * 0: Declared in C (through bridge) + * 1: Declared in C (through bridge) + * 2*: Inherited default from J + * 3: Declared in C + */ + public void test56() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(Ac1(Id0),J(Id0))"); + assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Ac1(Id0),Jd2(Id0))", "J"); + assertLinkage("C", "C3", "C3", "J2", "C3"); + } + + /* + * Cc3(Ac1(Ia0), J(Ia0)) -> Cc3(Ac1(Ia0), Jd2(Ia0)) + * + * 0: Declared in C (through bridge) + * 1: Declared in C (through bridge) + * 2*: Inherited default from J + * 3: Declared in C + */ + public void test57() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(Ac1(Ia0),J(Ia0))"); + assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Ac1(Ia0),Jd2(Ia0))", "J"); + assertLinkage("C", "C3", "C3", "J2", "C3"); + } + + /* + * Cc3(Aa1(Id0), J(Id0)) -> Cc3(Aa1(Id0), Jd2(Id0)) + * + * 0: Declared in C (through bridge) + * 1: Declared in C (through bridge) + * 2*: Inherited default from J + * 3: Declared in C + */ + public void test58() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(Aa1(Id0),J(Id0))"); + assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Aa1(Id0),Jd2(Id0))", "J"); + assertLinkage("C", "C3", "C3", "J2", "C3"); + } + + /* + * Cc3(Aa1(Ia0), J(Ia0)) -> Cc3(Aa1(Ia0), Jd2(Ia0)) + * + * 0: Declared in C (through bridge) + * 1: Declared in C (through bridge) + * 2*: Inherited default from J + * 3: Declared in C + */ + public void test59() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(Aa1(Ia0),J(Ia0))"); + assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Aa1(Ia0),Jd2(Ia0))", "J"); + assertLinkage("C", "C3", "C3", "J2", "C3"); + } + + /* + * Cc3(A(Id0), Jd2(Id0)) -> Cc3(Ac1(Id0), Jd2(Id0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited from A + * 2: Declared in C (through bridge) + * 3: Declared in C + */ + public void test60() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(A(Id0),Jd2(Id0))"); + assertLinkage("C", "C3", LINKAGE_ERROR, "C3", "C3"); + recompileSpec("Cc3(Ac1(Id0),Jd2(Id0))", "A"); + assertLinkage("C", "C3", "A1", "C3", "C3"); + } + + /* + * Cc3(A(Im0), Jd2(Ia0)) -> Cc3(Ac1(Im0), Jd2(Ia0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited from A + * 2: Declared in C (through bridge) + * 3: Declared in C + */ + public void test61() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(A(Ia0),Jd2(Ia0))"); + assertLinkage("C", "C3", LINKAGE_ERROR, "C3", "C3"); + recompileSpec("Cc3(Ac1(Ia0),Jd2(Ia0))", "A"); + assertLinkage("C", "C3", "A1", "C3", "C3"); + } + + /* + * Cc3(A(Im0), Ja2(Id0)) -> Cc3(Ac1(Id0), Ja2(Id0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited from A + * 2: Declared in C (through bridge) + * 3: Declared in C + */ + public void test62() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(A(Id0),Ja2(Id0))"); + assertLinkage("C", "C3", LINKAGE_ERROR, "C3", "C3"); + recompileSpec("Cc3(Ac1(Id0),Ja2(Id0))", "A"); + assertLinkage("C", "C3", "A1", "C3", "C3"); + } + + /* + * Cc3(A(Im0), Ja2(Ia0)) -> Cc3(Ac1(Ia0), Ja2(Ia0)) + * + * 0: Declared in C (through bridge) + * 1*: Inherited from A + * 2: Declared in C (through bridge) + * 3: Declared in C + */ + public void test63() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(A(Ia0),Ja2(Ia0))"); + assertLinkage("C", "C3", LINKAGE_ERROR, "C3", "C3"); + recompileSpec("Cc3(Ac1(Ia0),Ja2(Ia0))", "A"); + assertLinkage("C", "C3", "A1", "C3", "C3"); + } + + /* + * Cc3(Jd1(Id0), K(Id0)) -> Cc3(Jd1(Id0), Kd2(Id0)) + * + * 0: Declared in C (through bridge) + * 1: Declared in C (through bridge) + * 2*: Inherited default from K + * 3: Declared in C + */ + public void test64() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(Jd1(Id0),K(Id0))"); + assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Jd1(Id0),Kd2(Id0))", "K"); + assertLinkage("C", "C3", "C3", "K2", "C3"); + } + + /* + * Cc3(Jd1(Ia0), K(Ia0)) -> Cc3(Jd1(Ia0), Kd2(Ia0)) + * + * 0: Declared in C (through bridge) + * 1: Declared in C (through bridge) + * 2*: Inherited default from K + * 3: Declared in C + */ + public void test65() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(Jd1(Ia0),K(Ia0))"); + assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Jd1(Ia0),Kd2(Ia0))", "K"); + assertLinkage("C", "C3", "C3", "K2", "C3"); + } + + /* + * Cc3(Ja1(Id0), K(Id0)) -> Cc3(Ja1(Id0), Kd2(Id0)) + * + * 0: Declared in C (through bridge) + * 1: Declared in C (through bridge) + * 2*: Inherited default from K + * 3: Declared in C + */ + public void test66() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(Jd1(Id0),K(Id0))"); + assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Jd1(Id0),Kd2(Id0))", "K"); + assertLinkage("C", "C3", "C3", "K2", "C3"); + } + + /* + * Cc3(Ja1(Ia0), K(Ia0)) -> Cc3(Ja1(Ia0), Kd2(Ia0)) + * + * 0: Declared in C (through bridge) + * 1: Declared in C (through bridge) + * 2*: Inherited default from K + * 3: Declared in C + */ + public void test67() throws IOException, ReflectiveOperationException { + compileSpec("Cc3(Jd1(Ia0),K(Ia0))"); + assertLinkage("C", "C3", "C3", LINKAGE_ERROR, "C3"); + recompileSpec("Cc3(Jd1(Ia0),Kd2(Ia0))", "K"); + assertLinkage("C", "C3", "C3", "K2", "C3"); + } + + // Dan's set A + public void testA1() throws IOException, ReflectiveOperationException { + compileSpec("C(Id0)"); + assertLinkage("C", "I0"); + } + + public void testA2() throws IOException, ReflectiveOperationException { + compileSpec("C(A(Id0))"); + assertLinkage("C", "I0"); + } + + public void testA3() throws IOException, ReflectiveOperationException { + compileSpec("C(A(Id0),J)"); + assertLinkage("C", "I0"); + } + + public void testA4() throws IOException, ReflectiveOperationException { + compileSpec("D(C(Id0),Jd0(Id0))"); + assertLinkage("D", "J0"); + assertLinkage("C", "I0"); + } + /* Doesn't compile + public void testA5() throws IOException, ReflectiveOperationException { + compileSpec("C(A(Id0),Jd0)"); + assertLinkage("C", "J0"); + assertLinkage("A", "I0"); + } + */ + /* Doesn't compile + public void testA6() throws IOException, ReflectiveOperationException { + compileSpec("C(A(Id0,Jd0))"); + assertLinkage("C", "J0"); + assertLinkage("A", "I0"); + } + */ + /* Doesn't compile + public void testA7() throws IOException, ReflectiveOperationException { + compileSpec("C(A(I0,Jd0))"); + assertLinkage("C", "J0"); + assertLinkage("A", "I0"); + } + */ + public void testA8() throws IOException, ReflectiveOperationException { + compileSpec("C(Ac0(Id0))"); + assertLinkage("C", "A0"); + } + + /* Doesn't compile + public void testA9() throws IOException, ReflectiveOperationException { + compileSpec("C(Ac0,Id0)"); + assertLinkage("C", "A0"); + } + */ + + // Dan's set B + + /* B1 can't be done, needs a second concrete class D + public void testB1() throws IOException, ReflectiveOperationException { + compileSpec("Cc1(Dc0)"); + assertLinkage("C", "C1", "C1"); + assertLinkage("D", "A0", LINKAGE_ERROR); + } + */ + + public void testB2() throws IOException, ReflectiveOperationException { + compileSpec("Cc1(Ac0)"); + assertLinkage("C", "C1", "C1"); + } + + //??? B3 seems to suggest that we should create an abstract class + //public void testB3() throws IOException, ReflectiveOperationException { + // compileSpec("Ba1(Cc0)"); + // assertLinkage("B", "C0", "A1"); + //} + + // B4 needs too many classes + + public void testB5() throws IOException, ReflectiveOperationException { + compileSpec("Cc1(Aa1(Id0))"); + assertLinkage("C", "C1", "C1"); + } + + public void testB6() throws IOException, ReflectiveOperationException { + compileSpec("C(Ac1(Id0))"); + assertLinkage("C", "A1", "A1"); + } + + public void testB7() throws IOException, ReflectiveOperationException { + compileSpec("Cc1(Id0)"); + assertLinkage("C", "C1", "C1"); + } + + public void testB8() throws IOException, ReflectiveOperationException { + compileSpec("C(Jd1(Id0))"); + assertLinkage("C", "J1", "J1"); + } + + // B9 needs too many classes + + // The rest of Dan's tests need generics +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/ComboTestBase.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import com.sun.tools.javac.util.Pair; +import org.testng.annotations.AfterSuite; +import org.testng.annotations.Test; + +import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.*; + +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/** + * ComboTestBase + * + * @author Brian Goetz + */ +public abstract class ComboTestBase<T extends ComboTestBase<T>> extends JavacTemplateTestBase { + private final static Map<Class, ComboTestMetadata> metadataMap = new HashMap<>(); + private final static boolean DEBUG_PROPERTY_SET = !System.getProperty("combo.debug", "").equals(""); + private final static long timestamp = System.currentTimeMillis(); + private final static Map</* filename */ String, + Map</* combo vars */ Map<String, String>, /* data */ Map<String, String>>> debugInfo + = new HashMap<>(); + + protected final ComboTestMetadata<T> metadata; + protected final Object[] comboArgs; + private boolean debugOnly = false; + + // This class cannot have a constructor; it interferes with testng's ability to find the factory method + //in subclasses. Anything that would be done by a constructor should insteady be done by init blocks + //or the pseudoConstructor() method. + + /* init */ { + metadata = getMetadata((Class<T>) getClass()); + comboArgs = new Object[metadata.dimensions.length]; + } + + /** + * This method is meant to be called from an @Factory method in the subclass, passing its own class literal + * as the clazz argument. It will introspect over the provided class, find the dimension variables, and + * generate the combinatorial explosion of test cases. (It consults shouldSkip() to determine if a particular + * combination should be skipped.) + */ + public static<T extends ComboTestBase> Object[] factory(Class<T> clazz) throws ReflectiveOperationException { + int index = 0; + try { + ComboTestMetadata<T> md = getMetadata(clazz); + List<T> list = new ArrayList<>(); + for (Object[] args : md.makeLoop()) { + T instance = md.makeInstance(args); + if (!instance.shouldSkip()) { + instance.pseudoConstructor(args); + list.add(instance); + instance.templates.put("__INDEX__", instance.new StringTemplate(Integer.toString(index++))); + if (DEBUG_PROPERTY_SET) + ((ComboTestBase) instance).debugOnly = true; + } + } + + return list.toArray(); + } catch (Throwable t) { + // This is a needed annoyance; TestNG swallows exception data from exceptions thrown from @Factory methods + t.printStackTrace(System.err); + t.printStackTrace(System.out); + throw t; + } + } + + /** For debugging of test cases -- sets tests into debug-only mode, where we print out the instantiations of + * the dimension variables and the templates. + */ + public static<T extends ComboTestBase> Object[] debugFactory(Class<T> clazz) throws ReflectiveOperationException { + Object[] results = factory(clazz); + for (Object o : results) + ((ComboTestBase) o).debugOnly = true; + return results; + } + + private static<T extends ComboTestBase<T>> ComboTestMetadata<T> getMetadata(Class<T> clazz) { + try { + ComboTestMetadata<T> md = metadataMap.get(clazz); + if (md == null) { + md = new ComboTestMetadata<T>(clazz); + metadataMap.put(clazz, md); + } + return md; + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + // Should be private, but can't be + void pseudoConstructor(Object[] args) throws ReflectiveOperationException { + System.arraycopy(args, 0, comboArgs, 0, args.length); + } + + private void addTemplates() throws ReflectiveOperationException { + for (int i=0; i<comboArgs.length; i++) + addTemplate(metadata.dimensions[i].name, asTemplate(comboArgs[i])); + + for (ComboTestMetadata.TemplateMember<TemplateVar, Field> m : metadata.templateFields) + addTemplate(m.name, asTemplate(m.member.get(this))); + + for (ComboTestMetadata.TemplateMember<TemplateVar, Method> m : metadata.templateMethods) + addTemplate(m.name, asTemplate(m.member.invoke(this))); + } + + private void addAllSourceFiles() throws ReflectiveOperationException { + for (ComboTestMetadata.TemplateMember<SourceFile, Field> m : metadata.sourceFields) + addSourceFile(m.name, asTemplate(m.member.get(this))); + + for (ComboTestMetadata.TemplateMember<SourceFile, Method> m : metadata.sourceMethods) + addSourceFile(m.name, asTemplate(m.member.invoke(this))); + } + + private void addSourceFiles(String group) throws ReflectiveOperationException { + for (ComboTestMetadata.TemplateMember<SourceFile, Field> m : metadata.sourceFields) + if (group.equals(m.annotation.group())) + addSourceFile(m.name, asTemplate(m.member.get(this))); + + for (ComboTestMetadata.TemplateMember<SourceFile, Method> m : metadata.sourceMethods) + if (group.equals(m.annotation.group())) + addSourceFile(m.name, asTemplate(m.member.invoke(this))); + } + + /** + * Should the current combination of inputs be skipped? Default implementation returns false for + * all combinations (indicating all combinations are valid.) + */ + protected boolean shouldSkip() { + return false; + } + + protected boolean shouldRun() { + return false; + } + + /** + * Called after test() compiles, allowing subclass an opportunity to examine diagonstics and assert + * their properties. Default implementation calls assertCompileSucceeded(). + * @param group + */ + protected void postCompile(String group) { + assertCompileSucceeded(); + } + + protected String[] getCompileOptions(String group) { + return new String[0]; + } + + /** + * Called before test() compiles, allowing subclass an opportunity to set compilation options. + */ + protected void preCompile(String group) { + } + + @Test + public void test() throws Exception { + if (debugOnly) { + addTemplates(); + addAllSourceFiles(); + System.out.println("Test case : " + getTestCaseDescription()); + for (Pair<String, Template> e : sourceFiles) + System.out.println("Source file " + e.fst + ": " + e.snd); + + String testFile = getClass().getName(); + Map<Map<String, String>, Map<String, String>> testCaseMap = debugInfo.get(testFile); + if (testCaseMap == null) { + testCaseMap = new HashMap<>(); + debugInfo.put(testFile, testCaseMap); + } + + Map<String, String> comboKeys = new HashMap<>(); + for (int i=0; i<metadata.dimensions.length; i++) + comboKeys.put(metadata.dimensions[i].name, comboArgs[i].toString()); + String compileOptions = Arrays.asList(getCompileOptions("")).toString(); + Map<String, String> data = new HashMap<>(); + int count = 0; + for (Pair<String, Template> e : sourceFiles) { + count++; + data.put("File." + count + ".FileName", e.fst); + data.put("File." + count + ".Source", e.snd.toString()); + data.put("File." + count + ".Group", ""); + data.put("File." + count + ".CompileOptions", compileOptions); + } + Object prev = testCaseMap.put(comboKeys, data); + if (prev != null) + fail(String.format("Duplicate key for file %s, keys %s", testFile, comboKeys.toString())); + } + else { + boolean generate = shouldRun(); + boolean errors = false; + List<File> files = new ArrayList<>(); + List<String> groupsReversed = new ArrayList<>(metadata.groups); + Collections.reverse(groupsReversed); + for (String g : groupsReversed) { + addTemplates(); + addSourceFiles(g); + preCompile(g); + addCompileOptions(getCompileOptions(g)); + for (File f : files) + addClassPath(f); + files.add(0, compile(generate)); + errors |= diags.errorsFound(); + postCompile(g); + reset(); + } + + if (generate && !errors) { + run(loadClass("Main", files.toArray(new File[files.size()]))); + } + } + } + + @AfterSuite + public void dumpDebug() throws IOException { + if (DEBUG_PROPERTY_SET) { + for (Map.Entry<String, Map<Map<String, String>, Map<String, String>>> f : debugInfo.entrySet()) { + File out = new File(String.format("ComboLog-%s-%d.ser", f.getKey(), timestamp)); + try (ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(out))) { + os.writeObject(f.getValue()); + } + } + } + } + + protected void run(Class<?> clazz) throws ReflectiveOperationException { + assertTrue(clazz != null); + } + + protected String getTestCaseDescription() { + StringBuilder sb = new StringBuilder(); + sb.append("ComboTest[").append(getClass().getName()).append(": "); + for (int i=0; i<metadata.dimensions.length; i++) + sb.append(metadata.dimensions[i].name).append(" = ").append(comboArgs[i]).append(", "); + sb.append("]"); + return sb.toString(); + } + + protected boolean skipConstrained(int arity, Enum... enums) { + for (int i=arity; i<enums.length; i++) + if (enums[i].ordinal() != 0) + return true; + return false; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/ComboTestDebug.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * ComboTestDebug -- utility program for dumping or comparing combo test debug logs. Debug logs can be + * used for debugging combo tests, as they contain all the generated source files and options (but are + * primarily used for testing for regressions in the combo test framework when changes are made to the + * framework.) + * + * @author Brian Goetz + */ +public class ComboTestDebug { + public static List<String> files = new ArrayList<>(); + public static boolean compare; + + private static void compareFiles(String s1, String s2) throws IOException, ClassNotFoundException { + Map</* combo vars */ Map<String, String>, + /* data */ Map<String, String>> f1, f2; + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(s1))) { + f1 = (Map<Map<String, String>, Map<String, String>>) ois.readObject(); + } + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(s2))) { + f2 = (Map<Map<String, String>, Map<String, String>>) ois.readObject(); + } + + if (f1.size() != f2.size()) + System.out.printf("Number of test cases differ: %d, %d%n", f1.size(), f2.size()); + + if (!f1.keySet().equals(f2.keySet())) { + System.out.printf("Test cases differ%n"); + for (Map<String, String> k : f1.keySet()) { + if (!f2.containsKey(k)) + System.out.printf("Only in file 1: test case %s%n", k); + } + for (Map<String, String> k : f2.keySet()) { + if (!f1.containsKey(k)) + System.out.printf("Only in file 2: test case %s%n", k); + } + } + + for (Map<String, String> k : f1.keySet()) { + if (!f2.keySet().contains(k)) + continue; + Map<String, String> d1 = f1.get(k); + Map<String, String> d2 = f2.get(k); + if (!d1.equals(d2)) { + System.out.printf("Test case %s%n", k); + for (String s : d1.keySet()) + if (!d2.containsKey(s)) + System.out.printf(" Only in file 1: key %s%n", s); + else { + if (!d1.get(s).equals(d2.get(s))) { + System.out.printf(" Key values differ for key %s: [%s] [%s]%n", s, d1.get(s), d2.get(s)); + } + } + for (String s : d2.keySet()) + if (!d1.containsKey(s)) + System.out.printf(" Only in file 2: key %s%n", s); + System.out.println(); + } + } + } + + private static void dumpFile(String s) throws IOException, ClassNotFoundException { + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(s))) { + Map</* combo vars */ Map<String, String>, + /* data */ Map<String, String>> debugInfo; + + debugInfo = (Map<Map<String, String>, Map<String, String>>) ois.readObject(); + System.out.printf("%d test cases%n%n", debugInfo.size()); + for (Map.Entry<Map<String, String>, Map<String, String>> e : debugInfo.entrySet()) { + System.out.println("Test case: " + e.getKey()); + for (Map.Entry<String, String> de : new TreeMap<>(e.getValue()).entrySet()) + System.out.printf(" %s = %s%n", de.getKey(), de.getValue()); + System.out.println(); + } + } + + } + + public static void main(String[] args) throws IOException, ClassNotFoundException { + for (String a : args) { + if (a.equals("-c")) + compare = true; + else if (a.startsWith("-")) + usage(); + else + files.add(a); + } + + if (compare) { + if (files.size() != 2) + usage(); + compareFiles(files.get(0), files.get(1)); + } + else { + for (String f : files) + dumpFile(f); + } + } + + public static void usage() { + System.out.println("Usage: ComboTestDebug [ -c ] files..."); + System.exit(0); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/ComboTestMetadata.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.*; + +/** + * ComboTestMetadata + * + * @author Brian Goetz*/ +class ComboTestMetadata<T> { + public final Class<T> clazz; + public final Constructor<T> ctor; + public final List<TemplateMember<TemplateVar, Field>> templateFields = new ArrayList<>(); + public final List<TemplateMember<TemplateVar, Method>> templateMethods = new ArrayList<>(); + public final List<TemplateMember<SourceFile, Field>> sourceFields = new ArrayList<>(); + public final List<TemplateMember<SourceFile, Method>> sourceMethods = new ArrayList<>(); + public final Set<String> groups = new TreeSet<>(); + public final Dimension[] dimensions; + + public ComboTestMetadata(Class<T> clazz) throws ReflectiveOperationException { + this.clazz = clazz; + Constructor[] constructors = clazz.getConstructors(); + ctor = (constructors.length == 1 && constructors[0].getParameterTypes().length > 0) ? constructors[0] : null; + + Map<DimensionVar, Field> tmpDimFields = new LinkedHashMap<>(); + for (Field f : clazz.getDeclaredFields()) { + TemplateVar template = f.getAnnotation(TemplateVar.class); + if (template != null) { + if (!Template.class.isAssignableFrom(f.getType()) && !String.class.isAssignableFrom(f.getType())) + throw new AssertionError(String.format("Found @TemplateVar on field %s whose type is not Template or String", f)); + f.setAccessible(true); + templateFields.add(new TemplateMember<>(template.value(), template, f)); + } + + SourceFile source = f.getAnnotation(SourceFile.class); + if (source != null) { + if (!Template.class.isAssignableFrom(f.getType()) && !String.class.isAssignableFrom(f.getType())) + throw new AssertionError(String.format("Found @SourceFile on field %s whose type is not Template or String", f)); + f.setAccessible(true); + sourceFields.add(new TemplateMember<>(source.value(), source, f)); + groups.add(source.group()); + } + + DimensionVar dim = f.getAnnotation(DimensionVar.class); + if (dim != null) + tmpDimFields.put(dim, f); + } + + for (Method m : clazz.getDeclaredMethods()) { + TemplateVar template = m.getAnnotation(TemplateVar.class); + if (template != null) { + if (!Template.class.isAssignableFrom(m.getReturnType()) && !String.class.isAssignableFrom(m.getReturnType())) + throw new AssertionError(String.format("Found @TemplateVar on method %s whose return type is not Template or String", m)); + m.setAccessible(true); + templateMethods.add(new TemplateMember<>(template.value(), template, m)); + } + + SourceFile source = m.getAnnotation(SourceFile.class); + if (source != null) { + if (!Template.class.isAssignableFrom(m.getReturnType()) && !String.class.isAssignableFrom(m.getReturnType())) + throw new AssertionError(String.format("Found @SourceFile on method %s whose return type is not Template or String ", m)); + sourceMethods.add(new TemplateMember<>(source.value(), source, m)); + groups.add(source.group()); + } + } + + if (constructors.length > 1) + throw new AssertionError(String.format("Multiple constructors for class %s", clazz)); + else if (ctor != null && !tmpDimFields.isEmpty()) + throw new AssertionError(String.format("Class %s has both constructors and fields with @DimensionVar", clazz)); + else if (constructors.length == 0 && tmpDimFields.isEmpty()) + throw new AssertionError(String.format("Class %s has neither constructors nor fields with @DimensionVar", clazz)); + else if (ctor != null) { + Class<?>[] argTypes = ctor.getParameterTypes(); + Annotation[][] paramAnnotations = ctor.getParameterAnnotations(); + dimensions = new Dimension[argTypes.length]; + for (int i = 0, argTypesLength = argTypes.length; i < argTypesLength; i++) { + DimensionVar cd = null; + for (Annotation a : paramAnnotations[i]) + if (a instanceof DimensionVar) { + cd = (DimensionVar) a; + break; + } + if (cd == null) + throw new AssertionError(String.format("Parameter %d of constructor %s does not have @DimensionVar annotation", i, ctor)); + dimensions[i] = new Dimension(cd, argTypes[i], String.format("Parameter %d of constructor %s", i, ctor)); + } + } + else { + dimensions = new Dimension[tmpDimFields.size()]; + int i = 0; + for (Map.Entry<DimensionVar, Field> e : tmpDimFields.entrySet()) + dimensions[i++] = new Dimension(e.getKey(), e.getValue()); + } + } + + public T makeInstance(Object[] args) throws ReflectiveOperationException { + if (ctor != null) + return ctor.newInstance(args); + else { + T t = clazz.newInstance(); + for (int i=0; i< dimensions.length; i++) + dimensions[i].field.set(t, args[i]); + return t; + } + } + + public SyntheticLoop makeLoop() { + int n = dimensions.length; + Object[][] dimValues = new Object[n][]; + for (int i=0; i<n; i++) + dimValues[i] = dimensions[i].values; + return new SyntheticLoop(dimValues); + } + + static class Dimension { + public final Object[] values; + public final String name; + public final Field field; + + public Dimension(DimensionVar dv, Field f) throws ReflectiveOperationException { + field = f; + field.setAccessible(true); + name = dv.value(); + values = getValues(dv, f.getType(), String.format("Field %s", f.getName())); + } + + public Dimension(DimensionVar dv, Class<?> paramType, String descr) throws ReflectiveOperationException { + field = null; + name = dv.value(); + values = getValues(dv, paramType, descr); + } + + private Object[] getValues(DimensionVar dv, Class<?> clazz, String descr) throws ReflectiveOperationException { + if (Enum.class.isAssignableFrom(clazz) && Template.class.isAssignableFrom(clazz)) { + Method m = clazz.getDeclaredMethod("values"); + m.setAccessible(true); + return (Template[]) m.invoke(null); + } + else if (int.class.isAssignableFrom(clazz)) { + Object[] ts = new Object[dv.rangeUpper() - dv.rangeLower() + 1]; + for (int i=dv.rangeLower(); i <= dv.rangeUpper(); i++) + ts[i-dv.rangeLower()] = i; + return ts; + } + else + throw new AssertionError(String.format("Invalid type %s for %s", clazz.getName(), descr)); + } + } + + static class TemplateMember<A extends Annotation, M extends Member> { + public final String name; + public final A annotation; + public final M member; + + TemplateMember(String name, A annotation, M member) { + this.name = name; + this.annotation = annotation; + this.member = member; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/Diagnostics.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import javax.tools.Diagnostic; +import javax.tools.JavaFileObject; +import java.util.ArrayList; +import java.util.List; + +/** +* A container for compiler diagnostics, separated into errors and warnings, + * used by JavacTemplateTestBase. + * + * @author Brian Goetz +*/ +public class Diagnostics implements javax.tools.DiagnosticListener<JavaFileObject> { + + protected List<String> errors = new ArrayList<>(); + protected List<String> warnings = new ArrayList<>(); + + public void report(Diagnostic<? extends JavaFileObject> diagnostic) { + switch (diagnostic.getKind()) { + case ERROR: + errors.add(diagnostic.getCode()); + break; + + case WARNING: + case MANDATORY_WARNING: + warnings.add(diagnostic.getCode()); + break; + } + } + + /** Were there any errors found? */ + public boolean errorsFound() { + return !errors.isEmpty(); + } + + /** How many errors were found? */ + public int getErrorCount() { + return errors.size(); + } + + /** Get the error keys */ + public List<String> errors() { + return errors; + } + + public String toString() { return errors.toString(); } + + /** Clear all diagnostic state */ + public void reset() { + errors.clear(); + warnings.clear(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/DimensionVar.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** +* DimensionVar +*/ +@Retention(RetentionPolicy.RUNTIME) +@java.lang.annotation.Target({ElementType.PARAMETER, ElementType.FIELD}) +public @interface DimensionVar { + String value(); + int rangeLower() default 0; + int rangeUpper() default 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/JavacTemplateTestBase.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; + +import com.sun.source.util.JavacTask; +import com.sun.tools.javac.util.Pair; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterSuite; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import static org.testng.Assert.fail; + +/** + * Base class for template-driven TestNG javac tests that support on-the-fly + * source file generation, compilation, classloading, execution, and separate + * compilation. + * + * <p>Manages a set of templates (which have embedded tags of the form + * {@code #\{NAME\}}), source files (which are also templates), and compile + * options. Test cases can register templates and source files, cause them to + * be compiled, validate whether the set of diagnostic messages output by the + * compiler is correct, and optionally load and run the compiled classes. + * + * @author Brian Goetz + */ +@Test +public abstract class JavacTemplateTestBase { + private static final Set<String> suiteErrors = Collections.synchronizedSet(new HashSet<>()); + private static final AtomicInteger counter = new AtomicInteger(); + private static final File root = new File(System.getProperty("user.dir"), "gen"); + private static final File nullDir = new File(System.getProperty("user.dir"), "empty"); + + protected final Map<String, Template> templates = new HashMap<>(); + protected final Diagnostics diags = new Diagnostics(); + protected final List<Pair<String, Template>> sourceFiles = new ArrayList<>(); + protected final List<String> compileOptions = new ArrayList<>(); + protected final List<File> classpaths = new ArrayList<>(); + protected final Template.Resolver defaultResolver = new MapResolver(templates); + + private Template.Resolver currentResolver = defaultResolver; + + /** Add a template with a specified name */ + protected void addTemplate(String name, Template t) { + templates.put(name, t); + } + + /** Add a template with a specified name */ + protected void addTemplate(String name, String s) { + templates.put(name, new StringTemplate(s)); + } + + /** Add a source file */ + protected void addSourceFile(String name, Template t) { + sourceFiles.add(new Pair<>(name, t)); + } + + /** Add a File to the class path to be used when loading classes; File values + * will generally be the result of a previous call to {@link #compile()}. + * This enables testing of separate compilation scenarios if the class path + * is set up properly. + */ + protected void addClassPath(File path) { + classpaths.add(path); + } + + /** + * Add a set of compilation command-line options + */ + protected void addCompileOptions(String... opts) { + Collections.addAll(compileOptions, opts); + } + + /** Reset the compile options to the default (empty) value */ + protected void resetCompileOptions() { compileOptions.clear(); } + + /** Remove all templates */ + protected void resetTemplates() { templates.clear(); } + + /** Remove accumulated diagnostics */ + protected void resetDiagnostics() { diags.reset(); } + + /** Remove all source files */ + protected void resetSourceFiles() { sourceFiles.clear(); } + + /** Remove registered class paths */ + protected void resetClassPaths() { classpaths.clear(); } + + // Before each test method, reset everything + @BeforeMethod + public void reset() { + resetCompileOptions(); + resetDiagnostics(); + resetSourceFiles(); + resetTemplates(); + resetClassPaths(); + } + + // After each test method, if the test failed, capture source files and diagnostics and put them in the log + @AfterMethod + public void copyErrors(ITestResult result) { + if (!result.isSuccess()) { + suiteErrors.addAll(diags.errors()); + + List<Object> list = new ArrayList<>(); + Collections.addAll(list, result.getParameters()); + list.add("Test case: " + getTestCaseDescription()); + for (Pair<String, Template> e : sourceFiles) + list.add("Source file " + e.fst + ": " + e.snd); + if (diags.getErrorCount() != 0) + list.add("Compile diagnostics: " + diags.toString()); + result.setParameters(list.toArray(new Object[list.size()])); + } + } + + @AfterSuite + // After the suite is done, dump any errors to output + public void dumpErrors() { + if (!suiteErrors.isEmpty()) + System.err.println("Errors found in test suite: " + suiteErrors); + } + + /** + * Get a description of this test case; since test cases may be combinatorially + * generated, this should include all information needed to describe the test case + */ + protected String getTestCaseDescription() { + return this.toString(); + } + + /** Assert that all previous calls to compile() succeeded */ + protected void assertCompileSucceeded() { + if (diags.errorsFound()) + fail("Expected successful compilation"); + } + + /** + * If the provided boolean is true, assert all previous compiles succeeded, + * otherwise assert that a compile failed. + * */ + protected void assertCompileSucceededIff(boolean b) { + if (b) + assertCompileSucceeded(); + else + assertCompileFailed(); + } + + /** Assert that a previous call to compile() failed */ + protected void assertCompileFailed() { + if (!diags.errorsFound()) + fail("Expected failed compilation"); + } + + /** Assert that a previous call to compile() failed with a specific error key */ + protected void assertCompileFailed(String message) { + if (!diags.errorsFound()) + fail("Expected failed compilation: " + message); + } + + /** Assert that a previous call to compile() failed with all of the specified error keys */ + protected void assertCompileErrors(String... keys) { + if (!diags.errorsFound()) + fail("Expected failed compilation"); + for (String k : keys) + if (!diags.errors().contains(k)) + fail("Expected compilation error " + k); + } + + /** Convert an object, which may be a Template or a String, into a Template */ + protected Template asTemplate(Object o) { + if (o instanceof Template) + return (Template) o; + else if (o instanceof String) + return new StringTemplate((String) o); + else + return new StringTemplate(o.toString()); + } + + /** Compile all registered source files */ + protected void compile() throws IOException { + compile(false); + } + + /** Compile all registered source files, optionally generating class files + * and returning a File describing the directory to which they were written */ + protected File compile(boolean generate) throws IOException { + List<JavaFileObject> files = new ArrayList<>(); + for (Pair<String, Template> e : sourceFiles) + files.add(new FileAdapter(e.fst, asTemplate(e.snd))); + return compile(classpaths, files, generate); + } + + /** Compile all registered source files, using the provided list of class paths + * for finding required classfiles, optionally generating class files + * and returning a File describing the directory to which they were written */ + protected File compile(List<File> classpaths, boolean generate) throws IOException { + List<JavaFileObject> files = new ArrayList<>(); + for (Pair<String, Template> e : sourceFiles) + files.add(new FileAdapter(e.fst, asTemplate(e.snd))); + return compile(classpaths, files, generate); + } + + private File compile(List<File> classpaths, List<JavaFileObject> files, boolean generate) throws IOException { + JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager fm = systemJavaCompiler.getStandardFileManager(null, null, null); + if (classpaths.size() > 0) + fm.setLocation(StandardLocation.CLASS_PATH, classpaths); + JavacTask ct = (JavacTask) systemJavaCompiler.getTask(null, fm, diags, compileOptions, null, files); + if (generate) { + File destDir = new File(root, Integer.toString(counter.incrementAndGet())); + // @@@ Assert that this directory didn't exist, or start counter at max+1 + destDir.mkdirs(); + fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(destDir)); + ct.generate(); + return destDir; + } + else { + ct.analyze(); + return nullDir; + } + } + + /** Load the given class using the provided list of class paths */ + protected Class<?> loadClass(String className, File... destDirs) { + try { + List<URL> list = new ArrayList<>(); + for (File f : destDirs) + list.add(new URL("file:" + f.toString().replace("\\", "/") + "/")); + return Class.forName(className, true, new URLClassLoader(list.toArray(new URL[list.size()]))); + } catch (ClassNotFoundException | MalformedURLException e) { + throw new RuntimeException("Error loading class " + className, e); + } + } + + /** An implementation of Template which is backed by a String */ + protected class StringTemplate implements Template { + protected final String template; + + public StringTemplate(String template) { + this.template = template; + } + + public String expand(String selector) { + return Behavior.expandTemplate(template, currentResolver); + } + + public String toString() { + return expand(""); + } + + public StringTemplate with(final String key, final String value) { + return new StringTemplateWithResolver(template, new KeyResolver(key, value)); + } + + } + + /** An implementation of Template which is backed by a String and which + * encapsulates a Resolver for resolving embedded tags. */ + protected class StringTemplateWithResolver extends StringTemplate { + private final Resolver localResolver; + + public StringTemplateWithResolver(String template, Resolver localResolver) { + super(template); + this.localResolver = localResolver; + } + + @Override + public String expand(String selector) { + Resolver saved = currentResolver; + currentResolver = new ChainedResolver(currentResolver, localResolver); + try { + return super.expand(selector); + } + finally { + currentResolver = saved; + } + } + + @Override + public StringTemplate with(String key, String value) { + return new StringTemplateWithResolver(template, new ChainedResolver(localResolver, new KeyResolver(key, value))); + } + } + + /** A Resolver which uses a Map to resolve tags */ + private class KeyResolver implements Template.Resolver { + private final String key; + private final String value; + + public KeyResolver(String key, String value) { + this.key = key; + this.value = value; + } + + @Override + public Template lookup(String k) { + return key.equals(k) ? new StringTemplate(value) : null; + } + } + + private class FileAdapter extends SimpleJavaFileObject { + private final String filename; + private final Template template; + + public FileAdapter(String filename, Template template) { + super(URI.create("myfo:/" + filename), Kind.SOURCE); + this.template = template; + this.filename = filename; + } + + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return toString(); + } + + public String toString() { + return Template.Behavior.expandTemplate(template.expand(filename), defaultResolver); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SeparateCompilationComboTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import org.testng.annotations.Factory; + +import java.lang.reflect.Method; + +import static org.testng.Assert.assertEquals; + +/** + * SeparateCompilationComboTest + */ +public class SeparateCompilationComboTest extends ComboTestBase<SeparateCompilationComboTest> { + @Factory + public static Object[] testCombo() throws Exception { + return factory(SeparateCompilationComboTest.class); + } + + @DimensionVar("FOO") Dummy dummy; + + @SourceFile(value="Main.java", group="B") String a2 = "public class Main { public String main() { return Other.b(); } }"; + @SourceFile(value="Other.java", group="B") String b2 = "public class Other { public static String b() { return \"1\"; } }"; + @SourceFile(value="Other.java", group="A") String b1 = "public class Other { public static String b() { return \"2\"; } }"; + + @Override + protected boolean shouldRun() { + return true; + } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Object obj = clazz.newInstance(); + Method m = clazz.getMethod("main"); + String result = (String) m.invoke(obj); + assertEquals("2", result); + } + + static enum Dummy implements Template { + A(); + + public String expand(String selector) { return toString(); } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SeparateCompilationComboTest2.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import org.testng.annotations.Factory; + +import java.lang.reflect.Method; + +import static org.testng.Assert.assertEquals; + +/** + * SeparateCompilationComboTest2 + */ +public class SeparateCompilationComboTest2 extends ComboTestBase<SeparateCompilationComboTest2> { + @Factory + public static Object[] testCombo() throws Exception { + return factory(SeparateCompilationComboTest2.class); + } + + @DimensionVar("FOO") Dummy dummy; + + @SourceFile(value="Main.java", group="A" ) + String main = "public class Main { public String main() { return new B().x(); } }"; + @SourceFile(value="B.java", group="B" ) + String b = "public class B extends C { public String x() { return \"2\"; } }"; + @SourceFile(value="C.java", group="C" ) + String c = "public class C { public String x() { return \"1\"; } }"; + + @Override + protected boolean shouldRun() { + return true; + } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Object obj = clazz.newInstance(); + Method m = clazz.getMethod("main"); + String result = (String) m.invoke(obj); + assertEquals("2", result); + } + + static enum Dummy implements Template { + A(); + + public String expand(String selector) { return toString(); } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SeparateCompilationTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * SeparateCompilationTest -- test of test framework to do separate compilation + */ +@Test +public class SeparateCompilationTest extends JavacTemplateTestBase { + + public void testCompletelySeparate() throws ReflectiveOperationException, IOException { + addSourceFile("A.java", new StringTemplate("public class A { public String a() throws Throwable { return Class.forName(\"B\").getName(); } }")); + File aFile = compile(true); + assertCompileSucceeded(); + reset(); + + addSourceFile("B.java", new StringTemplate("public class B { }")); + File bFile = compile(true); + assertCompileSucceeded(); + + Class clazz = loadClass("A", aFile, bFile); + Method m = clazz.getMethod("a"); + Object instance = clazz.newInstance(); + String x = (String) m.invoke(instance); + assertEquals("B", x); + } + + public void testIdenticalRecompile() throws ReflectiveOperationException, IOException { + addSourceFile("A.java", new StringTemplate("public class A { public String a() { return B.b(); } }")); + addSourceFile("B.java", new StringTemplate("public class B { public static String b() { return \"1\"; } }")); + File aFile = compile(true); + assertCompileSucceeded(); + // Deleting B.class is not generally needed for test cases that simply replace B, but here we're testing the framework's ability to find B elsewhere + File bClass = new File(aFile, "B.class"); + assertTrue(bClass.exists()); + boolean deleted = bClass.delete(); + assertTrue(deleted); + reset(); + + addSourceFile("B.java", new StringTemplate("public class B { public static String b() { return \"1\"; } }")); + File bFile = compile(true); + assertCompileSucceeded(); + + Class clazz = loadClass("A", bFile, aFile); + Method m = clazz.getMethod("a"); + Object instance = clazz.newInstance(); + String x = (String) m.invoke(instance); + assertEquals("1", x); + } + + public void testConsistentRecompile() throws ReflectiveOperationException, IOException { + addSourceFile("A.java", new StringTemplate("public class A { public String a() { return B.b(); } }")); + addSourceFile("B.java", new StringTemplate("public class B { public static String b() { return \"1\"; } }")); + File aFile = compile(true); + assertCompileSucceeded(); + reset(); + + addSourceFile("B.java", new StringTemplate("public class B { public static String b() { return \"2\"; } }")); + File bFile = compile(true); + assertCompileSucceeded(); + + Class clazz = loadClass("A", bFile, aFile); + Method m = clazz.getMethod("a"); + Object instance = clazz.newInstance(); + String x = (String) m.invoke(instance); + assertEquals("2", x); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SourceFile.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * SourceFile + */ +@Retention(RetentionPolicy.RUNTIME) +@java.lang.annotation.Target({ElementType.METHOD, ElementType.FIELD}) +public @interface SourceFile { + String value(); + String group() default ""; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/StackProcessingUtils.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +/** + * Utility class for defining stack processing-like operations + * + * @author Maurizio Cimadamore + */ +public class StackProcessingUtils { + + /** + * interface representing a stack item - each item has an arity --- + * if arity is > 0 - then the item is an operator + */ + public interface StackItem<T extends StackItem<T>> { + int arity(); + } + + /** + * A reducer takes an element of the stack and produces a result of a given + * type by applying the stack item to zero or more operands + */ + public interface StackReducer<I extends StackItem<I>, T, O> { + T reduce(I item, T[] operands, O o); + Class<T> resultToken(); + O reducerArg(I item, int i); + } + + static class StackResult<T> { + T result; + int nextPos; + + StackResult(T result, int nextPos) { + this.result = result; + this.nextPos = nextPos; + } + } + + public static <I extends StackItem<I>, T, O> T process(I[] elems, StackReducer<I, T, O> reducer, O o) { + StackResult<T> result = processInternal(0, elems, reducer, o); + assert result.nextPos == elems.length; + return result.result; + } + + private static <I extends StackItem<I>, T, O> StackResult<T> processInternal(int start, I[] elems, StackReducer<I, T, O> reducer, O o) { + @SuppressWarnings("unchecked") + I head = elems[start]; + T[] operands = (T[])java.lang.reflect.Array.newInstance(reducer.resultToken(), head.arity()); + int nextPos = start + 1; + for (int i = 0 ; i < head.arity() ; i++) { + StackResult<T> partialResult = processInternal(nextPos, elems, reducer, reducer.reducerArg(head, i)); + nextPos = partialResult.nextPos; + operands[i] = partialResult.result; + } + return new StackResult<T>(reducer.reduce(head, operands, o), nextPos); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/StringTemplateTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/** + * StringTemplateTest + */ +@Test +public class StringTemplateTest extends JavacTemplateTestBase { + private void assertTemplate(String expected, Template template) { + String result = template.expand(""); + assertEquals(result, expected, "for " + template); + } + + public void testStringTemplate() { + assertTemplate("XYZ", new StringTemplate("XYZ")); + addTemplate("NONE", "SOME"); + assertTemplate("SOME", new StringTemplate("#{NONE}")); + + addTemplate("A", "#{B}"); + addTemplate("B", "C"); + assertTemplate("C", new StringTemplate("#{A}")); + } + + public void testChainedTemplate() { + assertTemplate("A", new StringTemplate("#{A}").with("A", "A")); + assertTemplate("AB", new StringTemplate("#{A}#{B}").with("A", "A").with("B", "B")); + assertTemplate("TWO", new StringTemplate("#{A}").with("A", "ONE").with("A", "TWO")); + addTemplate("A", "BASE_A"); + addTemplate("AA", "#{A}"); + assertTemplate("FAKE_A", new StringTemplate("#{A}").with("A", "FAKE_A")); + assertTemplate("BASE_A", new StringTemplate("#{AA}")); + assertTemplate("FAKE_A", new StringTemplate("#{AA}").with("A", "FAKE_A")); + + addTemplate("X", new StringTemplate("#{A}").with("A", "intercepted")); + assertTemplate("intercepted", new StringTemplate("#{B}").with("B", "#{X}")); + addTemplate("Y", new StringTemplate("#{A}")); + assertTemplate("again", new StringTemplate("#{B}").with("B", "#{Y}").with("A", "again")); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SyntheticLoop.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import java.util.Iterator; + +/** + * SyntheticLoop -- exhaustively generate a set of combinations of variables + * + * @author Brian Goetz + */ +public class SyntheticLoop implements Iterable<Object[]> { + final int dimensions; + final int[] maxs; + final Object[][] values; + + /** Simple version -- generate the cartesian product of the provided sets of axis values */ + public SyntheticLoop(Object[][] dimValues) { + dimensions = dimValues.length; + maxs = new int[dimensions]; + values = new Object[dimensions][]; + for (int i=0; i<dimensions; i++) { + values[i] = dimValues[i]; + maxs[i] = values[i].length; + } + } + +// /** Complex version -- allow the contents of some dimensions to depend on previous dimensions, such +// * as generating tuples (a,b) where foo(b) <= foo(a). Of the two arrays provided, for each index i, +// * exactly one of dimValues[i], dimGenerators[i] must be non-null. +// */ +// public SyntheticLoop(Object[][] dimValues, DimensionFactory[] dimGenerators) { +// @@@NYI +// } + + public Iterator<Object[]> iterator() { + return new Iterator<Object[]>() { + final int[] idxs = new int[dimensions]; + boolean hasNext = (dimensions > 0); + + public boolean hasNext() { + return hasNext; + } + + public Object[] next() { + Object[] ret = new Object[dimensions]; + for (int i=0; i<dimensions; i++) + ret[i] = values[i][idxs[i]]; + hasNext = !advance(dimensions-1); + return ret; + } + + public void remove() { throw new UnsupportedOperationException(); } + + private boolean advance(int i) { + if (i < 0) + return true; + if (++idxs[i] == maxs[i]) { + idxs[i] = 0; + return advance(i - 1); + } + return false; + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/SyntheticLoopTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import org.testng.annotations.Test; + +import java.util.Iterator; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * SyntheticLoopTest + */ +@Test +public class SyntheticLoopTest { + public void testEmpty() { + SyntheticLoop sl = new SyntheticLoop(new Object[0][]); + Iterator<Object[]> it = sl.iterator(); + assertTrue(!it.hasNext()); + } + + private void assertSL(String expected, SyntheticLoop sl) { + StringBuilder sb = new StringBuilder(); + for (Object[] row : sl) { + for (Object o : row) { + sb.append(o); + } + sb.append(" "); + } + assertEquals(sb.toString(), expected); + } + + private void assertSL(int size, SyntheticLoop sl) { + int count = 0; + for (Object row : sl) { + ++count; + } + assertEquals(count, size); + } + + public void testSimple() { + Object[] abc = {"A", "B", "C"}; + assertSL("A B C ", new SyntheticLoop(new Object[][] {abc})); + assertSL("AA AB AC BA BB BC CA CB CC ", new SyntheticLoop(new Object[][] {abc, abc})); + assertSL(9, new SyntheticLoop(new Object[][] {abc, abc})); + assertSL(9, new SyntheticLoop(new Object[][] {abc, abc, new Object[] { "X" }})); + assertSL(27, new SyntheticLoop(new Object[][] {abc, abc, abc})); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/Template.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A template into which tags of the form {@code #\{KEY\}} or + * {@code #\{KEY.SUBKEY\}} can be expanded. + */ +public interface Template { + String expand(String selector); + + interface Resolver { + public Template lookup(String key); + } + + public static class Behavior { + private static final Pattern pattern = Pattern.compile("#\\{([A-Z_][A-Z0-9_]*(?:\\[\\d+\\])?)(?:\\.([A-Z0-9_]*))?\\}"); + + public static String expandTemplate(String template, final Map<String, Template> vars) { + return expandTemplate(template, new MapResolver(vars)); + } + + public static String expandTemplate(String template, Resolver res) { + CharSequence in = template; + StringBuffer out = new StringBuffer(); + while (true) { + boolean more = false; + Matcher m = pattern.matcher(in); + while (m.find()) { + String major = m.group(1); + String minor = m.group(2); + Template key = res.lookup(major); + if (key == null) + throw new IllegalStateException("Unknown major key " + major); + + String replacement = key.expand(minor == null ? "" : minor); + more |= pattern.matcher(replacement).find(); + m.appendReplacement(out, replacement); + } + m.appendTail(out); + if (!more) + return out.toString(); + else { + in = out; + out = new StringBuffer(); + } + } + } + + } +} + +class MapResolver implements Template.Resolver { + private final Map<String, Template> vars; + + public MapResolver(Map<String, Template> vars) {this.vars = vars;} + + public Template lookup(String key) { + return vars.get(key); + } +} + +class ChainedResolver implements Template.Resolver { + private final Template.Resolver upstreamResolver, thisResolver; + + public ChainedResolver(Template.Resolver upstreamResolver, Template.Resolver thisResolver) { + this.upstreamResolver = upstreamResolver; + this.thisResolver = thisResolver; + } + + public Template.Resolver getUpstreamResolver() { + return upstreamResolver; + } + + @Override + public Template lookup(String key) { + Template result = thisResolver.lookup(key); + if (result == null) + result = upstreamResolver.lookup(key); + return result; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/TemplateTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.testng.Assert.assertEquals; + +/** + * TemplateTest + */ +@Test +public class TemplateTest { + Map<String, Template> vars = new HashMap<>(); + + @BeforeTest + void before() { vars.clear(); } + + private void assertTemplate(String expected, String template) { + String result = Template.Behavior.expandTemplate(template, vars); + assertEquals(result, expected, "for " + template); + } + + private String dotIf(String s) { + return s == null || s.isEmpty() ? "" : "." + s; + } + + public void testTemplateExpansion() { + vars.put("A", s -> "a" + dotIf(s)); + vars.put("B", s -> "b" + dotIf(s)); + vars.put("C", s -> "#{A}#{B}"); + vars.put("D", s -> "#{A" + dotIf(s) + "}#{B" + dotIf(s) + "}"); + vars.put("_D", s -> "d"); + + assertTemplate("", ""); + assertTemplate("foo", "foo"); + assertTemplate("a", "#{A}"); + assertTemplate("a", "#{A.}"); + assertTemplate("a.FOO", "#{A.FOO}"); + assertTemplate("aa", "#{A}#{A}"); + assertTemplate("ab", "#{C}"); + assertTemplate("ab", "#{C.FOO}"); + assertTemplate("ab", "#{C.}"); + assertTemplate("a.FOOb.FOO", "#{D.FOO}"); + assertTemplate("ab", "#{D}"); + assertTemplate("d", "#{_D}"); + assertTemplate("#{A", "#{A"); + } + + public void testIndexedTemplate() { + vars.put("A[0]", s -> "a" ); + vars.put("A[1]", s -> "b" ); + vars.put("A[2]", s -> "c" ); + vars.put("X", s -> "0"); + assertTemplate("a", "#{A[0]}"); + assertTemplate("b", "#{A[1]}"); + assertTemplate("c", "#{A[2]}"); + } + + public void testAngleBrackets() { + vars.put("X", s -> "xyz"); + assertTemplate("List<String> ls = xyz;", "List<String> ls = #{X};"); + } + + @Test(expectedExceptions = IllegalStateException.class ) + public void testUnknownKey() { + assertTemplate("#{Q}", "#{Q}"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/combo/TemplateVar.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2012, 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 tools.javac.combo; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * ComboTemplate + */ +@Retention(RetentionPolicy.RUNTIME) +@java.lang.annotation.Target({ElementType.METHOD, ElementType.FIELD}) +public @interface TemplateVar { + String value(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/ConditionalExpressionTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2012, 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 tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.ComboTestBase; +import tools.javac.combo.DimensionVar; +import tools.javac.combo.SourceFile; +import tools.javac.combo.Template; +import tools.javac.combo.TemplateVar; + +/** + * ConditionalExpressionTest + */ +public class ConditionalExpressionTest extends ComboTestBase<ConditionalExpressionTest> { + @Factory + public static Object[] testConditionalExpression() throws Exception { + return factory(ConditionalExpressionTest.class); + } + + @DimensionVar("TYPE1") Type type1; + @DimensionVar("TYPE2") Type type2; + @DimensionVar("TARGET") Type target; + @DimensionVar("STMT") Statement stmt; + + @SourceFile("Main.java") + String clientFile = "public class Main {\n" + + " #{STMT_DECL};\n" + + " #{DECL.1};\n" + + " #{DECL.2};\n" + + " void test(boolean cond) { \n" + + " #{STMT};" + + " }\n" + +"}"; + + @Override + protected void postCompile(String grp) { + CondResult res = Type.condType(type1, type2); + if (!res.check(type1, type2, target)) { + assertCompileFailed(); + } else { + assertCompileSucceeded(); + } + } + + @Override + protected boolean shouldSkip() { + return target == Type.INT_CONST; + } + + interface CondResult { + boolean check(Type op1, Type op2, Type target); + } + + static CondResult ERR = new CondResult() { + public boolean check(Type op1, Type op2, Type target) { return false; } + }; + + static CondResult TARGET = new CondResult() { + public boolean check(Type op1, Type op2, Type target) { + return op1.compatibleWith(target) && op2.compatibleWith(target); + } + }; + + enum TypeKind { + NUMERIC, + BOOLEAN, + OTHER; + } + + enum Type implements Template, CondResult { + BYTE("byte", "0", TypeKind.NUMERIC), + SHORT("short", "0", TypeKind.NUMERIC), + INT("int", "0", TypeKind.NUMERIC), + INT_CONST("int", "0", TypeKind.NUMERIC), + LONG("long", "0", TypeKind.NUMERIC), + FLOAT("float", "0", TypeKind.NUMERIC), + DOUBLE("double", "0", TypeKind.NUMERIC), + CHAR("char", "'0'", TypeKind.NUMERIC), + BOOLEAN("boolean", "false", TypeKind.BOOLEAN), + J_L_BYTE("Byte", "null", TypeKind.NUMERIC), + J_L_SHORT("Short", "null", TypeKind.NUMERIC), + J_L_INT("Integer", "null", TypeKind.NUMERIC), + J_L_LONG("Long", "null", TypeKind.NUMERIC), + J_L_FLOAT("Float", "null", TypeKind.NUMERIC), + J_L_DOUBLE("Double", "null", TypeKind.NUMERIC), + J_L_CHAR("Character", "null", TypeKind.NUMERIC), + J_L_BOOLEAN("Boolean", "null", TypeKind.BOOLEAN), + NUMBER("Number", "null", TypeKind.OTHER), + SERIAL("java.io.Serializable", "null", TypeKind.OTHER); + + public boolean check(Type op1, Type op2, Type target) { + return compatibleWith(target); + } + + public boolean isErroneous() { + return false; + } + + public String expand(String qualifier) { + return typeStr; + } + + boolean compatibleWith(Type that) { + return that.anyOf(compatibleWith[this.ordinal()]); + } + + boolean anyOf(Type... ts) { + for (Type t : ts) { + if (t == this) return true; + } + return false; + } + + Type unbox() { + switch (this) { + case BYTE: + case J_L_BYTE: return BYTE; + case SHORT: + case J_L_SHORT: return SHORT; + case INT: + case INT_CONST: + case J_L_INT: return INT; + case LONG: + case J_L_LONG: return LONG; + case FLOAT: + case J_L_FLOAT: return FLOAT; + case DOUBLE: + case J_L_DOUBLE: return DOUBLE; + case BOOLEAN: + case J_L_BOOLEAN: return BOOLEAN; + case CHAR: + case J_L_CHAR: return CHAR; + default: + return null; + } + } + + String typeStr; + String defaultVal; + TypeKind kind; + + Type(String typeStr, String defaultVal, TypeKind kind) { + this.typeStr = typeStr; + this.defaultVal = defaultVal; + this.kind = kind; + } + + static CondResult condType(Type type1, Type type2) { + if (type1.kind == TypeKind.BOOLEAN && type2.kind == TypeKind.BOOLEAN) { + //boolean conditional + return BOOLEAN; + } else if (type1.kind == TypeKind.NUMERIC && type2.kind == TypeKind.NUMERIC) { + //numeric conditional + Type unbox1 = type1.unbox(); + Type unbox2 = type2.unbox(); + if (type1 == INT_CONST && + unbox2.anyOf(CHAR, BYTE, SHORT)) { + return unbox2; + } + if (type2 == INT_CONST && + unbox1.anyOf(CHAR, BYTE, SHORT)) { + return unbox1; + } + if (unbox1 == unbox2) { + return unbox1; + } + if (unbox1 == CHAR) { + return condType(INT, unbox2); + } + if (unbox2 == CHAR) { + return condType(unbox1, INT); + } + if (unbox1.compatibleWith(unbox2)) { + return unbox2; + } else if (unbox2.compatibleWith(unbox1)) { + return unbox1; + } else { + return ERR; + } + } else { + //reference conditional + return TARGET; + } + } + + static Type[][] compatibleWith = new Type[][] { + /* BYTE */ { BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, J_L_BYTE, NUMBER, SERIAL }, + /* SHORT */ { SHORT, INT, LONG, FLOAT, DOUBLE, J_L_SHORT, NUMBER, SERIAL }, + /* INT */ { INT, LONG, FLOAT, DOUBLE, J_L_INT, NUMBER, SERIAL }, + /* INT_CONST */ { INT, LONG, FLOAT, DOUBLE, J_L_INT, NUMBER, SERIAL }, + /* LONG */ { LONG, FLOAT, DOUBLE, J_L_LONG, NUMBER, SERIAL }, + /* FLOAT */ { FLOAT, DOUBLE, J_L_FLOAT, NUMBER, SERIAL }, + /* DOUBLE */ { DOUBLE, J_L_DOUBLE, NUMBER, SERIAL }, + /* CHAR */ { CHAR, INT, LONG, FLOAT, DOUBLE, J_L_CHAR, SERIAL }, + /* BOOLEAN */ { BOOLEAN, J_L_BOOLEAN, SERIAL }, + /* J_L_BYTE */ { J_L_BYTE, BYTE, SHORT, INT, LONG, FLOAT, DOUBLE, NUMBER, SERIAL }, + /* J_L_SHORT */ { J_L_SHORT, SHORT, INT, LONG, FLOAT, DOUBLE, NUMBER, SERIAL }, + /* J_L_INT */ { J_L_INT, INT, LONG, FLOAT, DOUBLE, NUMBER, SERIAL }, + /* J_L_LONG */ { J_L_LONG, LONG, FLOAT, DOUBLE, NUMBER, SERIAL }, + /* J_L_FLOAT */ { J_L_FLOAT, FLOAT, DOUBLE, NUMBER, SERIAL }, + /* J_L_DOUBLE */ { J_L_DOUBLE, DOUBLE, NUMBER, SERIAL }, + /* J_L_CHAR */ { J_L_CHAR, CHAR, INT, LONG, FLOAT, DOUBLE, SERIAL }, + /* J_L_BOOLEAN */ { J_L_BOOLEAN, BOOLEAN, SERIAL }, + /* NUMBER */ { NUMBER, SERIAL }, + /* SERIAL */ { SERIAL } }; + } + + enum Statement implements Template { + ASSIGN("#{TARGET} t = cond ? #{EXPR.1} : #{EXPR.2}"), + METHOD("m(cond ? #{EXPR.1} : #{EXPR.2})"); + + String stmtStr; + + Statement(String stmtStr) { + this.stmtStr = stmtStr; + } + + public String expand(String qual) { + return stmtStr; + } + } + + @TemplateVar("STMT_DECL") + Template stmt_decl = new Template() { + public String expand(String selector) { + return stmt == Statement.ASSIGN ? "" : "void m(#{TARGET} arg) { }"; + } + }; + + @TemplateVar("DECL") + Template decl = new Template() { + public String expand(String selector) { + int sel = Integer.valueOf(selector); + Type t = sel == 1 ? type1 : type2; + return t == Type.INT_CONST ? "" : String.format("#{TYPE%d} m%d() { return %s; }", sel, sel, t.defaultVal); + } + }; + + @TemplateVar("EXPR") + Template expr = new Template() { + public String expand(String selector) { + int sel = Integer.valueOf(selector); + Type t = sel == 1 ? type1 : type2; + return t == Type.INT_CONST ? t.defaultVal : String.format("m%d()", sel); + } + }; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/DefaultMethodAddTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2012, 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 tools.javac.lambda; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +import org.testng.annotations.Factory; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import tools.javac.combo.*; + +/** + * DefaultMethodAddTest: Test default method by running bytecode compiled at different times + * where conflicting default methods are added or declared + */ +public class DefaultMethodAddTest extends ComboTestBase<DefaultMethodAddTest> { + @Factory + public static Object[] testDefaultMethodAdd() throws Exception { + return factory(DefaultMethodAddTest.class); + } + + @DimensionVar("SHAPE") CShapes shapeType; + + /** + * The separate-compilation operation to be performed, such as adding a default method, + * adding an abstract itnerface method, redeclaring an inherited default method as + * abstract interface method, etc + */ + @DimensionVar("ADD") AddType addType; + + @SourceFile(value="B.java", group="A") + String interfaceBModified = "interface B #{SHAPE.B_DECL} { #{ADD} }"; + + @SourceFile(value="B.java", group="B") + String interfaceB = "interface B #{SHAPE.B_DECL} {}"; + + @SourceFile(value="A.java", group="B") + String interfaceA = "interface A { default String m() { return \"A\"; } }"; + + @SourceFile(value="C.java", group="B") + String classC = "#{SHAPE.C}"; + + @SourceFile(value="Main.java", group="B") + String classMain = "public class Main {\n" + + " public String main() {\n" + + " return new C().m();\n" + + " }\n" + + "}"; + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + String result = null, output = null; + try { + result = (String) m.invoke(obj); + } catch (InvocationTargetException ex) { + output = ex.getCause().toString(); + } + if(shapeType == CShapes.C_B_A) { + if(addType == AddType.ADD) { + assertEquals(output, null); + assertEquals(result, "B"); + } + else //redeclare + assertEquals(output, "java.lang.AbstractMethodError: Method B.m()Ljava/lang/String; is abstract"); + } + else if(shapeType == CShapes.C_I_AB2) { + assertEquals(output, null); + assertEquals(result, "A"); + } + else if(shapeType == CShapes.C_CI) { + assertEquals(output, null); + assertEquals(result, "D"); + } + else if(shapeType == CShapes.C_I_AB3) { + assertEquals(output, null); + assertEquals(result, "AB"); + } + else + assertTrue(output.matches("java.lang.AbstractMethodError: Conflicting default methods: .+[.]m .+[.]m")); + } + + enum CShapes implements Template { //shapes of class hirarchy + //class C implements interface A, B + C_AB("", + "class C implements A, B {}"), + //class C implments interface B, B extends interface A + C_B_A("extends A", + "class C implements B {}"), + //class C implments interface AB, AB extends interface A, B + C_I_AB("", + "interface AB extends A, B { }\n" + + "class C implements AB {}"), + //class C implments interface AB, AB extends interface A, B and explicitly inherits the default method in A + C_I_AB2("", + "interface AB extends A, B { default String m() { return A.super.m(); } }\n" + + "class C implements AB {}"), + //class C implments interface AB, AB extends interface A, B and overrides the default method inherited + C_I_AB3("", + "interface AB extends A, B { default String m() { return \"AB\"; } }\n" + + "class C implements AB {}"), + //class C extends Class D implements Interface B + C_CI("", + "class D { public String m() { return \"D\"; } }\n" + + "class C extends D implements B {}"); + + private final String sB_DECL; + private final String sC; + + CShapes(String sB_DECL, String sC) { + this.sB_DECL = sB_DECL; + this.sC = sC; + } + + public String expand(String selector) { + switch(selector) { + case "B_DECL": return sB_DECL; + case "C": return sC; + default: return toString(); + } + } + } + + enum AddType implements Template { // add by adding default method or abstract interface method + ADD("default String m() { return \"B\"; }"), + REDECLARE("String m();"); + + final String newCode; + + AddType(String str) { + newCode = str; + } + + public String expand(String selector) { + return newCode; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/DefaultMethodRemoveTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2012, 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 tools.javac.lambda; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +import org.testng.annotations.Factory; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import tools.javac.combo.*; + +/** + * DefaultMethodRemoveTest: Test default method by running bytecode compiled at different times + * where default methods are removed or redeclared + */ +public class DefaultMethodRemoveTest extends ComboTestBase<DefaultMethodRemoveTest> { + @Factory + public static Object[] testDefalutMethodRemove() throws Exception { + return factory(DefaultMethodRemoveTest.class); + } + + @DimensionVar("SHAPE") CShapes shapeType; + + /** + * The separate-compilation operation to be performed, such as removing a default method, + * redeclaring as abstract interface method, etc + */ + @DimensionVar("REMOVE") RemoveType removeType; + + @SourceFile(value="B.java", group="A") + String interfaceBModified = "interface B #{SHAPE.B_DECL} { #{REMOVE} }"; + + @SourceFile(value="A.java", group="B") + String interfaceA = "interface A { default String m() { return \"A\"; } }"; + + @SourceFile(value="B.java", group="B") + String interfaceB = "interface B #{SHAPE.B_DECL} { default String m() { return \"B\"; } }"; + + @SourceFile(value="C.java", group="B") + String classC = "#{SHAPE.C}"; + + @SourceFile(value="Main.java", group="B") + String classMain = "public class Main {\n" + + " public String main() {\n" + + " return new C().m();\n" + + " }\n" + + "}"; + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void postCompile(String group) { + if( group.equals("A") && shapeType == CShapes.C_B_AD && removeType == RemoveType.REMOVE ) + assertCompileErrors("compiler.err.types.incompatible.unrelated.defaults"); + } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + String result = null, output = null; + try { + result = (String) m.invoke(obj); + } catch (InvocationTargetException ex) { + output = ex.getCause().toString(); + } + if(shapeType == CShapes.C_B_A) { + if(removeType == RemoveType.REMOVE) + assertEquals(result, "A"); + else + assertEquals(output, "java.lang.AbstractMethodError: Method B.m()Ljava/lang/String; is abstract"); + } + else if(shapeType == CShapes.C_CI) + assertEquals(result, "D"); + else if(shapeType == CShapes.C_B_AD) { + if(removeType == RemoveType.REMOVE) + assertEquals(output, "java.lang.AbstractMethodError: C.m()Ljava/lang/String;"); + else + assertEquals(output, "java.lang.AbstractMethodError: Method B.m()Ljava/lang/String; is abstract"); + } + else { //C_B + if( removeType == RemoveType.REMOVE) + assertEquals(output, "java.lang.NoSuchMethodError: C.m()Ljava/lang/String;"); + else + assertEquals(output, "java.lang.AbstractMethodError: C.m()Ljava/lang/String;"); + } + } + + enum CShapes implements Template { //shapes of class hirarchy + //class C implements interface B + C_B("", + "class C implements B {}"), + //class C implments interface B, B extends interface A + C_B_A("extends A", + "class C implements B {}"), + //class C implments interface B, B extends interface A, D + C_B_AD("extends A, D", + "interface D { default String m() { return \"D\"; } }\n" + + "class C implements B {}"), + //class C extends Class D implements Interface B + C_CI("", + "class D { public String m() { return \"D\"; } }\n" + + "class C extends D implements B {}"); + + private final String sB_DECL; + private final String sC; + + CShapes(String sB_DECL, String sC) { + this.sB_DECL = sB_DECL; + this.sC = sC; + } + + public String expand(String selector) { + switch(selector) { + case "B_DECL": return sB_DECL; + case "C": return sC; + default: return toString(); + } + } + } + + enum RemoveType implements Template { //remove by removing default method code or redeclaring the interface method + REMOVE(""), + REDECLARE("String m();"); + + final String newCode; + + RemoveType(String str) { + newCode = str; + } + + public String expand(String selector) { + return newCode; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/LambdaArgAdaptationTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2012, 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 tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.ComboTestBase; +import tools.javac.combo.DimensionVar; +import tools.javac.combo.SourceFile; +import tools.javac.combo.Template; + +import java.lang.reflect.Method; +import java.util.EnumMap; +import java.util.EnumSet; + +import static org.testng.Assert.assertEquals; + +/** + * LambdaArgAdaptationTest + */ +public class LambdaArgAdaptationTest extends ComboTestBase<LambdaArgAdaptationTest> { + @Factory + public static Object[] testArgAdaptation() throws Exception { + return factory(LambdaArgAdaptationTest.class); + } + + @DimensionVar("FROM") Types fromType; + @DimensionVar("TO") Types toType; + + @SourceFile("Sam.java") + String samFile = "public interface Sam { String m(#{FROM} arg); }"; + + @SourceFile("Main.java") + String clientFile = "public class Main {\n" + +" static String m(#{TO} arg) { \n" + +" return String.valueOf(arg);\n" + +" }\n" + +" public String main() { \n" + + " Sam s = Main::m;\n" + + " return s.m(#{FROM.ARG});\n" + + " }\n" + +"}"; + + + @Override + protected String[] getCompileOptions(String group) { + return new String[] { "-XDlambdaToMethod" }; + } + + @Override + protected void postCompile(String group) { + if (fromType == Types.VOID || toType == Types.VOID) + assertCompileFailed(); + else if (fromType.pr == PR.REF && toType.pr == PR.REF) + assertCompileSucceededIff(toType.clazz.isAssignableFrom(fromType.clazz)); + else if (fromType.pr == PR.PRIM && toType.pr == PR.REF) + assertCompileSucceededIff(toType.clazz.isAssignableFrom(Types.boxMap.get(fromType).clazz)); + else if (fromType.pr == PR.REF && toType.pr == PR.PRIM) + assertCompileSucceededIff(fromType.unboxed != null && Types.widenMap.get(fromType.unboxed).contains(toType)); + else if (fromType.pr == PR.PRIM && toType.pr == PR.PRIM) + assertCompileSucceededIff(Types.widenMap.get(fromType).contains(toType)); + else + assertCompileSucceeded(); + } + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + String result = (String) m.invoke(obj); + String formatted = fromType.asObject.toString(); + if (fromType == Types.CHAR || fromType == Types.BOXED_CHAR) { + result = result.replace("A", "65"); + formatted = formatted.replace("A", "65"); + } + assertEquals(result.replace(".0", ""), formatted.replace(".0", "")); + } +} + +enum PR {PRIM, REF} + +enum Types implements Template { + BYTE(PR.PRIM, "byte", byte.class, "(byte) 1", (byte) 1), + SHORT(PR.PRIM, "short", short.class, "(short) 2", (short) 2), + CHAR(PR.PRIM, "char", char.class, "'A'", 'A'), + INT(PR.PRIM, "int", int.class, "3", 3), + LONG(PR.PRIM, "long", long.class, "4L", 4L), + FLOAT(PR.PRIM, "float", float.class, "5.0f", 5.0f), + DOUBLE(PR.PRIM, "double", double.class, "6.0", 6.0), + BOOLEAN(PR.PRIM, "boolean", boolean.class, "true", true), + VOID(PR.PRIM, "void", void.class, "null", null), + BOXED_BYTE(PR.REF, "Byte", Byte.class, "Byte.valueOf((byte) 1)", (byte) 1, BYTE), + BOXED_SHORT(PR.REF, "Short", Short.class, "Short.valueOf((short) 2)", (short) 2, SHORT), + BOXED_CHAR(PR.REF, "Character", Character.class, "Character.valueOf('A')", 'A', CHAR), + BOXED_INT(PR.REF, "Integer", Integer.class, "Integer.valueOf(3)", 3, INT), + BOXED_LONG(PR.REF, "Long", Long.class, "Long.valueOf(4)", (long) 4, LONG), + BOXED_FLOAT(PR.REF, "Float", Float.class, "Float.valueOf(5.0f)", 5.0f, FLOAT), + BOXED_DOUBLE(PR.REF, "Double", Double.class, "Double.valueOf(6.0)", 6.0, DOUBLE), + BOXED_BOOLEAN(PR.REF, "Boolean", Boolean.class, "Boolean.valueOf(true)", true, BOOLEAN), + NUMBER(PR.REF, "Number", Number.class, "9", 9), + OBJECT(PR.REF, "Object", Object.class, "10", 10), + STRING(PR.REF, "String", String.class, "\"foo\"", "foo"); + + public static EnumMap<Types, Types> boxMap = new EnumMap<>(Types.class); + public static EnumMap<Types, Types> unboxMap = new EnumMap<>(Types.class); + public static EnumMap<Types, EnumSet<Types>> widenMap = new EnumMap<>(Types.class); + + static { + for (Types t : values()) { + if (t.unboxed != null) { + boxMap.put(t.unboxed, t); + unboxMap.put(t, t.unboxed); + } + } + + widenMap.put(BYTE, EnumSet.of(BYTE, SHORT, INT, LONG, FLOAT, DOUBLE)); + widenMap.put(SHORT, EnumSet.of(SHORT, INT, LONG, FLOAT, DOUBLE)); + widenMap.put(CHAR, EnumSet.of(CHAR, INT, LONG, FLOAT, DOUBLE)); + widenMap.put(INT, EnumSet.of(INT, LONG, FLOAT, DOUBLE)); + widenMap.put(LONG, EnumSet.of(LONG, FLOAT, DOUBLE)); + widenMap.put(FLOAT, EnumSet.of(FLOAT, DOUBLE)); + widenMap.put(DOUBLE, EnumSet.of(DOUBLE)); + widenMap.put(BOOLEAN, EnumSet.of(BOOLEAN)); + } + + final PR pr; + final String name; + final Class<?> clazz; + final String valueString; + final Object asObject; + final Types unboxed; + + Types(PR pr, String name, Class<?> clazz, String valueString, Object asObject) { + this (pr, name, clazz, valueString, asObject, null); + } + + Types(PR pr, String name, Class<?> clazz, String valueString, Object asObject, Types unboxed) { + this.pr = pr; + this.name = name; + this.clazz = clazz; + this.valueString = valueString; + this.asObject = asObject; + this.unboxed = unboxed; + } + + public String expand(String selector) { + switch (selector) { + case "ARG": return valueString; + default: return name; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/LambdaCaptureTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2012, 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 tools.javac.lambda; + +import org.testng.annotations.Factory; +import org.testng.annotations.Test; +import tools.javac.combo.*; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import static org.testng.Assert.assertEquals; + +/** + * LambdaCaptureTest + * + * @author Brian Goetz + */ +public class LambdaCaptureTest extends ComboTestBase<LambdaCaptureTest> { + @Factory + public static Object[] testCombo() throws Exception { + return factory(LambdaCaptureTest.class); + } + + final int arity; + final ArgType[] argTypes; + final int capArity; + final CapType[] capTypes; + final boolean shouldSkip; + + public LambdaCaptureTest(@DimensionVar(value = "ARITY", rangeLower = 0, rangeUpper = 2) int arity, + @DimensionVar("ARGTYPE1") ArgType argType1, + @DimensionVar("ARGTYPE2") ArgType argType2, + @DimensionVar(value = "CAP_ARITY", rangeLower = 0, rangeUpper = 2) int capArity, + @DimensionVar("CAPTYPE1") CapType capType1, + @DimensionVar("CAPTYPE2") CapType capType2) { + this.arity = arity; + this.capArity = capArity; + this.argTypes = new ArgType[] { argType1, argType2 }; + this.capTypes = new CapType[] { capType1, capType2 }; + shouldSkip = skipConstrained(arity, argTypes) || skipConstrained(capArity, capTypes); + } + + @Override + protected String[] getCompileOptions(String group) { + return new String[] { "-XDlambdaToMethod" }; + } + + @SourceFile("Main.java") String test + = "public class Main {\n" + + " #{CAPTYPE1} f0 = #{CAPV.1};\n" + + " #{CAPTYPE2} f1 = #{CAPV.2};\n" + + " interface Target { String m(#{SAM_FORMALS}); }\n" + + " public String convert() { #{CAPTYPE1} loc0 = #{CAPV.1};\n" + + " #{CAPTYPE2} loc1 = #{CAPV.2};\n" + + " Target lambda = (#{LAMBDA_FORMALS}) -> String.format(\"#{FORMAT_STRING}\" #{FORMAT_ARGS});\n" + + " return lambda.m(#{INVOKE_ARGS}); }\n" + + "}"; + + @TemplateVar("LAMBDA_FORMALS") Template formalArgs = new Template() { + public String expand(String selector) { + List<String> list = new ArrayList<>(); + for (int i=0; i<arity; i++) + list.add(argTypes[i].typeName + " a" + i); + return intersperse(list, ", "); + } + }; + + @TemplateVar("SAM_FORMALS") Template samArgs = new Template() { + public String expand(String selector) { + List<String> list = new ArrayList<>(); + for (int i=0; i<arity; i++) + list.add(argTypes[i].typeName + " a" + i); + return intersperse(list, ", "); + } + }; + + @TemplateVar("FORMAT_STRING") Template formatString = new Template() { + public String expand(String selector) { + List<String> list = new ArrayList<>(); + for (int i=0; i<capArity; i++) + list.add(capTypes[i].argType.format); + for (int i=0; i<arity; i++) + list.add(argTypes[i].format); + return intersperse(list, " "); + } + }; + + @TemplateVar("FORMAT_ARGS") Template formatArgs = new Template() { + public String expand(String selector) { + List<String> list = new ArrayList<>(); + for (int i=0; i<capArity; i++) + list.add((capTypes[i].lf == LF.LOCAL ? "loc" : "f") + i); + for (int i=0; i<arity; i++) + list.add("a" + i); + return ((list.size()) > 0 ? ", " : "") + intersperse(list, ", "); + } + }; + + @TemplateVar("INVOKE_ARGS") Template invokeArgs = new Template() { + public String expand(String selector) { + List<String> list = new ArrayList<>(); + for (int i=0; i<arity; i++) + list.add(argTypes[i].formatValue(i+capArity)); + return intersperse(list, ", "); + } + }; + + @TemplateVar("CAPV") Template capv = new Template() { + public String expand(String selector) { + int i = Integer.parseInt(selector) - 1; + return capTypes[i].argType.formatValue(i); + } + }; + + private String intersperse(List<String> list, String delim) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (String s : list) { + if (!first) + sb.append(delim); + first = false; + sb.append(s); + } + return sb.toString(); + } + + @Override + protected boolean shouldSkip() { + return shouldSkip; + } + + @Override + protected boolean shouldRun() { + return true; + } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Object obj = clazz.newInstance(); + Method m = clazz.getMethod("convert"); + Object o = m.invoke(obj); + + List<String> list = new ArrayList<>(); + for (int i=0; i<capArity; i++) + list.add(capTypes[i].argType.formatResult(i)); + for (int i=0; i<arity; i++) + list.add(argTypes[i].formatResult(i+capArity)); + String result = intersperse(list, " "); + assertEquals(result, o); + } + + static enum ArgType implements Template { + LONG("long", "Long", "J%d", null) { + String formatValue(int x) { return String.format("(long) %d", x); } + String formatResult(int x) { return "J" + Integer.toString(x); } + }, + BYTE("byte", "Byte", "B%d", LONG) { + String formatValue(int x) { return String.format("(byte) %d", x); } + String formatResult(int x) { return "B" + Integer.toString(x); } + }, + SHORT("short", "Short", "S%d", LONG) { + String formatValue(int x) { return String.format("(short) %d", x); } + String formatResult(int x) { return "S" + Integer.toString(x); } + }, + CHAR("char", "Character", "C%c", LONG) { + String formatValue(int x) { return String.format("(char) (((int) 'A') + %d)", x); } + String formatResult(int x) { return "C" + (char) (((int) 'A') + x); } + }, + INT("int", "Integer", "I%d", LONG) { + String formatValue(int x) { return String.format("%d", x); } + String formatResult(int x) { return "I" + Integer.toString(x); } + }, + BOOLEAN("boolean", "Boolean", "Z%b", null) { + String formatValue(int x) { return String.format("%d %% 2 != 0", x); } + String formatResult(int x) { return "Z" + (x % 2 != 0); } + }, + DOUBLE("double", "Double", "D%f", null) { + String formatValue(int x) { return String.format("(double) %d", x); } + String formatResult(int x) { return "D" + String.format("%f", (double) x); } + }, + FLOAT("float", "Float", "F%f", DOUBLE) { + String formatValue(int x) { return String.format("(float) %d", x); } + String formatResult(int x) { return "F" + String.format("%f", (float) x); } + }, + OBJECT("Object", "Object", "O%s", null) { + String formatValue(int x) { return String.format("Integer.toString(%d)", x); } + String formatResult(int x) { return "O" + Integer.toString(x); } + }, + STRING("String", "String", "S%s", null) { + String formatValue(int x) { return String.format("Integer.toString(%d)", x); } + String formatResult(int x) { return "S" + Integer.toString(x); } + }; + + final String typeName, boxed, format; + final ArgType widened; + + ArgType(String typeName, String boxed, String format, ArgType widened) { + this.typeName = typeName; + this.boxed = boxed; + this.format = format; + this.widened = widened; + } + + abstract String formatValue(int x); + abstract String formatResult(int x); + + public String expand(String selector) { + return typeName; + } + } + + private static enum LF { LOCAL, FIELD } + + static enum CapType implements Template { + LOCAL_INT(ArgType.INT, LF.LOCAL), + LOCAL_LONG(ArgType.LONG, LF.LOCAL), + LOCAL_STRING(ArgType.STRING, LF.LOCAL), + FIELD_INT(ArgType.INT, LF.FIELD), + FIELD_LONG(ArgType.LONG, LF.FIELD), + FIELD_STRING(ArgType.STRING, LF.FIELD); + + final ArgType argType; + final LF lf; + + CapType(ArgType argType, LF lf) { + this.argType = argType; + this.lf = lf; + } + + public String expand(String selector) { + return argType.typeName; + } + } +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/LambdaConversionTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2012, 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 tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.*; + +/** + * LambdaConversionTestCase + * + * @author Maurizio Cimadamore + */ +public class LambdaConversionTest extends ComboTestBase<LambdaConversionTest> { + + @Factory + public static Object[] testCombo() throws Exception { + return factory(LambdaConversionTest.class); + } + + @DimensionVar("PACKAGE") PackageKind samPkg; + @DimensionVar("MODIFIER") ModifierKind modKind; + @DimensionVar("CLASS") SamKind samKind; + @DimensionVar("METH") MethodKind meth; + @DimensionVar("RET") TypeKind retType; + @DimensionVar("ARG") TypeKind argType; + @DimensionVar("THROWN") TypeKind thrownType; + + @SourceFile("Sam.java") + String samSource = "#{PACKAGE.PACKAGE_DECL} \n #{CLASS}"; + + @SourceFile("PackageClass.java") + String packageSource = "#{PACKAGE.PACKAGE_DECL}\n #{MODIFIER} class PackageClass extends Exception { }"; + + @SourceFile("Client.java") + String clientSource = "#{PACKAGE.IMPORT_STATEMENT}\n class Client { Sam s = x -> null; }"; + + @Override + protected void postCompile(String group) { + if (samKind != SamKind.INTERFACE) { + assertCompileFailed("SAM type must be an interface"); + } else if (meth != MethodKind.NON_GENERIC) { + assertCompileFailed("target method must be non-generic"); + } else if (samPkg != PackageKind.NO_PKG && + modKind != ModifierKind.PUBLIC && + (retType == TypeKind.PKG_CLASS + || argType == TypeKind.PKG_CLASS + || thrownType == TypeKind.PKG_CLASS)) { + assertCompileFailed("target must not contain inaccessible types"); + } else { + assertCompileSucceeded(); + } + } + + static enum PackageKind implements Template { + NO_PKG(""), + PKG_A("a"); + + String pkg; + + PackageKind(String pkg) { + this.pkg = pkg; + } + + public String expand(String selector) { + if (this == NO_PKG) + return ""; + switch (selector) { + case "PACKAGE_DECL": return String.format("package %s;", pkg); + case "IMPORT_STATEMENT": return String.format("import %s.*;", pkg); + default: throw new IllegalArgumentException(selector); + } + } + } + + static enum SamKind implements Template { + CLASS("public class Sam { }"), + ABSTRACT_CLASS("public abstract class Sam { }"), + ANNOTATION("public @interface Sam { }"), + ENUM("public enum Sam { }"), + INTERFACE("public interface Sam { \n #{METH}; \n }"); + + String template; + + SamKind(String template) { this.template = template; } + + public String expand(String selector) { return template; } + } + + static enum ModifierKind implements Template { + PUBLIC("public"), + PACKAGE(""); + + String template; + + ModifierKind(String template) { this.template = template; } + + public String expand(String selector) { return template; } + } + + static enum TypeKind implements Template { + EXCEPTION("Exception"), + PKG_CLASS("PackageClass"); + + String template; + + private TypeKind(String template) { this.template = template; } + + public String expand(String selector) { return template; } + } + + static enum MethodKind implements Template { + NONE(""), + NON_GENERIC("public #{RET} m(#{ARG} s) throws #{THROWN};"), + GENERIC("public <X> #{RET} m(#{ARG} s) throws #{THROWN};"); + + String template; + + private MethodKind(String template) { this.template = template; } + + public String expand(String selector) { return template; } + } + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/LambdaExpressionTypeInferenceTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2012, 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 tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.ComboTestBase; +import tools.javac.combo.DimensionVar; +import tools.javac.combo.SourceFile; +import tools.javac.combo.Template; + +import static tools.javac.combo.StackProcessingUtils.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * LambdaExpressionTypeInferenceTest + */ +public class LambdaExpressionTypeInferenceTest extends ComboTestBase<LambdaExpressionTypeInferenceTest> { + @Factory + public static Object[] testLambdaExpressionInference() throws Exception { + return factory(LambdaExpressionTypeInferenceTest.class); + } + + @DimensionVar("FORMAL_SHAPE") FormalTypeShape formalShape; + @DimensionVar("TREE_SHAPE") TreeShape treeShape; + @DimensionVar("ARG_SHAPE_1") ArgTypeShape argShape1; + @DimensionVar("ARG_SHAPE_2") ArgTypeShape argShape2; + @DimensionVar("ARG_SHAPE_3") ArgTypeShape argShape3; + + @SourceFile("SAM.java") + String samFile = "public interface SAM<X,Y> {\n" + +" Y apply(X x);\n" + +"}"; + + + @SourceFile("Main.java") + String clientFile = "import java.util.*;\n" + +"public class Main {\n" + +" <A,R> void m(#{FORMAL_SHAPE} x) { }\n" + +" void test() {\n" + +" m(#{TREE_SHAPE});\n" + +" }\n" + +"}"; + + @Override + protected void postCompile(String grp) { + if (treeShape.compute().typeCheck(formalShape.compute(), new Type[] { argShape1.compute(), argShape2.compute(), argShape3.compute() }).isErroneous()) { + assertCompileFailed(); + } else { + assertCompileSucceeded(); + } + } + + @Override + protected boolean shouldSkip() { + return argShape2.ordinal() != 0 && treeShape.depth < 2 || argShape3.ordinal() != 0 && treeShape.depth < 3; + } + + static class Tree { + TreeTag treeTag; + Tree[] subtrees; + + Tree(TreeTag treeTag, Tree... subtrees) { + this.treeTag = treeTag; + this.subtrees = subtrees; + } + + public String toString() { + Object[] substrings = new Object[subtrees.length]; + for (int i = 0; i < subtrees.length; i++) { + substrings[i] = subtrees[i].toString(); + } + return String.format(treeTag.treeStr, substrings); + } + + Type typeCheck(Type target, Type[] argtypes) { + switch (treeTag) { + case INT: + Type type = new Type(TypeToken.INTEGER); + return type.compatibleWith(target) ? type : errType; + case STRING: + type = new Type(TypeToken.STRING); + return type.compatibleWith(target) ? type : errType; + case EXPR_LAMBDA: + Type restype = target.getReturnType(); + if (restype == null) { + return errType; + } + Type argtype = target.getArgType(); + int idx = subtrees[0].treeTag.argIndex(); + if (idx >= 0 && !argtypes[idx].compatibleWith(argtype)) { + //arg type mismatch + return errType; + } + + for (int i = 1; i < subtrees.length; i++) { + if (subtrees[i].typeCheck(restype, argtypes).isErroneous()) { + return errType; + } + } + return target; + case COND: + Type typeToCheck = target; + if (subtrees[0].treeTag == TreeTag.INT && subtrees[1].treeTag == TreeTag.INT) { + Type owntype = new Type(TypeToken.INTEGER); + return owntype.compatibleWith(target) ? owntype : errType; + } else { + for (int i = 0; i < subtrees.length; i++) { + if (subtrees[i].typeCheck(target, argtypes).isErroneous()) { + return errType; + } + } + return target; + } + default: + throw new AssertionError(); + } + } + } + + enum TreeTag implements StackItem<TreeTag> { + INT("1", 0), + STRING("\"\"", 0), + COND("true ? %s : %s", 2), + EXPR_LAMBDA("(%s)->%s", 2), + IMPLICIT_1("x1", 0), + IMPLICIT_2("x2", 0), + IMPLICIT_3("x3", 0), + EXPLICIT_1("#{ARG_SHAPE_1} x1", 0), + EXPLICIT_2("#{ARG_SHAPE_2} x2", 0), + EXPLICIT_3("#{ARG_SHAPE_3} x3", 0); + + String treeStr; + int arity; + + TreeTag(String treeStr, int arity) { + this.treeStr = treeStr; + this.arity = arity; + } + + public int arity() { + return arity; + } + + Tree apply(Tree... args) { + return new Tree(this, args); + } + + int argIndex() { + switch (this) { + case EXPLICIT_1: + return 0; + case EXPLICIT_2: + return 1; + case EXPLICIT_3: + return 2; + default: + return -1; + } + } + } + + enum TreeShape implements Template, StackReducer<TreeTag, Tree, Void> { + LAMBDA_INT(1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.INT), + LAMBDA_STRING(1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.STRING), + LAMBDA_COND_INT_INT(1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.INT, TreeTag.INT), + IMPL_LAMBDA_INT(1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_1, TreeTag.INT), + IMPL_LAMBDA_STRING(1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_1, TreeTag.STRING), + IMPL_LAMBDA_COND_INT_INT(1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_1, TreeTag.COND, TreeTag.INT, TreeTag.INT), + LAMBDA_LAMBDA_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.INT), + LAMBDA_LAMBDA_STRING(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.STRING), + LAMBDA_LAMBDA_COND_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.COND, TreeTag.INT, TreeTag.INT), + LAMBDA_IMPL_LAMBDA_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.INT), + LAMBDA_IMPL_LAMBDA_STRING(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.STRING), + LAMBDA_IMPL_LAMBDA_COND_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.COND, TreeTag.INT, TreeTag.INT), + LAMBDA_COND_LAMBDA_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.INT, TreeTag.INT), + LAMBDA_COND_LAMBDA_STRING_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.STRING, TreeTag.INT), + LAMBDA_COND_LAMBDA_COND_INT_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.COND, TreeTag.INT, TreeTag.INT, TreeTag.INT), + LAMBDA_COND_IMPL_LAMBDA_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.INT, TreeTag.INT), + LAMBDA_COND_IMPL_LAMBDA_STRING_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.STRING, TreeTag.INT), + LAMBDA_COND_IMPL_LAMBDA_COND_INT_INT_INT(2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.COND, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_2, TreeTag.COND, TreeTag.INT, TreeTag.INT, TreeTag.INT), + LAMBDA_LAMBDA_LAMBDA_INT(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_3, TreeTag.INT), + LAMBDA_LAMBDA_LAMBDA_STRING(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_3, TreeTag.STRING), + LAMBDA_LAMBDA_LAMBDA_COND_INT_INT(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_3, TreeTag.COND, TreeTag.INT, TreeTag.INT), + LAMBDA_LAMBDA_IMPL_LAMBDA_INT(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_3, TreeTag.INT), + LAMBDA_LAMBDA_IMPL_LAMBDA_STRING(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_3, TreeTag.STRING), + LAMBDA_LAMBDA_IMPL_LAMBDA_COND_INT_INT(3, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_1, TreeTag.EXPR_LAMBDA, TreeTag.EXPLICIT_2, TreeTag.EXPR_LAMBDA, TreeTag.IMPLICIT_3, TreeTag.COND, TreeTag.INT, TreeTag.INT); + + TreeTag[] treeTags; + int depth; + + TreeShape(int depth, TreeTag... treeTags) { + this.depth = depth; + this.treeTags = treeTags; + } + + Tree compute() { + return process(treeTags, this, null); + } + + public String expand(String selector) { + return compute().toString(); + } + + public Tree reduce(TreeTag tag, Tree[] args, Void _unused) { + return new Tree(tag, args); + } + + public Void reducerArg(TreeTag item, int i) { return null; } + + public Class<Tree> resultToken() { + return Tree.class; + } + } + + static class Type { + TypeToken typeToken; + Type[] typeargs; + + Type(TypeToken typeToken, Type... typeargs) { + this.typeToken = typeToken; + this.typeargs = typeargs; + } + + public String toString() { + Object[] typeargStrings = new Object[typeargs.length]; + for (int i = 0; i < typeargs.length; i++) { + typeargStrings[i] = typeargs[i].toString(); + } + return String.format(typeToken.tokenStr, typeargStrings); + } + + boolean isErroneous() { return false; } + + Type getReturnType() { + return typeToken == TypeToken.SAM ? + typeargs[1] : null; + } + + Type getArgType() { + return typeToken == TypeToken.SAM ? + typeargs[0] : null; + } + + boolean isFree() { + switch (typeToken) { + case A: + case R: + return true; + default: + for (Type t : typeargs) { + if (t.isFree()) return true; + } + return false; + } + } + + boolean isFreeVar() { + switch (typeToken) { + case A: + case R: + return true; + default: + return false; + } + } + + boolean compatibleWith(Type that) { + if (that.isFreeVar()) { + return true; + } else { + if (typeToken != that.typeToken) + return false; + if (typeargs.length != that.typeargs.length) { + return false; + } + for (int i = 0; i < typeargs.length ; i++) { + if (!typeargs[i].compatibleWith(that.typeargs[i])) + return false; + } + return true; + } + } + } + + static Type errType = new Type(null) { + boolean isErroneous() { + return true; + } + }; + + enum TypeToken implements StackItem<TypeToken> { + STRING("String", 0), + INTEGER("Integer", 0), + A("A", 0), + R("R", 0), + LIST("List<%s>", 1), + SAM("SAM<%s,%s>", 2); + + int arity; + String tokenStr; + + TypeToken(String tokenStr, int arity) { + this.tokenStr = tokenStr; + this.arity = arity; + } + + public int arity() { + return arity; + } + } + + enum ArgTypeShape implements Template, StackReducer<TypeToken, Type, Void> { + STRING(TypeToken.STRING), + INTEGER(TypeToken.INTEGER), + LIST_STRING(TypeToken.LIST, TypeToken.STRING), + LIST_INTEGER(TypeToken.LIST, TypeToken.INTEGER); + + TypeToken[] tokens; + + ArgTypeShape(TypeToken... tokens) { + this.tokens = tokens; + } + + Type compute() { + return process(tokens, this, null); + } + + public String expand(String selector) { + return compute().toString(); + } + + public Type reduce(TypeToken token, Type[] args, Void _unused) { + return new Type(token, args); + } + + public Void reducerArg(TypeToken item, int i) { return null; } + + public Class<Type> resultToken() { + return Type.class; + } + } + + enum FormalTypeShape implements Template, StackReducer<TypeToken, Type, Void> { + SAM_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.STRING), + SAM_Integer_String(TypeToken.SAM, TypeToken.INTEGER, TypeToken.STRING), + SAM_List_String_String(TypeToken.SAM, TypeToken.LIST, TypeToken.STRING, TypeToken.STRING), + SAM_List_Integer_String(TypeToken.SAM, TypeToken.LIST, TypeToken.INTEGER, TypeToken.STRING), + SAM_A_String(TypeToken.SAM, TypeToken.A, TypeToken.STRING), + SAM_List_A_String(TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.STRING), + SAM_String_R(TypeToken.SAM, TypeToken.STRING, TypeToken.R), + SAM_A_R(TypeToken.SAM, TypeToken.A, TypeToken.R), + SAM_List_A_R(TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.R), + SAM_String_SAM_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_Integer_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.INTEGER, TypeToken.STRING), + SAM_String_SAM_List_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_List_Integer_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.INTEGER, TypeToken.STRING), + SAM_String_SAM_A_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.STRING), + SAM_String_SAM_List_A_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.STRING), + SAM_String_SAM_String_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.R), + SAM_String_SAM_A_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.R), + SAM_String_SAM_List_A_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.R), + SAM_A_SAM_String_String(TypeToken.SAM, TypeToken.A, TypeToken.SAM, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_String_SAM_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_String_SAM_Integer_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.INTEGER, TypeToken.STRING), + SAM_String_SAM_String_SAM_List_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_String_SAM_List_Integer_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.INTEGER, TypeToken.STRING), + SAM_String_SAM_String_SAM_A_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.STRING), + SAM_String_SAM_String_SAM_List_A_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.STRING), + SAM_String_SAM_String_SAM_String_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.R), + SAM_String_SAM_String_SAM_A_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.R), + SAM_String_SAM_String_SAM_List_A_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.LIST, TypeToken.A, TypeToken.R), + SAM_String_SAM_A_SAM_String_String(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.SAM, TypeToken.STRING, TypeToken.STRING), + SAM_String_SAM_A_SAM_String_R(TypeToken.SAM, TypeToken.STRING, TypeToken.SAM, TypeToken.A, TypeToken.SAM, TypeToken.STRING, TypeToken.R); + + TypeToken[] tokens; + + FormalTypeShape(TypeToken... tokens) { + this.tokens = tokens; + } + + Type compute() { + return process(tokens, this, null); + } + + public String expand(String selector) { + return compute().toString(); + } + + public Type reduce(TypeToken token, Type[] args, Void _unused) { + return new Type(token, args); + } + + public Void reducerArg(TypeToken item, int i) { return null; } + + public Class<Type> resultToken() { + return Type.class; + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/LambdaReturnAdaptationTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2012, 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 tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.*; + +import java.lang.reflect.Method; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * LambaReturnAdaptationTest + */ +public class LambdaReturnAdaptationTest extends ComboTestBase<LambdaReturnAdaptationTest> { + @Factory + public static Object[] testReturnAdaptation() throws Exception { + return factory(LambdaReturnAdaptationTest.class); + } + + @DimensionVar("FROM") Types fromType; + @DimensionVar("TO") Types toType; + + @SourceFile("Sam.java") + String samFile = "public interface Sam { #{TO} m(int arg); }"; + + @SourceFile("Main.java") + String clientFile = "public class Main {\n" + +" static #{FROM} m(int arg) { \n" + +" #{RET};\n" + +" }\n" + +" public #{TO} main() { \n" + + " Sam s = Main::m;\n" + + " #{RET_MAIN};\n" + + " }\n" + +"}"; + + @TemplateVar("RET") + Template retTemplate = new Template() { + public String expand(String selector) { + if (fromType == Types.VOID) + return ""; + else if (fromType == Types.STRING) + return "return \"66\""; + else if (fromType == Types.BOOLEAN || fromType == Types.BOXED_BOOLEAN) + return "return true"; + else if (fromType.unboxed != null) + return "return (" + fromType.unboxed.name + ") arg"; + else + return "return (" + fromType.name + ") arg"; + } + }; + + @TemplateVar("RET_MAIN") + Template retMainTemplate = new Template() { + public String expand(String selector) { + return toType == Types.VOID ? "s.m(66)" : "return s.m(66)"; + } + }; + + @Override + protected String[] getCompileOptions(String group) { + return new String[] { "-XDlambdaToMethod" }; + } + + @Override + protected void postCompile(String group) { + if (fromType != Types.VOID && toType == Types.VOID) + assertCompileSucceeded(); + else if (fromType == Types.VOID && toType != Types.VOID) + assertCompileFailed(); + else if (fromType == Types.VOID && toType == Types.VOID) + assertCompileSucceeded(); + else if (fromType.pr == PR.REF && toType.pr == PR.REF) + assertCompileSucceededIff(toType.clazz.isAssignableFrom(fromType.clazz)); + else if (fromType.pr == PR.PRIM && toType.pr == PR.REF) + assertCompileSucceededIff(toType.clazz.isAssignableFrom(Types.boxMap.get(fromType).clazz)); + else if (fromType.pr == PR.REF && toType.pr == PR.PRIM) + assertCompileSucceededIff(fromType.unboxed != null && Types.widenMap.get(fromType.unboxed).contains(toType)); + else if (fromType.pr == PR.PRIM && toType.pr == PR.PRIM) + assertCompileSucceededIff(Types.widenMap.get(fromType).contains(toType)); + else + assertCompileSucceeded(); + } + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + Object in = 66; + Object out = m.invoke(obj); + if (toType == Types.VOID) + assertTrue(out == null); + else if (fromType == Types.BOOLEAN || fromType == Types.BOXED_BOOLEAN) + assertEquals("true", out.toString()); + else + assertEquals(in.toString(), out.toString().replace("B", "66").replace(".0", "")); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/MethodRefCaptureTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2012, 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 tools.javac.lambda; + +import org.testng.annotations.Factory; +import tools.javac.combo.*; + +/** + * MethodRefCaptureTest + * + * @author Brian Goetz + */ +public class MethodRefCaptureTest extends ComboTestBase<MethodRefCaptureTest> { + @Factory + public static Object[] testCombo() throws Exception { + return factory(MethodRefCaptureTest.class); + } + + @DimensionVar("VAR") ContextKind context; + @DimensionVar("LOCATION") LocationKind location; + @DimensionVar("ACC") AccessibilityKind accessibility; + @DimensionVar("METHOD") RefKind ref; + + @TemplateVar("PACKAGE") + String makePackage() { + return location == LocationKind.OTHER_PACKAGE ? "package a;" : ""; + } + + // @@@ not yet used + @TemplateVar("MAIN") String main = "public static void main(String[] args) { #{METHOD.SAMTYPE} x = #{VAR.REF}"; + + @SourceFile("Sam1.java") String sam1 = "#{PACKAGE} \npublic interface Sam1 { public String get(String s); }"; + @SourceFile("Sam2.java") String sam2 = "#{PACKAGE} \npublic interface Sam2<T> { public String get(T target, String s); }"; + @SourceFile("Sam3.java") String sam3 = "#{PACKAGE} \npublic interface Sam3<T> { public T get(String s); }"; + @SourceFile("A.java") String mainFile = "#{LOCATION.CAPTURING_CLASS}"; + @SourceFile("Target.java") String extFile = "#{LOCATION.EXT_CLASS}"; + + @Override + protected String[] getCompileOptions(String group) { + return new String[] { "-XDlambdaToMethod" }; + } + + @Override + protected void postCompile(String group) { + if (location == LocationKind.OTHER_FILE && accessibility == AccessibilityKind.PRIVATE) + assertCompileFailed(); + else if (location == LocationKind.OTHER_PACKAGE && accessibility != AccessibilityKind.PUBLIC) + assertCompileFailed(); + else + assertCompileSucceeded(); + } + + + static enum ContextKind implements Template { + STATIC_FIELD_INIT("static #{METHOD.SAMTYPE} var = #{METHOD.MREF};"), + STATIC_FIELD_CLINIT("static #{METHOD.SAMTYPE} var; static { var = #{METHOD.MREF}; };"), + INSTANCE_FIELD_INIT("#{METHOD.SAMTYPE} var = #{METHOD.MREF};"), + LOCAL_VAR("void m() { #{METHOD.SAMTYPE} var = #{METHOD.MREF}; };"), + METHOD_CALL("static void m(#{METHOD.SAMTYPE} s) { }; static void m() { m(#{METHOD.MREF}); };"), + RETURN("#{METHOD.SAMTYPE} m() { return #{METHOD.MREF}; };"), + ARRAY_INITIALIZER("#{METHOD.SAMTYPE}[] var = (#{METHOD.SAMTYPE}[]) new #{METHOD.ERASED_SAMTYPE}[] { (#{METHOD.SAMTYPE}) #{METHOD.MREF} };"), + CONDITIONAL("#{METHOD.SAMTYPE} var = new Object().equals(new Object()) ? #{METHOD.MREF} : #{METHOD.MREF};"), + CAST("#{METHOD.SAMTYPE} var = (#{METHOD.SAMTYPE}) #{METHOD.MREF};"); + + private final String t1; + + ContextKind(String t1) { this.t1 = t1; } + + public String expand(String selector) { + switch (selector) { + case "DECL": return t1; + default: throw new IllegalArgumentException(selector); + } + } + } + + static enum LocationKind implements Template { + SAME_CLASS("class Target { \n#{VAR.DECL} \n#{METHOD.DECL} }"), + ENCLOSING_CLASS("class Target { \n#{METHOD.DECL} \nstatic class Capturing { #{VAR.DECL} } }"), + SIBLING_INNER_CLASS("class Outer { \nstatic class Capturing { #{VAR.DECL} } \nstatic class Target { #{METHOD.DECL} } }"), + NESTED_CLASS("class Capturing { \n#{VAR.DECL} \nstatic class Target { #{METHOD.DECL} } }"), + OTHER_FILE("class Capturing { \n#{VAR.DECL} }", "class Target { \n#{METHOD.DECL} }"), + OTHER_PACKAGE("package a; \nimport b.*; \nclass Capturing { \n#{VAR.DECL} }", + "package b; \npublic class Target { \n#{METHOD.DECL} }"); + + private final String t1, t2; + + LocationKind(String t1, String t2) { + this.t1 = t1; + this.t2 = t2; + } + LocationKind(String t1) { this(t1, "class Unused {}"); } + + public String expand(String selector) { + switch (selector) { + case "CAPTURING_CLASS": return t1; + case "EXT_CLASS": return t2; + default: throw new IllegalArgumentException(selector); + } + } + } + + static enum AccessibilityKind implements Template { + PUBLIC("public"), + PROTECTED("protected"), + PACKAGE(""), + PRIVATE("private"); + + private final String template; + + AccessibilityKind(String template) { this.template = template; } + + public String expand(String selector) { return template; } + } + + static enum RefKind implements Template { + STATIC(0, "Target::staticMethod", "static #{ACC} String staticMethod(String s) { return \"1\"; }"), + UNBOUND_INSTANCE(1, "Target::instanceMethod", "#{ACC} String instanceMethod(String s) { return \"2\"; }"), + BOUND_INSTANCE(0, "(new Target())::instanceMethod", "#{ACC} String instanceMethod(String s) { return \"3\"; }"), + CTOR(2, "Target::new", "#{ACC} Target(String s) { }"), +// UNBOUND_INNER_CTOR, +// BOUND_INNER_CTOR +// , SUPER + ; + + private static final String[] sams = { "Sam1", "Sam2<Target>", "Sam3<Target>" }; + private final int samNo; + private final String t1, t2; + + RefKind(int samNo, String t1, String t2) { this.samNo = samNo; this.t1 = t1; this.t2 = t2; } + + public String expand(String selector) { + switch (selector) { + case "MREF": return t1; + case "DECL": return t2; + case "SAMTYPE": return sams[samNo]; + case "ERASED_SAMTYPE": return sams[samNo].replaceAll("<Target>", "<?>"); + default: throw new IllegalArgumentException(selector); + } + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/NestedGenericMethodCallTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2012, 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 tools.javac.lambda; + +import org.testng.annotations.Factory; +import org.testng.annotations.Test; +import tools.javac.combo.ComboTestBase; +import tools.javac.combo.DimensionVar; +import tools.javac.combo.SourceFile; +import tools.javac.combo.Template; + +import static tools.javac.combo.StackProcessingUtils.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/** + * NestedGenericMethodCallTest + */ +public class NestedGenericMethodCallTest extends ComboTestBase<NestedGenericMethodCallTest> { + @Factory + public static Object[] testNestedGenericMethodCallType() throws Exception { + return factory(NestedGenericMethodCallTest.class); + } + + @Test(enabled=false) + public void test() throws Exception { + //disabled as the test has never been updated to support graph inference + super.test(); + } + + @DimensionVar("ID_METHOD") IdMethod idMethod; + @DimensionVar("NIL_METHOD") NilMethod nilMethod; + @DimensionVar("CONS_METHOD") ConsMethod consMethod; + @DimensionVar("SHAPE") Shape shape; + @DimensionVar("TARGET") Type target; + + @SourceFile("Main.java") + String clientFile = "import java.util.*;\n" + +"public class Main {\n" + +" #{ID_METHOD}\n" + +" #{NIL_METHOD}\n" + +" #{CONS_METHOD}\n" + +" void test() { \n" + + " #{TARGET} t = #{SHAPE};\n" + + " }\n" + +"}"; + + @Override + protected void postCompile(String grp) { + Type res = shape.compile(idMethod, nilMethod, consMethod, target); + if (res.asSubtypeOf(target).isError()) { + assertCompileFailed("Bad result = " + res); + } else { + assertCompileSucceeded(); + } + } + + @Override + protected boolean shouldSkip() { + return target.free || target == Type.ERROR; + } + + enum Type implements Template { + Z("Z", true), + OBJECT("Object", false), + STRING("String", false), + LIST_Z("List<Z>", true), + LIST_OBJECT("List<Object>", false), + LIST_STRING("List<String>", false), + ERROR("<any>", false); + + String typeStr; + boolean free; + + Type(String typeStr, boolean free) { + this.typeStr = typeStr; + this.free = free; + } + + boolean isError() { + return this == ERROR; + } + + Type asSubtypeOf(Type that) { + return asSubtypeMap[this.ordinal()][that.ordinal()]; + } + + boolean isSubtypeOf(Type that) { + return !asSubtypeOf(that).isError(); + } + + public String expand(String selector) { + return typeStr; + } + + enum ConstraintKind { + Z_EXT_OBJ(false, OBJECT), + Z_EXT_STRING(false, STRING), + Z_EXT_LIST_OBJECT(false, LIST_OBJECT), + Z_EXT_LIST_STRING(false, STRING), + Z_SUP_OBJECT(true, OBJECT), + Z_SUP_STRING(true, STRING), + Z_SUP_LIST_OBJECT(true, LIST_OBJECT), + Z_SUP_LIST_STRING(true, LIST_STRING), + ERROR(false, Type.ERROR); + + boolean isSuper; + Type type; + + ConstraintKind(boolean isSuper, Type type) { + this.isSuper = isSuper; + this.type = type; + } + } + + static Type[][] asSubtypeMap = new Type[][] { + // Z OBJECT STRING LIST_Z LIST_OBJECT LIST_STRING ERROR + { /* Z */ OBJECT ,OBJECT ,STRING ,ERROR ,LIST_OBJECT ,LIST_STRING ,ERROR }, + { /* OBJECT */ OBJECT ,OBJECT ,ERROR ,ERROR ,ERROR ,ERROR ,ERROR }, + { /* STRING */ STRING ,STRING ,STRING ,ERROR ,ERROR ,ERROR ,ERROR }, + { /* LIST_Z */ LIST_OBJECT,LIST_OBJECT,ERROR ,LIST_OBJECT,LIST_OBJECT ,LIST_STRING ,ERROR }, + { /* LIST_OBJECT */ LIST_OBJECT,LIST_OBJECT,ERROR ,LIST_OBJECT,LIST_OBJECT ,ERROR ,ERROR }, + { /* LIST_STRING */ LIST_STRING,LIST_STRING,ERROR ,LIST_STRING,ERROR ,LIST_STRING ,ERROR }, + { /* ERROR */ ERROR ,ERROR ,ERROR ,ERROR ,ERROR ,ERROR ,ERROR }}; + + static boolean isSubtypes(Type[] ts1, Type[] ts2) { + assertEquals(ts1.length, ts2.length); + for (int i = 0; i < ts1.length ; i++) { + if (!ts1[i].isSubtypeOf(ts2[i])) return false; + } + return true; + } + + static Type listOf(Type t) { + switch (t) { + case OBJECT: return LIST_OBJECT; + case STRING: return LIST_STRING; + case Z: return LIST_Z; + default: return ERROR; + } + } + + static Type elemtype(Type t) { + switch (t) { + case LIST_OBJECT: return OBJECT; + case LIST_STRING: return STRING; + case LIST_Z: return Z; + default: return ERROR; + } + } + + static Type lub(Type t1, Type t2) { + if (!t1.asSubtypeOf(t2).isError()) { + return t2; + } else if (!t2.asSubtypeOf(t1).isError()) { + return t1; + } else { + return Type.ERROR; + } + } + } + + interface MethodSig { + Type apply(Type target, Type... argTypes); + Type[] getFormals(); + } + + enum IdMethod implements Template, MethodSig { + NON_GENERIC_1("String id(String s) { return null; };", Type.STRING), + NON_GENERIC_2("List<String> id(List<String> s) { return null; };", Type.LIST_STRING), + GENERIC_1("<Z> Z id(Z z) { return null; };", Type.Z), + GENERIC_2("<Z> List<Z> id(List<Z> z) { return null; };", Type.LIST_Z); + + String sig; + Type formal; + + IdMethod(String sig, Type formal) { + this.sig = sig; + this.formal = formal; + } + + public String expand(String selector) { + return sig; + } + + public Type apply(Type target, Type... argTypes) { + return argTypes[0].asSubtypeOf(formal); + } + + public Type[] getFormals() { + return new Type[] { formal }; + } + } + + enum NilMethod implements Template, MethodSig { + NON_GENERIC("List<String> nil() { return null; }", Type.LIST_STRING), + GENERIC("<Z> List<Z> nil() { return null; }", Type.LIST_Z); + + String sig; + Type returnType; + + NilMethod(String sig, Type returnType) { + this.sig = sig; + this.returnType = returnType; + } + + public String expand(String selector) { + return sig; + } + + public Type apply(Type target, Type... argTypes) { + return this == NON_GENERIC ? + Type.LIST_STRING : + returnType.asSubtypeOf(target); + } + + public Type[] getFormals() { + return null; + } + } + + enum ConsMethod implements Template, MethodSig { + NON_GENERIC("List<String> cons(String s, List<String> ls) { return null; }", Type.STRING, Type.LIST_STRING) { + Type getReturnType(Type target, Type... argTypes) { return Type.LIST_STRING; } + }, + GENERIC_1("<Z> List<Z> cons(Z z, List<String> ls) { return null; }", Type.Z, Type.LIST_STRING) { + Type getReturnType(Type target, Type... argTypes) { + switch (target) { + case LIST_OBJECT: + case LIST_STRING: + return Type.LIST_Z.asSubtypeOf(target); + default: + return Type.listOf(argTypes[0].asSubtypeOf(Type.Z)); + } + } + }, + GENERIC_2("<Z> List<Z> cons(String s, List<Z> lz) { return null; }", Type.STRING, Type.LIST_Z) { + Type getReturnType(Type target, Type... argTypes) { return Type.listOf(Type.elemtype(argTypes[1].asSubtypeOf(Type.LIST_Z))); } + }, + GENERIC_3("<Z> List<Z> cons(Z z, List<Z> lz) { return null; }", Type.Z, Type.LIST_Z) { + Type getReturnType(Type target, Type... argTypes) { + Type lub = Type.lub(argTypes[0], Type.elemtype(argTypes[1].asSubtypeOf(Type.LIST_Z))); + return lub.isError() ? lub : Type.listOf(lub); + } + }, + GENERIC_4("<U, V> List<U> cons(U z, List<V> lz) { return null; }", Type.Z, Type.LIST_Z) { + Type getReturnType(Type target, Type... argTypes) { + switch (target) { + case LIST_OBJECT: + case LIST_STRING: + return Type.LIST_Z.asSubtypeOf(target); + default: + return Type.listOf(argTypes[0].asSubtypeOf(Type.Z)); + } + } + }; + + String sig; + Type[] formals; + + ConsMethod(String sig, Type... formals) { + this.sig = sig; + this.formals = formals; + } + + public String expand(String selector) { + return sig; + } + + public Type apply(Type target, Type... argTypes) { + return Type.isSubtypes(argTypes, formals) ? + getReturnType(target, argTypes) : Type.ERROR; + } + + abstract Type getReturnType(Type target, Type... argTypes); + + public Type[] getFormals() { + return formals; + } + } + + enum Token implements StackItem<Token> { + NIL(0, "nil"), + DIAMOND(0, "new ArrayList<>"), + STRING(0, "\"\""), + ID(1, "id"), + CONS(2, "cons"); + + int arity; + String tokenStr; + + Token(int arity, String tokenStr) { + this.arity = arity; + this.tokenStr = tokenStr; + } + + public int arity() { return arity; } + } + + enum Shape implements Template { + NIL(Token.NIL), + ID_NIL(Token.ID, Token.NIL), + ID_ID_NIL(Token.ID, Token.ID, Token.NIL), + CONS_STRING_NIL(Token.CONS, Token.STRING, Token.NIL), + CONS_STRING_ID_NIL(Token.CONS, Token.STRING, Token.ID, Token.NIL), + CONS_ID_STRING_NIL(Token.CONS, Token.ID, Token.STRING, Token.NIL), + CONS_ID_STRING_ID_NIL(Token.CONS, Token.ID, Token.STRING, Token.ID, Token.NIL), + CONS_ID_STRING_CONS_STRING_NIL(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.STRING, Token.NIL), + CONS_ID_STRING_CONS_ID_STRING_NIL(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.ID, Token.STRING, Token.NIL), + CONS_ID_STRING_CONS_STRING_ID_NIL(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.STRING, Token.ID, Token.NIL), + CONS_ID_STRING_CONS_ID_STRING_ID_NIL(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.NIL), + CONS_ID_STRING_ID_CONS_ID_STRING_ID_NIL(Token.CONS, Token.ID, Token.STRING, Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.NIL), + ID_CONS_ID_STRING_ID_CONS_ID_STRING_ID_NIL(Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.NIL), + DIAMOND(Token.DIAMOND), + ID_DIAMOND(Token.ID, Token.DIAMOND), + CONS_STRING_DIAMOND(Token.CONS, Token.STRING, Token.DIAMOND), + CONS_STRING_ID_DIAMOND(Token.CONS, Token.STRING, Token.ID, Token.DIAMOND), + CONS_ID_STRING_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.DIAMOND), + CONS_ID_STRING_ID_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.ID, Token.DIAMOND), + CONS_ID_STRING_CONS_STRING_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.STRING, Token.DIAMOND), + CONS_ID_STRING_CONS_ID_STRING_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.ID, Token.STRING, Token.DIAMOND), + CONS_ID_STRING_CONS_STRING_ID_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.STRING, Token.ID, Token.DIAMOND), + CONS_ID_STRING_CONS_ID_STRING_ID_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.DIAMOND), + CONS_ID_STRING_ID_CONS_ID_STRING_ID_DIAMOND(Token.CONS, Token.ID, Token.STRING, Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.DIAMOND), + ID_CONS_ID_STRING_ID_CONS_ID_STRING_ID_DIAMOND(Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.CONS, Token.ID, Token.STRING, Token.ID, Token.DIAMOND); + + Token[] tokens; + + Shape(Token... tokens) { + this.tokens = tokens; + } + + Type compile(final IdMethod idMethod, final NilMethod nilMethod, final ConsMethod consMethod, Type target) { + return process(tokens, new StackReducer<Token, Type, Type>() { + public Type reduce(Token t, Type[] operands, Type target) { + switch (t) { + case DIAMOND: return Type.LIST_Z.asSubtypeOf(target); + case NIL: return nilMethod.apply(target, operands); + case STRING: return Type.STRING; + case ID: return idMethod.apply(target, operands); + case CONS: return consMethod.apply(target, operands); + default: throw new AssertionError(); + } + } + public Class<Type> resultToken() { return Type.class; } + public Type reducerArg(Token item, int i) { + switch (item) { + case ID: + return idMethod.getFormals()[i]; + case CONS: + return consMethod.getFormals()[i]; + default: return Type.OBJECT; + } + } + }, target); + } + + public String expand(String selector) { + return process(tokens, new StackReducer<Token, String, Void>() { + public String reduce(Token t, String[] operands, Void _unused) { + switch (t) { + case DIAMOND: + case NIL: + case ID: + case CONS: + StringBuilder buf = new StringBuilder(); + String sep = ""; + for (int i = 0; i < t.arity; i ++) { + buf.append(sep); + buf.append(operands[i]); + sep = ","; + } + buf.insert(0, "("); + buf.insert(0, t.tokenStr); + buf.append(")"); + return buf.toString(); + case STRING: + return t.tokenStr; + default: throw new AssertionError(); + } + } + public Class<String> resultToken() { return String.class; } + public Void reducerArg(Token item, int i) { return null; } + }, null); + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/SourceTargetVersionTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2012, 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 tools.javac.lambda; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.testng.annotations.Factory; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import tools.javac.combo.*; + +/** + * SourceTargetVersionTest: + * Test source and class file compatibility by compiling and running lambda specific code + * against some class files compiled separately with different source and target option + * (1.5, 1.6, and 1.7). + * The goal is achieved by putting source files in different groups (A, B, C..,), each group + * is set with its own compile options, and we compile in the reverse alphebetic order for the + * groups (..., C, B, A) to set a previously compiled group of files as classpath for the next group. + * And when executing, we Run class Main with classpath A:B:C... in the order. + * + */ +public class SourceTargetVersionTest extends ComboTestBase<SourceTargetVersionTest> { + @Factory + public static Object[] testSourceTarget() throws Exception { + return factory(SourceTargetVersionTest.class); + } + + @DimensionVar("SHAPE") CShapes shapeType; + @DimensionVar("CLIENT_CODE") ClientType clientType; + @DimensionVar("RELEASE") ReleaseVersion rv; + + @SourceFile(value="A.java", group="B") + String interfaceA = "interface A { #{SHAPE.METHOD} }"; + + @SourceFile(value="B.java", group="A") + String interfaceB = "interface B #{SHAPE.EXTEND} { default String m() { return \"B\"; } }"; + + @SourceFile(value="C.java", group="A") + String classC = "class C #{SHAPE.IMPLEMENT} {\n" + + " #{CLIENT_CODE}\n" + + "}"; + + @SourceFile(value="Main.java", group="A") + String classMain = "public class Main {\n" + + " public String main() {\n" + + " return new C().m();\n" + + " }\n" + + "}"; + + @TemplateVar("LAMBDA_EXP") String le = "public String m() {\n" + + " A a = () -> \"A\";\n" + + " return a.m();\n" + + "}"; + @TemplateVar("METHOD_REF") String mr = "String foo() {\n" + + " return \"foo\";\n" + + "}\n\n" + + + "public String m() {\n" + + " A a = this::foo;\n" + + " return a.m();\n" + + "}"; + + @Override + protected boolean shouldSkip() { + return (shapeType == CShapes.C_AB && clientType != ClientType.DEF_MED) || + (shapeType == CShapes.C && clientType == ClientType.DEF_MED); + } + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + String result = (String) m.invoke(obj); + String expected = clientType.returnValue; + assertEquals(result, expected); + } + + @Override + protected String[] getCompileOptions(String group) { + switch(group) { + case "B": return new String[]{"-source", rv.versionStr, "-target", rv.versionStr}; + default: return new String[0]; + } + } + + enum CShapes implements Template { //shapes of class hirarchy + //class C implements interface A, B + C_AB("", "", "implements A, B"), + //class C implments interface B, B extends interface A + C_B_A("String m();", "extends A", "implements B"), + //class C + C("String m();", "", ""); + + private final String absMethod; + private final String extend; + private final String implement; + + CShapes(String absMethod, String extend, String implement) { + this.absMethod = absMethod; + this.extend = extend; + this.implement = implement; + } + + public String expand(String selector) { + switch(selector) { + case "METHOD": return absMethod; + case "EXTEND": return extend; + case "IMPLEMENT": return implement; + default: return toString(); + } + } + } + + enum ClientType implements Template { // jdk8 specific code in client program + LAMBDA_EXP("#{LAMBDA_EXP}", "A"), // lambda expressions + METHOD_REF("#{METHOD_REF}", "foo"), // method references + DEF_MED("", "B"); // default method inheritance + + private final String clientCode; + private final String returnValue; + + ClientType(String str, String ret) { + clientCode = str; + returnValue = ret; + } + + public String expand(String selector) { + return clientCode; + } + } + + enum ReleaseVersion implements Template { + FIVE("1.5"), + SIX("1.6"), + SEVEN("1.7"); + + final String versionStr; + + ReleaseVersion(String vstr) { + versionStr = vstr; + } + + public String expand(String selector) { + return toString(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combo-tests/tests/tools/javac/lambda/StaticMethodTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,155 @@ +/* + * 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 tools.javac.lambda; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +import org.testng.annotations.Factory; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import tools.javac.combo.*; + +/** + * StaticMethodTest: Test static methods in interfaces by running bytecode + * compiled at different times; where a static method is added/declared + * in one interface and only that interface is recompiled. + * + * Static methods in interface are not inherited and + * are not visible to any implementing (extending) classes (interfaces). + * @bug 8005968 + * @author sogoel + * + */ +public class StaticMethodTest extends ComboTestBase<StaticMethodTest> { + @Factory + public static Object[] testStaticMethod() throws Exception { + return factory(StaticMethodTest.class); + } + + @DimensionVar("SHAPE") CShapes shapeType; + + @DimensionVar("ADD") AddType addType; + + @SourceFile(value="B.java", group="A") + String interfaceBModified = "interface B #{SHAPE.B_DECL} { #{ADD} }"; + + @SourceFile(value="B.java", group="B") + String interfaceB = "interface B #{SHAPE.B_DECL} {}"; + + @SourceFile(value="A.java", group="B") + String interfaceA = "interface A { static String m() { return \"A\"; } }"; + + @SourceFile(value="C.java", group="B") + String classC = "#{SHAPE.C}"; + + @SourceFile(value="Main.java", group="B") + String classMain = "public class Main {\n" + + " public String main() {\n" + + " return new C().m();\n" + + " }\n" + + "}"; + + @Override + protected boolean shouldRun() { return true; } + + @Override + protected void run(Class<?> clazz) throws ReflectiveOperationException { + Method m = clazz.getMethod("main"); + Object obj = clazz.newInstance(); + String result = null, output = null; + try { + result = (String) m.invoke(obj); + } catch (InvocationTargetException ex) { + output = ex.getCause().toString(); + } + assertEquals(output, null); + assertEquals(result, "C"); + } + + /* + * In order for class Main to compile in group B, object of class C should have + * access to a method m(). Therefore, a method m() is needed in class C. + * static m() in interfaces A, B is not visible to C. + */ + static String methodSig = "public String m () { return \"C\"; }"; + enum CShapes implements Template { //shapes of class hierarchy + //class C implements interface A, B + C_AB("", + "class C implements A, B { " + methodSig + " }"), + //class C implements interface B, B extends interface A + C_B_A("extends A", + "class C implements B { " + methodSig + " }"), + //class C implements interface AB, AB extends interface A, B + C_I_AB("", + "interface AB extends A, B { }\n" + + "class C implements AB { " + methodSig + " }"), + //class C implements interface AB, AB extends interface A, B and defines its own static method + C_I_AB2("", + "interface AB extends A, B { static String m() { return \"AB\"; } }\n" + + "class C implements AB { " + methodSig + " }"), + //class C extends Class D implements Interface B + C_CI("", + "class D { public String m() { return \"D\"; } }\n" + + "class C extends D implements B { " + methodSig + " }"); + + private final String sB_DECL; + private final String sC; + + CShapes(String sB_DECL, String sC) { + this.sB_DECL = sB_DECL; + this.sC = sC; + } + + public String expand(String selector) { + switch(selector) { + case "B_DECL": return sB_DECL; + case "C": return sC; + default: return toString(); + } + } + } + + enum AddType implements Template { // add by adding static method or abstract interface method + ADD("static String m() { return \"B\"; }"), + REDECLARE("String m();"); + + final String newCode; + + AddType(String str) { + newCode = str; + } + + public String expand(String selector) { + return newCode; + } + } +} +
--- a/make/common/internal/Defs-langtools.gmk Tue Aug 13 10:42:37 2013 -0700 +++ b/make/common/internal/Defs-langtools.gmk Wed Aug 14 15:53:13 2013 -0700 @@ -28,7 +28,8 @@ IMPORT_RT_PACKAGES += \ javax/annotation/processing \ javax/lang/model \ - javax/tools + javax/tools \ + com/sun/runtime IMPORT_TOOLS_PACKAGES += \ com/sun/javadoc \
--- a/make/docs/NON_CORE_PKGS.gmk Tue Aug 13 10:42:37 2013 -0700 +++ b/make/docs/NON_CORE_PKGS.gmk Wed Aug 14 15:53:13 2013 -0700 @@ -84,6 +84,8 @@ SCTPAPI_PKGS = com.sun.nio.sctp +LAMBDA_RTAPI = com.sun.runtime + ifeq ($(PLATFORM), macosx) APPLE_EXT_PKGS = com.apple.concurrent \ com.apple.eawt \ @@ -101,5 +103,6 @@ $(HTTPSERVER_PKGS) \ $(SMARTCARDIO_PKGS) \ $(SCTPAPI_PKGS) \ + $(LAMBDA_RTAPI) \ $(APPLE_EXT_PKGS)
--- a/make/java/java/FILES_java.gmk Tue Aug 13 10:42:37 2013 -0700 +++ b/make/java/java/FILES_java.gmk Wed Aug 14 15:53:13 2013 -0700 @@ -300,12 +300,20 @@ java/util/EnumMap.java \ java/util/Arrays.java \ java/util/ArraysParallelSortHelpers.java \ + java/util/ArrayPrefixHelpers.java \ + java/util/ArraySortHelpers.java \ java/util/DualPivotQuicksort.java \ java/util/TimSort.java \ java/util/ComparableTimSort.java \ + java/util/Comparators.java \ java/util/ConcurrentModificationException.java \ + java/util/OptionalDouble.java \ + java/util/OptionalInt.java \ + java/util/Optional.java \ + java/util/OptionalLong.java \ java/util/ServiceLoader.java \ java/util/ServiceConfigurationError.java \ + java/util/StringJoiner.java \ java/util/Timer.java \ java/util/TimerTask.java \ java/util/Objects.java \ @@ -334,6 +342,8 @@ java/util/concurrent/CyclicBarrier.java \ java/util/concurrent/DelayQueue.java \ java/util/concurrent/Delayed.java \ + java/util/concurrent/DoubleAdder.java \ + java/util/concurrent/DoubleMaxUpdater.java \ java/util/concurrent/Exchanger.java \ java/util/concurrent/ExecutionException.java \ java/util/concurrent/Executor.java \ @@ -348,6 +358,10 @@ java/util/concurrent/LinkedBlockingDeque.java \ java/util/concurrent/LinkedBlockingQueue.java \ java/util/concurrent/LinkedTransferQueue.java \ + java/util/concurrent/LongAdder.java \ + java/util/concurrent/LongAdderTable.java \ + java/util/concurrent/LongMaxUpdater.java \ + java/util/concurrent/package-info.java \ java/util/concurrent/Phaser.java \ java/util/concurrent/PriorityBlockingQueue.java \ java/util/concurrent/RecursiveAction.java \ @@ -360,6 +374,8 @@ java/util/concurrent/ScheduledFuture.java \ java/util/concurrent/ScheduledThreadPoolExecutor.java \ java/util/concurrent/Semaphore.java \ + java/util/concurrent/SequenceLock.java \ + java/util/concurrent/Striped64.java \ java/util/concurrent/SynchronousQueue.java \ java/util/concurrent/ThreadFactory.java \ java/util/concurrent/ThreadLocalRandom.java \ @@ -379,18 +395,17 @@ java/util/concurrent/atomic/AtomicReferenceArray.java \ java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java \ java/util/concurrent/atomic/AtomicStampedReference.java \ - java/util/concurrent/atomic/DoubleAccumulator.java \ - java/util/concurrent/atomic/DoubleAdder.java \ - java/util/concurrent/atomic/LongAccumulator.java \ - java/util/concurrent/atomic/LongAdder.java \ - java/util/concurrent/atomic/Striped64.java \ + java/util/concurrent/atomic/package-info.java \ + java/util/concurrent/extra/AtomicDoubleArray.java \ + java/util/concurrent/extra/AtomicDouble.java \ + java/util/concurrent/extra/ReadMostlyVector.java \ java/util/concurrent/locks/AbstractOwnableSynchronizer.java \ java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java \ java/util/concurrent/locks/AbstractQueuedSynchronizer.java \ - java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java \ java/util/concurrent/locks/Condition.java \ java/util/concurrent/locks/Lock.java \ java/util/concurrent/locks/LockSupport.java \ + java/util/concurrent/locks/package-info.java \ java/util/concurrent/locks/ReadWriteLock.java \ java/util/concurrent/locks/ReentrantLock.java \ java/util/concurrent/locks/ReentrantReadWriteLock.java \
--- a/make/java/java/Makefile Tue Aug 13 10:42:37 2013 -0700 +++ b/make/java/java/Makefile Wed Aug 14 15:53:13 2013 -0700 @@ -37,7 +37,7 @@ JAVAC_MAX_WARNINGS=true include $(BUILDDIR)/common/Defs.gmk -AUTO_FILES_JAVA_DIRS = java/util/function +AUTO_FILES_JAVA_DIRS = java/util/concurrent java/util/function java/util/stream # windows compiler flags ifeq ($(PLATFORM),windows) @@ -51,7 +51,7 @@ OTHER_CFLAGS += -DJDK_MAJOR_VERSION='"$(JDK_MAJOR_VERSION)"' \ -DJDK_MINOR_VERSION='"$(JDK_MINOR_VERSION)"' \ -DJDK_MICRO_VERSION='"$(JDK_MICRO_VERSION)"' \ - -DJDK_BUILD_NUMBER='"$(JDK_BUILD_NUMBER)"' + -DJDK_BUILD_NUMBER='"$(JDK_BUILD_NUMBER)"' ifdef JDK_UPDATE_VERSION OTHER_CFLAGS += -DJDK_UPDATE_VERSION='"$(JDK_UPDATE_VERSION)"' @@ -279,7 +279,7 @@ $(GENSRCDIR)/java/lang/UNIXProcess.java: $(PLATFORM_UNIX_PROCESS) $(install-file) -clean:: +clean:: $(RM) $(GENSRCDIR)/java/lang/UNIXProcess.java endif @@ -293,10 +293,10 @@ # # Special rules. # -clean:: +clean:: $(RM) -r $(CLASSHDRDIR) -clobber:: +clobber:: $(RM) -r $(CLASSBINDIR)/java/io $(CLASSBINDIR)/java/lang \ $(CLASSBINDIR)/java/security $(CLASSBINDIR)/java/util \ $(CLASSBINDIR)/sun/misc @@ -313,12 +313,6 @@ CAL_PROPS = calendars.properties # -# Rule to copy Hijrah-umalqura calendar properties file. -# -HIJRAH_UMALQURA_PROPS = hijrah-config-umalqura.properties - - -# # Rule to copy tzmappings file on Windows # ifeq ($(PLATFORM), windows) @@ -330,7 +324,7 @@ $(call chmod-file, 444) endif -build: $(LIBDIR)/$(PROPS) $(LIBDIR)/$(CAL_PROPS) $(LIBDIR)/$(HIJRAH_UMALQURA_PROPS) $(TZMAP) +build: $(LIBDIR)/$(PROPS) $(LIBDIR)/$(CAL_PROPS) $(TZMAP) $(LIBDIR)/$(PROPS): $(PLATFORM_SRC)/lib/$(PROPS) $(install-file) @@ -338,10 +332,7 @@ $(LIBDIR)/$(CAL_PROPS): $(SHARE_SRC)/lib/$(CAL_PROPS) $(install-file) -$(LIBDIR)/$(HIJRAH_UMALQURA_PROPS): $(SHARE_SRC)/lib/$(HIJRAH_UMALQURA_PROPS) - $(install-file) - -clean:: +clean:: $(RM) -r $(LIBDIR)/$(PROPS) $(TZMAP) # @@ -362,12 +353,12 @@ $(MV) $@.temp $@ $(call chmod-file, 444) -clean:: +clean:: $(RM) $(CURDATA) # -# Rules to create $(GENSRCDIR)/sun/lang/CharacterData*.java +# Rules to create $(GENSRCDIR)/sun/lang/CharacterData*.java # CHARACTERDATA = $(BUILDDIR)/tools/GenerateCharacter UNICODEDATA = $(BUILDDIR)/tools/UnicodeData @@ -422,12 +413,12 @@ $(install-file) clean:: - $(RM) $(GENSRCDIR)/java/lang/CharacterDataLatin1.java + $(RM) $(GENSRCDIR)/java/lang/CharacterDataLatin1.java $(RM) $(GENSRCDIR)/java/lang/CharacterData00.java - $(RM) $(GENSRCDIR)/java/lang/CharacterData01.java + $(RM) $(GENSRCDIR)/java/lang/CharacterData01.java $(RM) $(GENSRCDIR)/java/lang/CharacterData02.java $(RM) $(GENSRCDIR)/java/lang/CharacterData0E.java - $(RM) $(GENSRCDIR)/java/lang/CharacterDataUndefined.java + $(RM) $(GENSRCDIR)/java/lang/CharacterDataUndefined.java $(RM) $(GENSRCDIR)/java/lang/CharacterDataPrivateUse.java # @@ -448,7 +439,7 @@ build.tools.generatecharacter.CharacterName \ $(UNICODEDATA)/UnicodeData.txt $(UNINAME) -clean:: +clean:: $(RM) $(UNINAME) # @@ -456,15 +447,15 @@ # # -# Rule to precompile CoreResourceBundleControl.java +# Rule to precompile CoreResourceBundleControl.java # LOCALES_GEN_SH = localelist.sh $(GENSRCDIR)/sun/util/CoreResourceBundleControl.java: \ $(SHARE_SRC)/classes/sun/util/CoreResourceBundleControl-XLocales.java.template $(LOCALES_GEN_SH) - @$(prep-target) + @$(prep-target) NAWK="$(NAWK)" SED="$(SED)" $(SH) $(LOCALES_GEN_SH) "$(JRE_NONEXIST_LOCALES)" \ - $< $@ + $< $@ clean:: $(RM) $(GENSRCDIR)/sun/util/CoreResourceBundleControl.java
--- a/make/netbeans/common/java-data-native.ent Tue Aug 13 10:42:37 2013 -0700 +++ b/make/netbeans/common/java-data-native.ent Wed Aug 14 15:53:13 2013 -0700 @@ -31,13 +31,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> -<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3"> +<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/4"> <compilation-unit> <package-root>${root}/src/share/classes</package-root> <package-root>${root}/src/macosx/classes</package-root> <package-root>${root}/src/solaris/classes</package-root> <package-root>${root}/src/windows/classes</package-root> - <classpath mode="boot">${bootstrap.jdk}/jre/lib/rt.jar</classpath> +<!-- <classpath mode="boot">${bootstrap.jdk}/jre/lib/rt.jar</classpath> --> <built-to>${root}/build/${platform}-${arch}/classes</built-to> <javadoc-built-to>${root}/build/${platform}-${arch}/docs/api</javadoc-built-to> <source-level>1.8</source-level>
--- a/make/netbeans/common/java-data-no-native.ent Tue Aug 13 10:42:37 2013 -0700 +++ b/make/netbeans/common/java-data-no-native.ent Wed Aug 14 15:53:13 2013 -0700 @@ -31,10 +31,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --> -<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3"> +<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/4"> <compilation-unit> <package-root>${root}/src/share/classes</package-root> - <classpath mode="boot">${bootstrap.jdk}/jre/lib/rt.jar</classpath> +<!-- <classpath mode="boot">${bootstrap.jdk}/jre/lib/rt.jar</classpath> --> <built-to>${root}/build/${platform}-${arch}/classes</built-to> <javadoc-built-to>${root}/build/${platform}-${arch}/docs/api</javadoc-built-to> <source-level>1.8</source-level>
--- a/make/netbeans/common/shared.xml Tue Aug 13 10:42:37 2013 -0700 +++ b/make/netbeans/common/shared.xml Wed Aug 14 15:53:13 2013 -0700 @@ -77,10 +77,10 @@ <echo level="verbose">System configuration claims architecture is ${platform}-${arch}</echo> <property name="build.dir" location="${root}/build/${platform}-${arch}"/> <property name="bin.dir" location="${build.dir}/bin"/> - <property name="make.dir" location="${root}/make"/> <!-- this is old build make files! --> + <property name="make.dir" location="${root}/makefiles"/> <property name="gensrc.dir" location="${build.dir}/gensrc"/> <property name="classes.dir" location="${build.dir}/classes"/> - <property name="jtreg.dir" location="${build.dir}/jtreg/${ant.project.name}"/> + <property name="jtreg.dir" location="${build.dir}/testoutput"/> <property name="dist.dir" value="${root}/dist"/> <property name="includes" value="(nothing)"/> <property name="excludes" value=""/>
--- a/make/org/Makefile Tue Aug 13 10:42:37 2013 -0700 +++ b/make/org/Makefile Wed Aug 14 15:53:13 2013 -0700 @@ -31,7 +31,7 @@ PRODUCT = org include $(BUILDDIR)/common/Defs.gmk -SUBDIRS = ietf jcp +SUBDIRS = asm ietf jcp include $(BUILDDIR)/common/Subdirs.gmk all build clean clobber::
--- a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java Wed Aug 14 15:53:13 2013 -0700 @@ -193,8 +193,8 @@ } else { return new ConstantCallSite( MethodHandles.Lookup.IMPL_LOOKUP - .findConstructor(innerClass, constructorType) - .asType(constructorType.changeReturnType(samBase))); + .findConstructor(innerClass, constructorType) + .asType(constructorType.changeReturnType(samBase))); } } @@ -320,8 +320,8 @@ TypeConvertingMethodAdapter mv = new TypeConvertingMethodAdapter( cw.visitMethod(ACC_PRIVATE + ACC_FINAL, - NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, - null, null)); + NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE, + null, null)); mv.visitCode(); mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
--- a/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Wed Aug 14 15:53:13 2013 -0700 @@ -295,6 +295,9 @@ String invokerDesc = invokerType.toMethodDescriptorString(); mv = cw.visitMethod(Opcodes.ACC_STATIC, invokerName, invokerDesc, null, null); + + // Force inlining of this invoker method. + mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); } /** @@ -521,9 +524,6 @@ // Mark this method as a compiled LambdaForm mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Compiled;", true); - // Force inlining of this invoker method. - mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); - // iterate over the form's names, generating bytecode instructions for each // start iterating at the first name following the arguments for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) { @@ -943,9 +943,6 @@ // Suppress this method in backtraces displayed to the user. mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); - // Don't inline the interpreter entry. - mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true); - // create parameter array emitIconstInsn(invokerType.parameterCount()); mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); @@ -1008,9 +1005,6 @@ // Suppress this method in backtraces displayed to the user. mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true); - // Force inlining of this invoker method. - mv.visitAnnotation("Ljava/lang/invoke/ForceInline;", true); - // Load receiver emitAloadInsn(0);
--- a/src/share/classes/java/lang/invoke/LambdaForm.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/lang/invoke/LambdaForm.java Wed Aug 14 15:53:13 2013 -0700 @@ -69,7 +69,7 @@ * A lambda has a void result if and only if its result index is -1. * If a temporary has the type "V", it cannot be the subject of a NameRef, * even though possesses a number. - * Note that all reference types are erased to "L", which stands for {@code Object). + * Note that all reference types are erased to "L", which stands for {@code Object}. * All subword types (boolean, byte, short, char) are erased to "I" which is {@code int}. * The other types stand for the usual primitive types. * <p> @@ -592,7 +592,6 @@ private int invocationCounter = 0; @Hidden - @DontInline /** Interpretively invoke this form on the given arguments. */ Object interpretWithArguments(Object... argumentValues) throws Throwable { if (TRACE_INTERPRETER) @@ -607,7 +606,6 @@ } @Hidden - @DontInline /** Evaluate a single Name within this form, applying its function to its arguments. */ Object interpretName(Name name, Object[] values) throws Throwable { if (TRACE_INTERPRETER)
--- a/src/share/classes/java/lang/invoke/MemberName.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/lang/invoke/MemberName.java Wed Aug 14 15:53:13 2013 -0700 @@ -236,8 +236,7 @@ assert(MethodHandleNatives.refKindIsMethod(refKind)); if (clazz.isInterface()) assert(refKind == REF_invokeInterface || - refKind == REF_invokeStatic || - refKind == REF_invokeSpecial || + refKind == REF_invokeStatic || refKind == REF_invokeVirtual && isObjectPublicMethod()); } else { assert(false); @@ -270,7 +269,7 @@ assert(refKind == REF_invokeSpecial) : this; return true; } - assert(false) : this+" != "+MethodHandleNatives.refKindName((byte)originalRefKind); + assert(false) : this; return true; } private boolean staticIsConsistent() { @@ -288,8 +287,12 @@ assert(vmindex >= 0) : vmindex + ":" + this; assert(vmtarget instanceof Class); } else { - if (MethodHandleNatives.refKindDoesDispatch(refKind)) - assert(vmindex >= 0) : vmindex + ":" + this; + if (MethodHandleNatives.refKindDoesDispatch(refKind)) { + // invokeStatic can reference either method table or interface table + if (refKind != REF_invokeStatic) { + assert(vmindex >= 0) : vmindex + ":" + this; + } + } else assert(vmindex < 0) : vmindex; assert(vmtarget instanceof MemberName) : vmtarget + " in " + this; @@ -487,19 +490,14 @@ if (this.type == null) this.type = new Object[] { m.getReturnType(), m.getParameterTypes() }; if (wantSpecial) { - assert(!isAbstract()) : this; if (getReferenceKind() == REF_invokeVirtual) changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual); - else if (getReferenceKind() == REF_invokeInterface) - // invokeSpecial on a default method - changeReferenceKind(REF_invokeSpecial, REF_invokeInterface); } } public MemberName asSpecial() { switch (getReferenceKind()) { case REF_invokeSpecial: return this; case REF_invokeVirtual: return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual); - case REF_invokeInterface: return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeInterface); case REF_newInvokeSpecial: return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial); } throw new IllegalArgumentException(this.toString());
--- a/src/share/classes/java/lang/invoke/MethodHandleImpl.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleImpl.java Wed Aug 14 15:53:13 2013 -0700 @@ -312,9 +312,9 @@ } static class AsVarargsCollector extends MethodHandle { - private final MethodHandle target; - private final Class<?> arrayType; - private MethodHandle cache; + MethodHandle target; + final Class<?> arrayType; + MethodHandle cache; AsVarargsCollector(MethodHandle target, MethodType type, Class<?> arrayType) { super(type, reinvokerForm(type));
--- a/src/share/classes/java/lang/invoke/MethodHandleInfo.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleInfo.java Wed Aug 14 15:53:13 2013 -0700 @@ -27,10 +27,10 @@ import java.lang.invoke.MethodHandleNatives.Constants; /** - * Cracking (reflecting) method handles back into their constituent symbolic parts. - * + * Cracking (reflecting) method handles back into their constituent symbolic parts. + * */ -final class MethodHandleInfo { +public final class MethodHandleInfo { public static final int REF_getField = Constants.REF_getField, REF_getStatic = Constants.REF_getStatic, @@ -72,15 +72,15 @@ return methodType; } - public int getModifiers() { - return -1; //TODO - } + public int getModifiers() { + return -1; //TODO + } public int getReferenceKind() { return referenceKind; } - - static String getReferenceKindString(int referenceKind) { + + public static String getReferenceKindString(int referenceKind) { switch (referenceKind) { case REF_getField: return "getfield"; case REF_getStatic: return "getstatic";
--- a/src/share/classes/java/lang/invoke/MethodHandleNatives.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleNatives.java Wed Aug 14 15:53:13 2013 -0700 @@ -215,6 +215,7 @@ static boolean refKindDoesDispatch(byte refKind) { assert(refKindIsValid(refKind)); return (refKind == REF_invokeVirtual || + refKind == REF_invokeStatic || refKind == REF_invokeInterface); } static {
--- a/src/share/classes/java/lang/invoke/MethodHandleProxies.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/lang/invoke/MethodHandleProxies.java Wed Aug 14 15:53:13 2013 -0700 @@ -142,7 +142,7 @@ @CallerSensitive public static <T> T asInterfaceInstance(final Class<T> intfc, final MethodHandle target) { - if (!intfc.isInterface() || !Modifier.isPublic(intfc.getModifiers())) + if (!intfc.isInterface()/* || !Modifier.isPublic(intfc.getModifiers())*/) throw new IllegalArgumentException("not a public interface: "+intfc.getName()); final MethodHandle mh; if (System.getSecurityManager() != null) {
--- a/src/share/classes/java/nio/file/DirectoryStream.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/nio/file/DirectoryStream.java Wed Aug 14 15:53:13 2013 -0700 @@ -28,16 +28,21 @@ import java.util.Iterator; import java.io.Closeable; import java.io.IOException; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * An object to iterate over the entries in a directory. A directory stream - * allows for the convenient use of the for-each construct to iterate over a - * directory. + * allows for the convenient use of the for-each construct or the {@link + * Stream} API to iterate over a directory. * * <p> <b> While {@code DirectoryStream} extends {@code Iterable}, it is not a - * general-purpose {@code Iterable} as it supports only a single {@code - * Iterator}; invoking the {@link #iterator iterator} method to obtain a second - * or subsequent iterator throws {@code IllegalStateException}. </b> + * general-purpose {@code Iterable}. A {@code DirectoryStream} supports only a + * single iteration via either the {@link #iterator iterator} or the {@link + * #stream stream} method. Invoking either method to do a second or + * subsequent iteration throws {@code IllegalStateException}. </b> * * <p> An important property of the directory stream's {@code Iterator} is that * its {@link Iterator#hasNext() hasNext} method is guaranteed to read-ahead by @@ -61,13 +66,13 @@ * } * </pre> * - * <p> Once a directory stream is closed, then further access to the directory, - * using the {@code Iterator}, behaves as if the end of stream has been reached. - * Due to read-ahead, the {@code Iterator} may return one or more elements - * after the directory stream has been closed. Once these buffered elements - * have been read, then subsequent calls to the {@code hasNext} method returns - * {@code false}, and subsequent calls to the {@code next} method will throw - * {@code NoSuchElementException}. + * <p> Once a directory stream is closed, then further access to the + * directory, using the {@code Iterator} or {@code Stream}, behaves as if the + * end of stream has been reached. Due to read-ahead, one or more elements may + * be returned after the directory stream has been closed. Once these buffered + * elements have been read, then subsequent calls to the {@code hasNext} + * method returns {@code false}, and subsequent calls to the {@code next} + * method will throw {@code NoSuchElementException}. * * <p> A directory stream is not required to be <i>asynchronously closeable</i>. * If a thread is blocked on the directory stream's iterator reading from the @@ -75,13 +80,14 @@ * second thread may block until the read operation is complete. * * <p> If an I/O error is encountered when accessing the directory then it - * causes the {@code Iterator}'s {@code hasNext} or {@code next} methods to - * throw {@link DirectoryIteratorException} with the {@link IOException} as the - * cause. As stated above, the {@code hasNext} method is guaranteed to - * read-ahead by at least one element. This means that if {@code hasNext} method - * returns {@code true}, and is followed by a call to the {@code next} method, - * then it is guaranteed that the {@code next} method will not fail with a - * {@code DirectoryIteratorException}. + * causes the methods to throw {@link DirectoryIteratorException} with the + * {@link IOException} as the cause. This could be the {@code Iterator}'s + * {@code hasNext} or {@code next} method or one of the {@code Stream} methods. + * As stated above, the {@code hasNext} method is guaranteed to read-ahead by + * at least one element. This means that if {@code hasNext} method returns + * {@code true}, and is followed by a call to the {@code next} method, then it + * is guaranteed that the {@code next} method will not fail with a {@code + * DirectoryIteratorException}. * * <p> The elements returned by the iterator are in no specific order. Some file * systems maintain special links to the directory itself and the directory's @@ -151,9 +157,23 @@ * @return the iterator associated with this {@code DirectoryStream} * * @throws IllegalStateException - * if this directory stream is closed or the iterator has already - * been returned + * if this directory stream is closed or the iterator or stream + * has already been returned */ @Override Iterator<T> iterator(); + + /** + * Returns the stream associated with this {@code DirectoryStream}. + * + * @return the stream associated with this {@code DirectoryStream} + * + * @throws IllegalStateException + * if this directory stream is closed or the iterator or stream + * has already been returned + * @since 1.8 + */ + default Stream<T> stream() { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator(), Spliterator.DISTINCT), false); + } }
--- a/src/share/classes/java/nio/file/Files.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/nio/file/Files.java Wed Aug 14 15:53:13 2013 -0700 @@ -25,24 +25,54 @@ package java.nio.file; -import java.nio.file.attribute.*; -import java.nio.file.spi.FileSystemProvider; -import java.nio.file.spi.FileTypeDetector; -import java.nio.channels.Channels; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.SeekableByteChannel; -import java.io.Closeable; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.*; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.DosFileAttributes; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileOwnerAttributeView; +import java.nio.file.attribute.FileStoreAttributeView; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.PosixFileAttributeView; +import java.nio.file.attribute.PosixFileAttributes; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.UserPrincipal; +import java.nio.file.spi.FileSystemProvider; +import java.nio.file.spi.FileTypeDetector; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.MayHoldCloseableResource; +import java.util.Objects; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; import java.util.function.BiPredicate; import java.util.stream.CloseableStream; import java.util.stream.DelegatingStream; @@ -74,6 +104,21 @@ return path.getFileSystem().provider(); } + /** + * Convert a Closeable to a Runnable by converting checked IOException + * to UncheckedIOException + */ + private static Runnable asUncheckedRunnable(Closeable c) { + return () -> { + try { + c.close(); + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + }; + } + // -- File contents -- /** @@ -3228,29 +3273,7 @@ // -- Stream APIs -- /** - * Implementation of CloseableStream - */ - private static class DelegatingCloseableStream<T> extends DelegatingStream<T> - implements CloseableStream<T> - { - private final Closeable closeable; - - DelegatingCloseableStream(Closeable c, Stream<T> delegate) { - super(delegate); - this.closeable = c; - } - - public void close() { - try { - closeable.close(); - } catch (IOException ex) { - throw new UncheckedIOException(ex); - } - } - } - - /** - * Return a lazily populated {@code CloseableStream}, the elements of + * Return a lazily populated {@code Stream}, the elements of * which are the entries in the directory. The listing is not recursive. * * <p> The elements of the stream are {@link Path} objects that are @@ -3265,7 +3288,7 @@ * method. * * <p> When not using the try-with-resources construct, then the stream's - * {@link CloseableStream#close close} method should be invoked after the + * {@link Stream#close close} method should be invoked after the * operation is completed so as to free any resources held for the open * directory. Operating on a closed stream behaves as if the end of stream * has been reached. Due to read-ahead, one or more elements may be @@ -3278,7 +3301,7 @@ * * @param dir The path to the directory * - * @return The {@code CloseableStream} describing the content of the + * @return The {@code Stream} describing the content of the * directory * * @throws NotDirectoryException @@ -3294,43 +3317,47 @@ * @see #newDirectoryStream(Path) * @since 1.8 */ - public static CloseableStream<Path> list(Path dir) throws IOException { + @MayHoldCloseableResource.HoldsResource + public static Stream<Path> list(Path dir) throws IOException { DirectoryStream<Path> ds = Files.newDirectoryStream(dir); - final Iterator<Path> delegate = ds.iterator(); + try { + final Iterator<Path> delegate = ds.iterator(); - // Re-wrap DirectoryIteratorException to UncheckedIOException - Iterator<Path> it = new Iterator<Path>() { - public boolean hasNext() { - try { - return delegate.hasNext(); - } catch (DirectoryIteratorException e) { - throw new UncheckedIOException(e.getCause()); + // Re-wrap DirectoryIteratorException to UncheckedIOException + Iterator<Path> it = new Iterator<Path>() { + public boolean hasNext() { + try { + return delegate.hasNext(); + } catch (DirectoryIteratorException e) { + throw new UncheckedIOException(e.getCause()); + } } - } - public Path next() { - try { - return delegate.next(); - } catch (DirectoryIteratorException e) { - throw new UncheckedIOException(e.getCause()); + public Path next() { + try { + return delegate.next(); + } catch (DirectoryIteratorException e) { + throw new UncheckedIOException(e.getCause()); + } } - } - }; + }; - Stream<Path> s = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), - false); - return new DelegatingCloseableStream<>(ds, s); + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false) + .onClose(asUncheckedRunnable(ds)); + } catch (Error|RuntimeException e) { + ds.close(); + throw e; + } } /** - * Return a {@code CloseableStream} that is lazily populated with {@code + * Return a {@code Stream} that is lazily populated with {@code * Path} by walking the file tree rooted at a given starting file. The * file tree is traversed <em>depth-first</em>, the elements in the stream * are {@link Path} objects that are obtained as if by {@link * Path#resolve(Path) resolving} the relative path against {@code start}. * * <p> The {@code stream} walks the file tree as elements are consumed. - * The {@code CloseableStream} returned is guaranteed to have at least one + * The {@code Stream} returned is guaranteed to have at least one * element, the starting file itself. For each file visited, the stream * attempts to read its {@link BasicFileAttributes}. If the file is a * directory and can be opened successfully, entries in the directory, and @@ -3371,7 +3398,7 @@ * (or directory), then it is ignored and not included in the stream. * * <p> When not using the try-with-resources construct, then the stream's - * {@link CloseableStream#close close} method should be invoked after the + * {@link Stream#close close} method should be invoked after the * operation is completed so as to free any resources held for the open * directory. Operate the stream after it is closed will throw an * {@link java.lang.IllegalStateException}. @@ -3388,7 +3415,7 @@ * @param options * options to configure the traversal * - * @return the {@link CloseableStream} of {@link Path} + * @return the {@link Stream} of {@link Path} * * @throws IllegalArgumentException * if the {@code maxDepth} parameter is negative @@ -3401,21 +3428,23 @@ * if an I/O error is thrown when accessing the starting file. * @since 1.8 */ - public static CloseableStream<Path> walk(Path start, int maxDepth, - FileVisitOption... options) - throws IOException - { + @MayHoldCloseableResource.HoldsResource + public static Stream<Path> walk(Path start, int maxDepth, + FileVisitOption... options) + throws IOException { FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options); - - Stream<Path> s = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT), - false). - map(entry -> entry.file()); - return new DelegatingCloseableStream<>(iterator, s); + try { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT), false) + .onClose(iterator::close) + .map(entry -> entry.file()); + } catch (Error|RuntimeException e) { + iterator.close(); + throw e; + } } /** - * Return a {@code CloseableStream} that is lazily populated with {@code + * Return a {@code Stream} that is lazily populated with {@code * Path} by walking the file tree rooted at a given starting file. The * file tree is traversed <em>depth-first</em>, the elements in the stream * are {@link Path} objects that are obtained as if by {@link @@ -3433,7 +3462,7 @@ * @param options * options to configure the traversal * - * @return the {@link CloseableStream} of {@link Path} + * @return the {@link Stream} of {@link Path} * * @throws SecurityException * If the security manager denies access to the starting file. @@ -3446,15 +3475,15 @@ * @see #walk(Path, int, FileVisitOption...) * @since 1.8 */ - public static CloseableStream<Path> walk(Path start, - FileVisitOption... options) - throws IOException - { + @MayHoldCloseableResource.HoldsResource + public static Stream<Path> walk(Path start, + FileVisitOption... options) + throws IOException { return walk(start, Integer.MAX_VALUE, options); } /** - * Return a {@code CloseableStream} that is lazily populated with {@code + * Return a {@code Stream} that is lazily populated with {@code * Path} by searching for files in a file tree rooted at a given starting * file. * @@ -3463,7 +3492,7 @@ * {@link BiPredicate} is invoked with its {@link Path} and {@link * BasicFileAttributes}. The {@code Path} object is obtained as if by * {@link Path#resolve(Path) resolving} the relative path against {@code - * start} and is only included in the returned {@link CloseableStream} if + * start} and is only included in the returned {@link Stream} if * the {@code BiPredicate} returns true. Compare to calling {@link * java.util.stream.Stream#filter filter} on the {@code Stream} * returned by {@code walk} method, this method may be more efficient by @@ -3484,7 +3513,7 @@ * @param options * options to configure the traversal * - * @return the {@link CloseableStream} of {@link Path} + * @return the {@link Stream} of {@link Path} * * @throws IllegalArgumentException * if the {@code maxDepth} parameter is negative @@ -3499,24 +3528,26 @@ * @see #walk(Path, int, FileVisitOption...) * @since 1.8 */ - public static CloseableStream<Path> find(Path start, - int maxDepth, - BiPredicate<Path, BasicFileAttributes> matcher, - FileVisitOption... options) - throws IOException - { + @MayHoldCloseableResource.HoldsResource + public static Stream<Path> find(Path start, + int maxDepth, + BiPredicate<Path, BasicFileAttributes> matcher, + FileVisitOption... options) + throws IOException { FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options); - - Stream<Path> s = StreamSupport.stream( - Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT), - false). - filter(entry -> matcher.test(entry.file(), entry.attributes())). - map(entry -> entry.file()); - return new DelegatingCloseableStream<>(iterator, s); + try { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT), false) + .onClose(iterator::close) + .filter(entry -> matcher.test(entry.file(), entry.attributes())) + .map(entry -> entry.file()); + } catch (Error|RuntimeException e) { + iterator.close(); + throw e; + } } /** - * Read all lines from a file as a {@code CloseableStream}. Unlike {@link + * Read all lines from a file as a {@code Stream}. Unlike {@link * #readAllLines(Path, Charset) readAllLines}, this method does not read * all lines into a {@code List}, but instead populates lazily as the stream * is consumed. @@ -3534,7 +3565,7 @@ * it is also wrapped as an {@code UncheckedIOException}. * * <p> When not using the try-with-resources construct, then stream's - * {@link CloseableStream#close close} method should be invoked after + * {@link Stream#close close} method should be invoked after * operation is completed so as to free any resources held for the open * file. * @@ -3543,7 +3574,7 @@ * @param cs * the charset to use for decoding * - * @return the lines from the file as a {@code CloseableStream} + * @return the lines from the file as a {@code Stream} * * @throws IOException * if an I/O error occurs opening the file @@ -3557,10 +3588,14 @@ * @see java.io.BufferedReader#lines() * @since 1.8 */ - public static CloseableStream<String> lines(Path path, Charset cs) - throws IOException - { + @MayHoldCloseableResource.HoldsResource + public static Stream<String> lines(Path path, Charset cs) throws IOException { BufferedReader br = Files.newBufferedReader(path, cs); - return new DelegatingCloseableStream<>(br, br.lines()); + try { + return br.lines().onClose(asUncheckedRunnable(br)); + } catch (Error|RuntimeException e) { + br.close(); + throw e; + } } }
--- a/src/share/classes/java/util/ArrayDeque.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/ArrayDeque.java Wed Aug 14 15:53:13 2013 -0700 @@ -888,6 +888,19 @@ elements[i] = s.readObject(); } + /** + * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em> + * and <em>fail-fast</em> {@link Spliterator} over the elements in this + * deque. + * + * <p>The {@code Spliterator} reports {@link Spliterator#SIZED}, + * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and + * {@link Spliterator#NONNULL}. Overriding implementations should document + * the reporting of additional characteristic values. + * + * @return a {@code Spliterator} over the elements in this deque + * @since 1.8 + */ public Spliterator<E> spliterator() { return new DeqSpliterator<E>(this, -1, -1); }
--- a/src/share/classes/java/util/ArrayList.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/ArrayList.java Wed Aug 14 15:53:13 2013 -0700 @@ -1238,6 +1238,20 @@ } } + /** + * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em> + * and <em>fail-fast</em> {@link Spliterator} over the elements in this + * list. + * + * <p>The {@code Spliterator} reports {@link Spliterator#SIZED}, + * {@link Spliterator#SUBSIZED}, and {@link Spliterator#ORDERED}. + * Overriding implementations should document the reporting of additional + * characteristic values. + * + * @return a {@code Spliterator} over the elements in this list + * @since 1.8 + */ + @Override public Spliterator<E> spliterator() { return new ArrayListSpliterator<>(this, 0, -1, 0); }
--- a/src/share/classes/java/util/Arrays.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/Arrays.java Wed Aug 14 15:53:13 2013 -0700 @@ -1568,7 +1568,7 @@ // Parallel prefix /** - * Cumulates, in parallel, each element of the given array in place, + * Cumulates in parallel each element of the given array in place, * using the supplied function. For example if the array initially * holds {@code [2, 1, 0, 3]} and the operation performs addition, * then upon return the array holds {@code [2, 3, 3, 6]}. @@ -1613,7 +1613,7 @@ } /** - * Cumulates, in parallel, each element of the given array in place, + * Cumulates in parallel each element of the given array in place, * using the supplied function. For example if the array initially * holds {@code [2, 1, 0, 3]} and the operation performs addition, * then upon return the array holds {@code [2, 3, 3, 6]}. @@ -1656,19 +1656,16 @@ } /** - * Cumulates, in parallel, each element of the given array in place, + * Cumulates in parallel each element of the given array in place, * using the supplied function. For example if the array initially - * holds {@code [2.0, 1.0, 0.0, 3.0]} and the operation performs addition, - * then upon return the array holds {@code [2.0, 3.0, 3.0, 6.0]}. + * holds {@code [2, 1, 0, 3]} and the operation performs addition, + * then upon return the array holds {@code [2, 3, 3, 6]}. * Parallel prefix computation is usually more efficient than * sequential loops for large arrays. * - * <p> Because floating-point operations may not be strictly associative, - * the returned result may not be identical to the value that would be - * obtained if the operation was performed sequentially. - * * @param array the array, which is modified in-place by this method - * @param op a side-effect-free function to perform the cumulation + * @param op a side-effect-free, associative function to perform the + * cumulation * @throws NullPointerException if the specified array or function is null * @since 1.8 */ @@ -1702,7 +1699,7 @@ } /** - * Cumulates, in parallel, each element of the given array in place, + * Cumulates in parallel each element of the given array in place, * using the supplied function. For example if the array initially * holds {@code [2, 1, 0, 3]} and the operation performs addition, * then upon return the array holds {@code [2, 3, 3, 6]}.
--- a/src/share/classes/java/util/Collection.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/Collection.java Wed Aug 14 15:53:13 2013 -0700 @@ -504,7 +504,10 @@ * * <p>The returned {@code Spliterator} must report the characteristic * {@link Spliterator#SIZED}; implementations should document any additional - * characteristic values reported by the returned Spliterator. + * characteristic values reported by the returned spliterator. If + * this collection contains no elements then the returned spliterator is + * only required to report {@link Spliterator#SIZED} and is not required to + * report additional characteristic values (if any). * * <p>The default implementation should be overridden by subclasses that * can return a more efficient spliterator. In order to @@ -535,9 +538,18 @@ * The returned {@code Spliterator} additionally reports * {@link Spliterator#SUBSIZED}. * + * <p>If a spliterator covers no elements then the reporting of additional + * characteristic values, beyond that of {@code SIZED} and {@code SUBSIZED}, + * does not aid clients to control, specialize or simplify computation. + * However, this does enable shared use of an immutable and empty + * spliterator instance (see {@link Spliterators#emptySpliterator()} for + * empty collections, and enables clients to determine if such a spliterator + * covers no elements. + * * @return a {@code Spliterator} over the elements in this collection * @since 1.8 */ + @Override default Spliterator<E> spliterator() { return Spliterators.spliterator(this, 0); }
--- a/src/share/classes/java/util/Collections.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/Collections.java Wed Aug 14 15:53:13 2013 -0700 @@ -27,7 +27,6 @@ import java.io.Serializable; import java.io.ObjectOutputStream; import java.io.IOException; -import java.io.InvalidObjectException; import java.lang.reflect.Array; import java.util.function.BiConsumer; import java.util.function.BiFunction; @@ -35,6 +34,7 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.function.UnaryOperator; +import java.util.stream.IntStream; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -1148,7 +1148,14 @@ public Spliterator<E> spliterator() { return (Spliterator<E>)c.spliterator(); } - + @Override + public Stream<E> stream() { + return (Stream<E>)c.stream(); + } + @Override + public Stream<E> parallelStream() { + return (Stream<E>)c.parallelStream(); + } } /** @@ -2009,8 +2016,8 @@ * through the returned collection.<p> * * It is imperative that the user manually synchronize on the returned - * collection when traversing it via {@link Iterator} or - * {@link Spliterator}: + * collection when traversing it via {@link Iterator}, {@link Spliterator} + * or {@link Stream}: * <pre> * Collection c = Collections.synchronizedCollection(myCollection); * ... @@ -2120,6 +2127,14 @@ public Spliterator<E> spliterator() { return c.spliterator(); // Must be manually synched by user! } + @Override + public Stream<E> stream() { + return c.stream(); // Must be manually synched by user! + } + @Override + public Stream<E> parallelStream() { + return c.parallelStream(); // Must be manually synched by user! + } private void writeObject(ObjectOutputStream s) throws IOException { synchronized (mutex) {s.defaultWriteObject();} } @@ -3172,6 +3187,10 @@ } @Override public Spliterator<E> spliterator() {return c.spliterator();} + @Override + public Stream<E> stream() {return c.stream();} + @Override + public Stream<E> parallelStream() {return c.parallelStream();} } /** @@ -5097,6 +5116,22 @@ ") > toIndex(" + toIndex + ")"); return new CopiesList<>(toIndex - fromIndex, element); } + + // Override default methods in Collection + @Override + public Stream<E> stream() { + return IntStream.range(0, n).mapToObj(i -> element); + } + + @Override + public Stream<E> parallelStream() { + return IntStream.range(0, n).parallel().mapToObj(i -> element); + } + + @Override + public Spliterator<E> spliterator() { + return stream().spliterator(); + } } /** @@ -5504,6 +5539,10 @@ @Override public Spliterator<E> spliterator() {return s.spliterator();} + @Override + public Stream<E> stream() {return s.stream();} + @Override + public Stream<E> parallelStream() {return s.parallelStream();} private static final long serialVersionUID = 2454657854757543876L; @@ -5569,10 +5608,14 @@ @Override public void forEach(Consumer<? super E> action) {q.forEach(action);} @Override - public Spliterator<E> spliterator() {return q.spliterator();} - @Override public boolean removeIf(Predicate<? super E> filter) { return q.removeIf(filter); } + @Override + public Spliterator<E> spliterator() {return q.spliterator();} + @Override + public Stream<E> stream() {return q.stream();} + @Override + public Stream<E> parallelStream() {return q.parallelStream();} } }
--- a/src/share/classes/java/util/HashSet.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/HashSet.java Wed Aug 14 15:53:13 2013 -0700 @@ -312,6 +312,18 @@ } } + /** + * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em> + * and <em>fail-fast</em> {@link Spliterator} over the elements in this + * set. + * + * <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and + * {@link Spliterator#DISTINCT}. Overriding implementations should document + * the reporting of additional characteristic values. + * + * @return a {@code Spliterator} over the elements in this set + * @since 1.8 + */ public Spliterator<E> spliterator() { return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0); }
--- a/src/share/classes/java/util/LinkedHashMap.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/LinkedHashMap.java Wed Aug 14 15:53:13 2013 -0700 @@ -129,10 +129,20 @@ * exception for its correctness: <i>the fail-fast behavior of iterators * should be used only to detect bugs.</i> * + * <p>The spliterators returned by the spliterator method of the collections + * returned by all of this class's collection view methods are + * <em><a href="Spliterator.html#binding">late-binding</a></em>, + * <em>fail-fast</em>, and additionally report {@link Spliterator#ORDERED}. + * * <p>This class is a member of the * <a href="{@docRoot}/../technotes/guides/collections/index.html"> * Java Collections Framework</a>. * + * @implNote + * The spliterators returned by the spliterator method of the collections + * returned by all of this class's collection view methods are created from + * the iterators of the corresponding collections. + * * @param <K> the type of keys maintained by this map * @param <V> the type of mapped values *
--- a/src/share/classes/java/util/LinkedHashSet.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/LinkedHashSet.java Wed Aug 14 15:53:13 2013 -0700 @@ -170,13 +170,23 @@ } /** - * Creates a {@code Spliterator}, over the elements in this set, that - * reports {@code SIZED}, {@code DISTINCT} and {@code ORDERED}. - * Overriding implementations are expected to document if the - * {@code Spliterator} reports any additional and relevant characteristic - * values. + * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em> + * and <em>fail-fast</em> {@code Spliterator} over the elements in this set. + * + * <p>The {@code Spliterator} reports {@link Spliterator#SIZED}, + * {@link Spliterator#DISTINCT}, and {@code ORDERED}. Implementations + * should document the reporting of additional characteristic values. + * + * @implNote + * The implementation creates a + * <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator + * from the set's {@code Iterator}. The spliterator inherits the + * <em>fail-fast</em> properties of the set's iterator. + * The created {@code Spliterator} additionally reports + * {@link Spliterator#SUBSIZED}. * * @return a {@code Spliterator} over the elements in this set + * @since 1.8 */ @Override public Spliterator<E> spliterator() {
--- a/src/share/classes/java/util/LinkedList.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/LinkedList.java Wed Aug 14 15:53:13 2013 -0700 @@ -1149,6 +1149,23 @@ linkLast((E)s.readObject()); } + /** + * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em> + * and <em>fail-fast</em> {@link Spliterator} over the elements in this + * list. + * + * <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and + * {@link Spliterator#ORDERED}. Overriding implementations should document + * the reporting of additional characteristic values. + * + * @implNote + * The {@code Spliterator} additionally reports {@link Spliterator#SUBSIZED} + * and implements {@code trySplit} to permit limited parallelism.. + * + * @return a {@code Spliterator} over the elements in this list + * @since 1.8 + */ + @Override public Spliterator<E> spliterator() { return new LLSpliterator<E>(this, -1, 0); }
--- a/src/share/classes/java/util/List.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/List.java Wed Aug 14 15:53:13 2013 -0700 @@ -671,7 +671,7 @@ * The default implementation creates a * <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator * from the list's {@code Iterator}. The spliterator inherits the - * <em>fail-fast</em> properties of the collection's iterator. + * <em>fail-fast</em> properties of the list's iterator. * * @implNote * The created {@code Spliterator} additionally reports
--- a/src/share/classes/java/util/Map.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/Map.java Wed Aug 14 15:53:13 2013 -0700 @@ -579,10 +579,12 @@ } /** - * Performs the given action on each entry in this map, in the order entries - * are returned by an entry set iterator (which may be unspecified), until - * all entries have been processed or the action throws an {@code Exception}. - * Exceptions thrown by the action are relayed to the caller. + * Performs the given action on each entry in this map until all entries + * have been processed or the action throws an {@code Exception}. + * Exceptions thrown by the action are relayed to the caller. The entries + * will be processed in the same order as the entry set iterator unless that + * order is unspecified in which case implementations may use an order which + * differs from the entry set iterator. * * <p>The default implementation should be overridden by implementations if * they can provide a more performant implementation than an iterator-based
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/util/MayHoldCloseableResource.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012, 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 java.util; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * An object that may (but need not) hold one or more references to + * resources that will be released when closed. Such objects may be + * used with try-with-resources or related {@code try...finally} + * constructions that ensure they are closed as soon as they are no + * longer needed. Interface {@code MayHoldCloseableResource} indicates that + * only a minority of usages warrant resource control constructions: + * those specialized to known resource-bearing instances, or those + * that must operate in complete generality. + * + * <p>For example, most usages of the {@link java.util.stream.Stream} + * classes operate on data sources such as an array, {@code + * Collection}, or generator function that do not require or benefit + * from explicit resource control. However, some uses of IO channels + * as data sources do. + * + * <p>Annotation {@link HoldsResource} may be used to guide users or static + * analysis tools to deciding whether resource-control constructions are + * warranted when using particular instantiations of + * {@code MayHoldCloseableResource}. + * + * @see AutoCloseable + * @see HoldsResource + */ +public interface MayHoldCloseableResource extends AutoCloseable { + /** + * Closes this resource, relinquishing any underlying resources. + * This method is invoked automatically on objects managed by the + * {@code try}-with-resources statement. + * + * Implementers of this interface are strongly encouraged + * to make their {@code close} methods idempotent. + * + * @see AutoCloseable#close() + */ + @Override + void close(); + + /** + * Indicates that a variable holding a {@code MayHoldCloseableResource} or + * a method returning a {@code MayHoldCloseableResource} definitely does + * hold a closeable resource. + */ + @Retention(RetentionPolicy.CLASS) + @Documented + @interface HoldsResource { } +}
--- a/src/share/classes/java/util/PrimitiveIterator.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/PrimitiveIterator.java Wed Aug 14 15:53:13 2013 -0700 @@ -56,12 +56,12 @@ * primitive values occur when operating on primitive subtype specializations. * * @param <T> the type of elements returned by this PrimitiveIterator. The - * type must be a wrapper type for a primitive type, such as - * {@code Integer} for the primitive {@code int} type. + * type must be a wrapper type for a primitive type, such as {@code Integer} + * for the primitive {@code int} type. * @param <T_CONS> the type of primitive consumer. The type must be a - * primitive specialization of {@link java.util.function.Consumer} for - * {@code T}, such as {@link java.util.function.IntConsumer} for - * {@code Integer}. + * primitive specialization of {@link java.util.function.Consumer} for + * {@code T}, such as {@link java.util.function.IntConsumer} for + * {@code Integer}. * * @since 1.8 */
--- a/src/share/classes/java/util/PriorityQueue.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/PriorityQueue.java Wed Aug 14 15:53:13 2013 -0700 @@ -795,6 +795,19 @@ heapify(); } + /** + * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em> + * and <em>fail-fast</em> {@link Spliterator} over the elements in this + * queue. + * + * <p>The {@code Spliterator} reports {@link Spliterator#SIZED}, + * {@link Spliterator#SUBSIZED}, and {@link Spliterator#NONNULL}. + * Overriding implementations should document the reporting of additional + * characteristic values. + * + * @return a {@code Spliterator} over the elements in this queue + * @since 1.8 + */ public final Spliterator<E> spliterator() { return new PriorityQueueSpliterator<E>(this, 0, -1, 0); }
--- a/src/share/classes/java/util/Random.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/Random.java Wed Aug 14 15:53:13 2013 -0700 @@ -26,9 +26,13 @@ package java.util; import java.io.*; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; +import java.util.stream.StreamSupport; import sun.misc.Unsafe; @@ -85,6 +89,13 @@ private static final long addend = 0xBL; private static final long mask = (1L << 48) - 1; + private static final double DOUBLE_UNIT = 1.0 / (1L << 53); + + // IllegalArgumentException messages + static final String BadBound = "bound must be positive"; + static final String BadRange = "bound must be greater than origin"; + static final String BadSize = "size must be non-negative"; + /** * Creates a new random number generator. This constructor sets * the seed of the random number generator to a value very likely @@ -222,6 +233,85 @@ } /** + * The form of nextLong used by LongStream Spliterators. If + * origin is greater than bound, acts as unbounded form of + * nextLong, else as bounded form. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final long internalNextLong(long origin, long bound) { + long r = nextLong(); + if (origin < bound) { + long n = bound - origin, m = n - 1; + if ((n & m) == 0L) // power of two + r = (r & m) + origin; + else if (n > 0L) { // reject over-represented candidates + for (long u = r >>> 1; // ensure nonnegative + u + m - (r = u % n) < 0L; // rejection check + u = nextLong() >>> 1) // retry + ; + r += origin; + } + else { // range not representable as long + while (r < origin || r >= bound) + r = nextLong(); + } + } + return r; + } + + /** + * The form of nextInt used by IntStream Spliterators. + * Exactly the same as long version, except for types. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final int internalNextInt(int origin, int bound) { + if (origin < bound) { + int r = next(31); + int n = bound - origin, m = n - 1; + if ((n & m) == 0) + r = (int)((n * (long)r) >> 31) + origin; + else if (n > 0) { + for (int u = r; + u - (r = u % n) + m < 0; + u = next(31)) + ; + r += origin; + } + else { + while (r < origin || r >= bound) + r = next(32); + } + return r; + } + else { + return next(32); + } + } + + /** + * The form of nextDouble used by DoubleStream Spliterators. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final double internalNextDouble(double origin, double bound) { + double r = nextDouble(); + if (origin < bound) { + r = r * (bound - origin) + origin; + if (r >= bound) // correct for rounding + r = Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); + } + return r; + } + + /** * Returns the next pseudorandom, uniformly distributed {@code int} * value from this random number generator's sequence. The general * contract of {@code nextInt} is that one {@code int} value is @@ -289,7 +379,7 @@ * greatly increases the length of the sequence of values returned by * successive calls to this method if n is a small power of two. * - * @param n the bound on the random number to be returned. Must be + * @param bound the bound on the random number to be returned. Must be * positive. * @return the next pseudorandom, uniformly distributed {@code int} * value between {@code 0} (inclusive) and {@code n} (exclusive) @@ -297,20 +387,39 @@ * @throws IllegalArgumentException if n is not positive * @since 1.2 */ + public int nextInt(int bound) { + if (bound <= 0) + throw new IllegalArgumentException(BadBound); - public int nextInt(int n) { - if (n <= 0) - throw new IllegalArgumentException("n must be positive"); + int r = next(31); + int m = bound - 1; + if ((bound & m) == 0) // i.e., bound is a power of 2 + r = (int)((bound * (long)r) >> 31); + else { + for (int u = r; + u - (r = u % bound) + m < 0; + u = next(31)) + ; + } + return r; + } - if ((n & -n) == n) // i.e., n is a power of 2 - return (int)((n * (long)next(31)) >> 31); - - int bits, val; - do { - bits = next(31); - val = bits % n; - } while (bits - val + (n-1) < 0); - return val; + /** + * Returns a pseudorandom {@code int} value between the specified + * origin (inclusive) and the specified bound (exclusive). + * + * @param origin the least value returned + * @param bound the upper bound (exclusive) + * @return a pseudorandom {@code int} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} + */ + public int nextInt(int origin, int bound) { + // @@@ Document algorithm as for other int methods? + if (origin >= bound) + throw new IllegalArgumentException(BadRange); + return internalNextInt(origin, bound); } /** @@ -338,6 +447,40 @@ } /** + * Returns a pseudorandom {@code long} value between zero (inclusive) + * and the specified bound (exclusive). + * + * @param bound the bound on the random number to be returned. Must be + * positive. + * @return a pseudorandom {@code long} value between zero + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code bound} is not positive + */ + public long nextLong(long bound) { + if (bound <= 0) + throw new IllegalArgumentException(BadBound); + return internalNextLong(0, bound); + } + + /** + * Returns a pseudorandom {@code long} value between the specified + * origin (inclusive) and the specified bound (exclusive). + * + * @param origin the least value returned + * @param bound the upper bound (exclusive) + * @return a pseudorandom {@code long} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} + */ + public long nextLong(long origin, long bound) { + if (origin >= bound) + throw new IllegalArgumentException(BadRange); + return internalNextLong(origin, bound); + } + + + /** * Returns the next pseudorandom, uniformly distributed * {@code boolean} value from this random number generator's * sequence. The general contract of {@code nextBoolean} is that one @@ -442,8 +585,42 @@ * @see Math#random */ public double nextDouble() { - return (((long)(next(26)) << 27) + next(27)) - / (double)(1L << 53); + return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT; + } + + /** + * Returns a pseudorandom {@code double} value between 0.0 + * (inclusive) and the specified bound (exclusive). + * + * @param bound the bound on the random number to be returned. Must be + * positive. + * @return a pseudorandom {@code double} value between zero + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code bound} is not positive + */ + public double nextDouble(double bound) { + if (!(bound > 0.0)) + throw new IllegalArgumentException(BadBound); + double result = nextDouble() * bound; + return (result < bound) ? result : // correct for rounding + Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); + } + + /** + * Returns a pseudorandom {@code double} value between the specified + * origin (inclusive) and bound (exclusive). + * + * @param origin the least value returned + * @param bound the upper bound + * @return a pseudorandom {@code double} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} + */ + public double nextDouble(double origin, double bound) { + if (!(origin < bound)) + throw new IllegalArgumentException(BadRange); + return internalNextDouble(origin, bound); } private double nextNextGaussian; @@ -513,57 +690,430 @@ } } + // stream methods, coded in a way intended to better isolate for + // maintenance purposes the small differences across forms. + /** - * Returns a stream of pseudorandom, uniformly distributed - * {@code integer} values from this random number generator's - * sequence. Values are obtained as needed by calling - * {@link #nextInt()}. + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code int} values. * - * @return an infinite stream of {@code integer} values - * @since 1.8 + * @param streamSize the number of values to generate + * @return a stream of pseudorandom {@code int} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + */ + public IntStream ints(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, streamSize, Integer.MAX_VALUE, 0), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code int} + * values. + * + * @implNote This method is implemented to be equivalent to {@code + * ints(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code int} values */ public IntStream ints() { - return IntStream.generate(this::nextInt); + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, Long.MAX_VALUE, Integer.MAX_VALUE, 0), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code int} values, each conforming to the given + * origin and bound. + * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code int} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero, or {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + public IntStream ints(long streamSize, int randomNumberOrigin, + int randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * int} values, each conforming to the given origin and bound. + * + * @implNote This method is implemented to be equivalent to {@code + * ints(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code int} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + public IntStream ints(int randomNumberOrigin, int randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code long} values. + * + * @param streamSize the number of values to generate + * @return a stream of pseudorandom {@code long} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + */ + public LongStream longs(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, streamSize, Long.MAX_VALUE, 0L), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code long} + * values. + * + * @implNote This method is implemented to be equivalent to {@code + * longs(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code long} values + */ + public LongStream longs() { + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, Long.MAX_VALUE, Long.MAX_VALUE, 0L), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code long} values, each conforming to the + * given origin and bound. + * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code long} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero, or {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + public LongStream longs(long streamSize, long randomNumberOrigin, + long randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * long} values, each conforming to the given origin and bound. + * + * @implNote This method is implemented to be equivalent to {@code + * longs(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code long} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + public LongStream longs(long randomNumberOrigin, long randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code double} values, each between zero + * (inclusive) and one (exclusive). + * + * @param streamSize the number of values to generate + * @return a stream of {@code double} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + */ + public DoubleStream doubles(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, streamSize, Double.MAX_VALUE, 0.0), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * double} values, each between zero (inclusive) and one + * (exclusive). + * + * @implNote This method is implemented to be equivalent to {@code + * doubles(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code double} values + */ + public DoubleStream doubles() { + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, Long.MAX_VALUE, Double.MAX_VALUE, 0.0), + false); } /** - * Returns a stream of pseudorandom, uniformly distributed - * {@code long} values from this random number generator's - * sequence. Values are obtained as needed by calling - * {@link #nextLong()}. + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code double} values, each conforming to the + * given origin and bound. * - * @return an infinite stream of {@code long} values - * @since 1.8 + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code double} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} */ - public LongStream longs() { - return LongStream.generate(this::nextLong); + public DoubleStream doubles(long streamSize, double randomNumberOrigin, + double randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (!(randomNumberOrigin < randomNumberBound)) + throw new IllegalArgumentException(BadRange); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * double} values, each conforming to the given origin and bound. + * + * @implNote This method is implemented to be equivalent to {@code + * doubles(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code double} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) { + if (!(randomNumberOrigin < randomNumberBound)) + throw new IllegalArgumentException(BadRange); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); } /** - * Returns a stream of pseudorandom, uniformly distributed - * {@code double} values between {@code 0.0} and {@code 1.0} - * from this random number generator's sequence. Values are - * obtained as needed by calling {@link #nextDouble()}. - * - * @return an infinite stream of {@code double} values - * @since 1.8 + * Spliterator for int streams. We multiplex the four int + * versions into one class by treating a bound less than origin as + * unbounded, and also by treating "infinite" as equivalent to + * Long.MAX_VALUE. For splits, it uses the standard divide-by-two + * approach. The long and double versions of this class are + * identical except for types. */ - public DoubleStream doubles() { - return DoubleStream.generate(this::nextDouble); + static final class RandomIntsSpliterator implements Spliterator.OfInt { + final Random rng; + long index; + final long fence; + final int origin; + final int bound; + RandomIntsSpliterator(Random rng, long index, long fence, + int origin, int bound) { + this.rng = rng; this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomIntsSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomIntsSpliterator(rng, i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(IntConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(rng.internalNextInt(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(IntConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + Random r = rng; + int o = origin, b = bound; + do { + consumer.accept(r.internalNextInt(o, b)); + } while (++i < f); + } + } } /** - * Returns a stream of pseudorandom, Gaussian ("normally") - * distributed {@code double} values with mean {@code 0.0} - * and standard deviation {@code 1.0} from this random number - * generator's sequence. Values are obtained as needed by - * calling {@link #nextGaussian()}. - * - * @return an infinite stream of {@code double} values - * @since 1.8 + * Spliterator for long streams. */ - public DoubleStream gaussians() { - return DoubleStream.generate(this::nextGaussian); + static final class RandomLongsSpliterator implements Spliterator.OfLong { + final Random rng; + long index; + final long fence; + final long origin; + final long bound; + RandomLongsSpliterator(Random rng, long index, long fence, + long origin, long bound) { + this.rng = rng; this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomLongsSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomLongsSpliterator(rng, i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(LongConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(rng.internalNextLong(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(LongConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + Random r = rng; + long o = origin, b = bound; + do { + consumer.accept(r.internalNextLong(o, b)); + } while (++i < f); + } + } + + } + + /** + * Spliterator for double streams. + */ + static final class RandomDoublesSpliterator implements Spliterator.OfDouble { + final Random rng; + long index; + final long fence; + final double origin; + final double bound; + RandomDoublesSpliterator(Random rng, long index, long fence, + double origin, double bound) { + this.rng = rng; this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomDoublesSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomDoublesSpliterator(rng, i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(DoubleConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(rng.internalNextDouble(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(DoubleConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + Random r = rng; + double o = origin, b = bound; + do { + consumer.accept(r.internalNextDouble(o, b)); + } while (++i < f); + } + } } /**
--- a/src/share/classes/java/util/Set.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/Set.java Wed Aug 14 15:53:13 2013 -0700 @@ -394,7 +394,7 @@ * The default implementation creates a * <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator * from the set's {@code Iterator}. The spliterator inherits the - * <em>fail-fast</em> properties of the collection's iterator. + * <em>fail-fast</em> properties of the set's iterator. * * @implNote * The created {@code Spliterator} additionally reports
--- a/src/share/classes/java/util/SortedSet.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/SortedSet.java Wed Aug 14 15:53:13 2013 -0700 @@ -238,7 +238,7 @@ * The default implementation creates a * <em><a href="Spliterator.html#binding">late-binding</a></em> spliterator * from the sorted set's {@code Iterator}. The spliterator inherits the - * <em>fail-fast</em> properties of the collection's iterator. The + * <em>fail-fast</em> properties of the set's iterator. The * spliterator's comparator is the same as the sorted set's comparator. * * @implNote
--- a/src/share/classes/java/util/Spliterator.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/Spliterator.java Wed Aug 14 15:53:13 2013 -0700 @@ -74,7 +74,11 @@ * source prior to binding are reflected when the Spliterator is traversed. * After binding a Spliterator should, on a best-effort basis, throw * {@link ConcurrentModificationException} if structural interference is - * detected. Spliterators that do this are called <em>fail-fast</em>. + * detected. Spliterators that do this are called <em>fail-fast</em>. The + * bulk traversal method ({@link #forEachRemaining forEachRemaining()}) of a + * Spliterator may optimize traversal and check for structural interface after + * all elements have been traversed, rather than checking per-element and + * failing immediately. * * <p>Spliterators can provide an estimate of the number of remaining elements * via the {@link #estimateSize} method. Ideally, as reflected in characteristic
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/java/util/SplittableRandom.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,985 @@ +/* + * 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 java.util; + +import java.net.InetAddress; +import java.util.concurrent.atomic.AtomicLong; +import java.util.Spliterator; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; +import java.util.function.DoubleConsumer; +import java.util.stream.StreamSupport; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.DoubleStream; + +/** + * A generator of uniform pseudorandom values applicable for use in + * (among other contexts) isolated parallel computations that may + * generate subtasks. Class SplittableRandom supports methods for + * producing pseudorandom numbers of type {@code int}, {@code long}, + * and {@code double} with similar usages as for class + * {@link java.util.Random} but differs in the following ways: + * + * <ul> + * + * <li>Series of generated values pass the DieHarder suite testing + * independence and uniformity properties of random number generators. + * (Most recently validated with <a + * href="http://www.phy.duke.edu/~rgb/General/dieharder.php"> version + * 3.31.1</a>.) These tests validate only the methods for certain + * types and ranges, but similar properties are expected to hold, at + * least approximately, for others as well. The <em>period</em> + * (length of any series of generated values before it repeats) is at + * least 2<sup>64</sup>. </li> + * + * <li> Method {@link #split} constructs and returns a new + * SplittableRandom instance that shares no mutable state with the + * current instance. However, with very high probability, the + * values collectively generated by the two objects have the same + * statistical properties as if the same quantity of values were + * generated by a single thread using a single {@code + * SplittableRandom} object. </li> + * + * <li>Instances of SplittableRandom are <em>not</em> thread-safe. + * They are designed to be split, not shared, across threads. For + * example, a {@link java.util.concurrent.ForkJoinTask + * fork/join-style} computation using random numbers might include a + * construction of the form {@code new + * Subtask(aSplittableRandom.split()).fork()}. + * + * <li>This class provides additional methods for generating random + * streams, that employ the above techniques when used in {@code + * stream.parallel()} mode.</li> + * + * </ul> + * + * @author Guy Steele + * @author Doug Lea + * @since 1.8 + */ +public class SplittableRandom { + + /* + * Implementation Overview. + * + * This algorithm was inspired by the "DotMix" algorithm by + * Leiserson, Schardl, and Sukha "Deterministic Parallel + * Random-Number Generation for Dynamic-Multithreading Platforms", + * PPoPP 2012, as well as those in "Parallel random numbers: as + * easy as 1, 2, 3" by Salmon, Morae, Dror, and Shaw, SC 2011. It + * differs mainly in simplifying and cheapening operations. + * + * The primary update step (method nextSeed()) is to add a + * constant ("gamma") to the current (64 bit) seed, forming a + * simple sequence. The seed and the gamma values for any two + * SplittableRandom instances are highly likely to be different. + * + * Methods nextLong, nextInt, and derivatives do not return the + * sequence (seed) values, but instead a hash-like bit-mix of + * their bits, producing more independently distributed sequences. + * For nextLong, the mix64 bit-mixing function computes the same + * value as the "64-bit finalizer" function in Austin Appleby's + * MurmurHash3 algorithm. See + * http://code.google.com/p/smhasher/wiki/MurmurHash3 , which + * comments: "The constants for the finalizers were generated by a + * simple simulated-annealing algorithm, and both avalanche all + * bits of 'h' to within 0.25% bias." The mix32 function is + * equivalent to (int)(mix64(seed) >>> 32), but faster because it + * omits a step that doesn't contribute to result. + * + * The split operation uses the current generator to form the seed + * and gamma for another SplittableRandom. To conservatively + * avoid potential correlations between seed and value generation, + * gamma selection (method nextGamma) uses the "Mix13" constants + * for MurmurHash3 described by David Stafford + * (http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html) + * To avoid potential weaknesses in bit-mixing transformations, we + * restrict gammas to odd values with at least 12 and no more than + * 52 bits set. Rather than rejecting candidates with too few or + * too many bits set, method nextGamma flips some bits (which has + * the effect of mapping at most 4 to any given gamma value). + * This reduces the effective set of 64bit odd gamma values by + * about 2<sup>14</sup>, a very tiny percentage, and serves as an + * automated screening for sequence constant selection that is + * left as an empirical decision in some other hashing and crypto + * algorithms. + * + * The resulting generator thus transforms a sequence in which + * (typically) many bits change on each step, with an inexpensive + * mixer with good (but less than cryptographically secure) + * avalanching. + * + * The default (no-argument) constructor, in essence, invokes + * split() for a common "seeder" SplittableRandom. Unlike other + * cases, this split must be performed in a thread-safe manner, so + * we use an AtomicLong to represent the seed rather than use an + * explicit SplittableRandom. To bootstrap the seeder, we start + * off using a seed based on current time and host. This serves as + * a slimmed-down (and insecure) variant of SecureRandom that also + * avoids stalls that may occur when using /dev/random. + * + * It is a relatively simple matter to apply the basic design here + * to use 128 bit seeds. However, emulating 128bit arithmetic and + * carrying around twice the state add more overhead than appears + * warranted for current usages. + * + * File organization: First the non-public methods that constitute + * the main algorithm, then the main public methods, followed by + * some custom spliterator classes needed for stream methods. + */ + + /** + * The initial gamma value for (unsplit) SplittableRandoms. Must + * be odd with at least 12 and no more than 52 bits set. Currently + * set to the golden ratio scaled to 64bits. + */ + private static final long INITIAL_GAMMA = 0x9e3779b97f4a7c15L; + + /** + * The least non-zero value returned by nextDouble(). This value + * is scaled by a random value of 53 bits to produce a result. + */ + private static final double DOUBLE_UNIT = 1.0 / (1L << 53); + + /** + * The seed. Updated only via method nextSeed. + */ + private long seed; + + /** + * The step value. + */ + private final long gamma; + + /** + * Internal constructor used by all others except default constructor. + */ + private SplittableRandom(long seed, long gamma) { + this.seed = seed; + this.gamma = gamma; + } + + /** + * Computes MurmurHash3 64bit mix function. + */ + private static long mix64(long z) { + z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL; + z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L; + return z ^ (z >>> 33); + } + + /** + * Returns the 32 high bits of mix64(z) as int. + */ + private static int mix32(long z) { + z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL; + return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32); + } + + /** + * Returns the gamma value to use for a new split instance. + */ + private static long nextGamma(long z) { + z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L; // Stafford "Mix13" + z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL; + z = (z ^ (z >>> 31)) | 1L; // force to be odd + int n = Long.bitCount(z); // ensure enough 0 and 1 bits + return (n < 12 || n > 52) ? z ^ 0xaaaaaaaaaaaaaaaaL : z; + } + + /** + * Adds gamma to seed. + */ + private long nextSeed() { + return seed += gamma; + } + + /** + * The seed generator for default constructors. + */ + private static final AtomicLong seeder = + new AtomicLong(mix64((((long)hashedHostAddress()) << 32) ^ + System.currentTimeMillis()) ^ + mix64(System.nanoTime())); + + /** + * Returns hash of local host IP address, if available; else 0. + */ + private static int hashedHostAddress() { + try { + return InetAddress.getLocalHost().hashCode(); + } catch (Exception ex) { + return 0; + } + } + + // IllegalArgumentException messages + static final String BadBound = "bound must be positive"; + static final String BadRange = "bound must be greater than origin"; + static final String BadSize = "size must be non-negative"; + + /* + * Internal versions of nextX methods used by streams, as well as + * the public nextX(origin, bound) methods. These exist mainly to + * avoid the need for multiple versions of stream spliterators + * across the different exported forms of streams. + */ + + /** + * The form of nextLong used by LongStream Spliterators. If + * origin is greater than bound, acts as unbounded form of + * nextLong, else as bounded form. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final long internalNextLong(long origin, long bound) { + /* + * Four Cases: + * + * 1. If the arguments indicate unbounded form, act as + * nextLong(). + * + * 2. If the range is an exact power of two, apply the + * associated bit mask. + * + * 3. If the range is positive, loop to avoid potential bias + * when the implicit nextLong() bound (2<sup>64</sup>) is not + * evenly divisible by the range. The loop rejects candidates + * computed from otherwise over-represented values. The + * expected number of iterations under an ideal generator + * varies from 1 to 2, depending on the bound. The loop itself + * takes an unlovable form. Because the first candidate is + * already available, we need a break-in-the-middle + * construction, which is concisely but cryptically performed + * within the while-condition of a body-less for loop. + * + * 4. Otherwise, the range cannot be represented as a positive + * long. The loop repeatedly generates unbounded longs until + * obtaining a candidate meeting constraints (with an expected + * number of iterations of less than two). + */ + + long r = mix64(nextSeed()); + if (origin < bound) { + long n = bound - origin, m = n - 1; + if ((n & m) == 0L) // power of two + r = (r & m) + origin; + else if (n > 0L) { // reject over-represented candidates + for (long u = r >>> 1; // ensure nonnegative + u + m - (r = u % n) < 0L; // rejection check + u = mix64(nextSeed()) >>> 1) // retry + ; + r += origin; + } + else { // range not representable as long + while (r < origin || r >= bound) + r = mix64(nextSeed()); + } + } + return r; + } + + /** + * The form of nextInt used by IntStream Spliterators. + * Exactly the same as long version, except for types. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final int internalNextInt(int origin, int bound) { + int r = mix32(nextSeed()); + if (origin < bound) { + int n = bound - origin, m = n - 1; + if ((n & m) == 0) + r = (r & m) + origin; + else if (n > 0) { + for (int u = r >>> 1; + u + m - (r = u % n) < 0; + u = mix32(nextSeed()) >>> 1) + ; + r += origin; + } + else { + while (r < origin || r >= bound) + r = mix32(nextSeed()); + } + } + return r; + } + + /** + * The form of nextDouble used by DoubleStream Spliterators. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final double internalNextDouble(double origin, double bound) { + double r = (nextLong() >>> 11) * DOUBLE_UNIT; + if (origin < bound) { + r = r * (bound - origin) + origin; + if (r >= bound) // correct for rounding + r = Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); + } + return r; + } + + /* ---------------- public methods ---------------- */ + + /** + * Creates a new SplittableRandom instance using the specified + * initial seed. SplittableRandom instances created with the same + * seed in the same program generate identical sequences of values. + * + * @param seed the initial seed + */ + public SplittableRandom(long seed) { + this(seed, INITIAL_GAMMA); + } + + /** + * Creates a new SplittableRandom instance that is likely to + * generate sequences of values that are statistically independent + * of those of any other instances in the current program; and + * may, and typically does, vary across program invocations. + */ + public SplittableRandom() { // emulate seeder.split() + this.gamma = nextGamma(this.seed = seeder.addAndGet(INITIAL_GAMMA)); + } + + /** + * Constructs and returns a new SplittableRandom instance that + * shares no mutable state with this instance. However, with very + * high probability, the set of values collectively generated by + * the two objects has the same statistical properties as if the + * same quantity of values were generated by a single thread using + * a single SplittableRandom object. Either or both of the two + * objects may be further split using the {@code split()} method, + * and the same expected statistical properties apply to the + * entire set of generators constructed by such recursive + * splitting. + * + * @return the new SplittableRandom instance + */ + public SplittableRandom split() { + long s = nextSeed(); + return new SplittableRandom(s, nextGamma(s)); + } + + /** + * Returns a pseudorandom {@code int} value. + * + * @return a pseudorandom {@code int} value + */ + public int nextInt() { + return mix32(nextSeed()); + } + + /** + * Returns a pseudorandom {@code int} value between zero (inclusive) + * and the specified bound (exclusive). + * + * @param bound the bound on the random number to be returned. Must be + * positive. + * @return a pseudorandom {@code int} value between zero + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code bound} is not positive + */ + public int nextInt(int bound) { + if (bound <= 0) + throw new IllegalArgumentException(BadBound); + // Specialize internalNextInt for origin 0 + int r = mix32(nextSeed()); + int m = bound - 1; + if ((bound & m) == 0) // power of two + r &= m; + else { // reject over-represented candidates + for (int u = r >>> 1; + u + m - (r = u % bound) < 0; + u = mix32(nextSeed()) >>> 1) + ; + } + return r; + } + + /** + * Returns a pseudorandom {@code int} value between the specified + * origin (inclusive) and the specified bound (exclusive). + * + * @param origin the least value returned + * @param bound the upper bound (exclusive) + * @return a pseudorandom {@code int} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} + */ + public int nextInt(int origin, int bound) { + if (origin >= bound) + throw new IllegalArgumentException(BadRange); + return internalNextInt(origin, bound); + } + + /** + * Returns a pseudorandom {@code long} value. + * + * @return a pseudorandom {@code long} value + */ + public long nextLong() { + return mix64(nextSeed()); + } + + /** + * Returns a pseudorandom {@code long} value between zero (inclusive) + * and the specified bound (exclusive). + * + * @param bound the bound on the random number to be returned. Must be + * positive. + * @return a pseudorandom {@code long} value between zero + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code bound} is not positive + */ + public long nextLong(long bound) { + if (bound <= 0) + throw new IllegalArgumentException(BadBound); + // Specialize internalNextLong for origin 0 + long r = mix64(nextSeed()); + long m = bound - 1; + if ((bound & m) == 0L) // power of two + r &= m; + else { // reject over-represented candidates + for (long u = r >>> 1; + u + m - (r = u % bound) < 0L; + u = mix64(nextSeed()) >>> 1) + ; + } + return r; + } + + /** + * Returns a pseudorandom {@code long} value between the specified + * origin (inclusive) and the specified bound (exclusive). + * + * @param origin the least value returned + * @param bound the upper bound (exclusive) + * @return a pseudorandom {@code long} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} + */ + public long nextLong(long origin, long bound) { + if (origin >= bound) + throw new IllegalArgumentException(BadRange); + return internalNextLong(origin, bound); + } + + /** + * Returns a pseudorandom {@code double} value between zero + * (inclusive) and one (exclusive). + * + * @return a pseudorandom {@code double} value between zero + * (inclusive) and one (exclusive) + */ + public double nextDouble() { + return (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT; + } + + /** + * Returns a pseudorandom {@code double} value between 0.0 + * (inclusive) and the specified bound (exclusive). + * + * @param bound the bound on the random number to be returned. Must be + * positive. + * @return a pseudorandom {@code double} value between zero + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code bound} is not positive + */ + public double nextDouble(double bound) { + if (!(bound > 0.0)) + throw new IllegalArgumentException(BadBound); + double result = (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT * bound; + return (result < bound) ? result : // correct for rounding + Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); + } + + /** + * Returns a pseudorandom {@code double} value between the specified + * origin (inclusive) and bound (exclusive). + * + * @param origin the least value returned + * @param bound the upper bound + * @return a pseudorandom {@code double} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} + */ + public double nextDouble(double origin, double bound) { + if (!(origin < bound)) + throw new IllegalArgumentException(BadRange); + return internalNextDouble(origin, bound); + } + + /** + * Returns a pseudorandom {@code boolean} value. + * + * @return a pseudorandom {@code boolean} value + */ + public boolean nextBoolean() { + return mix32(nextSeed()) < 0; + } + + // stream methods, coded in a way intended to better isolate for + // maintenance purposes the small differences across forms. + + /** + * Returns a stream producing the given {@code streamSize} number + * of pseudorandom {@code int} values from this generator and/or + * one split from it. + * + * @param streamSize the number of values to generate + * @return a stream of pseudorandom {@code int} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + */ + public IntStream ints(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, streamSize, Integer.MAX_VALUE, 0), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code int} + * values from this generator and/or one split from it. + * + * @implNote This method is implemented to be equivalent to {@code + * ints(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code int} values + */ + public IntStream ints() { + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, Long.MAX_VALUE, Integer.MAX_VALUE, 0), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number + * of pseudorandom {@code int} values, each conforming to the + * given origin and bound. + * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code int} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero, or {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + public IntStream ints(long streamSize, int randomNumberOrigin, + int randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * int} values, each conforming to the given origin and bound. + * + * @implNote This method is implemented to be equivalent to {@code + * ints(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code int} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + public IntStream ints(int randomNumberOrigin, int randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.intStream + (new RandomIntsSpliterator + (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number + * of pseudorandom {@code long} values from this generator and/or + * one split from it. + * + * @param streamSize the number of values to generate + * @return a stream of pseudorandom {@code long} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + */ + public LongStream longs(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, streamSize, Long.MAX_VALUE, 0L), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * long} values from this generator and/or one split from it. + * + * @implNote This method is implemented to be equivalent to {@code + * longs(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code long} values + */ + public LongStream longs() { + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, Long.MAX_VALUE, Long.MAX_VALUE, 0L), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code long} values, each conforming to the + * given origin and bound. + * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code long} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero, or {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + public LongStream longs(long streamSize, long randomNumberOrigin, + long randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * long} values, each conforming to the given origin and bound. + * + * @implNote This method is implemented to be equivalent to {@code + * longs(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code long} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + public LongStream longs(long randomNumberOrigin, long randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.longStream + (new RandomLongsSpliterator + (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code double} values, each between zero + * (inclusive) and one (exclusive). + * + * @param streamSize the number of values to generate + * @return a stream of {@code double} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + */ + public DoubleStream doubles(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, streamSize, Double.MAX_VALUE, 0.0), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * double} values, each between zero (inclusive) and one + * (exclusive). + * + * @implNote This method is implemented to be equivalent to {@code + * doubles(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code double} values + */ + public DoubleStream doubles() { + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, Long.MAX_VALUE, Double.MAX_VALUE, 0.0), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code double} values, each conforming to the + * given origin and bound. + * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code double} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + public DoubleStream doubles(long streamSize, double randomNumberOrigin, + double randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (!(randomNumberOrigin < randomNumberBound)) + throw new IllegalArgumentException(BadRange); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * double} values, each conforming to the given origin and bound. + * + * @implNote This method is implemented to be equivalent to {@code + * doubles(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code double} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) { + if (!(randomNumberOrigin < randomNumberBound)) + throw new IllegalArgumentException(BadRange); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (this, 0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Spliterator for int streams. We multiplex the four int + * versions into one class by treating a bound less than origin as + * unbounded, and also by treating "infinite" as equivalent to + * Long.MAX_VALUE. For splits, it uses the standard divide-by-two + * approach. The long and double versions of this class are + * identical except for types. + */ + static final class RandomIntsSpliterator implements Spliterator.OfInt { + final SplittableRandom rng; + long index; + final long fence; + final int origin; + final int bound; + RandomIntsSpliterator(SplittableRandom rng, long index, long fence, + int origin, int bound) { + this.rng = rng; this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomIntsSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomIntsSpliterator(rng.split(), i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(IntConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(rng.internalNextInt(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(IntConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + SplittableRandom r = rng; + int o = origin, b = bound; + do { + consumer.accept(r.internalNextInt(o, b)); + } while (++i < f); + } + } + } + + /** + * Spliterator for long streams. + */ + static final class RandomLongsSpliterator implements Spliterator.OfLong { + final SplittableRandom rng; + long index; + final long fence; + final long origin; + final long bound; + RandomLongsSpliterator(SplittableRandom rng, long index, long fence, + long origin, long bound) { + this.rng = rng; this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomLongsSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomLongsSpliterator(rng.split(), i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(LongConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(rng.internalNextLong(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(LongConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + SplittableRandom r = rng; + long o = origin, b = bound; + do { + consumer.accept(r.internalNextLong(o, b)); + } while (++i < f); + } + } + + } + + /** + * Spliterator for double streams. + */ + static final class RandomDoublesSpliterator implements Spliterator.OfDouble { + final SplittableRandom rng; + long index; + final long fence; + final double origin; + final double bound; + RandomDoublesSpliterator(SplittableRandom rng, long index, long fence, + double origin, double bound) { + this.rng = rng; this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomDoublesSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomDoublesSpliterator(rng.split(), i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(DoubleConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(rng.internalNextDouble(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(DoubleConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + SplittableRandom r = rng; + double o = origin, b = bound; + do { + consumer.accept(r.internalNextDouble(o, b)); + } while (++i < f); + } + } + } + +}
--- a/src/share/classes/java/util/TreeMap.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/TreeMap.java Wed Aug 14 15:53:13 2013 -0700 @@ -790,8 +790,19 @@ /** * Returns a {@link Set} view of the keys contained in this map. - * The set's iterator returns the keys in ascending order. - * The set is backed by the map, so changes to the map are + * + * <p>The set's iterator returns the keys in ascending order. + * The set's spliterator is + * <em><a href="Spliterator.html#binding">late-binding</a></em>, + * <em>fail-fast</em>, and additionally reports {@link Spliterator#SORTED} + * and {@link Spliterator#ORDERED} with an encounter order that is ascending + * key order. The spliterator's comparator (see + * {@link java.util.Spliterator#getComparator()}) is {@code null} if + * the tree map's comparator (see {@link #comparator()}) is {@code null}. + * Otherwise, the spliterator's comparator is the same as or imposes the + * same total ordering as the tree map's comparator. + * + * <p>The set is backed by the map, so changes to the map are * reflected in the set, and vice-versa. If the map is modified * while an iteration over the set is in progress (except through * the iterator's own {@code remove} operation), the results of @@ -823,9 +834,15 @@ /** * Returns a {@link Collection} view of the values contained in this map. - * The collection's iterator returns the values in ascending order - * of the corresponding keys. - * The collection is backed by the map, so changes to the map are + * + * <p>The collection's iterator returns the values in ascending order + * of the corresponding keys. The collection's spliterator is + * <em><a href="Spliterator.html#binding">late-binding</a></em>, + * <em>fail-fast</em>, and additionally reports {@link Spliterator#ORDERED} + * with an encounter order that is ascending order of the corresponding + * keys. + * + * <p>The collection is backed by the map, so changes to the map are * reflected in the collection, and vice-versa. If the map is * modified while an iteration over the collection is in progress * (except through the iterator's own {@code remove} operation), @@ -843,8 +860,15 @@ /** * Returns a {@link Set} view of the mappings contained in this map. - * The set's iterator returns the entries in ascending key order. - * The set is backed by the map, so changes to the map are + * + * <p>The set's iterator returns the entries in ascending key order. The + * sets's spliterator is + * <em><a href="Spliterator.html#binding">late-binding</a></em>, + * <em>fail-fast</em>, and additionally reports {@link Spliterator#SORTED} and + * {@link Spliterator#ORDERED} with an encounter order that is ascending key + * order. + * + * <p>The set is backed by the map, so changes to the map are * reflected in the set, and vice-versa. If the map is modified * while an iteration over the set is in progress (except through * the iterator's own {@code remove} operation, or through the @@ -2944,16 +2968,11 @@ @Override public Comparator<Map.Entry<K, V>> getComparator() { - // Since SORTED is reported and Map.Entry elements are not comparable - // then a non-null comparator needs to be returned + // Adapt or create a key-based comparator if (tree.comparator != null) { - // Adapt the existing non-null comparator to compare entries - // by key return Map.Entry.comparingByKey(tree.comparator); } else { - // Return a comparator of entries by key, with K assumed to be - // of Comparable return (Comparator<Map.Entry<K, V>> & Serializable) (e1, e2) -> { @SuppressWarnings("unchecked") Comparable<? super K> k1 = (Comparable<? super K>) e1.getKey();
--- a/src/share/classes/java/util/TreeSet.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/TreeSet.java Wed Aug 14 15:53:13 2013 -0700 @@ -533,6 +533,25 @@ tm.readTreeSet(size, s, PRESENT); } + /** + * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em> + * and <em>fail-fast</em> {@link Spliterator} over the elements in this + * set. + * + * <p>The {@code Spliterator} reports {@link Spliterator#SIZED}, + * {@link Spliterator#DISTINCT}, {@link Spliterator#SORTED}, and + * {@link Spliterator#ORDERED}. Overriding implementations should document + * the reporting of additional characteristic values. + * + * <p>The spliterator's comparator (see + * {@link java.util.Spliterator#getComparator()}) is {@code null} if + * the tree set's comparator (see {@link #comparator()}) is {@code null}. + * Otherwise, the spliterator's comparator is the same as or imposes the + * same total ordering as the tree set's comparator. + * + * @return a {@code Spliterator} over the elements in this set + * @since 1.8 + */ public Spliterator<E> spliterator() { return TreeMap.keySpliteratorFor(m); }
--- a/src/share/classes/java/util/Vector.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/Vector.java Wed Aug 14 15:53:13 2013 -0700 @@ -1323,6 +1323,19 @@ modCount++; } + /** + * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em> + * and <em>fail-fast</em> {@link Spliterator} over the elements in this + * list. + * + * <p>The {@code Spliterator} reports {@link Spliterator#SIZED}, + * {@link Spliterator#SUBSIZED}, and {@link Spliterator#ORDERED}. + * Overriding implementations should document the reporting of additional + * characteristic values. + * + * @return a {@code Spliterator} over the elements in this list + * @since 1.8 + */ @Override public Spliterator<E> spliterator() { return new VectorSpliterator<>(this, null, 0, -1, 0);
--- a/src/share/classes/java/util/concurrent/ArrayBlockingQueue.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/ArrayBlockingQueue.java Wed Aug 14 15:53:13 2013 -0700 @@ -1396,6 +1396,19 @@ // } } + /** + * Creates a {@link Spliterator} over the elements in this queue. + * + * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT}, + * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}. + * + * @implNote + * The {@code Spliterator} implements {@code trySplit} to permit limited + * parallelism.. + * + * @return a {@code Spliterator} over the elements in this queue + * @since 1.8 + */ public Spliterator<E> spliterator() { return Spliterators.spliterator (this, Spliterator.ORDERED | Spliterator.NONNULL |
--- a/src/share/classes/java/util/concurrent/ConcurrentHashMap.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/ConcurrentHashMap.java Wed Aug 14 15:53:13 2013 -0700 @@ -1200,11 +1200,14 @@ * operations. It does not support the {@code add} or * {@code addAll} operations. * - * <p>The view's {@code iterator} is a "weakly consistent" iterator - * that will never throw {@link ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. + * <p>The view's {@code iterator} and {@code spliterator} are a + * "weakly consistent": they will never throw + * {@link ConcurrentModificationException}; are guaranteed to traverse + * elements as they existed upon construction; and may (but is not + * guaranteed to) reflect any modifications subsequent to construction. + * + * <p>The view's {@code spliterator} reports {@link Spliterator#CONCURRENT}, + * {@link Spliterator#DISTINCT}, and {@link Spliterator#NONNULL}. * * @return the set view */ @@ -1223,11 +1226,14 @@ * {@code retainAll}, and {@code clear} operations. It does not * support the {@code add} or {@code addAll} operations. * - * <p>The view's {@code iterator} is a "weakly consistent" iterator - * that will never throw {@link ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. + * <p>The view's {@code iterator} and {@code spliterator} are a + * "weakly consistent": they will never throw + * {@link ConcurrentModificationException}; are guaranteed to traverse + * elements as they existed upon construction; and may (but is not + * guaranteed to) reflect any modifications subsequent to construction. + * + * <p>The view's {@code spliterator} reports {@link Spliterator#CONCURRENT} + * and {@link Spliterator#NONNULL}. * * @return the collection view */ @@ -1245,11 +1251,14 @@ * {@code removeAll}, {@code retainAll}, and {@code clear} * operations. * - * <p>The view's {@code iterator} is a "weakly consistent" iterator - * that will never throw {@link ConcurrentModificationException}, - * and guarantees to traverse elements as they existed upon - * construction of the iterator, and may (but is not guaranteed to) - * reflect any modifications subsequent to construction. + * <p>The view's {@code iterator} and {@code spliterator} are a + * "weakly consistent": they will never throw + * {@link ConcurrentModificationException}; are guaranteed to traverse + * elements as they existed upon construction; and may (but is not + * guaranteed to) reflect any modifications subsequent to construction. + * + * <p>The view's {@code spliterator} reports {@link Spliterator#CONCURRENT}, + * {@link Spliterator#DISTINCT}, and {@link Spliterator#NONNULL}. * * @return the set view */
--- a/src/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Wed Aug 14 15:53:13 2013 -0700 @@ -1493,6 +1493,19 @@ } } + /** + * Creates a {@link Spliterator} over the elements in this deque. + * + * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT}, + * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}. + * + * @implNote + * The {@code Spliterator} implements {@code trySplit} to permit limited + * parallelism.. + * + * @return a {@code Spliterator} over the elements in this deque + * @since 1.8 + */ public Spliterator<E> spliterator() { return new CLDSpliterator<E>(this); }
--- a/src/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Wed Aug 14 15:53:13 2013 -0700 @@ -881,6 +881,20 @@ } } + /** + * Creates a {@link Spliterator} over the elements in this queue. + * + * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT}, + * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}. + * + * @implNote + * The {@code Spliterator} implements {@code trySplit} to permit limited + * parallelism.. + * + * @return a {@code Spliterator} over the elements in this queue + * @since 1.8 + */ + @Override public Spliterator<E> spliterator() { return new CLQSpliterator<E>(this); }
--- a/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Wed Aug 14 15:53:13 2013 -0700 @@ -34,6 +34,7 @@ */ package java.util.concurrent; +import java.io.Serializable; import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.AbstractSet; @@ -1795,8 +1796,18 @@ /** * Returns a {@link NavigableSet} view of the keys contained in this map. - * The set's iterator returns the keys in ascending order. - * The set is backed by the map, so changes to the map are + * + * <p>The set's iterator returns the keys in ascending order. + * The set's spliterator additionally reports {@link Spliterator#CONCURRENT}, + * {@link Spliterator#NONNULL}, {@link Spliterator#SORTED} and + * {@link Spliterator#ORDERED} with an encounter order that is ascending + * key order. The spliterator's comparator (see + * {@link java.util.Spliterator#getComparator()}) is {@code null} if + * the map's comparator (see {@link #comparator()}) is {@code null}. + * Otherwise, the spliterator's comparator is the same as or imposes the + * same total ordering as the map's comparator. + * + * <p>The set is backed by the map, so changes to the map are * reflected in the set, and vice-versa. The set supports element * removal, which removes the corresponding mapping from the map, * via the {@code Iterator.remove}, {@code Set.remove}, @@ -1804,10 +1815,10 @@ * operations. It does not support the {@code add} or {@code addAll} * operations. * - * <p>The view's {@code iterator} is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException - * ConcurrentModificationException}, and guarantees to traverse elements - * as they existed upon construction of the iterator, and may (but is not + * <p>The view's {@code iterator} and {@code spliterator} are a + * "weakly consistent": they will never throw + * {@link java.util.ConcurrentModificationException}; are guaranteed to + * traverse elements as they existed upon construction; and may (but is not * guaranteed to) reflect any modifications subsequent to construction. * * <p>This method is equivalent to method {@code navigableKeySet}. @@ -1826,9 +1837,14 @@ /** * Returns a {@link Collection} view of the values contained in this map. - * The collection's iterator returns the values in ascending order - * of the corresponding keys. - * The collection is backed by the map, so changes to the map are + * + * <p>The collection's iterator returns the values in ascending order + * of the corresponding keys. The collections's spliterator additionally + * reports {@link Spliterator#CONCURRENT}, {@link Spliterator#NONNULL}, and + * {@link Spliterator#ORDERED} with an encounter order that is ascending + * order of the corresponding keys. + * + * <p>The collection is backed by the map, so changes to the map are * reflected in the collection, and vice-versa. The collection * supports element removal, which removes the corresponding * mapping from the map, via the {@code Iterator.remove}, @@ -1836,10 +1852,10 @@ * {@code retainAll} and {@code clear} operations. It does not * support the {@code add} or {@code addAll} operations. * - * <p>The view's {@code iterator} is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException - * ConcurrentModificationException}, and guarantees to traverse elements - * as they existed upon construction of the iterator, and may (but is not + * <p>The view's {@code iterator} and {@code spliterator} are a + * "weakly consistent": they will never throw + * {@link java.util.ConcurrentModificationException}; are guaranteed to + * traverse elements as they existed upon construction; and may (but is not * guaranteed to) reflect any modifications subsequent to construction. */ public Collection<V> values() { @@ -1849,8 +1865,14 @@ /** * Returns a {@link Set} view of the mappings contained in this map. - * The set's iterator returns the entries in ascending key order. - * The set is backed by the map, so changes to the map are + * + * <p>The set's iterator returns the entries in ascending key order. The + * set's spliterator additionally reports {@link Spliterator#CONCURRENT}, + * {@link Spliterator#NONNULL}, {@link Spliterator#SORTED}, and + * {@link Spliterator#ORDERED} with an encounter order that is ascending + * key order. + * + * <p>The set is backed by the map, so changes to the map are * reflected in the set, and vice-versa. The set supports element * removal, which removes the corresponding mapping from the map, * via the {@code Iterator.remove}, {@code Set.remove}, @@ -1858,15 +1880,15 @@ * operations. It does not support the {@code add} or * {@code addAll} operations. * - * <p>The view's {@code iterator} is a "weakly consistent" iterator that - * will never throw {@link java.util.ConcurrentModificationException - * ConcurrentModificationException}, and guarantees to traverse elements - * as they existed upon construction of the iterator, and may (but is not + * <p>The view's {@code iterator} and {@code spliterator} are a + * "weakly consistent": they will never throw + * {@link java.util.ConcurrentModificationException}; are guaranteed to + * traverse elements as they existed upon construction; and may (but is not * guaranteed to) reflect any modifications subsequent to construction. * - * <p>The {@code Map.Entry} elements returned by - * {@code iterator.next()} do <em>not</em> support the - * {@code setValue} operation. + * <p>The {@code Map.Entry} elements traversed by the {@code iterator} + * or {@code spliterator} do <em>not</em> support the {@code setValue} + * operation. * * @return a set view of the mappings contained in this map, * sorted in ascending key order @@ -3434,7 +3456,8 @@ } public int characteristics() { - return Spliterator.CONCURRENT | Spliterator.NONNULL; + return Spliterator.CONCURRENT | Spliterator.ORDERED | + Spliterator.NONNULL; } } @@ -3528,8 +3551,17 @@ } public final Comparator<Map.Entry<K,V>> getComparator() { - return comparator == null ? null : - Map.Entry.comparingByKey(comparator); + // Adapt or create a key-based comparator + if (comparator != null) { + return Map.Entry.comparingByKey(comparator); + } + else { + return (Comparator<Map.Entry<K, V>> & Serializable) (e1, e2) -> { + @SuppressWarnings("unchecked") + Comparable<? super K> k1 = (Comparable<? super K>) e1.getKey(); + return k1.compareTo(e2.getKey()); + }; + } } }
--- a/src/share/classes/java/util/concurrent/ConcurrentSkipListSet.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/ConcurrentSkipListSet.java Wed Aug 14 15:53:13 2013 -0700 @@ -480,6 +480,24 @@ return new ConcurrentSkipListSet<E>(m.descendingMap()); } + /** + * Creates a {@link Spliterator} over the elements in this set. + * + * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT}, + * {@link Spliterator#NONNULL}, {@link Spliterator#DISTINCT}, + * {@link Spliterator#SORTED}, and {@link Spliterator#ORDERED} with an + * encounter order that is ascending order. Overriding implementations + * should document the reporting of additional characteristic values. + * + * <p>The spliterator's comparator (see + * {@link java.util.Spliterator#getComparator()}) is {@code null} if + * the set's comparator (see {@link #comparator()}) is {@code null}. + * Otherwise, the spliterator's comparator is the same as or imposes the + * same total ordering as the set's comparator. + * + * @return a {@code Spliterator} over the elements in this set + * @since 1.8 + */ @SuppressWarnings("unchecked") public Spliterator<E> spliterator() { if (m instanceof ConcurrentSkipListMap)
--- a/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Wed Aug 14 15:53:13 2013 -0700 @@ -1101,6 +1101,21 @@ return new COWIterator<E>(elements, index); } + /** + * Creates a {@link Spliterator} over the elements in this list. + * + * <p>The {@code Spliterator} reports {@link Spliterator#IMMUTABLE}, + * {@link Spliterator#ORDERED}, {@link Spliterator#SIZED}, and + * {@link Spliterator#SUBSIZED}. + * + * <p>The spliterator provides a snapshot of the state of the list + * when the spliterator was constructed. No synchronization is needed while + * operating on the spliterator. The spliterator does <em>NOT</em> support + * the {@code remove}, {@code set} or {@code add} methods. + * + * @return a {@code Spliterator} over the elements in this list + * @since 1.8 + */ public Spliterator<E> spliterator() { return Spliterators.spliterator (getArray(), Spliterator.IMMUTABLE | Spliterator.ORDERED);
--- a/src/share/classes/java/util/concurrent/CopyOnWriteArraySet.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/CopyOnWriteArraySet.java Wed Aug 14 15:53:13 2013 -0700 @@ -404,6 +404,22 @@ al.forEach(action); } + /** + * Creates a {@link Spliterator} over the elements in this set in the order + * in which these elements were added. + * + * <p>The {@code Spliterator} reports {@link Spliterator#IMMUTABLE}, + * {@link Spliterator#DISTINCT}, {@link Spliterator#SIZED}, and + * {@link Spliterator#SUBSIZED}. + * + * <p>The spliterator provides a snapshot of the state of the set + * when the spliterator was constructed. No synchronization is needed while + * operating on the spliterator. The spliterator does <em>NOT</em> support + * the {@code remove}, {@code set} or {@code add} methods. + * + * @return a {@code Spliterator} over the elements in this list + * @since 1.8 + */ public Spliterator<E> spliterator() { return Spliterators.spliterator (al.getArray(), Spliterator.IMMUTABLE | Spliterator.DISTINCT);
--- a/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/LinkedBlockingDeque.java Wed Aug 14 15:53:13 2013 -0700 @@ -1270,6 +1270,19 @@ } } + /** + * Creates a {@link Spliterator} over the elements in this deque. + * + * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT}, + * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}. + * + * @implNote + * The {@code Spliterator} implements {@code trySplit} to permit limited + * parallelism.. + * + * @return a {@code Spliterator} over the elements in this deque + * @since 1.8 + */ public Spliterator<E> spliterator() { return new LBDSpliterator<E>(this); }
--- a/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/LinkedBlockingQueue.java Wed Aug 14 15:53:13 2013 -0700 @@ -973,6 +973,19 @@ } } + /** + * Creates a {@link Spliterator} over the elements in this queue. + * + * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT}, + * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}. + * + * @implNote + * The {@code Spliterator} implements {@code trySplit} to permit limited + * parallelism.. + * + * @return a {@code Spliterator} over the elements in this queue + * @since 1.8 + */ public Spliterator<E> spliterator() { return new LBQSpliterator<E>(this); }
--- a/src/share/classes/java/util/concurrent/LinkedTransferQueue.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/LinkedTransferQueue.java Wed Aug 14 15:53:13 2013 -0700 @@ -1018,6 +1018,19 @@ } } + /** + * Creates a {@link Spliterator} over the elements in this queue. + * + * <p>The {@code Spliterator} reports {@link Spliterator#CONCURRENT}, + * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}. + * + * @implNote + * The {@code Spliterator} implements {@code trySplit} to permit limited + * parallelism.. + * + * @return a {@code Spliterator} over the elements in this queue + * @since 1.8 + */ public Spliterator<E> spliterator() { return new LTQSpliterator<E>(this); }
--- a/src/share/classes/java/util/concurrent/PriorityBlockingQueue.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/PriorityBlockingQueue.java Wed Aug 14 15:53:13 2013 -0700 @@ -1005,6 +1005,18 @@ } } + /** + * Creates a {@link Spliterator} over the elements in this queue. + * + * <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and + * {@link Spliterator#NONNULL}. + * + * @implNote + * The {@code Spliterator} additionally reports {@link Spliterator#SUBSIZED}. + * + * @return a {@code Spliterator} over the elements in this queue + * @since 1.8 + */ public Spliterator<E> spliterator() { return new PBQSpliterator<E>(this, null, 0, -1); }
--- a/src/share/classes/java/util/concurrent/SynchronousQueue.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/SynchronousQueue.java Wed Aug 14 15:53:13 2013 -0700 @@ -1077,6 +1077,12 @@ public void remove() { throw new IllegalStateException(); } } + /** + * Return an empty spliterator in which calls to + * {@link java.util.Spliterator#trySplit()} always return {@code null}. + * + * @return an empty spliterator. + */ public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
--- a/src/share/classes/java/util/concurrent/ThreadLocalRandom.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/concurrent/ThreadLocalRandom.java Wed Aug 14 15:53:13 2013 -0700 @@ -1,33 +1,4 @@ /* - * 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. - */ - -/* - * This file is available under and governed by the GNU General Public - * License version 2 only, as published by the Free Software Foundation. - * However, the following notice accompanied the original version of this - * file: - * * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ @@ -37,11 +8,16 @@ import java.io.ObjectStreamField; import java.util.Random; +import java.util.Spliterator; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; import java.util.stream.DoubleStream; import java.util.stream.IntStream; import java.util.stream.LongStream; +import java.util.stream.StreamSupport; /** * A random number generator isolated to the current thread. Like the @@ -85,28 +61,26 @@ * application-level overhead and footprint of most concurrent * programs. * + * Even though this class subclasses java.util.Random, it uses the + * same basic algorithm as java.util.SplittableRandom. (See its + * internal documentation for explanations, which are not repeated + * here.) Because ThreadLocalRandoms are not splittable + * though, we use only a single 64bit gamma. + * * Because this class is in a different package than class Thread, * field access methods use Unsafe to bypass access control rules. - * The base functionality of Random methods is conveniently - * isolated in method next(bits), that just reads and writes the - * Thread field rather than its own field. However, to conform to - * the requirements of the Random superclass constructor, the - * common static ThreadLocalRandom maintains an "initialized" - * field for the sake of rejecting user calls to setSeed while - * still allowing a call from constructor. Note that - * serialization is completely unnecessary because there is only a - * static singleton. But we generate a serial form containing - * "rnd" and "initialized" fields to ensure compatibility across - * versions. + * To conform to the requirements of the Random superclass + * constructor, the common static ThreadLocalRandom maintains an + * "initialized" field for the sake of rejecting user calls to + * setSeed while still allowing a call from constructor. Note + * that serialization is completely unnecessary because there is + * only a static singleton. But we generate a serial form + * containing "rnd" and "initialized" fields to ensure + * compatibility across versions. * - * Per-thread initialization is similar to that in the no-arg - * Random constructor, but we avoid correlation among not only - * initial seeds of those created in different threads, but also - * those created using class Random itself; while at the same time - * not changing any statistical properties. So we use the same - * underlying multiplicative sequence, but start the sequence far - * away from the base version, and then merge (xor) current time - * and per-thread probe bits to generate initial values. + * Implementations of non-core methods are mostly the same as in + * SplittableRandom, that were in part derived from a previous + * version of this class. * * The nextLocalGaussian ThreadLocal supports the very rarely used * nextGaussian method by providing a holder for the second of a @@ -115,24 +89,51 @@ * but we provide identical statistical properties. */ - // same constants as Random, but must be redeclared because private - private static final long multiplier = 0x5DEECE66DL; - private static final long addend = 0xBL; - private static final long mask = (1L << 48) - 1; - private static final int PROBE_INCREMENT = 0x61c88647; - - /** Generates the basis for per-thread initial seed values */ - private static final AtomicLong seedGenerator = - new AtomicLong(1269533684904616924L); - /** Generates per-thread initialization/probe field */ private static final AtomicInteger probeGenerator = - new AtomicInteger(0xe80f8647); + new AtomicInteger(); + + /** + * The next seed for default constructors. + */ + private static final AtomicLong seedGenerator = + new AtomicLong(mix64(System.currentTimeMillis()) ^ + mix64(System.nanoTime())); + + /** + * The seed increment + */ + private static final long GAMMA = 0x9e3779b97f4a7c15L; + + /** + * The increment for generating probe values + */ + private static final int PROBE_INCREMENT = 0x9e3779b9; + + /** + * The increment of seedGenerator per new instance + */ + private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL; + + // Constants from SplittableRandom + private static final double DOUBLE_UNIT = 1.0 / (1L << 53); + private static final float FLOAT_UNIT = 1.0f / (1 << 24); /** Rarely-used holder for the second of a pair of Gaussians */ private static final ThreadLocal<Double> nextLocalGaussian = new ThreadLocal<Double>(); + private static long mix64(long z) { + z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL; + z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L; + return z ^ (z >>> 33); + } + + private static int mix32(long z) { + z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL; + return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32); + } + /** * Field used only during singleton initialization. * True when constructor completes. @@ -155,16 +156,11 @@ * rely on (static) atomic generators to initialize the values. */ static final void localInit() { - int p = probeGenerator.getAndAdd(PROBE_INCREMENT); + int p = probeGenerator.addAndGet(PROBE_INCREMENT); int probe = (p == 0) ? 1 : p; // skip 0 - long current, next; - do { // same sequence as j.u.Random but different initial value - current = seedGenerator.get(); - next = current * 181783497276652981L; - } while (!seedGenerator.compareAndSet(current, next)); - long r = next ^ ((long)probe << 32) ^ System.nanoTime(); + long seed = mix64(seedGenerator.getAndAdd(SEEDER_INCREMENT)); Thread t = Thread.currentThread(); - UNSAFE.putLong(t, SEED, r); + UNSAFE.putLong(t, SEED, seed); UNSAFE.putInt(t, PROBE, probe); } @@ -191,124 +187,267 @@ throw new UnsupportedOperationException(); } - protected int next(int bits) { + final long nextSeed() { Thread t; long r; // read and update per-thread seed - UNSAFE.putLong - (t = Thread.currentThread(), SEED, - r = (UNSAFE.getLong(t, SEED) * multiplier + addend) & mask); - return (int) (r >>> (48-bits)); + UNSAFE.putLong(t = Thread.currentThread(), SEED, + r = UNSAFE.getLong(t, SEED) + GAMMA); + return r; + } + + // We must define this, but never use it. + protected int next(int bits) { + return (int)(mix64(nextSeed()) >>> (64 - bits)); + } + + // IllegalArgumentException messages + static final String BadBound = "bound must be positive"; + static final String BadRange = "bound must be greater than origin"; + static final String BadSize = "size must be non-negative"; + + /** + * The form of nextLong used by LongStream Spliterators. If + * origin is greater than bound, acts as unbounded form of + * nextLong, else as bounded form. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final long internalNextLong(long origin, long bound) { + long r = mix64(nextSeed()); + if (origin < bound) { + long n = bound - origin, m = n - 1; + if ((n & m) == 0L) // power of two + r = (r & m) + origin; + else if (n > 0L) { // reject over-represented candidates + for (long u = r >>> 1; // ensure nonnegative + u + m - (r = u % n) < 0L; // rejection check + u = mix64(nextSeed()) >>> 1) // retry + ; + r += origin; + } + else { // range not representable as long + while (r < origin || r >= bound) + r = mix64(nextSeed()); + } + } + return r; } /** - * Returns a pseudorandom, uniformly distributed value between the - * given least value (inclusive) and bound (exclusive). + * The form of nextInt used by IntStream Spliterators. + * Exactly the same as long version, except for types. + * + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final int internalNextInt(int origin, int bound) { + int r = mix32(nextSeed()); + if (origin < bound) { + int n = bound - origin, m = n - 1; + if ((n & m) == 0) + r = (r & m) + origin; + else if (n > 0) { + for (int u = r >>> 1; + u + m - (r = u % n) < 0; + u = mix32(nextSeed()) >>> 1) + ; + r += origin; + } + else { + while (r < origin || r >= bound) + r = mix32(nextSeed()); + } + } + return r; + } + + /** + * The form of nextDouble used by DoubleStream Spliterators. * - * @param least the least value returned - * @param bound the upper bound (exclusive) - * @throws IllegalArgumentException if least greater than or equal - * to bound - * @return the next value + * @param origin the least value, unless greater than bound + * @param bound the upper bound (exclusive), must not equal origin + * @return a pseudorandom value + */ + final double internalNextDouble(double origin, double bound) { + double r = (nextLong() >>> 11) * DOUBLE_UNIT; + if (origin < bound) { + r = r * (bound - origin) + origin; + if (r >= bound) // correct for rounding + r = Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); + } + return r; + } + + /** + * Returns a pseudorandom {@code int} value. + * + * @return a pseudorandom {@code int} value */ - public int nextInt(int least, int bound) { - if (least >= bound) - throw new IllegalArgumentException(); - return nextInt(bound - least) + least; + public int nextInt() { + return mix32(nextSeed()); + } + + /** + * Returns a pseudorandom {@code int} value between zero (inclusive) + * and the specified bound (exclusive). + * + * @param bound the bound on the random number to be returned. Must be + * positive. + * @return a pseudorandom {@code int} value between zero + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code bound} is not positive + */ + public int nextInt(int bound) { + if (bound <= 0) + throw new IllegalArgumentException(BadBound); + int r = mix32(nextSeed()); + int m = bound - 1; + if ((bound & m) == 0) // power of two + r &= m; + else { // reject over-represented candidates + for (int u = r >>> 1; + u + m - (r = u % bound) < 0; + u = mix32(nextSeed()) >>> 1) + ; + } + return r; } /** - * Returns a pseudorandom, uniformly distributed value - * between 0 (inclusive) and the specified value (exclusive). + * Returns a pseudorandom {@code int} value between the specified + * origin (inclusive) and the specified bound (exclusive). * - * @param n the bound on the random number to be returned. Must be - * positive. - * @return the next value - * @throws IllegalArgumentException if n is not positive + * @param origin the least value returned + * @param bound the upper bound (exclusive) + * @return a pseudorandom {@code int} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} */ - public long nextLong(long n) { - if (n <= 0) - throw new IllegalArgumentException("n must be positive"); - // Divide n by two until small enough for nextInt. On each - // iteration (at most 31 of them but usually much less), - // randomly choose both whether to include high bit in result - // (offset) and whether to continue with the lower vs upper - // half (which makes a difference only if odd). - long offset = 0; - while (n >= Integer.MAX_VALUE) { - int bits = next(2); - long half = n >>> 1; - long nextn = ((bits & 2) == 0) ? half : n - half; - if ((bits & 1) == 0) - offset += n - nextn; - n = nextn; - } - return offset + nextInt((int) n); + public int nextInt(int origin, int bound) { + if (origin >= bound) + throw new IllegalArgumentException(BadRange); + return internalNextInt(origin, bound); + } + + /** + * Returns a pseudorandom {@code long} value. + * + * @return a pseudorandom {@code long} value + */ + public long nextLong() { + return mix64(nextSeed()); } - @Override - public IntStream ints() { - return IntStream.generate(() -> current().nextInt()); - } - - @Override - public LongStream longs() { - return LongStream.generate(() -> current().nextLong()); - } - - @Override - public DoubleStream doubles() { - return DoubleStream.generate(() -> current().nextDouble()); - } - - @Override - public DoubleStream gaussians() { - return DoubleStream.generate(() -> current().nextGaussian()); + /** + * Returns a pseudorandom {@code long} value between zero (inclusive) + * and the specified bound (exclusive). + * + * @param bound the bound on the random number to be returned. Must be + * positive. + * @return a pseudorandom {@code long} value between zero + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code bound} is not positive + */ + public long nextLong(long bound) { + if (bound <= 0) + throw new IllegalArgumentException(BadBound); + long r = mix64(nextSeed()); + long m = bound - 1; + if ((bound & m) == 0L) // power of two + r &= m; + else { // reject over-represented candidates + for (long u = r >>> 1; + u + m - (r = u % bound) < 0L; + u = mix64(nextSeed()) >>> 1) + ; + } + return r; } /** - * Returns a pseudorandom, uniformly distributed value between the - * given least value (inclusive) and bound (exclusive). + * Returns a pseudorandom {@code long} value between the specified + * origin (inclusive) and the specified bound (exclusive). * - * @param least the least value returned + * @param origin the least value returned * @param bound the upper bound (exclusive) - * @return the next value - * @throws IllegalArgumentException if least greater than or equal - * to bound + * @return a pseudorandom {@code long} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} */ - public long nextLong(long least, long bound) { - if (least >= bound) - throw new IllegalArgumentException(); - return nextLong(bound - least) + least; + public long nextLong(long origin, long bound) { + if (origin >= bound) + throw new IllegalArgumentException(BadRange); + return internalNextLong(origin, bound); + } + + /** + * Returns a pseudorandom {@code double} value between zero + * (inclusive) and one (exclusive). + * + * @return a pseudorandom {@code double} value between zero + * (inclusive) and one (exclusive) + */ + public double nextDouble() { + return (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT; } /** - * Returns a pseudorandom, uniformly distributed {@code double} value - * between 0 (inclusive) and the specified value (exclusive). + * Returns a pseudorandom {@code double} value between 0.0 + * (inclusive) and the specified bound (exclusive). * - * @param n the bound on the random number to be returned. Must be + * @param bound the bound on the random number to be returned. Must be * positive. - * @return the next value - * @throws IllegalArgumentException if n is not positive + * @return a pseudorandom {@code double} value between zero + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code bound} is not positive */ - public double nextDouble(double n) { - if (n <= 0) - throw new IllegalArgumentException("n must be positive"); - return nextDouble() * n; + public double nextDouble(double bound) { + if (!(bound > 0.0)) + throw new IllegalArgumentException(BadBound); + double result = (mix64(nextSeed()) >>> 11) * DOUBLE_UNIT * bound; + return (result < bound) ? result : // correct for rounding + Double.longBitsToDouble(Double.doubleToLongBits(bound) - 1); } /** - * Returns a pseudorandom, uniformly distributed value between the - * given least value (inclusive) and bound (exclusive). + * Returns a pseudorandom {@code double} value between the specified + * origin (inclusive) and bound (exclusive). * - * @param least the least value returned - * @param bound the upper bound (exclusive) - * @return the next value - * @throws IllegalArgumentException if least greater than or equal - * to bound + * @param origin the least value returned + * @param bound the upper bound + * @return a pseudorandom {@code double} value between the origin + * (inclusive) and the bound (exclusive) + * @throws IllegalArgumentException if {@code origin} is greater than + * or equal to {@code bound} */ - public double nextDouble(double least, double bound) { - if (least >= bound) - throw new IllegalArgumentException(); - return nextDouble() * (bound - least) + least; + public double nextDouble(double origin, double bound) { + if (!(origin < bound)) + throw new IllegalArgumentException(BadRange); + return internalNextDouble(origin, bound); + } + + /** + * Returns a pseudorandom {@code boolean} value. + * + * @return a pseudorandom {@code boolean} value + */ + public boolean nextBoolean() { + return mix32(nextSeed()) < 0; + } + + /** + * Returns a pseudorandom {@code float} value between zero + * (inclusive) and one (exclusive). + * + * @return a pseudorandom {@code float} value between zero + * (inclusive) and one (exclusive) + */ + public float nextFloat() { + return (mix32(nextSeed()) >>> 8) * FLOAT_UNIT; } public double nextGaussian() { @@ -329,6 +468,442 @@ return v1 * multiplier; } + // stream methods, coded in a way intended to better isolate for + // maintenance purposes the small differences across forms. + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code int} values. + * + * @param streamSize the number of values to generate + * @return a stream of pseudorandom {@code int} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + */ + @Override + public IntStream ints(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.intStream + (new RandomIntsSpliterator + (0L, streamSize, Integer.MAX_VALUE, 0), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code int} + * values. + * + * @implNote This method is implemented to be equivalent to {@code + * ints(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code int} values + */ + @Override + public IntStream ints() { + return StreamSupport.intStream + (new RandomIntsSpliterator + (0L, Long.MAX_VALUE, Integer.MAX_VALUE, 0), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code int} values, each conforming to the given + * origin and bound. + * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code int} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero, or {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + @Override + public IntStream ints(long streamSize, int randomNumberOrigin, + int randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.intStream + (new RandomIntsSpliterator + (0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * int} values, each conforming to the given origin and bound. + * + * @implNote This method is implemented to be equivalent to {@code + * ints(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code int} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + @Override + public IntStream ints(int randomNumberOrigin, int randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.intStream + (new RandomIntsSpliterator + (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code long} values. + * + * @param streamSize the number of values to generate + * @return a stream of pseudorandom {@code long} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + */ + @Override + public LongStream longs(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.longStream + (new RandomLongsSpliterator + (0L, streamSize, Long.MAX_VALUE, 0L), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code long} + * values. + * + * @implNote This method is implemented to be equivalent to {@code + * longs(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code long} values + */ + @Override + public LongStream longs() { + return StreamSupport.longStream + (new RandomLongsSpliterator + (0L, Long.MAX_VALUE, Long.MAX_VALUE, 0L), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code long} values, each conforming to the + * given origin and bound. + * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code long} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero, or {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + @Override + public LongStream longs(long streamSize, long randomNumberOrigin, + long randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.longStream + (new RandomLongsSpliterator + (0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * long} values, each conforming to the given origin and bound. + * + * @implNote This method is implemented to be equivalent to {@code + * longs(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code long} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + @Override + public LongStream longs(long randomNumberOrigin, long randomNumberBound) { + if (randomNumberOrigin >= randomNumberBound) + throw new IllegalArgumentException(BadRange); + return StreamSupport.longStream + (new RandomLongsSpliterator + (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code double} values, each between zero + * (inclusive) and one (exclusive). + * + * @param streamSize the number of values to generate + * @return a stream of {@code double} values + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + */ + @Override + public DoubleStream doubles(long streamSize) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (0L, streamSize, Double.MAX_VALUE, 0.0), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * double} values, each between zero (inclusive) and one + * (exclusive). + * + * @implNote This method is implemented to be equivalent to {@code + * doubles(Long.MAX_VALUE)}. + * + * @return a stream of pseudorandom {@code double} values + */ + @Override + public DoubleStream doubles() { + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (0L, Long.MAX_VALUE, Double.MAX_VALUE, 0.0), + false); + } + + /** + * Returns a stream producing the given {@code streamSize} number of + * pseudorandom {@code double} values, each conforming to the + * given origin and bound. + * + * @param streamSize the number of values to generate + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code double} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code streamSize} is + * less than zero + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + @Override + public DoubleStream doubles(long streamSize, double randomNumberOrigin, + double randomNumberBound) { + if (streamSize < 0L) + throw new IllegalArgumentException(BadSize); + if (!(randomNumberOrigin < randomNumberBound)) + throw new IllegalArgumentException(BadRange); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (0L, streamSize, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Returns an effectively unlimited stream of pseudorandom {@code + * double} values, each conforming to the given origin and bound. + * + * @implNote This method is implemented to be equivalent to {@code + * doubles(Long.MAX_VALUE, randomNumberOrigin, randomNumberBound)}. + * + * @param randomNumberOrigin the origin of each random value + * @param randomNumberBound the bound of each random value + * @return a stream of pseudorandom {@code double} values, + * each with the given origin and bound + * @throws IllegalArgumentException if {@code randomNumberOrigin} + * is greater than or equal to {@code randomNumberBound} + */ + @Override + public DoubleStream doubles(double randomNumberOrigin, double randomNumberBound) { + if (!(randomNumberOrigin < randomNumberBound)) + throw new IllegalArgumentException(BadRange); + return StreamSupport.doubleStream + (new RandomDoublesSpliterator + (0L, Long.MAX_VALUE, randomNumberOrigin, randomNumberBound), + false); + } + + /** + * Spliterator for int streams. We multiplex the four int + * versions into one class by treating a bound less than origin as + * unbounded, and also by treating "infinite" as equivalent to + * Long.MAX_VALUE. For splits, it uses the standard divide-by-two + * approach. The long and double versions of this class are + * identical except for types. + */ + static final class RandomIntsSpliterator implements Spliterator.OfInt { + long index; + final long fence; + final int origin; + final int bound; + RandomIntsSpliterator(long index, long fence, + int origin, int bound) { + this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomIntsSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomIntsSpliterator(i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(IntConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(ThreadLocalRandom.current().internalNextInt(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(IntConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + int o = origin, b = bound; + ThreadLocalRandom rng = ThreadLocalRandom.current(); + do { + consumer.accept(rng.internalNextInt(o, b)); + } while (++i < f); + } + } + } + + /** + * Spliterator for long streams. + */ + static final class RandomLongsSpliterator implements Spliterator.OfLong { + long index; + final long fence; + final long origin; + final long bound; + RandomLongsSpliterator(long index, long fence, + long origin, long bound) { + this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomLongsSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomLongsSpliterator(i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(LongConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(ThreadLocalRandom.current().internalNextLong(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(LongConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + long o = origin, b = bound; + ThreadLocalRandom rng = ThreadLocalRandom.current(); + do { + consumer.accept(rng.internalNextLong(o, b)); + } while (++i < f); + } + } + + } + + /** + * Spliterator for double streams. + */ + static final class RandomDoublesSpliterator implements Spliterator.OfDouble { + long index; + final long fence; + final double origin; + final double bound; + RandomDoublesSpliterator(long index, long fence, + double origin, double bound) { + this.index = index; this.fence = fence; + this.origin = origin; this.bound = bound; + } + + public RandomDoublesSpliterator trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomDoublesSpliterator(i, index = m, origin, bound); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + public boolean tryAdvance(DoubleConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(ThreadLocalRandom.current().internalNextDouble(origin, bound)); + index = i + 1; + return true; + } + return false; + } + + public void forEachRemaining(DoubleConsumer consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + index = f; + double o = origin, b = bound; + ThreadLocalRandom rng = ThreadLocalRandom.current(); + do { + consumer.accept(rng.internalNextDouble(o, b)); + } while (++i < f); + } + } + } + + // Within-package utilities /* @@ -401,23 +976,26 @@ */ private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("rnd", long.class), - new ObjectStreamField("initialized", boolean.class) + new ObjectStreamField("initialized", boolean.class), }; /** * Saves the {@code ThreadLocalRandom} to a stream (that is, serializes it). + * @param s the stream + * @throws java.io.IOException if an I/O error occurs */ - private void writeObject(java.io.ObjectOutputStream out) + private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { - java.io.ObjectOutputStream.PutField fields = out.putFields(); + java.io.ObjectOutputStream.PutField fields = s.putFields(); fields.put("rnd", UNSAFE.getLong(Thread.currentThread(), SEED)); fields.put("initialized", true); - out.writeFields(); + s.writeFields(); } /** * Returns the {@link #current() current} thread's {@code ThreadLocalRandom}. + * @return the {@link #current() current} thread's {@code ThreadLocalRandom} */ private Object readResolve() { return current();
--- a/src/share/classes/java/util/stream/AbstractPipeline.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/AbstractPipeline.java Wed Aug 14 15:53:13 2013 -0700 @@ -71,6 +71,9 @@ */ abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>> extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> { + private static final String MSG_STREAM_LINKED = "stream has already been operated upon or closed"; + private static final String MSG_CONSUMED = "source already consumed or closed"; + /** * Backlink to the head of the pipeline chain (self if this is the source * stage). @@ -137,6 +140,8 @@ */ private boolean sourceAnyStateful; + private Runnable sourceCloseAction; + /** * True if pipeline is parallel, otherwise the pipeline is sequential; only * valid for the source stage. @@ -195,7 +200,7 @@ */ AbstractPipeline(AbstractPipeline<?, E_IN, ?> previousStage, int opFlags) { if (previousStage.linkedOrConsumed) - throw new IllegalStateException("stream has already been operated upon"); + throw new IllegalStateException(MSG_STREAM_LINKED); previousStage.linkedOrConsumed = true; previousStage.nextStage = this; @@ -221,7 +226,7 @@ final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) { assert getOutputShape() == terminalOp.inputShape(); if (linkedOrConsumed) - throw new IllegalStateException("stream has already been operated upon"); + throw new IllegalStateException(MSG_STREAM_LINKED); linkedOrConsumed = true; return isParallel() @@ -238,7 +243,7 @@ @SuppressWarnings("unchecked") final Node<E_OUT> evaluateToArrayNode(IntFunction<E_OUT[]> generator) { if (linkedOrConsumed) - throw new IllegalStateException("stream has already been operated upon"); + throw new IllegalStateException(MSG_STREAM_LINKED); linkedOrConsumed = true; // If the last intermediate operation is stateful then @@ -266,7 +271,7 @@ throw new IllegalStateException(); if (linkedOrConsumed) - throw new IllegalStateException("stream has already been operated upon"); + throw new IllegalStateException(MSG_STREAM_LINKED); linkedOrConsumed = true; if (sourceStage.sourceSpliterator != null) { @@ -282,7 +287,7 @@ return s; } else { - throw new IllegalStateException("source already consumed"); + throw new IllegalStateException(MSG_CONSUMED); } } @@ -302,12 +307,33 @@ return (S) this; } + @Override + public void close() { + linkedOrConsumed = true; + sourceSupplier = null; + sourceSpliterator = null; + if (sourceStage.sourceCloseAction != null) { + sourceStage.sourceCloseAction.run(); + sourceStage.sourceCloseAction = null; + } + } + + @Override + public S onClose(Runnable closeHandler) { + Runnable existingHandler = sourceStage.sourceCloseAction; + sourceStage.sourceCloseAction = + (existingHandler == null) + ? closeHandler + : Streams.composeWithExceptions(existingHandler, closeHandler); + return (S) this; + } + // Primitive specialization use co-variant overrides, hence is not final @Override @SuppressWarnings("unchecked") public Spliterator<E_OUT> spliterator() { if (linkedOrConsumed) - throw new IllegalStateException("stream has already been operated upon"); + throw new IllegalStateException(MSG_STREAM_LINKED); linkedOrConsumed = true; if (this == sourceStage) { @@ -324,7 +350,7 @@ return lazySpliterator(s); } else { - throw new IllegalStateException("source already consumed"); + throw new IllegalStateException(MSG_CONSUMED); } } else { @@ -424,7 +450,7 @@ sourceStage.sourceSupplier = null; } else { - throw new IllegalStateException("source already consumed"); + throw new IllegalStateException(MSG_CONSUMED); } if (isParallel()) {
--- a/src/share/classes/java/util/stream/BaseStream.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/BaseStream.java Wed Aug 14 15:53:13 2013 -0700 @@ -24,18 +24,96 @@ */ package java.util.stream; +import java.util.Collection; +import java.util.MayHoldCloseableResource; import java.util.Iterator; import java.util.Spliterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.IntConsumer; +import java.util.function.Predicate; /** - * Base interface for stream types such as {@link Stream}, {@link IntStream}, - * etc. Contains methods common to all stream types. + * A sequence of elements supporting sequential and parallel aggregate + * operations. The following example illustrates an aggregate operation using + * {@link Stream} and {@link IntStream}: + * + * <pre>{@code + * int sum = widgets.stream() + * .filter(w -> w.getColor() == RED) + * .mapToInt(w -> w.getWeight()) + * .sum(); + * }</pre> + * + * In this example, {@code widgets} is a {@code Collection<Widget>}. We create + * a stream of {@code Widget} objects via {@link Collection#stream Collection.stream()}, + * filter it to produce a stream containing only the red widgets, and then + * transform it into a stream of {@code int} values representing the weight of + * each red widget. Then this stream is summed to produce a total weight. + * + * <p>To perform a computation, stream + * <a href="package-summary.html#StreamOps">operations</a> are composed into a + * <em>stream pipeline</em>. A stream pipeline consists of a source (which + * might be an array, a collection, a generator function, an IO channel, + * etc), zero or more <em>intermediate operations</em> (which transform a + * stream into another stream, such as {@link Stream#filter(Predicate)}), and a + * <em>terminal operation</em> (which produces a result or side-effect, such + * as {@link IntStream#sum()} or {@link IntStream#forEach(IntConsumer)}). + * Streams are lazy; computation on the source data is only performed when the + * terminal operation is initiated, and source elements are consumed only + * as needed. + * + * <p>Collections and streams, while bearing some superficial similarities, + * have different goals. Collections are primarily concerned with the efficient + * management of, and access to, their elements. By contrast, streams do not + * provide a means to directly access or manipulate their elements, and are + * instead concerned with declaratively describing their source and the + * computational operations which will be performed in aggregate on that source. + * However, if the provided stream operations do not offer the desired + * functionality, the {@link #iterator()} and {@link #spliterator()} operations + * can be used to perform a controlled traversal. * - * @param <T> type of stream elements - * @param <S> type of stream implementing {@code BaseStream} + * <p>A stream pipeline, like the "widgets" example above, can be viewed as + * a <em>query</em> on the stream source. Unless the source was explicitly + * designed for concurrent modification (such as a {@link ConcurrentHashMap}), + * unpredictable or erroneous behavior may result from modifying the stream + * source while it is being queried. + * + * <p>Most stream operations accept parameters that describe user-specified + * behavior, such as the lambda expression {@code w -> w.getWeight()} passed to + * {@code mapToInt} in the example above. Such parameters are always instances + * of a <a href="../function/package-summary.html">functional interface</a> such + * as {@link java.util.function.Function}, and are often lambda expressions or + * method references. These parameters can never be null, should not modify the + * stream source, and should be + * <a href="package-summary.html#NonInterference">effectively stateless</a> + * (their result should not depend on any state that might change during + * execution of the stream pipeline.) + * + * <p>A stream should be operated on (invoking an intermediate or terminal stream + * operation) only once. This rules out, for example, "forked" streams, where + * the same source feeds two or more pipelines, or multiple traversals of the + * same stream. A stream implementation may throw {@link IllegalStateException} + * if it detects that the stream is being reused. However, since some stream + * operations may return their receiver rather than a new stream object, it may + * not be possible to detect reuse in all cases. + * + * <p>Stream pipelines may execute either sequentially or in + * <a href="package-summary.html#Parallelism">parallel</a>. This + * execution mode is a property of the stream. Streams are created + * with an initial choice of sequential or parallel execution. (For example, + * {@link Collection#stream() Collection.stream()} creates a sequential stream, + * and {@link Collection#parallelStream() Collection.parallelStream()} creates + * a parallel one.) This choice of execution mode may be modified by the + * {@link #sequential()} or {@link #parallel()} methods, and may be queried with + * the {@link #isParallel()} method. + * + * @param <T> the type of the stream elements + * @param <S> the type of of the stream implementing {@code BaseStream} * @since 1.8 + * @see <a href="package-summary.html">java.util.stream</a> */ -public interface BaseStream<T, S extends BaseStream<T, S>> { +public interface BaseStream<T, S extends BaseStream<T, S>> + extends MayHoldCloseableResource { /** * Returns an iterator for the elements of this stream. * @@ -57,14 +135,11 @@ Spliterator<T> spliterator(); /** - * Returns whether this stream, when executed, would execute in parallel - * (assuming no further modification of the stream, such as appending - * further intermediate operations or changing its parallelism). Calling - * this method after invoking an intermediate or terminal stream operation - * method may yield unpredictable results. + * Returns whether this stream, if a terminal operation were to be executed, + * would execute in parallel. Calling this method after invoking an + * terminal stream operation method may yield unpredictable results. * * @return {@code true} if this stream would execute in parallel if executed - * without further modification otherwise {@code false} */ boolean isParallel(); @@ -95,7 +170,8 @@ /** * Returns an equivalent stream that is * <a href="package-summary.html#Ordering">unordered</a>. May return - * itself if the stream was already unordered. + * itself, either because the stream was already unordered, or because + * the underlying stream state was modified to be unordered. * * <p>This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. @@ -103,4 +179,22 @@ * @return an unordered stream */ S unordered(); + + /** + * Returns an equivalent stream with an additional close handler. Close + * handlers are run when the {@link MayHoldCloseableResource#close()} method + * is called on the stream, and are executed in the order they were + * added. All close handlers are run, even if earlier close handlers throw + * exceptions. If any close handler throws an exception, the first + * exception thrown will be relayed to the caller of {@code close()}, with + * any remaining exceptions added to that exception as suppressed exceptions. + * May return itself. + * + * <p>This is an <a href="package-summary.html#StreamOps">intermediate + * operation</a>. + * + * @param closeHandler A task to execute when the stream is closed + * @return a stream with a handler that is run if the stream is closed + */ + S onClose(Runnable closeHandler); }
--- a/src/share/classes/java/util/stream/CloseableStream.java Tue Aug 13 10:42:37 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +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 java.util.stream; - -/** - * A {@code CloseableStream} is a {@code Stream} that can be closed. - * The close method is invoked to release resources that the object is - * holding (such as open files). - * - * @param <T> The type of stream elements - * @since 1.8 - */ -public interface CloseableStream<T> extends Stream<T>, AutoCloseable { - - /** - * Closes this resource, relinquishing any underlying resources. - * This method is invoked automatically on objects managed by the - * {@code try}-with-resources statement. Does nothing if called when - * the resource has already been closed. - * - * This method does not allow throwing checked {@code Exception}s like - * {@link AutoCloseable#close() AutoCloseable.close()}. Cases where the - * close operation may fail require careful attention by implementers. It - * is strongly advised to relinquish the underlying resources and to - * internally <em>mark</em> the resource as closed. The {@code close} - * method is unlikely to be invoked more than once and so this ensures - * that the resources are released in a timely manner. Furthermore it - * reduces problems that could arise when the resource wraps, or is - * wrapped, by another resource. - * - * @see AutoCloseable#close() - */ - void close(); -}
--- a/src/share/classes/java/util/stream/Collector.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/Collector.java Wed Aug 14 15:53:13 2013 -0700 @@ -26,6 +26,7 @@ import java.util.Collections; import java.util.EnumSet; +import java.util.Objects; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; @@ -33,71 +34,74 @@ import java.util.function.Supplier; /** - * A <a href="package-summary.html#Reduction">reduction operation</a> that - * folds input elements into a mutable result container, optionally transforming + * A <a href="package-summary.html#Reduction">mutable reduction operation</a> that + * accumulates input elements into a mutable result container, optionally transforming * the accumulated result into a final representation after all input elements - * have been processed. + * have been processed. Reduction operations can be performed either sequentially + * or in parallel. * * <p>Examples of mutable reduction operations include: * accumulating elements into a {@code Collection}; concatenating * strings using a {@code StringBuilder}; computing summary information about * elements such as sum, min, max, or average; computing "pivot table" summaries - * such as "maximum valued transaction by seller", etc. Reduction operations - * can be performed either sequentially or in parallel. - * - * <p>The following are examples of using the predefined {@code Collector} - * implementations in {@link Collectors} with the {@code Stream} API to perform - * mutable reduction tasks: - * <pre>{@code - * // Accumulate names into a List - * List<String> list = people.stream().map(Person::getName).collect(Collectors.toList()); - * - * // Accumulate names into a TreeSet - * Set<String> list = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new)); - * - * // Convert elements to strings and concatenate them, separated by commas - * String joined = things.stream() - * .map(Object::toString) - * .collect(Collectors.joining(", ")); - * - * // Find highest-paid employee - * Employee highestPaid = employees.stream() - * .collect(Collectors.maxBy(Comparators.comparing(Employee::getSalary))) - * .get(); - * - * // Group employees by department - * Map<Department, List<Employee>> byDept - * = employees.stream() - * .collect(Collectors.groupingBy(Employee::getDepartment)); - * - * // Find highest-paid employee by department - * Map<Department, Optional<Employee>> highestPaidByDept - * = employees.stream() - * .collect(Collectors.groupingBy(Employee::getDepartment, - * Collectors.maxBy(Comparators.comparing(Employee::getSalary)))); - * - * // Partition students into passing and failing - * Map<Boolean, List<Student>> passingFailing = - * students.stream() - * .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD)); - * - * }</pre> + * such as "maximum valued transaction by seller", etc. The class {@link Collectors} + * provides implementations of many common mutable reductions. * * <p>A {@code Collector} is specified by four functions that work together to * accumulate entries into a mutable result container, and optionally perform - * a final transform on the result. They are: creation of a new result container, - * incorporating a new data element into a result container, combining two - * result containers into one, and performing a final transform on the container. - * The combiner function is used during parallel operations, where - * subsets of the input are accumulated into separate result - * containers, and then the subresults merged into a combined result. The - * combiner function may merge one set of subresults into the other and return - * that, or it may return a new object to describe the combined results. + * a final transform on the result. They are: <ul> + * <li>creation of a new result container ({@link #supplier()})</li> + * <li>incorporating a new data element into a result container ({@link #accumulator()})</li> + * <li>combining two result containers into one ({@link #combiner()})</li> + * <li>performing an optional final transform on the container ({@link #finisher()})</li> + * </ul> * * <p>Collectors also have a set of characteristics, such as - * {@link Characteristics#CONCURRENT}. These characteristics provide - * hints that can be used by a reduction implementation to provide better - * performance. + * {@link Characteristics#CONCURRENT}, that provide hints that can be used by a + * reduction implementation to provide better performance. + * + * <p>A sequential implementation of a reduction using a collector would + * create a single result container using the supplier function, and invoke the + * accumulator function once for each input element. A parallel implementation + * would partition the input, create a result container for each partition, + * accumulate the contents of each partition into a subresult for that partition, + * and then use the combiner function to merge the subresults into a combined + * result. + * + * <p>To ensure that sequential and parallel executions produce equivalent + * results, the collector functions must satisfy an <em>identity</em> and an + * <a href="package-summary.html#Associativity">associativity</a> constraints. + * + * <p>The identity constraint says that for any partially accumulated result, + * combining it with an empty result container must produce an equivalent + * result. That is, for a partially accumulated result {@code a} that is the + * result of any series of accumulator and combiner invocations, {@code a} must + * be equivalent to {@code combiner.apply(a, supplier.get())}. + * + * <p>The associativity constraint says that splitting the computation must + * produce an equivalent result. That is, for any input elements {@code t1} + * and {@code t2}, the results {@code r1} and {@code r2} in the computation + * below must be equivalent: + * <pre>{@code + * A a1 = supplier.get(); + * accumulator.accept(a1, t1); + * accumulator.accept(a1, t2); + * R r1 = finisher.apply(a1); // result without splitting + * + * A a2 = supplier.get(); + * accumulator.accept(a2, t1); + * A a3 = supplier.get(); + * accumulator.accept(a3, t2); + * R r2 = finisher.apply(combiner.apply(a2, a3)); // result with splitting + * } </pre> + * + * <p>For collectors that do not have the {@code UNORDERED} characteristic, + * two accumulated results {@code a1} and {@code a2} are equivalent if + * {@code finisher.apply(a1).equals(finisher.apply(a2))}. For unordered + * collectors, equivalence is relaxed to allow for non-equality related to + * differences in order. (For example, an unordered collector that accumulated + * elements to a {@code List} would consider two lists equivalent if they + * contained the same elements, ignoring order.) * * <p>Libraries that implement reduction based on {@code Collector}, such as * {@link Stream#collect(Collector)}, must adhere to the following constraints: @@ -132,6 +136,20 @@ * originating data is unordered.</li> * </ul> * + * <p>In addition to the predefined implementations in {@link Collectors}, the + * static factory methods {@link #of(Supplier, BiConsumer, BinaryOperator, Characteristics...)} + * can be used to construct collectors. For example, you could create a collector + * that accumulates widgets into into a {@code TreeSet} with: + * + * <pre>{@code + * Collector<Widget, ?, TreeSet<Widget>> intoSet = + * Collector.of(TreeSet::new, TreeSet::add, + * (left, right) -> { left.addAll(right); return left; }); + * }</pre> + * + * (This behavior is also implemented by the predefined collector + * {@link Collectors#toCollection(Supplier)}). + * * @apiNote * Performing a reduction operation with a {@code Collector} should produce a * result equivalent to: @@ -144,20 +162,28 @@ * * <p>However, the library is free to partition the input, perform the reduction * on the partitions, and then use the combiner function to combine the partial - * results to achieve a parallel reduction. Depending on the specific reduction + * results to achieve a parallel reduction. (Depending on the specific reduction * operation, this may perform better or worse, depending on the relative cost - * of the accumulator and combiner functions. + * of the accumulator and combiner functions.) + * + * <p>Collectors are designed to be <em>composed</em>; many of the methods + * in {@link Collectors} are functions that take a collector and produce + * a new collector. For example, given the following collector that computes + * the sum of the salaries of a stream of employees: * - * <p>An example of an operation that can be easily modeled by {@code Collector} - * is accumulating elements into a {@code TreeSet}. In this case, the {@code - * resultSupplier()} function is {@code () -> new Treeset<T>()}, the - * {@code accumulator} function is - * {@code (set, element) -> set.add(element) }, and the combiner - * function is {@code (left, right) -> { left.addAll(right); return left; }}. - * (This behavior is implemented by - * {@code Collectors.toCollection(TreeSet::new)}). + * <pre>{@code + * Collector<Employee, ?, Integer> summingSalaries + * = Collectors.summingInt(Employee::getSalary)) + * } </pre> * - * TODO Associativity and commutativity + * If we wanted to create a collector to tabulate the sum of salaries by + * department, we could reuse the "sum of salaries" logic using + * {@link Collectors#groupingBy(Function, Collector)}: + * + * <pre>{@code + * Collector<Employee, ?, Map<Department, Integer>> summingSalariesByDept + * = Collectors.groupingBy(Employee::getDepartment, summingSalaries); + * } </pre> * * @see Stream#collect(Collector) * @see Collectors @@ -177,25 +203,25 @@ Supplier<A> supplier(); /** - * A function that folds a new value into a mutable result container. + * A function that folds a value into a mutable result container. * - * @return a function which folds a new value into a mutable result container + * @return a function which folds a value into a mutable result container */ BiConsumer<A, T> accumulator(); /** * A function that accepts two partial results and merges them. The * combiner function may fold state from one argument into the other and - * return that, or may return a new result object. + * return that, or may return a new result container. * - * @return a function which combines two partial results into a cumulative + * @return a function which combines two partial results into a combined * result */ BinaryOperator<A> combiner(); /** * Perform the final transformation from the intermediate accumulation type - * {@code A} to the final result representation {@code R}. + * {@code A} to the final result type {@code R}. * * <p>If the characteristic {@code IDENTITY_TRANSFORM} is * set, this function may be presumed to be an identity transform with an @@ -228,12 +254,17 @@ * @param <T> The type of input elements for the new collector * @param <R> The type of intermediate accumulation result, and final result, * for the new collector + * @throws NullPointerException if any argument is null * @return the new {@code Collector} */ public static<T, R> Collector<T, R, R> of(Supplier<R> supplier, BiConsumer<R, T> accumulator, BinaryOperator<R> combiner, Characteristics... characteristics) { + Objects.requireNonNull(supplier); + Objects.requireNonNull(accumulator); + Objects.requireNonNull(combiner); + Objects.requireNonNull(characteristics); Set<Characteristics> cs = (characteristics.length == 0) ? Collectors.CH_ID : Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH, @@ -254,6 +285,7 @@ * @param <T> The type of input elements for the new collector * @param <A> The intermediate accumulation type of the new collector * @param <R> The final result type of the new collector + * @throws NullPointerException if any argument is null * @return the new {@code Collector} */ public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier, @@ -261,6 +293,11 @@ BinaryOperator<A> combiner, Function<A, R> finisher, Characteristics... characteristics) { + Objects.requireNonNull(supplier); + Objects.requireNonNull(accumulator); + Objects.requireNonNull(combiner); + Objects.requireNonNull(finisher); + Objects.requireNonNull(characteristics); Set<Characteristics> cs = Collectors.CH_NOID; if (characteristics.length > 0) { cs = EnumSet.noneOf(Characteristics.class); @@ -288,8 +325,9 @@ CONCURRENT, /** - * Indicates that the result container has no intrinsic order, such as - * a {@link Set}. + * Indicates that the collection operation does not commit to preserving + * the encounter order of input elements. (This might be true if the + * result container has no intrinsic order, such as a {@link Set}.) */ UNORDERED,
--- a/src/share/classes/java/util/stream/Collectors.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/Collectors.java Wed Aug 14 15:53:13 2013 -0700 @@ -62,37 +62,35 @@ * operations, such as accumulating elements into collections, summarizing * elements according to various criteria, etc. * - * <p>The following are examples of using the predefined {@code Collector} - * implementations in {@link Collectors} with the {@code Stream} API to perform - * mutable reduction tasks: + * <p>The following are examples of using the predefined collectors to perform + * common mutable reduction tasks: * * <pre>{@code * // Accumulate names into a List * List<String> list = people.stream().map(Person::getName).collect(Collectors.toList()); * * // Accumulate names into a TreeSet - * Set<String> list = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new)); + * Set<String> set = people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new)); * * // Convert elements to strings and concatenate them, separated by commas * String joined = things.stream() * .map(Object::toString) * .collect(Collectors.joining(", ")); * - * // Find highest-paid employee - * Employee highestPaid = employees.stream() - * .collect(Collectors.maxBy(Comparator.comparing(Employee::getSalary))) - * .get(); + * // Compute sum of salaries of employee + * int highestPaid = employees.stream() + * .collect(Collectors.summingInt(Employee::getSalary))); * * // Group employees by department * Map<Department, List<Employee>> byDept * = employees.stream() * .collect(Collectors.groupingBy(Employee::getDepartment)); * - * // Find highest-paid employee by department - * Map<Department, Optional<Employee>> highestPaidByDept + * // Compute sum of salaries by department + * Map<Department, Integer> highestPaidByDept * = employees.stream() * .collect(Collectors.groupingBy(Employee::getDepartment, - * Collectors.maxBy(Comparator.comparing(Employee::getSalary)))); + * Collectors.summingInt(Employee::getSalary))); * * // Partition students into passing and failing * Map<Boolean, List<Student>> passingFailing = @@ -101,8 +99,6 @@ * * }</pre> * - * TODO explanation of parallel collection - * * @since 1.8 */ public final class Collectors { @@ -217,7 +213,8 @@ /** * Returns a {@code Collector} that accumulates the input elements into a * new {@code List}. There are no guarantees on the type, mutability, - * serializability, or thread-safety of the {@code List} returned. + * serializability, or thread-safety of the {@code List} returned; if more + * control over the returned {@code List} is required, use {@link #toCollection(Supplier)}. * * @param <T> the type of the input elements * @return a {@code Collector} which collects all the input elements into a @@ -233,7 +230,9 @@ /** * Returns a {@code Collector} that accumulates the input elements into a * new {@code Set}. There are no guarantees on the type, mutability, - * serializability, or thread-safety of the {@code Set} returned. + * serializability, or thread-safety of the {@code Set} returned; if more + * control over the returned {@code List} is required, use + * {@link #toCollection(Supplier)}. * * <p>This is an {@link Collector.Characteristics#UNORDERED unordered} * Collector. @@ -354,6 +353,43 @@ } /** + * Adapts a {@code Collector} to perform an additional finishing + * transformation. For example, one could adapt the {@link #toList()} + * collector to always produce an immutable list with: + * <pre>{@code + * List<String> people + * = people.stream().collect(collectingAndThen(toList(), Collections::unmodifiableList)); + * }</pre> + * + * @param <T> the type of the input elements + * @param <A> intermediate accumulation type of the downstream collector + * @param <R> result type of the downstream collector + * @param <RR> result type of the resulting collector + * @param finisher a function to be applied to the final result of the downstream collector + * @param downstream a collector + * @return a collector which performs the action of the downstream collector, + * followed by an additional finishing step + */ + public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream, + Function<R,RR> finisher) { + Set<Collector.Characteristics> characteristics = downstream.characteristics(); + if (characteristics.contains(Collector.Characteristics.IDENTITY_FINISH)) { + if (characteristics.size() == 1) + characteristics = Collectors.CH_NOID; + else { + characteristics = EnumSet.copyOf(characteristics); + characteristics.remove(Collector.Characteristics.IDENTITY_FINISH); + characteristics = Collections.unmodifiableSet(characteristics); + } + } + return new CollectorImpl<>(downstream.supplier(), + downstream.accumulator(), + downstream.combiner(), + downstream.finisher().andThen(finisher), + characteristics); + } + + /** * Returns a {@code Collector} accepting elements of type {@code T} that * counts the number of input elements. If no elements are present, the * result is 0.
--- a/src/share/classes/java/util/stream/DelegatingStream.java Tue Aug 13 10:42:37 2013 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,270 +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 java.util.stream; - -import java.util.Comparator; -import java.util.Iterator; -import java.util.Objects; -import java.util.Optional; -import java.util.Spliterator; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.BinaryOperator; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.IntFunction; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.function.ToDoubleFunction; -import java.util.function.ToIntFunction; -import java.util.function.ToLongFunction; - -/** - * A {@code Stream} implementation that delegates operations to another {@code - * Stream}. - * - * @param <T> type of stream elements for this stream and underlying delegate - * stream - * - * @since 1.8 - */ -public class DelegatingStream<T> implements Stream<T> { - final private Stream<T> delegate; - - /** - * Construct a {@code Stream} that delegates operations to another {@code - * Stream}. - * - * @param delegate the underlying {@link Stream} to which we delegate all - * {@code Stream} methods - * @throws NullPointerException if the delegate is null - */ - public DelegatingStream(Stream<T> delegate) { - this.delegate = Objects.requireNonNull(delegate); - } - - // -- BaseStream methods -- - - @Override - public Spliterator<T> spliterator() { - return delegate.spliterator(); - } - - @Override - public boolean isParallel() { - return delegate.isParallel(); - } - - @Override - public Iterator<T> iterator() { - return delegate.iterator(); - } - - // -- Stream methods -- - - @Override - public Stream<T> filter(Predicate<? super T> predicate) { - return delegate.filter(predicate); - } - - @Override - public <R> Stream<R> map(Function<? super T, ? extends R> mapper) { - return delegate.map(mapper); - } - - @Override - public IntStream mapToInt(ToIntFunction<? super T> mapper) { - return delegate.mapToInt(mapper); - } - - @Override - public LongStream mapToLong(ToLongFunction<? super T> mapper) { - return delegate.mapToLong(mapper); - } - - @Override - public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) { - return delegate.mapToDouble(mapper); - } - - @Override - public <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) { - return delegate.flatMap(mapper); - } - - @Override - public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) { - return delegate.flatMapToInt(mapper); - } - - @Override - public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) { - return delegate.flatMapToLong(mapper); - } - - @Override - public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) { - return delegate.flatMapToDouble(mapper); - } - - @Override - public Stream<T> distinct() { - return delegate.distinct(); - } - - @Override - public Stream<T> sorted() { - return delegate.sorted(); - } - - @Override - public Stream<T> sorted(Comparator<? super T> comparator) { - return delegate.sorted(comparator); - } - - @Override - public void forEach(Consumer<? super T> action) { - delegate.forEach(action); - } - - @Override - public void forEachOrdered(Consumer<? super T> action) { - delegate.forEachOrdered(action); - } - - @Override - public Stream<T> peek(Consumer<? super T> consumer) { - return delegate.peek(consumer); - } - - @Override - public Stream<T> limit(long maxSize) { - return delegate.limit(maxSize); - } - - @Override - public Stream<T> substream(long startingOffset) { - return delegate.substream(startingOffset); - } - - @Override - public Stream<T> substream(long startingOffset, long endingOffset) { - return delegate.substream(startingOffset, endingOffset); - } - - @Override - public <A> A[] toArray(IntFunction<A[]> generator) { - return delegate.toArray(generator); - } - - @Override - public Object[] toArray() { - return delegate.toArray(); - } - - @Override - public T reduce(T identity, BinaryOperator<T> accumulator) { - return delegate.reduce(identity, accumulator); - } - - @Override - public Optional<T> reduce(BinaryOperator<T> accumulator) { - return delegate.reduce(accumulator); - } - - @Override - public <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, - BinaryOperator<U> combiner) { - return delegate.reduce(identity, accumulator, combiner); - } - - @Override - public <R> R collect(Supplier<R> resultFactory, - BiConsumer<R, ? super T> accumulator, - BiConsumer<R, R> combiner) { - return delegate.collect(resultFactory, accumulator, combiner); - } - - @Override - public <R, A> R collect(Collector<? super T, A, ? extends R> collector) { - return delegate.collect(collector); - } - - @Override - public Optional<T> max(Comparator<? super T> comparator) { - return delegate.max(comparator); - } - - @Override - public Optional<T> min(Comparator<? super T> comparator) { - return delegate.min(comparator); - } - - @Override - public long count() { - return delegate.count(); - } - - @Override - public boolean anyMatch(Predicate<? super T> predicate) { - return delegate.anyMatch(predicate); - } - - @Override - public boolean allMatch(Predicate<? super T> predicate) { - return delegate.allMatch(predicate); - } - - @Override - public boolean noneMatch(Predicate<? super T> predicate) { - return delegate.noneMatch(predicate); - } - - @Override - public Optional<T> findFirst() { - return delegate.findFirst(); - } - - @Override - public Optional<T> findAny() { - return delegate.findAny(); - } - - @Override - public Stream<T> unordered() { - return delegate.unordered(); - } - - @Override - public Stream<T> sequential() { - return delegate.sequential(); - } - - @Override - public Stream<T> parallel() { - return delegate.parallel(); - } -}
--- a/src/share/classes/java/util/stream/DistinctOps.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/DistinctOps.java Wed Aug 14 15:53:13 2013 -0700 @@ -101,7 +101,7 @@ if (StreamOpFlag.DISTINCT.isKnown(flags)) { return sink; } else if (StreamOpFlag.SORTED.isKnown(flags)) { - return new Sink.ChainedReference<T>(sink) { + return new Sink.ChainedReference<T, T>(sink) { boolean seenNull; T lastSeen; @@ -132,7 +132,7 @@ } }; } else { - return new Sink.ChainedReference<T>(sink) { + return new Sink.ChainedReference<T, T>(sink) { Set<T> seen; @Override
--- a/src/share/classes/java/util/stream/DoublePipeline.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/DoublePipeline.java Wed Aug 14 15:53:13 2013 -0700 @@ -191,7 +191,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Double> opWrapSink(int flags, Sink<Double> sink) { - return new Sink.ChainedDouble(sink) { + return new Sink.ChainedDouble<Double>(sink) { @Override public void accept(double t) { downstream.accept(mapper.applyAsDouble(t)); @@ -208,7 +208,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Double> opWrapSink(int flags, Sink<U> sink) { - return new Sink.ChainedDouble(sink) { + return new Sink.ChainedDouble<U>(sink) { @Override @SuppressWarnings("unchecked") public void accept(double t) { @@ -226,7 +226,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Double> opWrapSink(int flags, Sink<Integer> sink) { - return new Sink.ChainedDouble(sink) { + return new Sink.ChainedDouble<Integer>(sink) { @Override public void accept(double t) { downstream.accept(mapper.applyAsInt(t)); @@ -243,7 +243,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Double> opWrapSink(int flags, Sink<Long> sink) { - return new Sink.ChainedDouble(sink) { + return new Sink.ChainedDouble<Long>(sink) { @Override public void accept(double t) { downstream.accept(mapper.applyAsLong(t)); @@ -259,7 +259,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) { @Override Sink<Double> opWrapSink(int flags, Sink<Double> sink) { - return new Sink.ChainedDouble(sink) { + return new Sink.ChainedDouble<Double>(sink) { @Override public void begin(long size) { downstream.begin(-1); @@ -267,10 +267,11 @@ @Override public void accept(double t) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - DoubleStream result = mapper.apply(t); - if (result != null) - result.sequential().forEach(i -> downstream.accept(i)); + try (DoubleStream result = mapper.apply(t)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(i -> downstream.accept(i)); + } } }; } @@ -296,7 +297,7 @@ StreamOpFlag.NOT_SIZED) { @Override Sink<Double> opWrapSink(int flags, Sink<Double> sink) { - return new Sink.ChainedDouble(sink) { + return new Sink.ChainedDouble<Double>(sink) { @Override public void begin(long size) { downstream.begin(-1); @@ -313,16 +314,16 @@ } @Override - public final DoubleStream peek(DoubleConsumer consumer) { - Objects.requireNonNull(consumer); + public final DoubleStream peek(DoubleConsumer action) { + Objects.requireNonNull(action); return new StatelessOp<Double>(this, StreamShape.DOUBLE_VALUE, 0) { @Override Sink<Double> opWrapSink(int flags, Sink<Double> sink) { - return new Sink.ChainedDouble(sink) { + return new Sink.ChainedDouble<Double>(sink) { @Override public void accept(double t) { - consumer.accept(t); + action.accept(t); downstream.accept(t); } }; @@ -436,14 +437,14 @@ } @Override - public final <R> R collect(Supplier<R> resultFactory, + public final <R> R collect(Supplier<R> supplier, ObjDoubleConsumer<R> accumulator, BiConsumer<R, R> combiner) { BinaryOperator<R> operator = (left, right) -> { combiner.accept(left, right); return left; }; - return evaluate(ReduceOps.makeDouble(resultFactory, accumulator, operator)); + return evaluate(ReduceOps.makeDouble(supplier, accumulator, operator)); } @Override
--- a/src/share/classes/java/util/stream/DoubleStream.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/DoubleStream.java Wed Aug 14 15:53:13 2013 -0700 @@ -25,12 +25,14 @@ package java.util.stream; import java.util.Arrays; +import java.util.Collection; import java.util.DoubleSummaryStatistics; import java.util.Objects; import java.util.OptionalDouble; import java.util.PrimitiveIterator; import java.util.Spliterator; import java.util.Spliterators; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.DoubleBinaryOperator; import java.util.function.DoubleConsumer; @@ -45,40 +47,79 @@ import java.util.function.Supplier; /** - * A sequence of primitive double elements supporting sequential and parallel - * bulk operations. Streams support lazy intermediate operations (transforming - * a stream to another stream) such as {@code filter} and {@code map}, and terminal - * operations (consuming the contents of a stream to produce a result or - * side-effect), such as {@code forEach}, {@code findFirst}, and {@code - * iterator}. Once an operation has been performed on a stream, it - * is considered <em>consumed</em> and no longer usable for other operations. + * A sequence of elements supporting sequential and parallel aggregate + * operations. The following example illustrates an aggregate operation using + * {@link Stream} and {@link DoubleStream}: + * + * <pre>{@code + * double sum = widgets.stream() + * .filter(w -> w.getColor() == RED) + * .mapToDouble(w -> w.getWeight()) + * .sum(); + * }</pre> * - * <p>For sequential stream pipelines, all operations are performed in the - * <a href="package-summary.html#Ordering">encounter order</a> of the pipeline - * source, if the pipeline source has a defined encounter order. + * In this example, {@code widgets} is a {@code Collection<Widget>}. We create + * a stream of {@code Widget} objects via {@link Collection#stream Collection.stream()}, + * filter it to produce a stream containing only the red widgets, and then + * transform it into a stream of {@code double} values representing the weight of + * each red widget. Then this stream is summed to produce a total weight. + * + * <p>To perform a computation, stream + * <a href="package-summary.html#StreamOps">operations</a> are composed into a + * <em>stream pipeline</em>. A stream pipeline consists of a source (which + * might be an array, a collection, a generator function, an IO channel, + * etc), zero or more <em>intermediate operations</em> (which transform a + * stream into another stream, such as {@link DoubleStream#filter(DoublePredicate)}), and a + * <em>terminal operation</em> (which produces a result or side-effect, such + * as {@link DoubleStream#sum()} or {@link DoubleStream#forEach(DoubleConsumer)}. + * Streams are lazy; computation on the source data is only performed when the + * terminal operation is initiated, and source elements are consumed only + * as needed. * - * <p>For parallel stream pipelines, unless otherwise specified, intermediate - * stream operations preserve the <a href="package-summary.html#Ordering"> - * encounter order</a> of their source, and terminal operations - * respect the encounter order of their source, if the source - * has an encounter order. Provided that and parameters to stream operations - * satisfy the <a href="package-summary.html#NonInterference">non-interference - * requirements</a>, and excepting differences arising from the absence of - * a defined encounter order, the result of a stream pipeline should be the - * stable across multiple executions of the same operations on the same source. - * However, the timing and thread in which side-effects occur (for those - * operations which are allowed to produce side-effects, such as - * {@link #forEach(DoubleConsumer)}), are explicitly nondeterministic for parallel - * execution of stream pipelines. + * <p>Collections and streams, while bearing some superficial similarities, + * have different goals. Collections are primarily concerned with the efficient + * management of, and access to, their elements. By contrast, streams do not + * provide a means to directly access or manipulate their elements, and are + * instead concerned with declaratively describing their source and the + * computational operations which will be performed in aggregate on that source. + * However, if the provided stream operations do not offer the desired + * functionality, the {@link #iterator()} and {@link #spliterator()} operations + * can be used to perform a controlled traversal. + * + * <p>A stream pipeline, like the "widgets" example above, can be viewed as + * a <em>query</em> on the stream source. Unless the source was explicitly + * designed for concurrent modification (such as a {@link ConcurrentHashMap}), + * unpredictable or erroneous behavior may result from modifying the stream + * source while it is being queried. * - * <p>Unless otherwise noted, passing a {@code null} argument to any stream - * method may result in a {@link NullPointerException}. + * <p>Most stream operations accept parameters that describe user-specified + * behavior, such as the lambda expression {@code w -> w.getWeight()} passed to + * {@code mapToDouble} in the example above. Such parameters are always instances + * of a <a href="../function/package-summary.html">functional interface</a> such + * as {@link java.util.function.Function}, and are often lambda expressions or + * method references. These parameters can never be null, should not modify the + * stream source, and should be + * <a href="package-summary.html#NonInterference">effectively stateless</a> + * (their result should not depend on any state that might change during + * execution of the stream pipeline.) * - * @apiNote - * Streams are not data structures; they do not manage the storage for their - * elements, nor do they support access to individual elements. However, - * you can use the {@link #iterator()} or {@link #spliterator()} operations to - * perform a controlled traversal. + * <p>A stream should be operated on (invoking an intermediate or terminal stream + * operation) only once. This rules out, for example, "forked" streams, where + * the same source feeds two or more pipelines, or multiple traversals of the + * same stream. A stream implementation may throw {@link IllegalStateException} + * if it detects that the stream is being reused. However, since some stream + * operations may return their receiver rather than a new stream object, it may + * not be possible to detect reuse in all cases. + * + * <p>Stream pipelines may execute either sequentially or in + * <a href="package-summary.html#Parallelism">parallel</a>. This + * execution mode is a property of the stream. Streams are created + * with an initial choice of sequential or parallel execution. (For example, + * {@link Collection#stream() Collection.stream()} creates a sequential stream, + * and {@link Collection#parallelStream() Collection.parallelStream()} creates + * a parallel one.) This choice of execution mode may be modified by the + * {@link #sequential()} or {@link #parallel()} methods, and may be queried with + * the {@link #isParallel()} method. * * @since 1.8 * @see <a href="package-summary.html">java.util.stream</a> @@ -159,22 +200,13 @@ /** * Returns a stream consisting of the results of replacing each element of * this stream with the contents of the stream produced by applying the - * provided mapping function to each element. + * provided mapping function to each element. (If the result of the mapping + * function is {@code null}, this is treated as if the result was an empty + * stream.) * * <p>This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. * - * @apiNote - * The {@code flatMap()} operation has the effect of applying a one-to-many - * tranformation to the elements of the stream, and then flattening the - * resulting elements into a new stream. For example, if {@code orders} - * is a stream of purchase orders, and each purchase order contains a - * collection of line items, then the following produces a stream of line - * items: - * <pre>{@code - * orderStream.flatMap(order -> order.getLineItems().stream())... - * }</pre> - * * @param mapper a <a href="package-summary.html#NonInterference"> * non-interfering, stateless</a> function to apply to * each element which produces an {@code DoubleStream} of new @@ -232,12 +264,12 @@ * .collect(Collectors.toDoubleSummaryStastistics()); * }</pre> * - * @param consumer a <a href="package-summary.html#NonInterference"> + * @param action a <a href="package-summary.html#NonInterference"> * non-interfering</a> action to perform on the elements as * they are consumed from the stream * @return the new stream */ - DoubleStream peek(DoubleConsumer consumer); + DoubleStream peek(DoubleConsumer action); /** * Returns a stream consisting of the elements of this stream, truncated @@ -254,8 +286,8 @@ /** * Returns a stream consisting of the remaining elements of this stream - * after indexing {@code startInclusive} elements into the stream. If the - * {@code startInclusive} index lies past the end of this stream then an + * after discarding the first {@code startInclusive} elements of the stream. + * If this stream contains fewer than {@code startInclusive} elements then an * empty stream will be returned. * * <p>This is a <a href="package-summary.html#StreamOps">stateful @@ -269,10 +301,10 @@ /** * Returns a stream consisting of the remaining elements of this stream - * after indexing {@code startInclusive} elements into the stream and - * truncated to contain no more than {@code endExclusive - startInclusive} - * elements. If the {@code startInclusive} index lies past the end - * of this stream then an empty stream will be returned. + * after discarding the first {@code startInclusive} elements and truncating + * the result to be no longer than {@code endExclusive - startInclusive} + * elements in length. If this stream contains fewer than + * {@code startInclusive} elements then an empty stream will be returned. * * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting * stateful intermediate operation</a>. @@ -421,12 +453,12 @@ /** * Performs a <a href="package-summary.html#MutableReduction">mutable * reduction</a> operation on the elements of this stream. A mutable - * reduction is one in which the reduced value is a mutable value holder, + * reduction is one in which the reduced value is a mutable result container, * such as an {@code ArrayList}, and elements are incorporated by updating - * the state of the result, rather than by replacing the result. This + * the state of the result rather than by replacing the result. This * produces a result equivalent to: * <pre>{@code - * R result = resultFactory.get(); + * R result = supplier.get(); * for (double element : this stream) * accumulator.accept(result, element); * return result; @@ -440,7 +472,7 @@ * operation</a>. * * @param <R> type of the result - * @param resultFactory a function that creates a new result container. + * @param supplier a function that creates a new result container. * For a parallel execution, this function may be * called multiple times and must return a fresh value * each time. @@ -455,7 +487,7 @@ * @return the result of the reduction * @see Stream#collect(Supplier, BiConsumer, BiConsumer) */ - <R> R collect(Supplier<R> resultFactory, + <R> R collect(Supplier<R> supplier, ObjDoubleConsumer<R> accumulator, BiConsumer<R, R> combiner); @@ -467,12 +499,15 @@ * yield more accurate results. If any stream element is a {@code NaN} or * the sum is at any point a {@code NaN} then the sum will be {@code NaN}. * This is a special case of a - * <a href="package-summary.html#MutableReduction">reduction</a> and is + * <a href="package-summary.html#Reduction">reduction</a> and is * equivalent to: * <pre>{@code * return reduce(0, Double::sum); * }</pre> * + * <p>This is a <a href="package-summary.html#StreamOps">terminal + * operation</a>. + * * @return the sum of elements in this stream */ double sum(); @@ -483,12 +518,15 @@ * element will be {@code Double.NaN} if any stream element was NaN. Unlike * the numerical comparison operators, this method considers negative zero * to be strictly smaller than positive zero. This is a special case of a - * <a href="package-summary.html#MutableReduction">reduction</a> and is + * <a href="package-summary.html#Reduction">reduction</a> and is * equivalent to: * <pre>{@code * return reduce(Double::min); * }</pre> * + * <p>This is a <a href="package-summary.html#StreamOps">terminal + * operation</a>. + * * @return an {@code OptionalDouble} containing the minimum element of this * stream, or an empty optional if the stream is empty */ @@ -501,12 +539,15 @@ * the numerical comparison operators, this method considers negative zero * to be strictly smaller than positive zero. This is a * special case of a - * <a href="package-summary.html#MutableReduction">reduction</a> and is + * <a href="package-summary.html#Reduction">reduction</a> and is * equivalent to: * <pre>{@code * return reduce(Double::max); * }</pre> * + * <p>This is a <a href="package-summary.html#StreamOps">terminal + * operation</a>. + * * @return an {@code OptionalDouble} containing the maximum element of this * stream, or an empty optional if the stream is empty */ @@ -514,7 +555,7 @@ /** * Returns the count of elements in this stream. This is a special case of - * a <a href="package-summary.html#MutableReduction">reduction</a> and is + * a <a href="package-summary.html#Reduction">reduction</a> and is * equivalent to: * <pre>{@code * return mapToLong(e -> 1L).sum(); @@ -535,7 +576,10 @@ * magnitude tend to yield more accurate results. If any recorded value is * a {@code NaN} or the sum is at any point a {@code NaN} then the average * will be {@code NaN}. This is a special case of a - * <a href="package-summary.html#MutableReduction">reduction</a>. + * <a href="package-summary.html#Reduction">reduction</a>. + * + * <p>This is a <a href="package-summary.html#StreamOps">terminal + * operation</a>. * * @return an {@code OptionalDouble} containing the average element of this * stream, or an empty optional if the stream is empty @@ -545,7 +589,10 @@ /** * Returns a {@code DoubleSummaryStatistics} describing various summary data * about the elements of this stream. This is a special - * case of a <a href="package-summary.html#MutableReduction">reduction</a>. + * case of a <a href="package-summary.html#Reduction">reduction</a>. + * + * <p>This is a <a href="package-summary.html#StreamOps">terminal + * operation</a>. * * @return a {@code DoubleSummaryStatistics} describing various summary data * about the elements of this stream @@ -602,9 +649,8 @@ /** * Returns an {@link OptionalDouble} describing the first element of this - * stream (in the encounter order), or an empty {@code OptionalDouble} if - * the stream is empty. If the stream has no encounter order, then any - * element may be returned. + * stream, or an empty {@code OptionalDouble} if the stream is empty. If + * the stream has no encounter order, then any element may be returned. * * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting * terminal operation</a>. @@ -624,8 +670,8 @@ * <p>The behavior of this operation is explicitly nondeterministic; it is * free to select any element in the stream. This is to allow for maximal * performance in parallel operations; the cost is that multiple invocations - * on the same source may not return the same result. (If the first element - * in the encounter order is desired, use {@link #findFirst()} instead.) + * on the same source may not return the same result. (If a stable result + * is desired, use {@link #findFirst()} instead.) * * @return an {@code OptionalDouble} describing some element of this stream, * or an empty {@code OptionalDouble} if the stream is empty @@ -637,6 +683,9 @@ * Returns a {@code Stream} consisting of the elements of this stream, * boxed to {@code Double}. * + * <p>This is an <a href="package-summary.html#StreamOps">intermediate + * operation</a>. + * * @return a {@code Stream} consistent of the elements of this stream, * each boxed to a {@code Double} */ @@ -686,7 +735,7 @@ } /** - * Returns a sequential stream whose elements are the specified values. + * Returns a sequential ordered stream whose elements are the specified values. * * @param values the elements of the new stream * @return the new stream @@ -696,7 +745,7 @@ } /** - * Returns an infinite sequential {@code DoubleStream} produced by iterative + * Returns an infinite sequential ordered {@code DoubleStream} produced by iterative * application of a function {@code f} to an initial element {@code seed}, * producing a {@code Stream} consisting of {@code seed}, {@code f(seed)}, * {@code f(f(seed))}, etc. @@ -734,8 +783,8 @@ } /** - * Returns a sequential {@code DoubleStream} where each element is - * generated by an {@code DoubleSupplier}. This is suitable for generating + * Returns a sequential stream where each element is generated by + * the provided {@code DoubleSupplier}. This is suitable for generating * constant streams, streams of random elements, etc. * * @param s the {@code DoubleSupplier} for generated elements @@ -748,15 +797,16 @@ } /** - * Creates a lazy concatenated {@code DoubleStream} whose elements are all the - * elements of a first {@code DoubleStream} succeeded by all the elements of the - * second {@code DoubleStream}. The resulting stream is ordered if both + * Creates a lazily concatenated stream whose elements are all the + * elements of the first stream followed by all the elements of the + * second stream. The resulting stream is ordered if both * of the input streams are ordered, and parallel if either of the input - * streams is parallel. + * streams is parallel. When the resulting stream is closed, the close + * handlers for both input streams are invoked. * * @param a the first stream - * @param b the second stream to concatenate on to end of the first stream - * @return the concatenation of the two streams + * @param b the second stream + * @return the concatenation of the two input streams */ public static DoubleStream concat(DoubleStream a, DoubleStream b) { Objects.requireNonNull(a); @@ -764,15 +814,16 @@ Spliterator.OfDouble split = new Streams.ConcatSpliterator.OfDouble( a.spliterator(), b.spliterator()); - return StreamSupport.doubleStream(split, a.isParallel() || b.isParallel()); + DoubleStream stream = StreamSupport.doubleStream(split, a.isParallel() || b.isParallel()); + return stream.onClose(Streams.composedClose(a, b)); } /** * A mutable builder for a {@code DoubleStream}. * - * <p>A stream builder has a lifecycle, where it starts in a building - * phase, during which elements can be added, and then transitions to a - * built phase, after which elements may not be added. The built phase + * <p>A stream builder has a lifecycle, which starts in a building + * phase, during which elements can be added, and then transitions to a built + * phase, after which elements may not be added. The built phase * begins when the {@link #build()} method is called, which creates an * ordered stream whose elements are the elements that were added to the * stream builder, in the order they were added.
--- a/src/share/classes/java/util/stream/IntPipeline.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/IntPipeline.java Wed Aug 14 15:53:13 2013 -0700 @@ -189,7 +189,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Integer> opWrapSink(int flags, Sink<Long> sink) { - return new Sink.ChainedInt(sink) { + return new Sink.ChainedInt<Long>(sink) { @Override @SuppressWarnings("unchecked") public void accept(int t) { @@ -206,7 +206,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Integer> opWrapSink(int flags, Sink<Double> sink) { - return new Sink.ChainedInt(sink) { + return new Sink.ChainedInt<Double>(sink) { @Override @SuppressWarnings("unchecked") public void accept(int t) { @@ -229,7 +229,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Integer> opWrapSink(int flags, Sink<Integer> sink) { - return new Sink.ChainedInt(sink) { + return new Sink.ChainedInt<Integer>(sink) { @Override public void accept(int t) { downstream.accept(mapper.applyAsInt(t)); @@ -246,7 +246,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Integer> opWrapSink(int flags, Sink<U> sink) { - return new Sink.ChainedInt(sink) { + return new Sink.ChainedInt<U>(sink) { @Override @SuppressWarnings("unchecked") public void accept(int t) { @@ -264,7 +264,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Integer> opWrapSink(int flags, Sink<Long> sink) { - return new Sink.ChainedInt(sink) { + return new Sink.ChainedInt<Long>(sink) { @Override public void accept(int t) { downstream.accept(mapper.applyAsLong(t)); @@ -281,7 +281,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Integer> opWrapSink(int flags, Sink<Double> sink) { - return new Sink.ChainedInt(sink) { + return new Sink.ChainedInt<Double>(sink) { @Override public void accept(int t) { downstream.accept(mapper.applyAsDouble(t)); @@ -297,7 +297,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) { @Override Sink<Integer> opWrapSink(int flags, Sink<Integer> sink) { - return new Sink.ChainedInt(sink) { + return new Sink.ChainedInt<Integer>(sink) { @Override public void begin(long size) { downstream.begin(-1); @@ -305,10 +305,11 @@ @Override public void accept(int t) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - IntStream result = mapper.apply(t); - if (result != null) - result.sequential().forEach(i -> downstream.accept(i)); + try (IntStream result = mapper.apply(t)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(i -> downstream.accept(i)); + } } }; } @@ -334,7 +335,7 @@ StreamOpFlag.NOT_SIZED) { @Override Sink<Integer> opWrapSink(int flags, Sink<Integer> sink) { - return new Sink.ChainedInt(sink) { + return new Sink.ChainedInt<Integer>(sink) { @Override public void begin(long size) { downstream.begin(-1); @@ -351,16 +352,16 @@ } @Override - public final IntStream peek(IntConsumer consumer) { - Objects.requireNonNull(consumer); + public final IntStream peek(IntConsumer action) { + Objects.requireNonNull(action); return new StatelessOp<Integer>(this, StreamShape.INT_VALUE, 0) { @Override Sink<Integer> opWrapSink(int flags, Sink<Integer> sink) { - return new Sink.ChainedInt(sink) { + return new Sink.ChainedInt<Integer>(sink) { @Override public void accept(int t) { - consumer.accept(t); + action.accept(t); downstream.accept(t); } }; @@ -475,14 +476,14 @@ } @Override - public final <R> R collect(Supplier<R> resultFactory, + public final <R> R collect(Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiConsumer<R, R> combiner) { BinaryOperator<R> operator = (left, right) -> { combiner.accept(left, right); return left; }; - return evaluate(ReduceOps.makeInt(resultFactory, accumulator, operator)); + return evaluate(ReduceOps.makeInt(supplier, accumulator, operator)); } @Override
--- a/src/share/classes/java/util/stream/IntStream.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/IntStream.java Wed Aug 14 15:53:13 2013 -0700 @@ -25,6 +25,7 @@ package java.util.stream; import java.util.Arrays; +import java.util.Collection; import java.util.IntSummaryStatistics; import java.util.Objects; import java.util.OptionalDouble; @@ -32,6 +33,7 @@ import java.util.PrimitiveIterator; import java.util.Spliterator; import java.util.Spliterators; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.IntBinaryOperator; @@ -46,40 +48,79 @@ import java.util.function.Supplier; /** - * A sequence of primitive integer elements supporting sequential and parallel - * bulk operations. Streams support lazy intermediate operations (transforming - * a stream to another stream) such as {@code filter} and {@code map}, and terminal - * operations (consuming the contents of a stream to produce a result or - * side-effect), such as {@code forEach}, {@code findFirst}, and {@code - * iterator}. Once an operation has been performed on a stream, it - * is considered <em>consumed</em> and no longer usable for other operations. + * A sequence of elements supporting sequential and parallel aggregate + * operations. The following example illustrates an aggregate operation using + * {@link Stream} and {@link IntStream}: + * + * <pre>{@code + * int sum = widgets.stream() + * .filter(w -> w.getColor() == RED) + * .mapToInt(w -> w.getWeight()) + * .sum(); + * }</pre> * - * <p>For sequential stream pipelines, all operations are performed in the - * <a href="package-summary.html#Ordering">encounter order</a> of the pipeline - * source, if the pipeline source has a defined encounter order. + * In this example, {@code widgets} is a {@code Collection<Widget>}. We create + * a stream of {@code Widget} objects via {@link Collection#stream Collection.stream()}, + * filter it to produce a stream containing only the red widgets, and then + * transform it into a stream of {@code int} values representing the weight of + * each red widget. Then this stream is summed to produce a total weight. + * + * <p>To perform a computation, stream + * <a href="package-summary.html#StreamOps">operations</a> are composed into a + * <em>stream pipeline</em>. A stream pipeline consists of a source (which + * might be an array, a collection, a generator function, an IO channel, + * etc), zero or more <em>intermediate operations</em> (which transform a + * stream into another stream, such as {@link IntStream#filter(IntPredicate)}), and a + * <em>terminal operation</em> (which produces a result or side-effect, such + * as {@link IntStream#sum()} or {@link IntStream#forEach(IntConsumer)}). + * Streams are lazy; computation on the source data is only performed when the + * terminal operation is initiated, and source elements are consumed only + * as needed. * - * <p>For parallel stream pipelines, unless otherwise specified, intermediate - * stream operations preserve the <a href="package-summary.html#Ordering"> - * encounter order</a> of their source, and terminal operations - * respect the encounter order of their source, if the source - * has an encounter order. Provided that and parameters to stream operations - * satisfy the <a href="package-summary.html#NonInterference">non-interference - * requirements</a>, and excepting differences arising from the absence of - * a defined encounter order, the result of a stream pipeline should be the - * stable across multiple executions of the same operations on the same source. - * However, the timing and thread in which side-effects occur (for those - * operations which are allowed to produce side-effects, such as - * {@link #forEach(IntConsumer)}), are explicitly nondeterministic for parallel - * execution of stream pipelines. + * <p>Collections and streams, while bearing some superficial similarities, + * have different goals. Collections are primarily concerned with the efficient + * management of, and access to, their elements. By contrast, streams do not + * provide a means to directly access or manipulate their elements, and are + * instead concerned with declaratively describing their source and the + * computational operations which will be performed in aggregate on that source. + * However, if the provided stream operations do not offer the desired + * functionality, the {@link #iterator()} and {@link #spliterator()} operations + * can be used to perform a controlled traversal. + * + * <p>A stream pipeline, like the "widgets" example above, can be viewed as + * a <em>query</em> on the stream source. Unless the source was explicitly + * designed for concurrent modification (such as a {@link ConcurrentHashMap}), + * unpredictable or erroneous behavior may result from modifying the stream + * source while it is being queried. * - * <p>Unless otherwise noted, passing a {@code null} argument to any stream - * method may result in a {@link NullPointerException}. + * <p>Most stream operations accept parameters that describe user-specified + * behavior, such as the lambda expression {@code w -> w.getWeight()} passed to + * {@code mapToInt} in the example above. Such parameters are always instances + * of a <a href="../function/package-summary.html">functional interface</a> such + * as {@link java.util.function.Function}, and are often lambda expressions or + * method references. These parameters can never be null, should not modify the + * stream source, and should be + * <a href="package-summary.html#NonInterference">effectively stateless</a> + * (their result should not depend on any state that might change during + * execution of the stream pipeline.) * - * @apiNote - * Streams are not data structures; they do not manage the storage for their - * elements, nor do they support access to individual elements. However, - * you can use the {@link #iterator()} or {@link #spliterator()} operations to - * perform a controlled traversal. + * <p>A stream should be operated on (invoking an intermediate or terminal stream + * operation) only once. This rules out, for example, "forked" streams, where + * the same source feeds two or more pipelines, or multiple traversals of the + * same stream. A stream implementation may throw {@link IllegalStateException} + * if it detects that the stream is being reused. However, since some stream + * operations may return their receiver rather than a new stream object, it may + * not be possible to detect reuse in all cases. + * + * <p>Stream pipelines may execute either sequentially or in + * <a href="package-summary.html#Parallelism">parallel</a>. This + * execution mode is a property of the stream. Streams are created + * with an initial choice of sequential or parallel execution. (For example, + * {@link Collection#stream() Collection.stream()} creates a sequential stream, + * and {@link Collection#parallelStream() Collection.parallelStream()} creates + * a parallel one.) This choice of execution mode may be modified by the + * {@link #sequential()} or {@link #parallel()} methods, and may be queried with + * the {@link #isParallel()} method. * * @since 1.8 * @see <a href="package-summary.html">java.util.stream</a> @@ -160,22 +201,13 @@ /** * Returns a stream consisting of the results of replacing each element of * this stream with the contents of the stream produced by applying the - * provided mapping function to each element. + * provided mapping function to each element. (If the result of the mapping + * function is {@code null}, this is treated as if the result was an empty + * stream.) * * <p>This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. * - * @apiNote - * The {@code flatMap()} operation has the effect of applying a one-to-many - * tranformation to the elements of the stream, and then flattening the - * resulting elements into a new stream. For example, if {@code orders} - * is a stream of purchase orders, and each purchase order contains a - * collection of line items, then the following produces a stream of line - * items: - * <pre>{@code - * orderStream.flatMap(order -> order.getLineItems().stream())... - * }</pre> - * * @param mapper a <a href="package-summary.html#NonInterference"> * non-interfering, stateless</a> function to apply to * each element which produces an {@code IntStream} of new @@ -230,12 +262,12 @@ * .collect(Collectors.toIntSummaryStastistics()); * }</pre> * - * @param consumer a <a href="package-summary.html#NonInterference"> + * @param action a <a href="package-summary.html#NonInterference"> * non-interfering</a> action to perform on the elements as * they are consumed from the stream * @return the new stream */ - IntStream peek(IntConsumer consumer); + IntStream peek(IntConsumer action); /** * Returns a stream consisting of the elements of this stream, truncated @@ -252,8 +284,8 @@ /** * Returns a stream consisting of the remaining elements of this stream - * after indexing {@code startInclusive} elements into the stream. If the - * {@code startInclusive} index lies past the end of this stream then an + * after discarding the first {@code startInclusive} elements of the stream. + * If this stream contains fewer than {@code startInclusive} elements then an * empty stream will be returned. * * <p>This is a <a href="package-summary.html#StreamOps">stateful @@ -267,10 +299,10 @@ /** * Returns a stream consisting of the remaining elements of this stream - * after indexing {@code startInclusive} elements into the stream and - * truncated to contain no more than {@code endExclusive - startInclusive} - * elements. If the {@code startInclusive} index lies past the end - * of this stream then an empty stream will be returned. + * after discarding the first {@code startInclusive} elements and truncating + * the result to be no longer than {@code endExclusive - startInclusive} + * elements in length. If this stream contains fewer than + * {@code startInclusive} elements then an empty stream will be returned. * * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting * stateful intermediate operation</a>. @@ -419,12 +451,12 @@ /** * Performs a <a href="package-summary.html#MutableReduction">mutable * reduction</a> operation on the elements of this stream. A mutable - * reduction is one in which the reduced value is a mutable value holder, + * reduction is one in which the reduced value is a mutable result container, * such as an {@code ArrayList}, and elements are incorporated by updating - * the state of the result, rather than by replacing the result. This + * the state of the result rather than by replacing the result. This * produces a result equivalent to: * <pre>{@code - * R result = resultFactory.get(); + * R result = supplier.get(); * for (int element : this stream) * accumulator.accept(result, element); * return result; @@ -437,7 +469,7 @@ * operation</a>. * * @param <R> type of the result - * @param resultFactory a function that creates a new result container. + * @param supplier a function that creates a new result container. * For a parallel execution, this function may be * called multiple times and must return a fresh value * each time. @@ -452,18 +484,21 @@ * @return the result of the reduction * @see Stream#collect(Supplier, BiConsumer, BiConsumer) */ - <R> R collect(Supplier<R> resultFactory, + <R> R collect(Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiConsumer<R, R> combiner); /** * Returns the sum of elements in this stream. This is a special case - * of a <a href="package-summary.html#MutableReduction">reduction</a> + * of a <a href="package-summary.html#Reduction">reduction</a> * and is equivalent to: * <pre>{@code * return reduce(0, Integer::sum); * }</pre> * + * <p>This is a <a href="package-summary.html#StreamOps">terminal + * operation</a>. + * * @return the sum of elements in this stream */ int sum(); @@ -471,7 +506,7 @@ /** * Returns an {@code OptionalInt} describing the minimum element of this * stream, or an empty optional if this stream is empty. This is a special - * case of a <a href="package-summary.html#MutableReduction">reduction</a> + * case of a <a href="package-summary.html#Reduction">reduction</a> * and is equivalent to: * <pre>{@code * return reduce(Integer::min); @@ -479,7 +514,6 @@ * * <p>This is a <a href="package-summary.html#StreamOps">terminal operation</a>. * - * @return an {@code OptionalInt} containing the minimum element of this * stream, or an empty {@code OptionalInt} if the stream is empty */ @@ -488,7 +522,7 @@ /** * Returns an {@code OptionalInt} describing the maximum element of this * stream, or an empty optional if this stream is empty. This is a special - * case of a <a href="package-summary.html#MutableReduction">reduction</a> + * case of a <a href="package-summary.html#Reduction">reduction</a> * and is equivalent to: * <pre>{@code * return reduce(Integer::max); @@ -504,7 +538,7 @@ /** * Returns the count of elements in this stream. This is a special case of - * a <a href="package-summary.html#MutableReduction">reduction</a> and is + * a <a href="package-summary.html#Reduction">reduction</a> and is * equivalent to: * <pre>{@code * return mapToLong(e -> 1L).sum(); @@ -520,7 +554,10 @@ * Returns an {@code OptionalDouble} describing the arithmetic mean of elements of * this stream, or an empty optional if this stream is empty. This is a * special case of a - * <a href="package-summary.html#MutableReduction">reduction</a>. + * <a href="package-summary.html#Reduction">reduction</a>. + * + * <p>This is a <a href="package-summary.html#StreamOps">terminal + * operation</a>. * * @return an {@code OptionalDouble} containing the average element of this * stream, or an empty optional if the stream is empty @@ -530,7 +567,10 @@ /** * Returns an {@code IntSummaryStatistics} describing various * summary data about the elements of this stream. This is a special - * case of a <a href="package-summary.html#MutableReduction">reduction</a>. + * case of a <a href="package-summary.html#Reduction">reduction</a>. + * + * <p>This is a <a href="package-summary.html#StreamOps">terminal + * operation</a>. * * @return an {@code IntSummaryStatistics} describing various summary data * about the elements of this stream @@ -587,9 +627,8 @@ /** * Returns an {@link OptionalInt} describing the first element of this - * stream (in the encounter order), or an empty {@code OptionalInt} if the - * stream is empty. If the stream has no encounter order, then any element - * may be returned. + * stream, or an empty {@code OptionalInt} if the stream is empty. If the + * stream has no encounter order, then any element may be returned. * * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting * terminal operation</a>. @@ -609,8 +648,8 @@ * <p>The behavior of this operation is explicitly nondeterministic; it is * free to select any element in the stream. This is to allow for maximal * performance in parallel operations; the cost is that multiple invocations - * on the same source may not return the same result. (If the first element - * in the encounter order is desired, use {@link #findFirst()} instead.) + * on the same source may not return the same result. (If a stable result + * is desired, use {@link #findFirst()} instead.) * * @return an {@code OptionalInt} describing some element of this stream, or * an empty {@code OptionalInt} if the stream is empty @@ -622,6 +661,9 @@ * Returns a {@code LongStream} consisting of the elements of this stream, * converted to {@code long}. * + * <p>This is an <a href="package-summary.html#StreamOps">intermediate + * operation</a>. + * * @return a {@code LongStream} consisting of the elements of this stream, * converted to {@code long} */ @@ -631,6 +673,9 @@ * Returns a {@code DoubleStream} consisting of the elements of this stream, * converted to {@code double}. * + * <p>This is an <a href="package-summary.html#StreamOps">intermediate + * operation</a>. + * * @return a {@code DoubleStream} consisting of the elements of this stream, * converted to {@code double} */ @@ -640,6 +685,9 @@ * Returns a {@code Stream} consisting of the elements of this stream, * each boxed to an {@code Integer}. * + * <p>This is an <a href="package-summary.html#StreamOps">intermediate + * operation</a>. + * * @return a {@code Stream} consistent of the elements of this stream, * each boxed to an {@code Integer} */ @@ -688,7 +736,7 @@ } /** - * Returns a sequential stream whose elements are the specified values. + * Returns a sequential ordered stream whose elements are the specified values. * * @param values the elements of the new stream * @return the new stream @@ -698,7 +746,7 @@ } /** - * Returns an infinite sequential {@code IntStream} produced by iterative + * Returns an infinite sequential ordered {@code IntStream} produced by iterative * application of a function {@code f} to an initial element {@code seed}, * producing a {@code Stream} consisting of {@code seed}, {@code f(seed)}, * {@code f(f(seed))}, etc. @@ -736,8 +784,8 @@ } /** - * Returns a sequential {@code IntStream} where each element is - * generated by an {@code IntSupplier}. This is suitable for generating + * Returns a sequential stream where each element is generated by + * the provided {@code IntSupplier}. This is suitable for generating * constant streams, streams of random elements, etc. * * @param s the {@code IntSupplier} for generated elements @@ -750,7 +798,7 @@ } /** - * Returns a sequential {@code IntStream} from {@code startInclusive} + * Returns a sequential ordered {@code IntStream} from {@code startInclusive} * (inclusive) to {@code endExclusive} (exclusive) by an incremental step of * {@code 1}. * @@ -776,7 +824,7 @@ } /** - * Returns a sequential {@code IntStream} from {@code startInclusive} + * Returns a sequential ordered {@code IntStream} from {@code startInclusive} * (inclusive) to {@code endInclusive} (inclusive) by an incremental step of * {@code 1}. * @@ -802,15 +850,16 @@ } /** - * Creates a lazy concatenated {@code IntStream} whose elements are all the - * elements of a first {@code IntStream} succeeded by all the elements of the - * second {@code IntStream}. The resulting stream is ordered if both + * Creates a lazily concatenated stream whose elements are all the + * elements of the first stream followed by all the elements of the + * second stream. The resulting stream is ordered if both * of the input streams are ordered, and parallel if either of the input - * streams is parallel. + * streams is parallel. When the resulting stream is closed, the close + * handlers for both input streams are invoked. * * @param a the first stream - * @param b the second stream to concatenate on to end of the first stream - * @return the concatenation of the two streams + * @param b the second stream + * @return the concatenation of the two input streams */ public static IntStream concat(IntStream a, IntStream b) { Objects.requireNonNull(a); @@ -818,15 +867,16 @@ Spliterator.OfInt split = new Streams.ConcatSpliterator.OfInt( a.spliterator(), b.spliterator()); - return StreamSupport.intStream(split, a.isParallel() || b.isParallel()); + IntStream stream = StreamSupport.intStream(split, a.isParallel() || b.isParallel()); + return stream.onClose(Streams.composedClose(a, b)); } /** * A mutable builder for an {@code IntStream}. * - * <p>A stream builder has a lifecycle, where it starts in a building - * phase, during which elements can be added, and then transitions to a - * built phase, after which elements may not be added. The built phase + * <p>A stream builder has a lifecycle, which starts in a building + * phase, during which elements can be added, and then transitions to a built + * phase, after which elements may not be added. The built phase * begins when the {@link #build()} method is called, which creates an * ordered stream whose elements are the elements that were added to the * stream builder, in the order they were added.
--- a/src/share/classes/java/util/stream/LongPipeline.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/LongPipeline.java Wed Aug 14 15:53:13 2013 -0700 @@ -186,7 +186,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Long> opWrapSink(int flags, Sink<Double> sink) { - return new Sink.ChainedLong(sink) { + return new Sink.ChainedLong<Double>(sink) { @Override public void accept(long t) { downstream.accept((double) t); @@ -208,7 +208,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Long> opWrapSink(int flags, Sink<Long> sink) { - return new Sink.ChainedLong(sink) { + return new Sink.ChainedLong<Long>(sink) { @Override @SuppressWarnings("unchecked") public void accept(long t) { @@ -226,7 +226,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Long> opWrapSink(int flags, Sink<U> sink) { - return new Sink.ChainedLong(sink) { + return new Sink.ChainedLong<U>(sink) { @Override @SuppressWarnings("unchecked") public void accept(long t) { @@ -244,7 +244,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Long> opWrapSink(int flags, Sink<Integer> sink) { - return new Sink.ChainedLong(sink) { + return new Sink.ChainedLong<Integer>(sink) { @Override @SuppressWarnings("unchecked") public void accept(long t) { @@ -262,7 +262,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<Long> opWrapSink(int flags, Sink<Double> sink) { - return new Sink.ChainedLong(sink) { + return new Sink.ChainedLong<Double>(sink) { @Override public void accept(long t) { downstream.accept(mapper.applyAsDouble(t)); @@ -278,7 +278,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) { @Override Sink<Long> opWrapSink(int flags, Sink<Long> sink) { - return new Sink.ChainedLong(sink) { + return new Sink.ChainedLong<Long>(sink) { @Override public void begin(long size) { downstream.begin(-1); @@ -286,10 +286,11 @@ @Override public void accept(long t) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - LongStream result = mapper.apply(t); - if (result != null) - result.sequential().forEach(i -> downstream.accept(i)); + try (LongStream result = mapper.apply(t)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(i -> downstream.accept(i)); + } } }; } @@ -315,7 +316,7 @@ StreamOpFlag.NOT_SIZED) { @Override Sink<Long> opWrapSink(int flags, Sink<Long> sink) { - return new Sink.ChainedLong(sink) { + return new Sink.ChainedLong<Long>(sink) { @Override public void begin(long size) { downstream.begin(-1); @@ -332,16 +333,16 @@ } @Override - public final LongStream peek(LongConsumer consumer) { - Objects.requireNonNull(consumer); + public final LongStream peek(LongConsumer action) { + Objects.requireNonNull(action); return new StatelessOp<Long>(this, StreamShape.LONG_VALUE, 0) { @Override Sink<Long> opWrapSink(int flags, Sink<Long> sink) { - return new Sink.ChainedLong(sink) { + return new Sink.ChainedLong<Long>(sink) { @Override public void accept(long t) { - consumer.accept(t); + action.accept(t); downstream.accept(t); } };
--- a/src/share/classes/java/util/stream/LongStream.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/LongStream.java Wed Aug 14 15:53:13 2013 -0700 @@ -25,6 +25,7 @@ package java.util.stream; import java.util.Arrays; +import java.util.Collection; import java.util.LongSummaryStatistics; import java.util.Objects; import java.util.OptionalDouble; @@ -32,6 +33,7 @@ import java.util.PrimitiveIterator; import java.util.Spliterator; import java.util.Spliterators; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.LongBinaryOperator; @@ -46,40 +48,79 @@ import java.util.function.Supplier; /** - * A sequence of primitive long elements supporting sequential and parallel - * bulk operations. Streams support lazy intermediate operations (transforming - * a stream to another stream) such as {@code filter} and {@code map}, and terminal - * operations (consuming the contents of a stream to produce a result or - * side-effect), such as {@code forEach}, {@code findFirst}, and {@code - * iterator}. Once an operation has been performed on a stream, it - * is considered <em>consumed</em> and no longer usable for other operations. + * A sequence of elements supporting sequential and parallel aggregate + * operations. The following example illustrates an aggregate operation using + * {@link Stream} and {@link LongStream}: + * + * <pre>{@code + * long sum = widgets.stream() + * .filter(w -> w.getColor() == RED) + * .mapToLong(w -> w.getWeight()) + * .sum(); + * }</pre> * - * <p>For sequential stream pipelines, all operations are performed in the - * <a href="package-summary.html#Ordering">encounter order</a> of the pipeline - * source, if the pipeline source has a defined encounter order. + * In this example, {@code widgets} is a {@code Collection<Widget>}. We create + * a stream of {@code Widget} objects via {@link Collection#stream Collection.stream()}, + * filter it to produce a stream containing only the red widgets, and then + * transform it into a stream of {@code long} values representing the weight of + * each red widget. Then this stream is summed to produce a total weight. + * + * <p>To perform a computation, stream + * <a href="package-summary.html#StreamOps">operations</a> are composed into a + * <em>stream pipeline</em>. A stream pipeline consists of a source (which + * might be an array, a collection, a generator function, an IO channel, + * etc), zero or more <em>intermediate operations</em> (which transform a + * stream into another stream, such as {@link LongStream#filter(LongPredicate)}), and a + * <em>terminal operation</em> (which produces a result or side-effect, such + * as {@link LongStream#sum()} or {@link LongStream#forEach(LongConsumer)}). + * Streams are lazy; computation on the source data is only performed when the + * terminal operation is initiated, and source elements are consumed only + * as needed. * - * <p>For parallel stream pipelines, unless otherwise specified, intermediate - * stream operations preserve the <a href="package-summary.html#Ordering"> - * encounter order</a> of their source, and terminal operations - * respect the encounter order of their source, if the source - * has an encounter order. Provided that and parameters to stream operations - * satisfy the <a href="package-summary.html#NonInterference">non-interference - * requirements</a>, and excepting differences arising from the absence of - * a defined encounter order, the result of a stream pipeline should be the - * stable across multiple executions of the same operations on the same source. - * However, the timing and thread in which side-effects occur (for those - * operations which are allowed to produce side-effects, such as - * {@link #forEach(LongConsumer)}), are explicitly nondeterministic for parallel - * execution of stream pipelines. + * <p>Collections and streams, while bearing some superficial similarities, + * have different goals. Collections are primarily concerned with the efficient + * management of, and access to, their elements. By contrast, streams do not + * provide a means to directly access or manipulate their elements, and are + * instead concerned with declaratively describing their source and the + * computational operations which will be performed in aggregate on that source. + * However, if the provided stream operations do not offer the desired + * functionality, the {@link #iterator()} and {@link #spliterator()} operations + * can be used to perform a controlled traversal. + * + * <p>A stream pipeline, like the "widgets" example above, can be viewed as + * a <em>query</em> on the stream source. Unless the source was explicitly + * designed for concurrent modification (such as a {@link ConcurrentHashMap}), + * unpredictable or erroneous behavior may result from modifying the stream + * source while it is being queried. * - * <p>Unless otherwise noted, passing a {@code null} argument to any stream - * method may result in a {@link NullPointerException}. + * <p>Most stream operations accept parameters that describe user-specified + * behavior, such as the lambda expression {@code w -> w.getWeight()} passed to + * {@code mapToLong} in the example above. Such parameters are always instances + * of a <a href="../function/package-summary.html">functional interface</a> such + * as {@link java.util.function.Function}, and are often lambda expressions or + * method references. These parameters can never be null, should not modify the + * stream source, and should be + * <a href="package-summary.html#NonInterference">effectively stateless</a> + * (their result should not depend on any state that might change during + * execution of the stream pipeline.) * - * @apiNote - * Streams are not data structures; they do not manage the storage for their - * elements, nor do they support access to individual elements. However, - * you can use the {@link #iterator()} or {@link #spliterator()} operations to - * perform a controlled traversal. + * <p>A stream should be operated on (invoking an intermediate or terminal stream + * operation) only once. This rules out, for example, "forked" streams, where + * the same source feeds two or more pipelines, or multiple traversals of the + * same stream. A stream implementation may throw {@link IllegalStateException} + * if it detects that the stream is being reused. However, since some stream + * operations may return their receiver rather than a new stream object, it may + * not be possible to detect reuse in all cases. + * + * <p>Stream pipelines may execute either sequentially or in + * <a href="package-summary.html#Parallelism">parallel</a>. This + * execution mode is a property of the stream. Streams are created + * with an initial choice of sequential or parallel execution. (For example, + * {@link Collection#stream() Collection.stream()} creates a sequential stream, + * and {@link Collection#parallelStream() Collection.parallelStream()} creates + * a parallel one.) This choice of execution mode may be modified by the + * {@link #sequential()} or {@link #parallel()} methods, and may be queried with + * the {@link #isParallel()} method. * * @since 1.8 * @see <a href="package-summary.html">java.util.stream</a> @@ -160,22 +201,13 @@ /** * Returns a stream consisting of the results of replacing each element of * this stream with the contents of the stream produced by applying the - * provided mapping function to each element. + * provided mapping function to each element. (If the result of the mapping + * function is {@code null}, this is treated as if the result was an empty + * stream.) * * <p>This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. * - * @apiNote - * The {@code flatMap()} operation has the effect of applying a one-to-many - * tranformation to the elements of the stream, and then flattening the - * resulting elements into a new stream. For example, if {@code orders} - * is a stream of purchase orders, and each purchase order contains a - * collection of line items, then the following produces a stream of line - * items: - * <pre>{@code - * orderStream.flatMap(order -> order.getLineItems().stream())... - * }</pre> - * * @param mapper a <a href="package-summary.html#NonInterference"> * non-interfering, stateless</a> function to apply to * each element which produces an {@code LongStream} of new @@ -230,12 +262,12 @@ * .collect(Collectors.toLongSummaryStastistics()); * }</pre> * - * @param consumer a <a href="package-summary.html#NonInterference"> + * @param action a <a href="package-summary.html#NonInterference"> * non-interfering</a> action to perform on the elements as * they are consumed from the stream * @return the new stream */ - LongStream peek(LongConsumer consumer); + LongStream peek(LongConsumer action); /** * Returns a stream consisting of the elements of this stream, truncated @@ -252,8 +284,8 @@ /** * Returns a stream consisting of the remaining elements of this stream - * after indexing {@code startInclusive} elements into the stream. If the - * {@code startInclusive} index lies past the end of this stream then an + * after discarding the first {@code startInclusive} elements of the stream. + * If this stream contains fewer than {@code startInclusive} elements then an * empty stream will be returned. * * <p>This is a <a href="package-summary.html#StreamOps">stateful @@ -267,10 +299,10 @@ /** * Returns a stream consisting of the remaining elements of this stream - * after indexing {@code startInclusive} elements into the stream and - * truncated to contain no more than {@code endExclusive - startInclusive} - * elements. If the {@code startInclusive} index lies past the end - * of this stream then an empty stream will be returned. + * after discarding the first {@code startInclusive} elements and truncating + * the result to be no longer than {@code endExclusive - startInclusive} + * elements in length. If this stream contains fewer than + * {@code startInclusive} elements then an empty stream will be returned. * * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting * stateful intermediate operation</a>. @@ -419,9 +451,9 @@ /** * Performs a <a href="package-summary.html#MutableReduction">mutable * reduction</a> operation on the elements of this stream. A mutable - * reduction is one in which the reduced value is a mutable value holder, + * reduction is one in which the reduced value is a mutable result container, * such as an {@code ArrayList}, and elements are incorporated by updating - * the state of the result, rather than by replacing the result. This + * the state of the result rather than by replacing the result. This * produces a result equivalent to: * <pre>{@code * R result = resultFactory.get(); @@ -458,12 +490,15 @@ /** * Returns the sum of elements in this stream. This is a special case - * of a <a href="package-summary.html#MutableReduction">reduction</a> + * of a <a href="package-summary.html#Reduction">reduction</a> * and is equivalent to: * <pre>{@code * return reduce(0, Long::sum); * }</pre> * + * <p>This is a <a href="package-summary.html#StreamOps">terminal + * operation</a>. + * * @return the sum of elements in this stream */ long sum(); @@ -471,7 +506,7 @@ /** * Returns an {@code OptionalLong} describing the minimum element of this * stream, or an empty optional if this stream is empty. This is a special - * case of a <a href="package-summary.html#MutableReduction">reduction</a> + * case of a <a href="package-summary.html#Reduction">reduction</a> * and is equivalent to: * <pre>{@code * return reduce(Long::min); @@ -479,7 +514,6 @@ * * <p>This is a <a href="package-summary.html#StreamOps">terminal operation</a>. * - * @return an {@code OptionalLong} containing the minimum element of this * stream, or an empty {@code OptionalLong} if the stream is empty */ @@ -488,7 +522,7 @@ /** * Returns an {@code OptionalLong} describing the maximum element of this * stream, or an empty optional if this stream is empty. This is a special - * case of a <a href="package-summary.html#MutableReduction">reduction</a> + * case of a <a href="package-summary.html#Reduction">reduction</a> * and is equivalent to: * <pre>{@code * return reduce(Long::max); @@ -504,7 +538,7 @@ /** * Returns the count of elements in this stream. This is a special case of - * a <a href="package-summary.html#MutableReduction">reduction</a> and is + * a <a href="package-summary.html#Reduction">reduction</a> and is * equivalent to: * <pre>{@code * return map(e -> 1L).sum(); @@ -520,7 +554,10 @@ * Returns an {@code OptionalDouble} describing the arithmetic mean of elements of * this stream, or an empty optional if this stream is empty. This is a * special case of a - * <a href="package-summary.html#MutableReduction">reduction</a>. + * <a href="package-summary.html#Reduction">reduction</a>. + * + * <p>This is a <a href="package-summary.html#StreamOps">terminal + * operation</a>. * * @return an {@code OptionalDouble} containing the average element of this * stream, or an empty optional if the stream is empty @@ -530,7 +567,10 @@ /** * Returns a {@code LongSummaryStatistics} describing various summary data * about the elements of this stream. This is a special case of a - * <a href="package-summary.html#MutableReduction">reduction</a>. + * <a href="package-summary.html#Reduction">reduction</a>. + * + * <p>This is a <a href="package-summary.html#StreamOps">terminal + * operation</a>. * * @return a {@code LongSummaryStatistics} describing various summary data * about the elements of this stream @@ -587,9 +627,8 @@ /** * Returns an {@link OptionalLong} describing the first element of this - * stream (in the encounter order), or an empty {@code OptionalLong} if the - * stream is empty. If the stream has no encounter order, then any element - * may be returned. + * stream, or an empty {@code OptionalLong} if the stream is empty. If the + * stream has no encounter order, then any element may be returned. * * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting * terminal operation</a>. @@ -609,8 +648,8 @@ * <p>The behavior of this operation is explicitly nondeterministic; it is * free to select any element in the stream. This is to allow for maximal * performance in parallel operations; the cost is that multiple invocations - * on the same source may not return the same result. (If the first element - * in the encounter order is desired, use {@link #findFirst()} instead.) + * on the same source may not return the same result. (If a stable result + * is desired, use {@link #findFirst()} instead.) * * @return an {@code OptionalLong} describing some element of this stream, * or an empty {@code OptionalLong} if the stream is empty @@ -622,6 +661,9 @@ * Returns a {@code DoubleStream} consisting of the elements of this stream, * converted to {@code double}. * + * <p>This is an <a href="package-summary.html#StreamOps">intermediate + * operation</a>. + * * @return a {@code DoubleStream} consisting of the elements of this stream, * converted to {@code double} */ @@ -631,6 +673,9 @@ * Returns a {@code Stream} consisting of the elements of this stream, * each boxed to a {@code Long}. * + * <p>This is an <a href="package-summary.html#StreamOps">intermediate + * operation</a>. + * * @return a {@code Stream} consistent of the elements of this stream, * each boxed to {@code Long} */ @@ -679,7 +724,7 @@ } /** - * Returns a sequential stream whose elements are the specified values. + * Returns a sequential ordered stream whose elements are the specified values. * * @param values the elements of the new stream * @return the new stream @@ -689,7 +734,7 @@ } /** - * Returns an infinite sequential {@code LongStream} produced by iterative + * Returns an infinite sequential ordered {@code LongStream} produced by iterative * application of a function {@code f} to an initial element {@code seed}, * producing a {@code Stream} consisting of {@code seed}, {@code f(seed)}, * {@code f(f(seed))}, etc. @@ -727,9 +772,9 @@ } /** - * Returns a sequential {@code LongStream} where each element is generated - * by a {@code LongSupplier}. This is suitable for generating constant - * streams, streams of random elements, etc. + * Returns a sequential stream where each element is generated by + * the provided {@code LongSupplier}. This is suitable for generating + * constant streams, streams of random elements, etc. * * @param s the {@code LongSupplier} for generated elements * @return a new sequential {@code LongStream} @@ -741,7 +786,7 @@ } /** - * Returns a sequential {@code LongStream} from {@code startInclusive} + * Returns a sequential ordered {@code LongStream} from {@code startInclusive} * (inclusive) to {@code endExclusive} (exclusive) by an incremental step of * {@code 1}. * @@ -774,7 +819,7 @@ } /** - * Returns a sequential {@code LongStream} from {@code startInclusive} + * Returns a sequential ordered {@code LongStream} from {@code startInclusive} * (inclusive) to {@code endInclusive} (inclusive) by an incremental step of * {@code 1}. * @@ -808,15 +853,16 @@ } /** - * Creates a lazy concatenated {@code LongStream} whose elements are all the - * elements of a first {@code LongStream} succeeded by all the elements of the - * second {@code LongStream}. The resulting stream is ordered if both + * Creates a lazily concatenated stream whose elements are all the + * elements of the first stream followed by all the elements of the + * second stream. The resulting stream is ordered if both * of the input streams are ordered, and parallel if either of the input - * streams is parallel. + * streams is parallel. When the resulting stream is closed, the close + * handlers for both input streams are invoked. * * @param a the first stream - * @param b the second stream to concatenate on to end of the first stream - * @return the concatenation of the two streams + * @param b the second stream + * @return the concatenation of the two input streams */ public static LongStream concat(LongStream a, LongStream b) { Objects.requireNonNull(a); @@ -824,15 +870,16 @@ Spliterator.OfLong split = new Streams.ConcatSpliterator.OfLong( a.spliterator(), b.spliterator()); - return StreamSupport.longStream(split, a.isParallel() || b.isParallel()); + LongStream stream = StreamSupport.longStream(split, a.isParallel() || b.isParallel()); + return stream.onClose(Streams.composedClose(a, b)); } /** * A mutable builder for a {@code LongStream}. * - * <p>A stream builder has a lifecycle, where it starts in a building - * phase, during which elements can be added, and then transitions to a - * built phase, after which elements may not be added. The built phase + * <p>A stream builder has a lifecycle, which starts in a building + * phase, during which elements can be added, and then transitions to a built + * phase, after which elements may not be added. The built phase begins * begins when the {@link #build()} method is called, which creates an * ordered stream whose elements are the elements that were added to the * stream builder, in the order they were added.
--- a/src/share/classes/java/util/stream/PipelineHelper.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/PipelineHelper.java Wed Aug 14 15:53:13 2013 -0700 @@ -28,7 +28,7 @@ import java.util.function.IntFunction; /** - * Helper class for executing <a href="package-summary.html#StreamPipelines"> + * Helper class for executing <a href="package-summary.html#StreamOps"> * stream pipelines</a>, capturing all of the information about a stream * pipeline (output shape, intermediate operations, stream flags, parallelism, * etc) in one place.
--- a/src/share/classes/java/util/stream/ReferencePipeline.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/ReferencePipeline.java Wed Aug 14 15:53:13 2013 -0700 @@ -163,7 +163,7 @@ StreamOpFlag.NOT_SIZED) { @Override Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) { - return new Sink.ChainedReference<P_OUT>(sink) { + return new Sink.ChainedReference<P_OUT, P_OUT>(sink) { @Override public void begin(long size) { downstream.begin(-1); @@ -173,7 +173,7 @@ @SuppressWarnings("unchecked") public void accept(P_OUT u) { if (predicate.test(u)) - downstream.accept((Object) u); + downstream.accept(u); } }; } @@ -188,7 +188,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) { - return new Sink.ChainedReference<P_OUT>(sink) { + return new Sink.ChainedReference<P_OUT, R>(sink) { @Override public void accept(P_OUT u) { downstream.accept(mapper.apply(u)); @@ -205,7 +205,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<P_OUT> opWrapSink(int flags, Sink<Integer> sink) { - return new Sink.ChainedReference<P_OUT>(sink) { + return new Sink.ChainedReference<P_OUT, Integer>(sink) { @Override public void accept(P_OUT u) { downstream.accept(mapper.applyAsInt(u)); @@ -222,7 +222,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<P_OUT> opWrapSink(int flags, Sink<Long> sink) { - return new Sink.ChainedReference<P_OUT>(sink) { + return new Sink.ChainedReference<P_OUT, Long>(sink) { @Override public void accept(P_OUT u) { downstream.accept(mapper.applyAsLong(u)); @@ -239,7 +239,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) { @Override Sink<P_OUT> opWrapSink(int flags, Sink<Double> sink) { - return new Sink.ChainedReference<P_OUT>(sink) { + return new Sink.ChainedReference<P_OUT, Double>(sink) { @Override public void accept(P_OUT u) { downstream.accept(mapper.applyAsDouble(u)); @@ -257,7 +257,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) { @Override Sink<P_OUT> opWrapSink(int flags, Sink<R> sink) { - return new Sink.ChainedReference<P_OUT>(sink) { + return new Sink.ChainedReference<P_OUT, R>(sink) { @Override public void begin(long size) { downstream.begin(-1); @@ -266,10 +266,11 @@ @Override @SuppressWarnings("unchecked") public void accept(P_OUT u) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - Stream<? extends R> result = mapper.apply(u); - if (result != null) - result.sequential().forEach(downstream); + try (Stream<? extends R> result = mapper.apply(u)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(downstream); + } } }; } @@ -284,7 +285,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) { @Override Sink<P_OUT> opWrapSink(int flags, Sink<Integer> sink) { - return new Sink.ChainedReference<P_OUT>(sink) { + return new Sink.ChainedReference<P_OUT, Integer>(sink) { IntConsumer downstreamAsInt = downstream::accept; @Override public void begin(long size) { @@ -293,10 +294,11 @@ @Override public void accept(P_OUT u) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - IntStream result = mapper.apply(u); - if (result != null) - result.sequential().forEach(downstreamAsInt); + try (IntStream result = mapper.apply(u)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(downstreamAsInt); + } } }; } @@ -311,7 +313,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) { @Override Sink<P_OUT> opWrapSink(int flags, Sink<Double> sink) { - return new Sink.ChainedReference<P_OUT>(sink) { + return new Sink.ChainedReference<P_OUT, Double>(sink) { DoubleConsumer downstreamAsDouble = downstream::accept; @Override public void begin(long size) { @@ -320,10 +322,11 @@ @Override public void accept(P_OUT u) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - DoubleStream result = mapper.apply(u); - if (result != null) - result.sequential().forEach(downstreamAsDouble); + try (DoubleStream result = mapper.apply(u)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(downstreamAsDouble); + } } }; } @@ -338,7 +341,7 @@ StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED) { @Override Sink<P_OUT> opWrapSink(int flags, Sink<Long> sink) { - return new Sink.ChainedReference<P_OUT>(sink) { + return new Sink.ChainedReference<P_OUT, Long>(sink) { LongConsumer downstreamAsLong = downstream::accept; @Override public void begin(long size) { @@ -347,10 +350,11 @@ @Override public void accept(P_OUT u) { - // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it - LongStream result = mapper.apply(u); - if (result != null) - result.sequential().forEach(downstreamAsLong); + try (LongStream result = mapper.apply(u)) { + // We can do better that this too; optimize for depth=0 case and just grab spliterator and forEach it + if (result != null) + result.sequential().forEach(downstreamAsLong); + } } }; } @@ -358,17 +362,17 @@ } @Override - public final Stream<P_OUT> peek(Consumer<? super P_OUT> tee) { - Objects.requireNonNull(tee); + public final Stream<P_OUT> peek(Consumer<? super P_OUT> action) { + Objects.requireNonNull(action); return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE, 0) { @Override Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) { - return new Sink.ChainedReference<P_OUT>(sink) { + return new Sink.ChainedReference<P_OUT, P_OUT>(sink) { @Override @SuppressWarnings("unchecked") public void accept(P_OUT u) { - tee.accept(u); + action.accept(u); downstream.accept(u); } }; @@ -495,7 +499,7 @@ } @Override - public final <R, A> R collect(Collector<? super P_OUT, A, ? extends R> collector) { + public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) { A container; if (isParallel() && (collector.characteristics().contains(Collector.Characteristics.CONCURRENT)) @@ -513,10 +517,10 @@ } @Override - public final <R> R collect(Supplier<R> resultFactory, + public final <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super P_OUT> accumulator, BiConsumer<R, R> combiner) { - return evaluate(ReduceOps.makeRef(resultFactory, accumulator, combiner)); + return evaluate(ReduceOps.makeRef(supplier, accumulator, combiner)); } @Override
--- a/src/share/classes/java/util/stream/Sink.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/Sink.java Wed Aug 14 15:53:13 2013 -0700 @@ -241,11 +241,10 @@ * implementation of the {@code accept()} method must call the correct * {@code accept()} method on the downstream {@code Sink}. */ - static abstract class ChainedReference<T> implements Sink<T> { - @SuppressWarnings("rawtypes") - protected final Sink downstream; + static abstract class ChainedReference<T, E_OUT> implements Sink<T> { + protected final Sink<? super E_OUT> downstream; - public ChainedReference(Sink downstream) { + public ChainedReference(Sink<? super E_OUT> downstream) { this.downstream = Objects.requireNonNull(downstream); } @@ -274,11 +273,10 @@ * The implementation of the {@code accept()} method must call the correct * {@code accept()} method on the downstream {@code Sink}. */ - static abstract class ChainedInt implements Sink.OfInt { - @SuppressWarnings("rawtypes") - protected final Sink downstream; + static abstract class ChainedInt<E_OUT> implements Sink.OfInt { + protected final Sink<? super E_OUT> downstream; - public ChainedInt(Sink downstream) { + public ChainedInt(Sink<? super E_OUT> downstream) { this.downstream = Objects.requireNonNull(downstream); } @@ -307,11 +305,10 @@ * The implementation of the {@code accept()} method must call the correct * {@code accept()} method on the downstream {@code Sink}. */ - static abstract class ChainedLong implements Sink.OfLong { - @SuppressWarnings("rawtypes") - protected final Sink downstream; + static abstract class ChainedLong<E_OUT> implements Sink.OfLong { + protected final Sink<? super E_OUT> downstream; - public ChainedLong(Sink downstream) { + public ChainedLong(Sink<? super E_OUT> downstream) { this.downstream = Objects.requireNonNull(downstream); } @@ -340,11 +337,10 @@ * The implementation of the {@code accept()} method must call the correct * {@code accept()} method on the downstream {@code Sink}. */ - static abstract class ChainedDouble implements Sink.OfDouble { - @SuppressWarnings("rawtypes") - protected final Sink downstream; + static abstract class ChainedDouble<E_OUT> implements Sink.OfDouble { + protected final Sink<? super E_OUT> downstream; - public ChainedDouble(Sink downstream) { + public ChainedDouble(Sink<? super E_OUT> downstream) { this.downstream = Objects.requireNonNull(downstream); }
--- a/src/share/classes/java/util/stream/SliceOps.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/SliceOps.java Wed Aug 14 15:53:13 2013 -0700 @@ -182,7 +182,7 @@ @Override Sink<T> opWrapSink(int flags, Sink<T> sink) { - return new Sink.ChainedReference<T>(sink) { + return new Sink.ChainedReference<T, T>(sink) { long n = skip; long m = limit >= 0 ? limit : Long.MAX_VALUE; @@ -291,7 +291,7 @@ @Override Sink<Integer> opWrapSink(int flags, Sink<Integer> sink) { - return new Sink.ChainedInt(sink) { + return new Sink.ChainedInt<Integer>(sink) { long n = skip; long m = limit >= 0 ? limit : Long.MAX_VALUE; @@ -400,7 +400,7 @@ @Override Sink<Long> opWrapSink(int flags, Sink<Long> sink) { - return new Sink.ChainedLong(sink) { + return new Sink.ChainedLong<Long>(sink) { long n = skip; long m = limit >= 0 ? limit : Long.MAX_VALUE; @@ -509,7 +509,7 @@ @Override Sink<Double> opWrapSink(int flags, Sink<Double> sink) { - return new Sink.ChainedDouble(sink) { + return new Sink.ChainedDouble<Double>(sink) { long n = skip; long m = limit >= 0 ? limit : Long.MAX_VALUE;
--- a/src/share/classes/java/util/stream/SortedOps.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/SortedOps.java Wed Aug 14 15:53:13 2013 -0700 @@ -129,7 +129,7 @@ } @Override - public Sink<T> opWrapSink(int flags, Sink sink) { + public Sink<T> opWrapSink(int flags, Sink<T> sink) { Objects.requireNonNull(sink); // If the input is already naturally sorted and this operation @@ -280,12 +280,12 @@ /** * {@link ForkJoinTask} for implementing sort on SIZED reference streams. */ - private static final class SizedRefSortingSink<T> extends Sink.ChainedReference<T> { + private static final class SizedRefSortingSink<T> extends Sink.ChainedReference<T, T> { private final Comparator<? super T> comparator; private T[] array; private int offset; - SizedRefSortingSink(Sink<T> sink, Comparator<? super T> comparator) { + SizedRefSortingSink(Sink<? super T> sink, Comparator<? super T> comparator) { super(sink); this.comparator = comparator; } @@ -320,11 +320,11 @@ /** * {@link Sink} for implementing sort on reference streams. */ - private static final class RefSortingSink<T> extends Sink.ChainedReference<T> { + private static final class RefSortingSink<T> extends Sink.ChainedReference<T, T> { private final Comparator<? super T> comparator; private ArrayList<T> list; - RefSortingSink(Sink<T> sink, Comparator<? super T> comparator) { + RefSortingSink(Sink<? super T> sink, Comparator<? super T> comparator) { super(sink); this.comparator = comparator; } @@ -352,11 +352,11 @@ /** * {@link Sink} for implementing sort on SIZED int streams. */ - private static final class SizedIntSortingSink extends Sink.ChainedInt { + private static final class SizedIntSortingSink extends Sink.ChainedInt<Integer> { private int[] array; private int offset; - SizedIntSortingSink(Sink downstream) { + SizedIntSortingSink(Sink<? super Integer> downstream) { super(downstream); } @@ -386,10 +386,10 @@ /** * {@link Sink} for implementing sort on int streams. */ - private static final class IntSortingSink extends Sink.ChainedInt { + private static final class IntSortingSink extends Sink.ChainedInt<Integer> { private SpinedBuffer.OfInt b; - IntSortingSink(Sink sink) { + IntSortingSink(Sink<? super Integer> sink) { super(sink); } @@ -417,11 +417,11 @@ /** * {@link Sink} for implementing sort on SIZED long streams. */ - private static final class SizedLongSortingSink extends Sink.ChainedLong { + private static final class SizedLongSortingSink extends Sink.ChainedLong<Long> { private long[] array; private int offset; - SizedLongSortingSink(Sink downstream) { + SizedLongSortingSink(Sink<? super Long> downstream) { super(downstream); } @@ -451,10 +451,10 @@ /** * {@link Sink} for implementing sort on long streams. */ - private static final class LongSortingSink extends Sink.ChainedLong { + private static final class LongSortingSink extends Sink.ChainedLong<Long> { private SpinedBuffer.OfLong b; - LongSortingSink(Sink sink) { + LongSortingSink(Sink<? super Long> sink) { super(sink); } @@ -482,11 +482,11 @@ /** * {@link Sink} for implementing sort on SIZED double streams. */ - private static final class SizedDoubleSortingSink extends Sink.ChainedDouble { + private static final class SizedDoubleSortingSink extends Sink.ChainedDouble<Double> { private double[] array; private int offset; - SizedDoubleSortingSink(Sink downstream) { + SizedDoubleSortingSink(Sink<? super Double> downstream) { super(downstream); } @@ -516,10 +516,10 @@ /** * {@link Sink} for implementing sort on double streams. */ - private static final class DoubleSortingSink extends Sink.ChainedDouble { + private static final class DoubleSortingSink extends Sink.ChainedDouble<Double> { private SpinedBuffer.OfDouble b; - DoubleSortingSink(Sink sink) { + DoubleSortingSink(Sink<? super Double> sink) { super(sink); }
--- a/src/share/classes/java/util/stream/Stream.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/Stream.java Wed Aug 14 15:53:13 2013 -0700 @@ -25,12 +25,14 @@ package java.util.stream; import java.util.Arrays; +import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.Objects; import java.util.Optional; import java.util.Spliterator; import java.util.Spliterators; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.BinaryOperator; @@ -44,51 +46,82 @@ import java.util.function.ToLongFunction; import java.util.function.UnaryOperator; -// @@@ Specification to-do list @@@ -// - Describe the difference between sequential and parallel streams -// - More general information about reduce, better definitions for associativity, more description of -// how reduce employs parallelism, more examples -// - Role of stream flags in various operations, specifically ordering -// - Whether each op preserves encounter order -// @@@ Specification to-do list @@@ - /** - * A sequence of elements supporting sequential and parallel bulk operations. - * Streams support lazy intermediate operations (transforming a stream to - * another stream) such as {@code filter} and {@code map}, and terminal - * operations (consuming the contents of a stream to produce a result or - * side-effect), such as {@code forEach}, {@code findFirst}, and {@code - * iterator}. Once an operation has been performed on a stream, it - * is considered <em>consumed</em> and no longer usable for other operations. + * A sequence of elements supporting sequential and parallel aggregate + * operations. The following example illustrates an aggregate operation using + * {@link Stream} and {@link IntStream}: + * + * <pre>{@code + * int sum = widgets.stream() + * .filter(w -> w.getColor() == RED) + * .mapToInt(w -> w.getWeight()) + * .sum(); + * }</pre> * - * <p>For sequential stream pipelines, all operations are performed in the - * <a href="package-summary.html#Ordering">encounter order</a> of the pipeline - * source, if the pipeline source has a defined encounter order. + * In this example, {@code widgets} is a {@code Collection<Widget>}. We create + * a stream of {@code Widget} objects via {@link Collection#stream Collection.stream()}, + * filter it to produce a stream containing only the red widgets, and then + * transform it into a stream of {@code int} values representing the weight of + * each red widget. Then this stream is summed to produce a total weight. + * + * <p>To perform a computation, stream + * <a href="package-summary.html#StreamOps">operations</a> are composed into a + * <em>stream pipeline</em>. A stream pipeline consists of a source (which + * might be an array, a collection, a generator function, an I/O channel, + * etc), zero or more <em>intermediate operations</em> (which transform a + * stream into another stream, such as {@link Stream#filter(Predicate)}), and a + * <em>terminal operation</em> (which produces a result or side-effect, such + * as {@link Stream#count()} or {@link Stream#forEach(Consumer)}). + * Streams are lazy; computation on the source data is only performed when the + * terminal operation is initiated, and source elements are consumed only + * as needed. * - * <p>For parallel stream pipelines, unless otherwise specified, intermediate - * stream operations preserve the <a href="package-summary.html#Ordering"> - * encounter order</a> of their source, and terminal operations - * respect the encounter order of their source, if the source - * has an encounter order. Provided that and parameters to stream operations - * satisfy the <a href="package-summary.html#NonInterference">non-interference - * requirements</a>, and excepting differences arising from the absence of - * a defined encounter order, the result of a stream pipeline should be the - * stable across multiple executions of the same operations on the same source. - * However, the timing and thread in which side-effects occur (for those - * operations which are allowed to produce side-effects, such as - * {@link #forEach(Consumer)}), are explicitly nondeterministic for parallel - * execution of stream pipelines. + * <p>Collections and streams, while bearing some superficial similarities, + * have different goals. Collections are primarily concerned with the efficient + * management of, and access to, their elements. By contrast, streams do not + * provide a means to directly access or manipulate their elements, and are + * instead concerned with declaratively describing their source and the + * computational operations which will be performed in aggregate on that source. + * However, if the provided stream operations do not offer the desired + * functionality, the {@link #iterator()} and {@link #spliterator()} operations + * can be used to perform a controlled traversal. + * + * <p>A stream pipeline, like the "widgets" example above, can be viewed as + * a <em>query</em> on the stream source. Unless the source was explicitly + * designed for concurrent modification (such as a {@link ConcurrentHashMap}), + * unpredictable or erroneous behavior may result from modifying the stream + * source while it is being queried. * - * <p>Unless otherwise noted, passing a {@code null} argument to any stream - * method may result in a {@link NullPointerException}. + * <p>Most stream operations accept parameters that describe user-specified + * behavior, such as the lambda expression {@code w -> w.getWeight()} passed to + * {@code mapToInt} in the example above. Such parameters are always instances + * of a <a href="../function/package-summary.html">functional interface</a> such + * as {@link java.util.function.Function}, and are often lambda expressions or + * method references. These parameters can never be null, should not modify the + * stream source, and should be + * <a href="package-summary.html#NonInterference">effectively stateless</a> + * (their result should not depend on any state that might change during + * execution of the stream pipeline.) * - * @apiNote - * Streams are not data structures; they do not manage the storage for their - * elements, nor do they support access to individual elements. However, - * you can use the {@link #iterator()} or {@link #spliterator()} operations to - * perform a controlled traversal. + * <p>A stream should be operated on (invoking an intermediate or terminal stream + * operation) only once. This rules out, for example, "forked" streams, where + * the same source feeds two or more pipelines, or multiple traversals of the + * same stream. A stream implementation may throw {@link IllegalStateException} + * if it detects that the stream is being reused. However, since some stream + * operations may return their receiver rather than a new stream object, it may + * not be possible to detect reuse in all cases. * - * @param <T> type of elements + * <p>Stream pipelines may execute either sequentially or in + * <a href="package-summary.html#Parallelism">parallel</a>. This + * execution mode is a property of the stream. Streams are created + * with an initial choice of sequential or parallel execution. (For example, + * {@link Collection#stream() Collection.stream()} creates a sequential stream, + * and {@link Collection#parallelStream() Collection.parallelStream()} creates + * a parallel one.) This choice of execution mode may be modified by the + * {@link #sequential()} or {@link #parallel()} methods, and may be queried with + * the {@link #isParallel()} method. + * + * @param <T> the type of the stream elements * @since 1.8 * @see <a href="package-summary.html">java.util.stream</a> */ @@ -168,9 +201,9 @@ /** * Returns a stream consisting of the results of replacing each element of * this stream with the contents of the stream produced by applying the - * provided mapping function to each element. If the result of the mapping - * function is {@code null}, this is treated as if the result is an empty - * stream. + * provided mapping function to each element. (If the result of the mapping + * function is {@code null}, this is treated as if the result was an empty + * stream.) * * <p>This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. @@ -197,9 +230,9 @@ /** * Returns an {@code IntStream} consisting of the results of replacing each * element of this stream with the contents of the stream produced by - * applying the provided mapping function to each element. If the result of - * the mapping function is {@code null}, this is treated as if the result is - * an empty stream. + * applying the provided mapping function to each element. (If the result + * of the mapping function is {@code null}, this is treated as if the result + * was an empty stream.) * * <p>This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. @@ -214,9 +247,9 @@ /** * Returns a {@code LongStream} consisting of the results of replacing each * element of this stream with the contents of the stream produced - * by applying the provided mapping function to each element. If the result - * of the mapping function is {@code null}, this is treated as if the - * result is an empty stream. + * by applying the provided mapping function to each element. (If the result + * of the mapping function is {@code null}, this is treated as if the result + * was an empty stream.) * * <p>This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. @@ -231,9 +264,9 @@ /** * Returns a {@code DoubleStream} consisting of the results of replacing each * element of this stream with the contents of the stream produced - * by applying the provided mapping function to each element. If the result + * by applying the provided mapping function to each element. (If the result * of the mapping function is {@code null}, this is treated as if the result - * is an empty stream. + * was an empty stream.) * * <p>This is an <a href="package-summary.html#StreamOps">intermediate * operation</a>. @@ -260,7 +293,7 @@ * Returns a stream consisting of the elements of this stream, sorted * according to natural order. If the elements of this stream are not * {@code Comparable}, a {@code java.lang.ClassCastException} may be thrown - * when the stream pipeline is executed. + * when the terminal operation is executed. * * <p>This is a <a href="package-summary.html#StreamOps">stateful * intermediate operation</a>. @@ -307,12 +340,12 @@ * .collect(Collectors.intoList()); * }</pre> * - * @param consumer a <a href="package-summary.html#NonInterference"> + * @param action a <a href="package-summary.html#NonInterference"> * non-interfering</a> action to perform on the elements as * they are consumed from the stream * @return the new stream */ - Stream<T> peek(Consumer<? super T> consumer); + Stream<T> peek(Consumer<? super T> action); /** * Returns a stream consisting of the elements of this stream, truncated @@ -329,8 +362,8 @@ /** * Returns a stream consisting of the remaining elements of this stream - * after indexing {@code startInclusive} elements into the stream. If the - * {@code startInclusive} index lies past the end of this stream then an + * after discarding the first {@code startInclusive} elements of the stream. + * If this stream contains fewer than {@code startInclusive} elements then an * empty stream will be returned. * * <p>This is a <a href="package-summary.html#StreamOps">stateful @@ -344,10 +377,10 @@ /** * Returns a stream consisting of the remaining elements of this stream - * after indexing {@code startInclusive} elements into the stream and - * truncated to contain no more than {@code endExclusive - startInclusive} - * elements. If the {@code startInclusive} index lies past the end - * of this stream then an empty stream will be returned. + * after discarding the first {@code startInclusive} elements and truncating + * the result to be no longer than {@code endExclusive - startInclusive} + * elements in length. If this stream contains fewer than + * {@code startInclusive} elements then an empty stream will be returned. * * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting * stateful intermediate operation</a>. @@ -405,11 +438,23 @@ /** * Returns an array containing the elements of this stream, using the - * provided {@code generator} function to allocate the returned array. + * provided {@code generator} function to allocate the returned array, as + * well as any additional arrays that might be required for a partitioned + * execution or for resizing. * * <p>This is a <a href="package-summary.html#StreamOps">terminal * operation</a>. * + * @apiNote + * The generator function takes an integer, which is the size of the + * desired array, and produces an array of the desired size. This can be + * concisely expressed with an array constructor reference: + * <pre>{@code + * Person[] men = people.stream() + * .filter(p -> p.getGender() == MALE) + * .toArray(Person[]::new); + * }</pre> + * * @param <A> the element type of the resulting array * @param generator a function which produces a new array of the desired * type and the provided length @@ -451,7 +496,7 @@ * Integer sum = integers.reduce(0, (a, b) -> a+b); * }</pre> * - * or more compactly: + * or: * * <pre>{@code * Integer sum = integers.reduce(0, Integer::sum); @@ -511,7 +556,7 @@ /** * Performs a <a href="package-summary.html#Reduction">reduction</a> on the * elements of this stream, using the provided identity, accumulation - * function, and a combining functions. This is equivalent to: + * function, and combining functions. This is equivalent to: * <pre>{@code * U result = identity; * for (T element : this stream) @@ -537,8 +582,8 @@ * by an explicit combination of {@code map} and {@code reduce} operations. * The {@code accumulator} function acts as a fused mapper and accumulator, * which can sometimes be more efficient than separate mapping and reduction, - * such as in the case where knowing the previously reduced value allows you - * to avoid some computation. + * such as when knowing the previously reduced value allows you to avoid + * some computation. * * @param <U> The type of the result * @param identity the identity value for the combiner function @@ -561,12 +606,12 @@ /** * Performs a <a href="package-summary.html#MutableReduction">mutable * reduction</a> operation on the elements of this stream. A mutable - * reduction is one in which the reduced value is a mutable value holder, + * reduction is one in which the reduced value is a mutable result container, * such as an {@code ArrayList}, and elements are incorporated by updating - * the state of the result, rather than by replacing the result. This + * the state of the result rather than by replacing the result. This * produces a result equivalent to: * <pre>{@code - * R result = resultFactory.get(); + * R result = supplier.get(); * for (T element : this stream) * accumulator.accept(result, element); * return result; @@ -579,10 +624,11 @@ * operation</a>. * * @apiNote There are many existing classes in the JDK whose signatures are - * a good match for use as arguments to {@code collect()}. For example, - * the following will accumulate strings into an ArrayList: + * well-suited for use with method references as arguments to {@code collect()}. + * For example, the following will accumulate strings into an {@code ArrayList}: * <pre>{@code - * List<String> asList = stringStream.collect(ArrayList::new, ArrayList::add, ArrayList::addAll); + * List<String> asList = stringStream.collect(ArrayList::new, ArrayList::add, + * ArrayList::addAll); * }</pre> * * <p>The following will take a stream of strings and concatenates them into a @@ -594,7 +640,7 @@ * }</pre> * * @param <R> type of the result - * @param resultFactory a function that creates a new result container. + * @param supplier a function that creates a new result container. * For a parallel execution, this function may be * called multiple times and must return a fresh value * each time. @@ -608,24 +654,24 @@ * must be compatible with the accumulator function * @return the result of the reduction */ - <R> R collect(Supplier<R> resultFactory, + <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner); /** * Performs a <a href="package-summary.html#MutableReduction">mutable * reduction</a> operation on the elements of this stream using a - * {@code Collector} object to describe the reduction. A {@code Collector} + * {@code Collector}. A {@code Collector} * encapsulates the functions used as arguments to * {@link #collect(Supplier, BiConsumer, BiConsumer)}, allowing for reuse of - * collection strategies, and composition of collect operations such as + * collection strategies and composition of collect operations such as * multiple-level grouping or partitioning. * * <p>This is a <a href="package-summary.html#StreamOps">terminal * operation</a>. * * <p>When executed in parallel, multiple intermediate results may be - * instantiated, populated, and merged, so as to maintain isolation of + * instantiated, populated, and merged so as to maintain isolation of * mutable data structures. Therefore, even when executed in parallel * with non-thread-safe data structures (such as {@code ArrayList}), no * additional synchronization is needed for a parallel reduction. @@ -638,16 +684,16 @@ * * <p>The following will classify {@code Person} objects by city: * <pre>{@code - * Map<String, Collection<Person>> peopleByCity - * = personStream.collect(Collectors.groupBy(Person::getCity)); + * Map<String, List<Person>> peopleByCity + * = personStream.collect(Collectors.groupingBy(Person::getCity)); * }</pre> * * <p>The following will classify {@code Person} objects by state and city, * cascading two {@code Collector}s together: * <pre>{@code - * Map<String, Map<String, Collection<Person>>> peopleByStateAndCity - * = personStream.collect(Collectors.groupBy(Person::getState, - * Collectors.groupBy(Person::getCity))); + * Map<String, Map<String, List<Person>>> peopleByStateAndCity + * = personStream.collect(Collectors.groupingBy(Person::getState, + * Collectors.groupingBy(Person::getCity))); * }</pre> * * @param <R> the type of the result @@ -657,12 +703,12 @@ * @see #collect(Supplier, BiConsumer, BiConsumer) * @see Collectors */ - <R, A> R collect(Collector<? super T, A, ? extends R> collector); + <R, A> R collect(Collector<? super T, A, R> collector); /** * Returns the minimum element of this stream according to the provided * {@code Comparator}. This is a special case of a - * <a href="package-summary.html#MutableReduction">reduction</a>. + * <a href="package-summary.html#Reduction">reduction</a>. * * <p>This is a <a href="package-summary.html#StreamOps">terminal operation</a>. * @@ -677,7 +723,7 @@ /** * Returns the maximum element of this stream according to the provided * {@code Comparator}. This is a special case of a - * <a href="package-summary.html#MutableReduction">reduction</a>. + * <a href="package-summary.html#Reduction">reduction</a>. * * <p>This is a <a href="package-summary.html#StreamOps">terminal * operation</a>. @@ -692,7 +738,7 @@ /** * Returns the count of elements in this stream. This is a special case of - * a <a href="package-summary.html#MutableReduction">reduction</a> and is + * a <a href="package-summary.html#Reduction">reduction</a> and is * equivalent to: * <pre>{@code * return mapToLong(e -> 1L).sum(); @@ -753,10 +799,9 @@ boolean noneMatch(Predicate<? super T> predicate); /** - * Returns an {@link Optional} describing the first element of this stream - * (in the encounter order), or an empty {@code Optional} if the stream is - * empty. If the stream has no encounter order, then any element may be - * returned. + * Returns an {@link Optional} describing the first element of this stream, + * or an empty {@code Optional} if the stream is empty. If the stream has + * no encounter order, then any element may be returned. * * <p>This is a <a href="package-summary.html#StreamOps">short-circuiting * terminal operation</a>. @@ -777,8 +822,8 @@ * <p>The behavior of this operation is explicitly nondeterministic; it is * free to select any element in the stream. This is to allow for maximal * performance in parallel operations; the cost is that multiple invocations - * on the same source may not return the same result. (If the first element - * in the encounter order is desired, use {@link #findFirst()} instead.) + * on the same source may not return the same result. (If a stable result + * is desired, use {@link #findFirst()} instead.) * * @return an {@code Optional} describing some element of this stream, or an * empty {@code Optional} if the stream is empty @@ -821,20 +866,19 @@ } /** - * Returns a sequential stream whose elements are the specified values. + * Returns a sequential ordered stream whose elements are the specified values. * * @param <T> the type of stream elements * @param values the elements of the new stream * @return the new stream */ @SafeVarargs - @SuppressWarnings("varargs") // Creating a stream from an array is safe public static<T> Stream<T> of(T... values) { return Arrays.stream(values); } /** - * Returns an infinite sequential {@code Stream} produced by iterative + * Returns an infinite sequential ordered {@code Stream} produced by iterative * application of a function {@code f} to an initial element {@code seed}, * producing a {@code Stream} consisting of {@code seed}, {@code f(seed)}, * {@code f(f(seed))}, etc. @@ -872,8 +916,8 @@ } /** - * Returns a sequential {@code Stream} where each element is - * generated by a {@code Supplier}. This is suitable for generating + * Returns a sequential stream where each element is generated by + * the provided {@code Supplier}. This is suitable for generating * constant streams, streams of random elements, etc. * * @param <T> the type of stream elements @@ -887,16 +931,16 @@ } /** - * Creates a lazy concatenated {@code Stream} whose elements are all the - * elements of a first {@code Stream} succeeded by all the elements of the - * second {@code Stream}. The resulting stream is ordered if both + * Creates a lazily concatenated stream whose elements are all the + * elements of the first stream followed by all the elements of the + * second stream. The resulting stream is ordered if both * of the input streams are ordered, and parallel if either of the input - * streams is parallel. + * streams is parallel. When the resulting stream is closed, the close + * handlers for both input streams are invoked. * * @param <T> The type of stream elements * @param a the first stream - * @param b the second stream to concatenate on to end of the first - * stream + * @param b the second stream * @return the concatenation of the two input streams */ public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) { @@ -906,7 +950,8 @@ @SuppressWarnings("unchecked") Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>( (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator()); - return StreamSupport.stream(split, a.isParallel() || b.isParallel()); + Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel()); + return stream.onClose(Streams.composedClose(a, b)); } /** @@ -915,7 +960,7 @@ * {@code Builder} (without the copying overhead that comes from using * an {@code ArrayList} as a temporary buffer.) * - * <p>A {@code Stream.Builder} has a lifecycle, where it starts in a building + * <p>A stream builder has a lifecycle, which starts in a building * phase, during which elements can be added, and then transitions to a built * phase, after which elements may not be added. The built phase begins * when the {@link #build()} method is called, which creates an ordered
--- a/src/share/classes/java/util/stream/StreamSpliterators.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/StreamSpliterators.java Wed Aug 14 15:53:13 2013 -0700 @@ -1326,7 +1326,7 @@ return new InfiniteSupplyingSpliterator.OfLong(estimate = estimate >>> 1, s); } } - + static final class OfDouble extends InfiniteSupplyingSpliterator<Double> implements Spliterator.OfDouble { final DoubleSupplier s;
--- a/src/share/classes/java/util/stream/StreamSupport.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/StreamSupport.java Wed Aug 14 15:53:13 2013 -0700 @@ -32,12 +32,8 @@ * Low-level utility methods for creating and manipulating streams. * * <p>This class is mostly for library writers presenting stream views - * of their data structures; most static stream methods for end users are in - * {@link Streams}. - * - * <p>Unless otherwise stated, streams are created as sequential - * streams. A sequential stream can be transformed into a parallel stream by - * calling the {@code parallel()} method on the created stream. + * of data structures; most static stream methods intended for end users are in + * the various {@code Stream} classes. * * @since 1.8 */ @@ -80,7 +76,7 @@ * {@code Supplier} of {@code Spliterator}. * * <p>The {@link Supplier#get()} method will be invoked on the supplier no - * more than once, and after the terminal operation of the stream pipeline + * more than once, and only after the terminal operation of the stream pipeline * commences. * * <p>For spliterators that report a characteristic of {@code IMMUTABLE} @@ -88,7 +84,7 @@ * <a href="../Spliterator.html#binding">late-binding</a>, it is likely * more efficient to use {@link #stream(java.util.Spliterator, boolean)} * instead. - * The use of a {@code Supplier} in this form provides a level of + * <p>The use of a {@code Supplier} in this form provides a level of * indirection that reduces the scope of potential interference with the * source. Since the supplier is only invoked after the terminal operation * commences, any modifications to the source up to the start of the @@ -148,7 +144,7 @@ * {@code Supplier} of {@code Spliterator.OfInt}. * * <p>The {@link Supplier#get()} method will be invoked on the supplier no - * more than once, and after the terminal operation of the stream pipeline + * more than once, and only after the terminal operation of the stream pipeline * commences. * * <p>For spliterators that report a characteristic of {@code IMMUTABLE} @@ -156,7 +152,7 @@ * <a href="../Spliterator.html#binding">late-binding</a>, it is likely * more efficient to use {@link #intStream(java.util.Spliterator.OfInt, boolean)} * instead. - * The use of a {@code Supplier} in this form provides a level of + * <p>The use of a {@code Supplier} in this form provides a level of * indirection that reduces the scope of potential interference with the * source. Since the supplier is only invoked after the terminal operation * commences, any modifications to the source up to the start of the @@ -215,7 +211,7 @@ * {@code Supplier} of {@code Spliterator.OfLong}. * * <p>The {@link Supplier#get()} method will be invoked on the supplier no - * more than once, and after the terminal operation of the stream pipeline + * more than once, and only after the terminal operation of the stream pipeline * commences. * * <p>For spliterators that report a characteristic of {@code IMMUTABLE} @@ -223,7 +219,7 @@ * <a href="../Spliterator.html#binding">late-binding</a>, it is likely * more efficient to use {@link #longStream(java.util.Spliterator.OfLong, boolean)} * instead. - * The use of a {@code Supplier} in this form provides a level of + * <p>The use of a {@code Supplier} in this form provides a level of * indirection that reduces the scope of potential interference with the * source. Since the supplier is only invoked after the terminal operation * commences, any modifications to the source up to the start of the @@ -282,7 +278,7 @@ * {@code Supplier} of {@code Spliterator.OfDouble}. * * <p>The {@link Supplier#get()} method will be invoked on the supplier no - * more than once, and after the terminal operation of the stream pipeline + * more than once, and only after the terminal operation of the stream pipeline * commences. * * <p>For spliterators that report a characteristic of {@code IMMUTABLE} @@ -290,7 +286,7 @@ * <a href="../Spliterator.html#binding">late-binding</a>, it is likely * more efficient to use {@link #doubleStream(java.util.Spliterator.OfDouble, boolean)} * instead. - * The use of a {@code Supplier} in this form provides a level of + * <p>The use of a {@code Supplier} in this form provides a level of * indirection that reduces the scope of potential interference with the * source. Since the supplier is only invoked after the terminal operation * commences, any modifications to the source up to the start of the
--- a/src/share/classes/java/util/stream/Streams.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/Streams.java Wed Aug 14 15:53:13 2013 -0700 @@ -25,6 +25,7 @@ package java.util.stream; import java.util.Comparator; +import java.util.MayHoldCloseableResource; import java.util.Objects; import java.util.Spliterator; import java.util.function.Consumer; @@ -833,4 +834,61 @@ } } } + + /** + * Given two Runnables, return a Runnable that executes both in sequence, + * even if the first throws an exception, and if both throw exceptions, add + * any exceptions thrown by the second as suppressed exceptions of the first. + */ + static Runnable composeWithExceptions(Runnable a, Runnable b) { + return new Runnable() { + @Override + public void run() { + try { + a.run(); + } + catch (Error|RuntimeException e1) { + try { + b.run(); + } + catch (Error|RuntimeException e2) { + e1.addSuppressed(e2); + } + finally { + throw e1; + } + } + b.run(); + } + }; + } + + /** + * Given two MayHoldCloseableResource objects, return a Runnable that + * executes both of their close methods in sequence, + * even if the first throws an exception, and if both throw exceptions, add + * any exceptions thrown by the second as suppressed exceptions of the first. + */ + static Runnable composedClose(MayHoldCloseableResource a, MayHoldCloseableResource b) { + return new Runnable() { + @Override + public void run() { + try { + a.close(); + } + catch (Error|RuntimeException e1) { + try { + b.close(); + } + catch (Error|RuntimeException e2) { + e1.addSuppressed(e2); + } + finally { + throw e1; + } + } + b.close(); + } + }; + } }
--- a/src/share/classes/java/util/stream/package-info.java Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/classes/java/util/stream/package-info.java Wed Aug 14 15:53:13 2013 -0700 @@ -24,347 +24,482 @@ */ /** - * <h1>java.util.stream</h1> - * - * Classes to support functional-style operations on streams of values, as in the following: + * Classes to support functional-style operations on streams of values, as in + * the following: * * <pre>{@code - * int sumOfWeights = blocks.stream().filter(b -> b.getColor() == RED) - * .mapToInt(b -> b.getWeight()) - * .sum(); + * int sum = widgets.stream() + * .filter(b -> b.getColor() == RED) + * .mapToInt(b -> b.getWeight()) + * .sum(); * }</pre> * - * <p>Here we use {@code blocks}, which might be a {@code Collection}, as a source for a stream, - * and then perform a filter-map-reduce ({@code sum()} is an example of a <a href="package-summary.html#Reduction">reduction</a> - * operation) on the stream to obtain the sum of the weights of the red blocks. + * <p>Here we use {@code widgets}, a {@code Collection<Widget>}, + * as a source for a stream, and then perform a filter-map-reduce ({@code sum()} + * is an example of a <a href="package-summary.html#Reduction">reduction</a> + * operation) on the stream to obtain the sum of the weights of the red widgets. * - * <p>The key abstraction used in this approach is {@link java.util.stream.Stream}, as well as its primitive - * specializations {@link java.util.stream.IntStream}, {@link java.util.stream.LongStream}, - * and {@link java.util.stream.DoubleStream}. Streams differ from Collections in several ways: + * <p>The key abstraction introduced in this package is <em>stream</em>. The + * classes {@link java.util.stream.Stream}, {@link java.util.stream.IntStream}, + * {@link java.util.stream.LongStream}, and {@link java.util.stream.DoubleStream} + * are streams over objects and the primitive {@code int}, {@code long} and { + * {@code double} types. Streams differ from collections in several ways: * * <ul> - * <li>No storage. A stream is not a data structure that stores elements; instead, they - * carry values from a source (which could be a data structure, a generator, an IO channel, etc) - * through a pipeline of computational operations.</li> - * <li>Functional in nature. An operation on a stream produces a result, but does not modify - * its underlying data source. For example, filtering a {@code Stream} produces a new {@code Stream}, - * rather than removing elements from the underlying source.</li> - * <li>Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, - * can be implemented lazily, exposing opportunities for optimization. (For example, "find the first - * {@code String} matching a pattern" need not examine all the input strings.) Stream operations - * are divided into intermediate ({@code Stream}-producing) operations and terminal (value-producing) - * operations; all intermediate operations are lazy.</li> - * <li>Possibly unbounded. While collections have a finite size, streams need not. Operations - * such as {@code limit(n)} or {@code findFirst()} can allow computations on infinite streams - * to complete in finite time.</li> + * <li>No storage. A stream is not a data structure that stores elements; + * instead, it conveys values from a source such as a data structure, + * an array, a generator function, or an I/O channel, through a pipeline of + * computational operations.</li> + * <li>Functional in nature. An operation on a stream produces a result, + * but does not modify its source. For example, filtering a {@code Stream} + * obtained from a collection produces a new {@code Stream} without the + * filtered elements, rather than removing elements from the source + * collection.</li> + * <li>Laziness-seeking. Many stream operations, such as filtering, mapping, + * or duplicate removal, can be implemented lazily, exposing opportunities + * for optimization. (For example, "find the first {@code String} with + * three consecutive vowels" need not examine all the input strings.) + * Stream operations are divided into intermediate ({@code Stream}-producing) + * operations and terminal (value- or side-effect-producing) operations. + * Intermediate operations are always lazy.</li> + * <li>Possibly unbounded. While collections have a finite size, streams + * need not. Short-circuting operations such as {@code limit(n)} or + * {@code findFirst()} can allow computations on infinite streams to + * complete in finite time.</li> + * <li>Consumable. The elements of a stream are only be visited once during + * the life of a stream. Like an {@link java.util.Iterator}, a new stream + * must be generated to revisit the same elements of the source. + * </li> + * </ul> + * + * Streams can be obtained in a number of ways. Some examples include: + * <ul> + * <li>From a {@link java.util.Collection} via the {@code stream()} and + * {@code parallelStream()} methods;</li> + * <li>From an array via {@link java.util.Arrays#stream(Object[])};</li> + * <li>The lines of a file can be obtained from {@link java.io.BufferedReader#lines()};</li> + * <li>Streams of file paths can be obtained from methods in {@link java.nio.file.Files};</li> + * <li>Streams of random numbers can be obtained from {@link java.util.Random#ints()};</li> + * <li>Numerous other stream-bearing methods in the JDK, including + * {@link java.util.BitSet#stream()}, + * {@link java.util.regex.Pattern#splitAsStream(java.lang.CharSequence)}, + * and {@link java.util.jar.JarFile#stream()}.</li> * </ul> * - * <h2><a name="StreamPipelines">Stream pipelines</a></h2> + * <p>Additional stream sources can be provided by third-party libraries using + * <a href="package-summary.html#StreamSources">these techniques</a>. + * + * <h2><a name="StreamOps">Stream operations and pipelines</a></h2> * - * <p>Streams are used to create <em>pipelines</em> of <a href="package-summary.html#StreamOps">operations</a>. A - * complete stream pipeline has several components: a source (which may be a {@code Collection}, - * an array, a generator function, or an IO channel); zero or more <em>intermediate operations</em> - * such as {@code Stream.filter} or {@code Stream.map}; and a <em>terminal operation</em> such - * as {@code Stream.forEach} or {@code java.util.stream.Stream.reduce}. Stream operations may take as parameters - * <em>function values</em> (which are often lambda expressions, but could be method references - * or objects) which parameterize the behavior of the operation, such as a {@code Predicate} - * passed to the {@code Stream#filter} method. + * <p>Stream <a href="package-summary.html#StreamOps">operations</a> are + * combined to form <em>stream pipelines</em>. A stream pipeline consists of a + * source (such as a {@code Collection}, an array, a generator function, or an + * I/O channel); followed by zero or more <em>intermediate operations</em> such + * as {@code Stream.filter} or {@code Stream.map}; and a <em>terminal + * operation</em> such as {@code Stream.forEach} or {@code Stream.reduce}. * - * <p>Intermediate operations return a new {@code Stream}. They are lazy; executing an - * intermediate operation such as {@link java.util.stream.Stream#filter Stream.filter} does - * not actually perform any filtering, instead creating a new {@code Stream} that, when - * traversed, contains the elements of the initial {@code Stream} that match the - * given {@code Predicate}. Consuming elements from the stream source does not - * begin until the terminal operation is executed. + * <p>Intermediate operations return a new {@code Stream}. They are always + * <em>lazy</em>; executing an intermediate operation such as + * {@code Stream.filter} does not actually perform any filtering, but instead + * creates a new {@code Stream} that, when traversed, contains the elements of + * the initial {@code Stream} that match the given {@code Predicate}. Traversal + * of the pipeline source does not begin until the terminal operation of the + * pipeline is executed. * - * <p>Terminal operations consume the {@code Stream} and produce a result or a side-effect. - * After a terminal operation is performed, the stream can no longer be used and you must - * return to the data source, or select a new data source, to get a new stream. For example, - * obtaining the sum of weights of all red blocks, and then of all blue blocks, requires a - * filter-map-reduce on two different streams: - * <pre>{@code - * int sumOfRedWeights = blocks.stream().filter(b -> b.getColor() == RED) - * .mapToInt(b -> b.getWeight()) - * .sum(); - * int sumOfBlueWeights = blocks.stream().filter(b -> b.getColor() == BLUE) - * .mapToInt(b -> b.getWeight()) - * .sum(); - * }</pre> - * - * <p>However, there are other techniques that allow you to obtain both results in a single - * pass if multiple traversal is impractical or inefficient. TODO provide link + * <p>Terminal operations, such as {@code Stream.forEach} or + * {@code IntStream.sum}, may traverse the stream to produce a result or a + * side-effect. After the terminal operation is performed, the stream pipeline + * is considered consumed, and can no longer be used; if you need to traverse + * the same data source again, you must return to the data source to get a new + * stream. * - * <h3><a name="StreamOps">Stream operations</a></h3> + * <p> Processing streams lazily allows for significant efficiencies; in a + * pipeline such as the filter-map-sum example above, filtering, mapping, and + * summing can be fused into a single pass on the data, with minimal + * intermediate state. Laziness also allows avoiding examining all the data + * when it is not necessary; for operations such as "find the first string + * longer than 1000 characters", it is only necessary to examine just enough + * strings to find one that has the desired characteristics without examining + * all of the strings available from the source. (This behavior becomes even + * more important when the input stream is infinite and not merely large.) * - * <p>Intermediate stream operation (such as {@code filter} or {@code sorted}) always produce a - * new {@code Stream}, and are always<em>lazy</em>. Executing a lazy operations does not - * trigger processing of the stream contents; all processing is deferred until the terminal - * operation commences. Processing streams lazily allows for significant efficiencies; in a - * pipeline such as the filter-map-sum example above, filtering, mapping, and addition can be - * fused into a single pass, with minimal intermediate state. Laziness also enables us to avoid - * examining all the data when it is not necessary; for operations such as "find the first - * string longer than 1000 characters", one need not examine all the input strings, just enough - * to find one that has the desired characteristics. (This behavior becomes even more important - * when the input stream is infinite and not merely large.) + * <h3>Stream operations</h3> * - * <p>Intermediate operations are further divided into <em>stateless</em> and <em>stateful</em> - * operations. Stateless operations retain no state from previously seen values when processing - * a new value; examples of stateless intermediate operations include {@code filter} and - * {@code map}. Stateful operations may incorporate state from previously seen elements in - * processing new values; examples of stateful intermediate operations include {@code distinct} - * and {@code sorted}. Stateful operations may need to process the entire input before - * producing a result; for example, one cannot produce any results from sorting a stream until - * one has seen all elements of the stream. As a result, under parallel computation, some - * pipelines containing stateful intermediate operations have to be executed in multiple passes. - * Pipelines containing exclusively stateless intermediate operations can be processed in a - * single pass, whether sequential or parallel. + * <p>Stream operations are divided into intermediate and terminal operations. + * Intermediate operations are further divided into <em>stateless</em> and + * <em>stateful</em> operations. Stateless operations retain no state from + * previously seen values when processing a new value--each value is processed + * independently of operations on other values. Stateless intermediate + * operations include {@code filter} and {@code map}. Stateful operations may + * incorporate state from previously seen elements in processing new values. + * Examples of stateful intermediate operations include {@code distinct} and + * {@code sorted}. Stateful operations may need to process the entire input + * before producing a result. For example, one cannot produce any results from + * sorting a stream until one has seen all elements of the stream. As a result, + * under parallel computation, some pipelines containing stateful intermediate + * operations have to be executed in multiple passes or may need to buffer + * significant data. Pipelines containing exclusively stateless intermediate + * operations can be processed in a single pass, whether sequential or parallel, + * with minimal data buffering. * - * <p>Further, some operations are deemed <em>short-circuiting</em> operations. An intermediate - * operation is short-circuiting if, when presented with infinite input, it may produce a - * finite stream as a result. A terminal operation is short-circuiting if, when presented with - * infinite input, it may terminate in finite time. (Having a short-circuiting operation is a - * necessary, but not sufficient, condition for the processing of an infinite stream to - * terminate normally in finite time.) + * <p>Further, some operations are deemed <em>short-circuiting</em> operations. + * An intermediate operation is short-circuiting if, when presented with + * infinite input, it may produce a finite stream as a result. A terminal + * operation is short-circuiting if, when presented with infinite input, it may + * terminate in finite time. (Having a short-circuiting operation is a + * necessary, but not sufficient, condition for the processing of an infinite + * stream to terminate normally in finite time.) * - * Terminal operations (such as {@code forEach} or {@code findFirst}) are always eager - * (they execute completely before returning), and produce a non-{@code Stream} result, such - * as a primitive value or a {@code Collection}, or have side-effects. + * <p>With the exception of the {@code iterator()} and {@code spliterator()} + * terminal operations (which are provided as an "escape hatch" to enable + * arbitrary stream traversals in the event that the existing operations are not + * sufficient to the task), terminal operations are always <em>eager</em>, + * executing completely before returning. * * <h3>Parallelism</h3> * - * <p>By recasting aggregate operations as a pipeline of operations on a stream of values, many - * aggregate operations can be more easily parallelized. A {@code Stream} can execute either - * in serial or in parallel. When streams are created, they are either created as sequential - * or parallel streams; the parallel-ness of streams can also be switched by the - * {@link java.util.stream Stream#sequential()} and {@link java.util.stream.Stream#parallel()} - * operations. The {@code Stream} implementations in the JDK create serial streams unless - * parallelism is explicitly requested. For example, {@code Collection} has methods + * <p>Processing values with an explicit loop is inherently serial. To more + * easily parallelize operations it helps to recast the computations as + * aggregate operations upon a stream of values. Aggregate operations behave as + * if each stream value is processed independently of (and possibly + * simultaneously to) all other values in the stream. All streams can execute + * either in serial or in parallel with an identical set of operations + * available for both modes. The stream + * implementations in the JDK create serial streams unless parallelism is + * explicitly requested. For example, {@code Collection} has methods * {@link java.util.Collection#stream} and {@link java.util.Collection#parallelStream}, - * which produce sequential and parallel streams respectively; other stream-bearing methods - * such as {@link java.util.stream.IntStream#range(int, int)} produce sequential - * streams but these can be efficiently parallelized by calling {@code parallel()} on the - * result. The set of operations on serial and parallel streams is identical. To execute the - * "sum of weights of blocks" query in parallel, we would do: + * which produce sequential and parallel streams respectively; other + * stream-bearing methods such as {@link java.util.stream.IntStream#range(int, int)} + * produce sequential streams but these can be efficiently parallelized by + * calling {@code parallel()} on the result. To execute the prior "sum of + * weights of widgets" query in parallel, we would do: + * + * <pre>{@literal + * int sumOfWeights = widgets.}<b>{@literal parallelStream()}</b>{@literal .filter(b -> b.getColor() == RED) + * .mapToInt(b -> b.getWeight()) + * .sum(); + * }</pre> + * + * <p>The only difference between the serial and parallel versions of this + * example is the creation of the initial stream, "{@code parallelStream()}" + * versus "{@code stream()}". Whether a stream will execute in serial or + * parallel can be determined with the {@code isParallel()} method and can also + * be modified before execution using the + * {@link java.util.stream.BaseStream#sequential()} and + * {@link java.util.stream.BaseStream#parallel()} operations. When the terminal + * operation is initiated, the stream pipeline is executed sequentially or in + * parallel depending on the mode of the stream on which it is invoked. + * + * <p>Except for operations identified as explicitly nondeterministic (such + * as {@code findAny())}, whether a stream executes sequentially or in parallel + * should not change the result of the computation. + * + * <p>Most stream operations accept parameters that describe user-specified + * behavior, which are often lambda expressions. To preserve correct behavior, + * these <em>behavioral parameters</em> must be <em>non-interfering</em>, and in + * most cases must be <em>stateless</em>. Such parameters are always instances + * of a <a href="../function/package-summary.html">functional interface</a> such + * as {@link java.util.function.Function}, and are often lambda expressions or + * method references. + * + * <h3><a name="Non-Interference">Non-interference</a></h3> + * + * Streams enable you to execute possibly-parallel aggregate operations over a + * variety of data sources, including even non-thread-safe collections such as + * {@code ArrayList}. This is possible only if we can prevent + * <em>interference</em> with the data source during the execution of a stream + * pipeline. (Except for the escape-hatch methods {@code iterator()} and + * {@code spliterator()}, execution begins when the terminal operation is + * invoked, and ends when the terminal operation completes.) For most data + * sources, preventing interference means ensuring that the data source is + * <em>not modified at all</em> during the execution of the stream pipeline. + * The concurrent collections, which are specifically + * designed to handle concurrent modification, do allow modification during + * stream execution. + * + * <p>Accordingly, behavioral parameters passed to stream methods should never + * modify the stream's data source. An implementation is said to + * <em>interfere</em> with the data source if it modifies, or causes to be + * modified, the stream's data source. The need for non-interference applies + * to all pipelines, not just parallel ones. Unless the stream source is + * concurrent, modifying a stream's data source during execution of a stream + * pipeline can cause exceptions, incorrect answers, or nonconformant results. + * + * <p>Further, results may be nondeterministic or incorrect if the behavioral + * parameters of stream operations are <em>stateful</em>. A stateful lambda + * (or other object implementing the appropriate functional interface) is one + * whose result depends on any state which might change during the execution + * of the stream pipeline. An example of a stateful lambda is: * * <pre>{@code - * int sumOfWeights = blocks.parallelStream().filter(b -> b.getColor() == RED) - * .mapToInt(b -> b.getWeight()) - * .sum(); + * Set<Integer> seen = Collections.synchronizedSet(new HashSet<>()); + * stream.parallel().map(e -> { if (seen.add(e)) return 0; else return e; })... + * }</pre> + * + * Here, if the mapping operation is performed in parallel, the results for the + * same input could vary from run to run, due to thread scheduling differences, + * whereas, with a stateless lambda expression the results would always be the + * same. + * + * For well-behaved stream sources, the source can be modified before the + * terminal operation commences and those modifications will be reflected in + * the covered elements. For example, consider the following code: + * + * <pre>{@code + * List<String> l = new ArrayList(Arrays.asList("one", "two")); + * Stream<String> sl = l.stream(); + * l.add("three"); + * String s = sl.collect(joining(" ")); * }</pre> * - * <p>The only difference between the serial and parallel versions of this example code is - * the creation of the initial {@code Stream}. Whether a {@code Stream} will execute in serial - * or parallel can be determined by the {@code Stream#isParallel} method. When the terminal - * operation is initiated, the entire stream pipeline is either executed sequentially or in - * parallel, determined by the last operation that affected the stream's serial-parallel - * orientation (which could be the stream source, or the {@code sequential()} or - * {@code parallel()} methods.) + * First a list is created consisting of two strings: "one"; and "two". Then a + * stream is created from that list. Next the list is modified by adding a third + * string: "three". Finally the elements of the stream are collected and joined + * together. Since the list was modified before the terminal {@code collect} + * operation commenced the result will be a string of "one two three". All the + * streams returned from JDK classes are well-behaved in this manner; for + * streams generated by other libraries, see + * <a href="package-summary.html#StreamSources">Low-level stream + * construction</a> for requirements for building well-behaved streams. + * + * ** The example isn't about concurrent sources ** + * <p>Some streams, particularly those whose stream sources are concurrent, can + * tolerate concurrent modification during execution of a stream pipeline. + * However, in no case should behavioral parameters to stream operations modify + * the stream source. For example, constructions like the following may fail + * to terminate, produce inaccurate results, or throw {@link java.util.ConcurrentModificationException}: + * <pre>{@code + * List<String> l = new ArrayList(Arrays.asList("one", "two")); + * Stream<String> sl = l.stream(); + * String s = sl.peek(s -> l.add("BAD LAMBDA")).collect(joining(" ")); + * }</pre> + * will fail as the {@code peek} operation will attempt to add the string + * "BAD LAMBDA" to the source after the terminal operation has commenced. + * + * <h3>Side-effects</h3> * - * <p>In order for the results of parallel operations to be deterministic and consistent with - * their serial equivalent, the function values passed into the various stream operations should - * be <a href="#NonInteference"><em>stateless</em></a>. + * Side-effects in behavioral parameters to stream operations are, in general, + * discouraged, as they can often lead to unwitting violations of the + * statelessness requirement, as well as other thread-safety hazards. Many + * computations where one might be tempted to use side effects can be more + * safely and efficiently expressed without side-effects, such as using + * <a href="package-summary.html#Reduction">reduction</a> instead of mutable + * accumulators. (Side-effects such as using {@code println()} for debugging + * purposes are usually harmless.) A small number of stream operations, such as + * {@code forEach()} and {@code peek()}, can only operate only via side-effects; + * these should be used with care. + * + * <p>As an example of how to transform a stream pipeline that inappropriately + * uses side-effects to one that does not, the following code searches a stream + * of strings for those matching a given regular expression, and puts the + * matches in a list. + * + * <pre>{@code + * ArrayList<String> results = new ArrayList<>(); + * stream.filter(s -> pattern.matcher(s).matches()) + * .forEach(s -> results.add(s)); // BAD! Uses side-effects! + * }</pre> + * + * This code uses side-effects unnecessarily. If executed in parallel, the + * non-thread-safety of {@code ArrayList} would cause incorrect results, and + * adding needed synchronization would cause contention, undermining the + * benefit of parallelism. And, using side-effects here are completely + * unnecessarily; the {@code forEach()} can be replaced with a reduction + * operation that is safer, more efficient, and more amenable to + * parallelization. + * + * <pre>{@code + * List<String>results = + * stream.filter(s -> pattern.matcher(s).matches()) + * .collect(Collectors.toList()); // No side-effects! + * }</pre> * * <h3><a name="Ordering">Ordering</a></h3> * - * <p>Streams may or may not have an <em>encounter order</em>. An encounter - * order specifies the order in which elements are provided by the stream to the - * operations pipeline. Whether or not there is an encounter order depends on - * the source, the intermediate operations, and the terminal operation. - * Certain stream sources (such as {@code List} or arrays) are intrinsically - * ordered, whereas others (such as {@code HashSet}) are not. Some intermediate - * operations may impose an encounter order on an otherwise unordered stream, - * such as {@link java.util.stream.Stream#sorted()}, and others may render an - * ordered stream unordered (such as {@link java.util.stream.Stream#unordered()}). - * Some terminal operations may ignore encounter order, such as - * {@link java.util.stream.Stream#forEach}. + * <p>Streams may or may not have a defined <em>encounter order</em>. Whether + * or not a stream has an encounter order depends on the source and the + * intermediate operations. Certain stream sources (such as {@code List} or + * arrays) are intrinsically ordered, whereas others (such as {@code HashSet}) + * are not. Some intermediate operations, such as {@code sorted()}, may impose + * an encounter order on an otherwise unordered stream, and others may render an + * ordered stream unordered, such as {@link java.util.stream.BaseStream#unordered()}. + * Further, some terminal operations may ignore encounter order, such as + * {@code forEach()}. * - * <p>If a Stream is ordered, most operations are constrained to operate on the + * <p>If a stream is ordered, most operations are constrained to operate on the * elements in their encounter order; if the source of a stream is a {@code List} * containing {@code [1, 2, 3]}, then the result of executing {@code map(x -> x*2)} * must be {@code [2, 4, 6]}. However, if the source has no defined encounter - * order, than any of the six permutations of the values {@code [2, 4, 6]} would - * be a valid result. Many operations can still be efficiently parallelized even - * under ordering constraints. + * order, then any of the six permutations of the values {@code [2, 4, 6]} would + * be a valid result. * * <p>For sequential streams, ordering is only relevant to the determinism * of operations performed repeatedly on the same source. (An {@code ArrayList} * is constrained to iterate elements in order; a {@code HashSet} is not, and * repeated iteration might produce a different order.) * - * <p>For parallel streams, relaxing the ordering constraint can enable - * optimized implementation for some operations. For example, duplicate - * filtration on an ordered stream must completely process the first partition - * before it can return any elements from a subsequent partition, even if those - * elements are available earlier. On the other hand, without the constraint of - * ordering, duplicate filtration can be done more efficiently by using - * a shared {@code ConcurrentHashSet}. There will be cases where the stream - * is structurally ordered (the source is ordered and the intermediate - * operations are order-preserving), but the user does not particularly care - * about the encounter order. In some cases, explicitly de-ordering the stream - * with the {@link java.util.stream.Stream#unordered()} method may result in + * <p>For parallel streams, relaxing the ordering constraint can sometimes enable + * more efficient implementation for some operations. Certain aggregate operations, + * such as filtering duplicates ({@code distinct()}) or grouped reductions + * ({@code Collectors.groupingBy()}) can be performed more efficiently using + * concurrent data structures rather than merging if ordering of elements + * is not relevant. Operations that are intrinsically tied to encounter order, + * such as {@code limit()} or {@code forEachOrdered()}, may require + * buffering to ensure proper ordering, undermining the benefit of parallelism. + * In cases where the stream is structurally ordered (the source is ordered and + * the intermediate operations are order-preserving), but the user does not + * particularly <em>care</em> about the encounter order, explicitly de-ordering + * the stream with {@link java.util.stream.BaseStream#unordered()} may result in * improved parallel performance for some stateful or terminal operations. - * - * <h3><a name="Non-Interference">Non-interference</a></h3> - * - * The {@code java.util.stream} package enables you to execute possibly-parallel - * bulk-data operations over a variety of data sources, including even non-thread-safe - * collections such as {@code ArrayList}. This is possible only if we can - * prevent <em>interference</em> with the data source during the execution of a - * stream pipeline. (Execution begins when the terminal operation is invoked, and ends - * when the terminal operation completes.) For most data sources, preventing interference - * means ensuring that the data source is <em>not modified at all</em> during the execution - * of the stream pipeline. (Some data sources, such as concurrent collections, are - * specifically designed to handle concurrent modification.) - * - * <p>Accordingly, lambda expressions (or other objects implementing the appropriate functional - * interface) passed to stream methods should never modify the stream's data source. An - * implementation is said to <em>interfere</em> with the data source if it modifies, or causes - * to be modified, the stream's data source. The need for non-interference applies to all - * pipelines, not just parallel ones. Unless the stream source is concurrent, modifying a - * stream's data source during execution of a stream pipeline can cause exceptions, incorrect - * answers, or nonconformant results. - * - * <p>Further, results may be nondeterministic or incorrect if the lambda expressions passed to - * stream operations are <em>stateful</em>. A stateful lambda (or other object implementing the - * appropriate functional interface) is one whose result depends on any state which might change - * during the execution of the stream pipeline. An example of a stateful lambda is: - * <pre>{@code - * Set<Integer> seen = Collections.synchronizedSet(new HashSet<>()); - * stream.parallel().map(e -> { if (seen.add(e)) return 0; else return e; })... - * }</pre> - * Here, if the mapping operation is performed in parallel, the results for the same input - * could vary from run to run, due to thread scheduling differences, whereas, with a stateless - * lambda expression the results would always be the same. - * - * <h3>Side-effects</h3> + * However, most stream pipelines, such as the "sum of weight of blocks" example + * above, can still be efficiently parallelized even under ordering constraints. * * <h2><a name="Reduction">Reduction operations</a></h2> * - * A <em>reduction</em> operation takes a stream of elements and processes them in a way - * that reduces to a single value or summary description, such as finding the sum or maximum - * of a set of numbers. (In more complex scenarios, the reduction operation might need to - * extract data from the elements before reducing that data to a single value, such as - * finding the sum of weights of a set of blocks. This would require extracting the weight - * from each block before summing up the weights.) + * A <em>reduction</em> operation (also called a <em>fold</em>) takes a sequence + * of input elements and combines them into a single summary result by repeated + * application of a combining operation, such as finding the sum or maximum of + * a set of numbers, or accumulating them into a list. The streams classes have + * many forms of reduction operations, called + * {@link java.util.stream.Stream#reduce(java.util.function.BinaryOperator) reduce()} + * and {@link java.util.stream.Stream#collect(java.util.stream.Collector) collect()}, + * for performing reductions. * - * <p>Of course, such operations can be readily implemented as simple sequential loops, as in: + * <p>Of course, such operations can be readily implemented as simple sequential + * loops, as in: * <pre>{@code * int sum = 0; * for (int x : numbers) { * sum += x; * } * }</pre> - * However, there may be a significant advantage to preferring a {@link java.util.stream.Stream#reduce reduce operation} - * over a mutative accumulation such as the above -- a properly constructed reduce operation is - * inherently parallelizable so long as the - * {@link java.util.function.BinaryOperator reduction operaterator} - * has the right characteristics. Specifically the operator must be - * <a href="#Associativity">associative</a>. For example, given a - * stream of numbers for which we want to find the sum, we can write: + * However, there may be a significant reason to prefer a reduce operation + * over a mutative accumulation such as the above. Not only is a reduction + * "more abstract" -- it operates on the stream as a whole rather than individual + * elements -- but a properly constructed reduce operation is inherently + * parallelizable, so long as the function(s) used to process the elements + * have the right characteristics. (Specifically, operators passed to + * {@code reduce()} must be <a href="package-summary.html#Associativity">associative</a>.) + * For example, given a stream of numbers for which we want to find the sum, we + * can write: * <pre>{@code - * int sum = numbers.reduce(0, (x,y) -> x+y); + * int sum = numbers.stream().reduce(0, (x,y) -> x+y); + * }</pre> + * or: + * <pre>{@code + * int sum = numbers.stream().reduce(0, Integer::sum); * }</pre> - * or more succinctly: + * + * <p>These reduction operations can run safely in parallel with almost no + * modification: * <pre>{@code - * int sum = numbers.reduce(0, Integer::sum); + * int sum = numbers.parallelStream().reduce(0, Integer::sum); * }</pre> * - * <p>(The primitive specializations of {@link java.util.stream.Stream}, such as - * {@link java.util.stream.IntStream}, even have convenience methods for common reductions, - * such as {@link java.util.stream.IntStream#sum() sum} and {@link java.util.stream.IntStream#max() max}, - * which are implemented as simple wrappers around reduce.) + * <p>The primitive stream classes, such as {@link java.util.stream.IntStream}, + * have convenience methods for common reductions, such as + * {@link java.util.stream.IntStream#sum() sum()} and {@link java.util.stream.IntStream#max() max()}. * - * <p>Reduction parallellizes well since the implementation of {@code reduce} can operate on - * subsets of the stream in parallel, and then combine the intermediate results to get the final - * correct answer. Even if you were to use a parallelizable form of the - * {@link java.util.stream.Stream#forEach(Consumer) forEach()} method - * in place of the original for-each loop above, you would still have to provide thread-safe - * updates to the shared accumulating variable {@code sum}, and the required synchronization - * would likely eliminate any performance gain from parallelism. Using a {@code reduce} method - * instead removes all of the burden of parallelizing the reduction operation, and the library - * can provide an efficient parallel implementation with no additional synchronization needed. + * <p>Reduction parallellizes well because the implementation of {@code reduce()} + * can operate on subsets of the stream in parallel, and then combine the + * intermediate results to get the final correct answer. Even if you were to + * use a parallelizable form of {@link java.util.stream.Stream#forEach(Consumer) forEach()} + * in place of the original for-each loop above, you would still have to provide + * thread-safe updates to the shared accumulating variable {@code sum}, and + * the required synchronization would likely eliminate any performance gain from + * parallelism. Using a {@code reduce()} method instead removes all of the + * burden of parallelizing the reduction operation, and the library can provide + * an efficient parallel implementation with no additional synchronization. * - * <p>The "blocks" examples shown earlier shows how reduction combines with other operations - * to replace for loops with bulk operations. If {@code blocks} is a collection of {@code Block} - * objects, which have a {@code getWeight} method, we can find the heaviest block with: + * <p>The "widgets" examples shown earlier shows how reduction combines with + * other operations to replace for loops with bulk operations. If {@code widgets} + * is a collection of {@code Widget} objects, which have a {@code getWeight} method, + * we can find the heaviest widget with: * <pre>{@code - * OptionalInt heaviest = blocks.stream() - * .mapToInt(Block::getWeight) - * .reduce(Integer::max); + * OptionalInt heaviest = widgets.stream() + * .mapToInt(Widget::getWeight) + * .reduce(Integer::max); * }</pre> * - * <p>In its more general form, a {@code reduce} operation on elements of type {@code <T>} - * yielding a result of type {@code <U>} requires three parameters: + * <p>In its more general form, a {@code reduce} operation on elements of type + * {@code <T>} yielding a result of type {@code <U>} requires three parameters: * <pre>{@code * <U> U reduce(U identity, - * BiFunction<U, ? super T, U> accumlator, + * BiFunction<U, ? super T, U> accumulator, * BinaryOperator<U> combiner); * }</pre> - * Here, the <em>identity</em> element is both an initial seed for the reduction, and a default - * result if there are no elements. The <em>accumulator</em> function takes a partial result and - * the next element, and produce a new partial result. The <em>combiner</em> function combines - * the partial results of two accumulators to produce a new partial result, and eventually the - * final result. + * Here, the <em>identity</em> element is both an initial seed for the reduction, + * and a default result if there are no elements. The <em>accumulator</em> + * function takes a partial result and the next element, and produce a new + * partial result. The <em>combiner</em> function combines the partial results + * of two accumulators to produce a new partial result, and eventually the final + * result. (The combiner is necessary in parallel reductions, where the input + * is partitioned, a partial accumulation is computed for each partition, and + * then the partial results are combined.) * - * <p>This form is a generalization of the two-argument form, and is also a generalization of - * the map-reduce construct illustrated above. If we wanted to re-cast the simple {@code sum} - * example using the more general form, {@code 0} would be the identity element, while - * {@code Integer::sum} would be both the accumulator and combiner. For the sum-of-weights - * example, this could be re-cast as: + * <p>This form is a generalization of the two-argument form, and is also a + * generalization of the map-reduce construct illustrated above. If we wanted + * to re-cast the simple {@code sum} example using the more general form, + * {@code 0} would be the identity element, while {@code Integer::sum} would be + * both the accumulator and combiner. For the sum-of-weights example, this could + * be re-cast as: * <pre>{@code - * int sumOfWeights = blocks.stream().reduce(0, - * (sum, b) -> sum + b.getWeight()) - * Integer::sum); + * int sumOfWeights = widgets.stream().reduce(0, + * (sum, b) -> sum + b.getWeight()) + * Integer::sum); * }</pre> - * though the map-reduce form is more readable and generally preferable. The generalized form - * is provided for cases where significant work can be optimized away by combining mapping and - * reducing into a single function. + * though the map-reduce form is more readable and therefore generally preferable. + * The generalized form is provided for cases where significant work can be + * optimized away by combining mapping and reducing into a single function. * - * <p>More formally, the {@code identity} value must be an <em>identity</em> for the combiner - * function. This means that for all {@code u}, {@code combiner.apply(identity, u)} is equal - * to {@code u}. Additionally, the {@code combiner} function must be - * <a href="#Associativity">associative</a> and must be compatible with the {@code accumulator} - * function; for all {@code u} and {@code t}, the following must hold: - * <pre>{@code - * combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t) - * }</pre> + * <p>More formally, the {@code identity} value must be an <em>identity</em> for + * the combiner function. This means that for all {@code u}, + * {@code combiner.apply(identity, u)} is equal to {@code u}. Additionally, the + * {@code combiner} function must be <a href="package-summary.html#Associativity">associative</a> and + * must be compatible with the {@code accumulator} function; for all {@code u} + * and {@code t}, {@code combiner.apply(u, accumulator.apply(identity, t))} must + * be {@code equals()} to {@code accumulator.apply(u, t)}. * * <h3><a name="MutableReduction">Mutable Reduction</a></h3> * - * A <em>mutable</em> reduction operation is similar to an ordinary reduction, in that it reduces - * a stream of values to a single value, but instead of producing a distinct single-valued result, it - * mutates a general <em>result container</em>, such as a {@code Collection} or {@code StringBuilder}, + * A <em>mutable reduction operation</em> accumulates input elements into a + * mutable result container, such as a {@code Collection} or {@code StringBuilder}, * as it processes the elements in the stream. * - * <p>For example, if we wanted to take a stream of strings and concatenate them into a single - * long string, we <em>could</em> achieve this with ordinary reduction: + * <p>If we wanted to take a stream of strings and concatenate them into a + * single long string, we <em>could</em> achieve this with ordinary reduction: * <pre>{@code * String concatenated = strings.reduce("", String::concat) * }</pre> * - * We would get the desired result, and it would even work in parallel. However, we might not - * be happy about the performance! Such an implementation would do a great deal of string - * copying, and the run time would be <em>O(n^2)</em> in the number of elements. A more - * performant approach would be to accumulate the results into a {@link java.lang.StringBuilder}, which - * is a mutable container for accumulating strings. We can use the same technique to + * We would get the desired result, and it would even work in parallel. However, + * we might not be happy about the performance! Such an implementation would do + * a great deal of string copying, and the run time would be <em>O(n^2)</em> in + * the number of characters. A more performant approach would be to accumulate + * the results into a {@link java.lang.StringBuilder}, which is a mutable + * container for accumulating strings. We can use the same technique to * parallelize mutable reduction as we do with ordinary reduction. * - * <p>The mutable reduction operation is called {@link java.util.stream.Stream#collect(Collector) collect()}, as it - * collects together the desired results into a result container such as {@code StringBuilder}. - * A {@code collect} operation requires three things: a factory function which will construct - * new instances of the result container, an accumulating function that will update a result - * container by incorporating a new element, and a combining function that can take two - * result containers and merge their contents. The form of this is very similar to the general + * <p>The mutable reduction operation is called {@link java.util.stream.Stream#collect(Collector) collect()}, + * as it collects together the desired results into a result container such + * as {@code StringBuilder}. A {@code collect} operation requires three things: + * a factory function to construct new instances of the result container, an + * accumulating function that will incorporate an input element into a result + * container, and a combining function that can take two result containers and + * merge their contents. The form of this is very similar to the general * form of ordinary reduction: * <pre>{@code * <R> R collect(Supplier<R> resultFactory, * BiConsumer<R, ? super T> accumulator, * BiConsumer<R, R> combiner); * }</pre> - * As with {@code reduce()}, the benefit of expressing {@code collect} in this abstract way is - * that it is directly amenable to parallelization: we can accumulate partial results in parallel - * and then combine them. For example, to collect the String representations of the elements - * in a stream into an {@code ArrayList}, we could write the obvious sequential for-each form: + * As with {@code reduce()}, the benefit of expressing {@code collect} in this + * abstract way is that it is directly amenable to parallelization: we can + * accumulate partial results in parallel and then combine them, so long as the + * accumulation and combining functions satisfy the appropriate requirements. + * For example, to collect the String representations of the elements in a + * stream into an {@code ArrayList}, we could write the obvious sequential + * for-each form: * <pre>{@code * ArrayList<String> strings = new ArrayList<>(); * for (T element : stream) { @@ -377,56 +512,72 @@ * (c, e) -> c.add(e.toString()), * (c1, c2) -> c1.addAll(c2)); * }</pre> - * or, noting that we have buried a mapping operation inside the accumulator function, more - * succinctly as: + * or, noting that we have buried a mapping operation inside the accumulator + * function, more succinctly as: * <pre>{@code * ArrayList<String> strings = stream.map(Object::toString) * .collect(ArrayList::new, ArrayList::add, ArrayList::addAll); * }</pre> - * Here, our supplier is just the {@link java.util.ArrayList#ArrayList() ArrayList constructor}, the - * accumulator adds the stringified element to an {@code ArrayList}, and the combiner simply - * uses {@link java.util.ArrayList#addAll addAll} to copy the strings from one container into the other. + * Here, our supplier is just the {@link java.util.ArrayList#ArrayList() + * ArrayList constructor}, the accumulator adds the stringified element to an + * {@code ArrayList}, and the combiner simply uses {@link java.util.ArrayList#addAll addAll} + * to copy the strings from one container into the other. * - * <p>As with the regular reduction operation, the ability to parallelize only comes if an - * <a href="package-summary.html#Associativity">associativity</a> condition is met. The {@code combiner} is associative - * if for result containers {@code r1}, {@code r2}, and {@code r3}: + * <p>Packaging mutable reductions into a collector has another advantage: + * composability. The class {@link java.util.stream.Collectors} contains a + * number of predefined factories for collectors, including some combinators + * that take one collector and produce a derived collector. For example, given + * the following collector that computes the sum of the salaries of a stream of + * employees: + * * <pre>{@code - * combiner.accept(r1, r2); - * combiner.accept(r1, r3); - * }</pre> - * is equivalent to - * <pre>{@code - * combiner.accept(r2, r3); - * combiner.accept(r1, r2); - * }</pre> - * where equivalence means that {@code r1} is left in the same state (according to the meaning - * of {@link java.lang.Object#equals equals} for the element types). Similarly, the {@code resultFactory} - * must act as an <em>identity</em> with respect to the {@code combiner} so that for any result - * container {@code r}: + * Collector<Employee, ?, Integer> summingSalaries + * = Collectors.summingInt(Employee::getSalary)) + * } </pre> + * + * If we wanted to create a collector to tabulate the sum of salaries by + * department, we could reuse {@code summingSalaries} using + * {@link java.util.stream.Collectors#groupingBy(java.util.function.Function, java.util.stream.Collector)}: + * * <pre>{@code - * combiner.accept(r, resultFactory.get()); - * }</pre> - * does not modify the state of {@code r} (again according to the meaning of - * {@link java.lang.Object#equals equals}). Finally, the {@code accumulator} and {@code combiner} must be - * compatible such that for a result container {@code r} and element {@code t}: - * <pre>{@code - * r2 = resultFactory.get(); - * accumulator.accept(r2, t); - * combiner.accept(r, r2); - * }</pre> - * is equivalent to: + * Map<Department, Integer> salariesByDept + * = employees.stream().collect(Collectors.groupingBy(Employee::getDepartment, + * summingSalaries)); + * } </pre> + * + * <p>As with the regular reduction operation, the ability to parallelize only + * comes if appropriate conditions are met. For any partially accumulated result, + * combining it with an empty result container must produce an equivalent + * result. That is, for a partially accumulated result {@code a} that is the + * result of any series of accumulator and combiner invocations, {@code a} must + * be equivalent to {@code combiner.apply(a, supplier.get())}. + * + * <p>Further, however the computation is split, it must produce an equivalent + * result. For any input elements {@code t1} and {@code t2}, the results + * {@code r1} and {@code r2} in the computation below must be equivalent: * <pre>{@code - * accumulator.accept(r,t); - * }</pre> - * where equivalence means that {@code r} is left in the same state (again according to the - * meaning of {@link java.lang.Object#equals equals}). + * A a1 = supplier.get(); + * accumulator.accept(a1, t1); + * accumulator.accept(a1, t2); + * R r1 = finisher.apply(a1); // result without splitting * - * <p> The three aspects of {@code collect}: supplier, accumulator, and combiner, are often very - * tightly coupled, and it is convenient to introduce the notion of a {@link java.util.stream.Collector} as - * being an object that embodies all three aspects. There is a {@link java.util.stream.Stream#collect(Collector) collect} - * method that simply takes a {@code Collector} and returns the resulting container. - * The above example for collecting strings into a {@code List} can be rewritten using a - * standard {@code Collector} as: + * A a2 = supplier.get(); + * accumulator.accept(a2, t1); + * A a3 = supplier.get(); + * accumulator.accept(a3, t2); + * R r2 = finisher.apply(combiner.apply(a2, a3)); // result with splitting + * } </pre> + * + * <p>Here, equivalence generally means according to {@link java.lang.Object#equals(Object)}, + * but in some cases equivalence may be relaxed to account for differences in + * order. + * + * <p> The three aspects of {@code collect}: supplier, accumulator, and combiner, + * are often very tightly coupled, and it is convenient to introduce the notion + * of a {@link java.util.stream.Collector} as being an object that embodies all + * three aspects. There is a {@link java.util.stream.Stream#collect(Collector) collect} + * method that simply takes a {@code Collector}. The above example for collecting + * strings into a {@code List} can be rewritten using a standard {@code Collector} as: * <pre>{@code * ArrayList<String> strings = stream.map(Object::toString) * .collect(Collectors.toList()); @@ -434,35 +585,33 @@ * * <h3><a name="ConcurrentReduction">Reduction, Concurrency, and Ordering</a></h3> * - * With some complex reduction operations, for example a collect that produces a - * {@code Map}, such as: + * With some complex reduction operations, for example a {@code collect()} that + * produces a {@code Map}, such as: * <pre>{@code * Map<Buyer, List<Transaction>> salesByBuyer * = txns.parallelStream() * .collect(Collectors.groupingBy(Transaction::getBuyer)); * }</pre> - * (where {@link java.util.stream.Collectors#groupingBy} is a utility function - * that returns a {@link java.util.stream.Collector} for grouping sets of elements based on some key) * it may actually be counterproductive to perform the operation in parallel. * This is because the combining step (merging one {@code Map} into another by key) * can be expensive for some {@code Map} implementations. * * <p>Suppose, however, that the result container used in this reduction * was a concurrently modifiable collection -- such as a - * {@link java.util.concurrent.ConcurrentHashMap ConcurrentHashMap}. In that case, - * the parallel invocations of the accumulator could actually deposit their results - * concurrently into the same shared result container, eliminating the need for the combiner to - * merge distinct result containers. This potentially provides a boost - * to the parallel execution performance. We call this a <em>concurrent</em> reduction. + * {@link java.util.concurrent.ConcurrentHashMap}. In that case, the parallel + * invocations of the accumulator could actually deposit their results + * concurrently into the same shared result container, eliminating the need for + * the combiner to merge distinct result containers. This potentially provides + * a boost to the parallel execution performance. We call this a <em>concurrent</em> + * reduction. * - * <p>A {@link java.util.stream.Collector} that supports concurrent reduction is marked with the - * {@link java.util.stream.Collector.Characteristics#CONCURRENT} characteristic. - * Having a concurrent collector is a necessary condition for performing a - * concurrent reduction, but that alone is not sufficient. If you imagine multiple - * accumulators depositing results into a shared container, the order in which - * results are deposited is non-deterministic. Consequently, a concurrent reduction - * is only possible if ordering is not important for the stream being processed. - * The {@link java.util.stream.Stream#collect(Collector)} + * <p>A {@link java.util.stream.Collector} that supports concurrent reduction is + * marked with the {@link java.util.stream.Collector.Characteristics#CONCURRENT} + * characteristic. However, a concurrent collection also has a downside. If + * multiple threads are depositing results concurrently into a shared container, + * the order in which results are deposited is non-deterministic. Consequently, + * a concurrent reduction is only possible if ordering is not important for the + * stream being processed. The {@link java.util.stream.Stream#collect(Collector)} * implementation will only perform a concurrent reduction if * <ul> * <li>The stream is parallel;</li> @@ -472,15 +621,16 @@ * <li>Either the stream is unordered, or the collector has the * {@link java.util.stream.Collector.Characteristics#UNORDERED} characteristic. * </ul> - * For example: + * You can ensure the stream is unordered by using the + * {@link java.util.stream.BaseStream#unordered()} method. For example: * <pre>{@code * Map<Buyer, List<Transaction>> salesByBuyer * = txns.parallelStream() * .unordered() * .collect(groupingByConcurrent(Transaction::getBuyer)); * }</pre> - * (where {@link java.util.stream.Collectors#groupingByConcurrent} is the concurrent companion - * to {@code groupingBy}). + * (where {@link java.util.stream.Collectors#groupingByConcurrent} is the + * concurrent equivalent of {@code groupingBy}). * * <p>Note that if it is important that the elements for a given key appear in the * order they appear in the source, then we cannot use a concurrent reduction, @@ -488,79 +638,73 @@ * be constrained to implement either a sequential reduction or a merge-based * parallel reduction. * - * <h2><a name="Associativity">Associativity</a></h2> + * <h3><a name="Associativity">Associativity</a></h3> * - * An operator or function {@code op} is <em>associative</em> if the following holds: + * An operator or function {@code op} is <em>associative</em> if the following + * holds: * <pre>{@code * (a op b) op c == a op (b op c) * }</pre> - * The importance of this to parallel evaluation can be seen if we expand this to four terms: + * The importance of this to parallel evaluation can be seen if we expand this + * to four terms: * <pre>{@code * a op b op c op d == (a op b) op (c op d) * }</pre> - * So we can evaluate {@code (a op b)} in parallel with {@code (c op d)} and then invoke {@code op} on - * the results. - * TODO what does associative mean for mutative combining functions? - * FIXME: we described mutative associativity above. + * So we can evaluate {@code (a op b)} in parallel with {@code (c op d)}, and + * then invoke {@code op} on the results. * - * <h2><a name="StreamSources">Stream sources</a></h2> - * TODO where does this section go? + * <h2><a name="StreamSources">Low-level stream construction</a></h2> * - * XXX - change to section to stream construction gradually introducing more - * complex ways to construct - * - construction from Collection - * - construction from Iterator - * - construction from array - * - construction from generators - * - construction from spliterator + * So far, all the stream examples have used methods like + * {@link java.util.Collection#stream()} or {@link java.util.Arrays#stream(Object[])} + * to obtain a stream. How are those stream-bearing methods implemented? + * + * <p>The class {@link java.util.stream.StreamSupport} has a number of low-level + * methods for creating a stream, all using some form of a {@link java.util.Spliterator}. + * A spliterator is the parallel analogue of an {@link java.util.Iterator}; it + * describes a (possibly infinite) collection of elements, with support for + * sequentially advancing, bulk traversal, and splitting off some portion of the + * input into another spliterator which can be processed in parallel. At the + * lowest level, all streams are driven by a spliterator. * - * XXX - the following is quite low-level but important aspect of stream constriction - * - * <p>A pipeline is initially constructed from a spliterator (see {@link java.util.Spliterator}) supplied by a stream source. - * The spliterator covers elements of the source and provides element traversal operations - * for a possibly-parallel computation. See methods on {@link java.util.stream.Streams} for construction - * of pipelines using spliterators. + * <p>There are a number of implementation choices in implementing a spliterator, + * nearly all of which are tradeoffs between simplicity of implementation and + * runtime performance of streams using that spliterator. The simplest, but + * least performant, way to create a spliterator is to create one from an iterator + * using {@link java.util.Spliterators#spliteratorUnknownSize(java.util.Iterator, int)}. + * While such a spliterator will work, it will likely offer poor parallel + * performance, since we have lost sizing information (how big is the underlying + * data set), as well as being constrained to a simplistic splitting algorithm. * - * <p>A source may directly supply a spliterator. If so, the spliterator is traversed, split, or queried - * for estimated size after, and never before, the terminal operation commences. It is strongly recommended - * that the spliterator report a characteristic of {@code IMMUTABLE} or {@code CONCURRENT}, or be - * <em>late-binding</em> and not bind to the elements it covers until traversed, split or queried for - * estimated size. + * <p>A higher-quality spliterator will provide balanced and known-size splits, + * accurate sizing information, and a number of other + * {@link java.util.Spliterator#characteristics() characteristics} of the + * spliterator or data that can be used by implementations to optimize + * execution. * - * <p>If a source cannot directly supply a recommended spliterator then it may indirectly supply a spliterator - * using a {@code Supplier}. The spliterator is obtained from the supplier after, and never before, the terminal + * <p>Spliterators for mutable data sources have an additional challenge; timing + * of binding to the data, since the data could change between the time the + * spliterator is created and the time the stream pipeline is executed. Ideally, + * a spliterator for a stream would report a characteristic of {@code IMMUTABLE} + * or {@code CONCURRENT}; if not it should be <a href="../Spliterator.html#binding"><em>late-binding</em></a>. + * If a source cannot directly supply a recommended spliterator, it may + * indirectly supply a spliterator using a {@code Supplier}, and construct a + * stream via the {@code Supplier}-accepting versions of + * {@link java.util.stream.StreamSupport#stream(Supplier, int, boolean) stream()}. + * The spliterator is obtained from the supplier only after the terminal * operation of the stream pipeline commences. * - * <p>Such requirements significantly reduce the scope of potential interference to the interval starting - * with the commencing of the terminal operation and ending with the producing a result or side-effect. See - * <a href="package-summary.html#Non-Interference">Non-Interference</a> for - * more details. - * - * XXX - move the following to the non-interference section - * - * <p>A source can be modified before the terminal operation commences and those modifications will be reflected in - * the covered elements. Afterwards, and depending on the properties of the source, further modifications - * might not be reflected and the throwing of a {@code ConcurrentModificationException} may occur. + * <p>These requirements significantly reduce the scope of potential interference + * between mutations of the stream source and execution of stream pipelines. + * Streams based on spliterators with the desired characteristics, or those using + * the Supplier-based factory forms, are immune to modifications of the data + * source prior to commencement of the terminal operation (provided the behavioral + * parameters to the stream operations meet the required criteria for non-interference + * and statelessness). See <a href="package-summary.html#Non-Interference">Non-Interference</a> + * for more details. * - * <p>For example, consider the following code: - * <pre>{@code - * List<String> l = new ArrayList(Arrays.asList("one", "two")); - * Stream<String> sl = l.stream(); - * l.add("three"); - * String s = sl.collect(joining(" ")); - * }</pre> - * First a list is created consisting of two strings: "one"; and "two". Then a stream is created from that list. - * Next the list is modified by adding a third string: "three". Finally the elements of the stream are collected - * and joined together. Since the list was modified before the terminal {@code collect} operation commenced - * the result will be a string of "one two three". However, if the list is modified after the terminal operation - * commences, as in: - * <pre>{@code - * List<String> l = new ArrayList(Arrays.asList("one", "two")); - * Stream<String> sl = l.stream(); - * String s = sl.peek(s -> l.add("BAD LAMBDA")).collect(joining(" ")); - * }</pre> - * then a {@code ConcurrentModificationException} will be thrown since the {@code peek} operation will attempt - * to add the string "BAD LAMBDA" to the list after the terminal operation has commenced. + * @since 1.8 */ +package java.util.stream; -package java.util.stream; +import java.util.function.BinaryOperator; \ No newline at end of file
--- a/src/share/lib/security/java.security-linux Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/lib/security/java.security-linux Wed Aug 14 15:53:13 2013 -0700 @@ -197,14 +197,12 @@ com.sun.org.apache.xalan.internal.xsltc.trax.,\ com.sun.org.apache.xalan.internal.xsltc.util.,\ com.sun.org.apache.xml.internal.res.,\ - com.sun.org.apache.xml.internal.security.,\ com.sun.org.apache.xml.internal.serializer.utils.,\ com.sun.org.apache.xml.internal.utils.,\ com.sun.org.glassfish.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ oracle.jrockit.jfr.,\ - org.jcp.xml.dsig.internal.,\ jdk.internal.,\ jdk.nashorn.internal.,\ jdk.nashorn.tools. @@ -241,14 +239,12 @@ com.sun.org.apache.xalan.internal.xsltc.trax.,\ com.sun.org.apache.xalan.internal.xsltc.util.,\ com.sun.org.apache.xml.internal.res.,\ - com.sun.org.apache.xml.internal.security.,\ com.sun.org.apache.xml.internal.serializer.utils.,\ com.sun.org.apache.xml.internal.utils.,\ com.sun.org.glassfish.,\ com.oracle.xmlns.internal.,\ com.oracle.webservices.internal.,\ oracle.jrockit.jfr.,\ - org.jcp.xml.dsig.internal.,\ jdk.internal.,\ jdk.nashorn.internal.,\ jdk.nashorn.tools.
--- a/src/share/native/common/check_code.c Tue Aug 13 10:42:37 2013 -0700 +++ b/src/share/native/common/check_code.c Wed Aug 14 15:53:13 2013 -0700 @@ -1301,7 +1301,10 @@ && clazz_info != context->currentclass_info && clazz_info != context->superclass_info) { int not_found = 1; - + jclass cb = object_fullinfo_to_classclass(context, clazz_info); + int is_interface = cb && JVM_IsInterface(env, cb); + + if (!is_interface) { jclass super = (*env)->GetSuperclass(env, context->class); while(super != 0) { jclass tmp_cb; @@ -1329,6 +1332,7 @@ } } } + } if (opcode == JVM_OPC_invokeinterface) { unsigned int args1; unsigned int args2;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-ng/README Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,20 @@ +Running the TestNG based unit tests requires downloading the TestNG +library and placing its jar file into the lib subdirectory: + + # download and install TestNG + wget http://testng.org/testng-x.y.z.zip + unzip testng-x.y.z.zip + mkdir lib + cp testng-x.y.z/testng.jar lib + + # run tests + ant test + +This will fail with a message like + + taskdef class org.testng.TestNGAntTask cannot be found + +if the TestNG jar file is not installed properly or if the wrong +version is present. (Check build.xml to find the version of TestNG +that is required.) Only the jar file is necessary. The unzipped +hierarchy can be removed.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-ng/agent/Readme Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,118 @@ +To run this: + 1. unzip the bundle to a dir called agent (you should have already done + so, to be reading this) + 2. have the latest version of lambda repo built fully and in your PATH + 3. you can use the pre-built dist/SerializationInjector.jar if not you + can build it with "ant -f agent/make/build.xml dist" + 4. before running this modify the conf/agent.props if required, but not + necessary. + 5. At the time of this writing only test-ng's test-libs are supported. + + make the following changes in the lambda/jdk/test-ng/build.xml + for the test-libs target, you might want to bump the heap from 2g + to 4g if your choice of platform and the JVM allows. + + <jvmarg value="-javaagent:/YOUR_PATH/agent/dist/SerializationInjectorAgent.jar=/YOUR_PATH/agent/conf/agent.props"/> + + actual patch shown in appendix + + 6. to run simply run the test-ng test as "ant test-libs" + Note: make sure are running with the very latest lambda build + 7. what you expect to see is in the Appendix. + Note: you will see on failure which is a serialization negative test + +Details: +Directory structure: + src - containing the sources + build - intermediate classes + dist - jar used as an agent + make - build.xml and an nbproject, feel free to modify it + requires jdk8 with asm that recognizes v52.0 classfiles + conf - config files + +Description of sources: + Agent.java: installs an agent. + SerializationInjector: does the following + * parses properties and initializes the output + * initialize and install the VM shutdown hook, to print the test + statistics. + * using asm interrogates the target class, upon encountering an + indy, will save the params, invokes the indy, and restores the + original args to the indy + * invoke the indy + * invoke the SerializeAndDeserializeTest (SAND). + with the lambdaObject returned by the indy and the + non-primitive parameters and these will be passed to SAND. + Primitives are excluded + TestLambdaSerialization: + * the SAND will check the arguments if they are Serializable, if so, + then the LamdbaObject will be Serialized and Deserialized, and the + deserialized LambdaObject will be returned, otherwise the original + LambdaObject will be returned, the outcome and exception if any + will be recorded. + + The final report will be printed out when the JVM terminates. + +Running the agent: +java -jar path_to/jtreg.jar \ + -javaoption:"-javaagent:path_to/agent/dist/SerializationInjectorAgent.jar=/w/ksrini/Lambda/agent.props" \ + test/foo/Lambda2.java + +agent.props is a property file by which one can specify + * a regular expression to exclude classes + * specify an output file to log details + * specifiy debugging if required + +ex: +com.oracle.lambda.exclude.files=^java.*|^sun.misc.*|^javax.* +com.oracle.lambda.log.file=some_path/agent.log +com.oracle.lambda.debug=true + +A note about logfiles, the agent is installed into jtreg, however jtreg +may invoke a test in a different VM as specified by the test and/or command +line. + +Thus if a file exists the agent will create a unique file in the specified +directory. Therefore it is recommended that the desired output directory is +clean before starting a new test. + + +Appendix: + +1. Patch to install the agent into test-ng +diff --git a/test-ng/build.xml b/test-ng/build.xml +--- a/test-ng/build.xml ++++ b/test-ng/build.xml +@@ -55,6 +56,7 @@ + <jvmarg value="-esa" /> + <jvmarg value="-Xverify:all" /> + <jvmarg value="-Xmx2g" /> ++ <jvmarg value="-javaagent:/YOUR_PATH/agent/dist/SerializationInjectorAgent.jar=/YOUR_PATH/agent/conf/agent.props"/> + </testng> + </target> + + +2. sample log +[testng] [TestNG] Running: + [testng] Ant suite + [testng] + [testng] + [testng] =============================================== + [testng] Ant suite + [testng] Total tests run: 7338, Failures: 1, Skips: 0 + [testng] =============================================== + [testng] + [testng] Lambda Serialization Test Status: + [testng] success: 2432727 + [testng] failed : 1099035 + [testng] Non Serializable Class(es): + [testng] NonSerializableObject: java.util.stream.op.Nodes$ArrayNode [92] + [testng] Throwable: + [testng] null + [testng] + [testng] NonSerializableObject: java.util.stream.op.Nodes$ConcNode [71] + [testng] Throwable: + [testng] null + [testng] + [testng] NonSerializableObject: org.openjdk.tests.java.util.stream.op.Group +........
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-ng/agent/conf/agent.props Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,6 @@ +#com.oracle.lambda.exclude.files=java..*|sun.*|javax.*|org.testng.*|com.google.*|com.sun.tools.javac.*|.*javatest.*|com.sun.tools.classfile.*|com.beust.*|.*TTShape.*|.*TestHarness.* +#com.oracle.lambda.log.file=/tmp/agent_log/agent.log +#com.oracle.lambda.debug=true +#com.oracle.lambda.serialize.file=$CUSTOM/serialize.list +com.oracle.lambda.storeStackTraces=true +#com.oracle.lambda.deserialization.workaround=true
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-ng/agent/conf/serialize.list Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,59 @@ +java/lang/invoke/MagicLambdaImpl +java/util/Optional +java/util/OptionalDouble +java/util/OptionalLong +java/util/OptionalInt +java/util/Spliterator +java/util/ArrayList\$SubList +java/util/SubList +java/util/RandomAccessSubList +java/util/HashMap\$.*Spliterator +java/util/TreeMap\$.*Spliterator +java/util/function/Function\$\$Lambda.* +java/util/function/Functions\$\$Lambda.* +java/util/stream/Collectors.* +java/util/stream/LambdaTestHelpers\$\$Lambda.* +java/util/stream/Nodes.*NodeBuilder +java/util/stream/Nodes\$.*Node +java/util/stream/AbstractSpinedBuffer +java/util/stream/SpinedBuffer +java/util/stream/.*Pipeline +java/util/stream/AbstractPipeline\$.* +java/util/stream/IntermediateOp +java/util/stream/.*StreamTestData\$.* +java/util/stream/.*NodeTest +java/util/stream/TestData\$AbstractTestData.* +org/openjdk/tests/java/util/stream/OpTestCase\$TestData +org/openjdk/tests/java/util/stream/OpTestCase\$ExerciseDataStreamBuilder +org/openjdk/tests/java/util/stream/OpTestCase\$ExerciseDataTerminalBuilder +org/openjdk/tests/java/util/stream/IntNodeTest +org/openjdk/tests/java/util/stream/UnorderedStreamTest +org/openjdk/tests/java/util/stream/GroupByOpTest +org/openjdk/tests/java/util/stream/TabulatorsTest +org/openjdk/tests/java/util/stream/TabulatorsTest\$.*Assertion +org/openjdk/tests/java/util/stream/StreamLinkTest +org.openjdk.tests.java.util.stream.SpliteratorLateBindingFailFastTest\$SpliteratorDataBuilder.* +org.openjdk.tests.java.util.stream.SpliteratorTraversingAndSplittingTest\$SpliteratorDataBuilder.* +org.openjdk.tests.java.util.stream.SliceOpTest.* +org.openjdk.tests.java.util.stream.MatchOpTest.* +org.openjdk.tests.java.util.stream.InfiniteStreamWithLimitOpTest.* +java.util.HashMap\$Values +java.util.LinkedHashMap\$KeyIterator +java.util.HashMap\$Entry +java.util.LinkedList\$Node +java.util.TreeMap\$Entry +java.util.HashMap\$EntrySet +java.util.HashMap\$KeySet +java.util.IdentityHashMap\$KeySet +java.util.LinkedHashMap\$ValueIterator +java.util.TreeMap\$KeySet +java.util.TreeMap\$EntrySet +java.util.TreeMap\$Values +java.util.WeakHashMap +java.util.WeakHashMap\$EntrySet +java.util.WeakHashMap\$KeySet +java.util.WeakHashMap\$Values +java.util.LinkedHashMap\$EntryIterator +java.util.IdentityHashMap\$EntrySet +java.util.IdentityHashMap\$Values +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-ng/agent/make/build.xml Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,75 @@ +<project name="SerializationInjectorAgent" default="dist" basedir=".."> + <!-- Requires ant 1.6.1+ and Lambda (NOT TL)+--> + + <!-- set global properties for this build --> + <property name="build.sysclasspath" value="ignore"/> + <property name="build.dir" value="../../../build/test-ng/agent" /> + <property name="src" value="${basedir}/src"/> + <property name="make" value="${basedir}/make"/> + <property name="conf" value="${basedir}/conf"/> + <property name="classes" value="${build.dir}/classes"/> + <property name="dist" value="${build.dir}/dist"/> + + <target name="init"> + <!-- Create the time stamp --> + <tstamp/> + <!-- Create the build directory structure used by compile --> + <mkdir dir="${build.dir}"/> + <mkdir dir="${dist}"/> + <mkdir dir="${classes}"/> + <echo message="${java.home}"/> + </target> + + <target name="compile" depends="init"> + <!-- Compile the java code from ${src} into ${build} --> + <javac + source="1.7" + srcdir="${src}" + destdir="${build.dir}/classes" + verbose="no" + debug="on" + > + <compilerarg value="-XDignore.symbol.file=true"/> + </javac> + </target> + <target name="dist" depends="compile"> + <!-- Put everything in jar file --> + <jar destfile="${dist}/SerializationInjectorAgent.jar"> + <manifest> + <attribute name="Main-Class" value="com.oracle.lambda.Main"/> + <attribute name="PreMain-Class" value="com.oracle.lambda.Agent"/> + </manifest> + <fileset dir="${classes}"/> + </jar> + </target> + <target name="run" depends="dist"> + <condition property="run.arg" value="-o /tmp/ksrini/XX.jar -f /w/ksrini/JDK/hg-lambda/jdk8/jdk/test-ng/serialize.list /tmp/ksrini/rt.jar"> + <not> + <isset property="run.arg"/> + </not> + </condition> + <java jar="${dist}/SerializationInjectorAgent.jar" + dir="." + fork="true" + > + <jvmarg value="-Dcom.oracle.lambda.debug=true"/> + <arg value="${run.arg}"/> + </java> + </target> + <target name="clean"> + <!-- Delete the ${build} and ${dist} directory trees --> + <delete dir="${build}"/> + <delete dir="${dist}"/> + </target> + <target name="bundle" depends="clean, dist"> + <zip destfile="${dist}/bundle.zip"> + <zipfileset dir="${src}"/> + <zipfileset file="${dist}/SerializationInjectorAgent.jar"/> + <zipfileset dir="${make}" excludes="**/netbeans/**"/> + <zipfileset dir="${conf}"/> + <zipfileset file="Readme"/> + </zip> + + </target> + +</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-ng/agent/src/com/oracle/lambda/Agent.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2012, 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 com.oracle.lambda; + +import java.io.IOException; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.security.ProtectionDomain; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.TreeSet; + +public class Agent { + public static void premain(String agentArgs, Instrumentation instrumentation) + throws IOException, NoSuchFieldException, IllegalAccessException { + // If run without a config file, all we do is set up the shutdown hook + if (agentArgs != null) { + final SerializationInjector si = new SerializationInjector(); + instrumentation.addTransformer(new ClassFileTransformer() { + @Override + public byte[] transform(final ClassLoader cl, String string, + Class<?> type, ProtectionDomain pd, + byte[] bytes) throws IllegalClassFormatException { + return si.transformClass(cl, bytes, EnumSet.noneOf(SerializationInjector.Options.class)); + } + }); + } + else { + // Horrible horrible hack. + // TreeSet/HashSet are based off of HashSet/HashMap, and serializing their spliterators + // tries to serialize the underlying map, which uses a non-serializable sentinel object. + // This hack swaps that out for a serializable sentinel + Field f = HashSet.class.getDeclaredField("PRESENT"); + f.setAccessible(true); + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); + f.set(null, Boolean.TRUE); + + f = TreeSet.class.getDeclaredField("PRESENT"); + f.setAccessible(true); + modifiersField.setInt(f, f.getModifiers() & ~Modifier.FINAL); + f.set(null, Boolean.TRUE); + + TestLambdaSerialization.initializeShutDownHook(); + } + } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-ng/agent/src/com/oracle/lambda/Main.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2012, 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 com.oracle.lambda; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.regex.Pattern; + +import jdk.internal.org.objectweb.asm.ClassReader; +import static java.nio.file.StandardOpenOption.*; +import static java.nio.file.StandardCopyOption.*; + +/** + * + * @author kumasrin + */ +public class Main { + + static void usage() { + System.out.println("usage: -o output_dir path_to/SomeClass.class"); + System.out.println("usage: -o output_dir path_to_classes"); + System.out.println("usage: -o output_dir -s path_to_jar"); + System.exit(1); + } + + /* + * a standalone tester for this + */ + public static void main(String... inargs) throws Exception { + String[] args = inargs; + if (inargs != null && inargs.length == 1) { + args = inargs[0].split("\\s"); + } + if (args == null || args.length < 3) { + usage(); + } + File outDir = null; + File inFile = null; + boolean serializeOnly = false; + for (int i = 0 ; i < args.length ; i++) { + switch (args[i]) { + case "-o": + i++; + outDir = new File(args[i]); + break; + case "-s": + serializeOnly = true; + break; + default: + inFile = new File(args[i]); + } + } + if (inFile.isDirectory()) { + if (outDir.exists()) { + outDir.mkdirs(); + } + URL[] urls = {inFile.toURI().toURL()}; + ClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); + try (DirectoryStream<Path> paths = + Files.newDirectoryStream(inFile.toPath(), "*")) { + for (Path p : paths) { + doFile(inFile, outDir, p.toFile(), cl); + } + } + } else if (inFile.getName().endsWith(".jar")) { + if (outDir.exists()) { + outDir.mkdirs(); + } + URL[] urls = {inFile.toURI().toURL()}; + ClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); + Pattern pattern = Pattern.compile(".*"); + EnumSet<SerializationInjector.Options> options + = serializeOnly ? EnumSet.of(SerializationInjector.Options.SERIALIZE_ONLY) : EnumSet.noneOf(SerializationInjector.Options.class); + doJar(inFile, outDir, cl, pattern, options); + } else { + outDir.mkdirs(); + URL[] urls = {inFile.getParentFile().toURI().toURL()}; + ClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); + doFile(null, outDir, inFile, cl); + } + } + static void copyStream(InputStream in, OutputStream out) throws IOException { + byte[] buf = new byte[8192]; + int n = in.read(buf); + while (n > 0) { + out.write(buf, 0, n); + n = in.read(buf); + } + } + + static byte[] readFully(InputStream is) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + copyStream(is, baos); + return baos.toByteArray(); + } + + static void doJar(File inFile, File outDir, ClassLoader cl, Pattern pattern, + EnumSet<SerializationInjector.Options> options) throws IOException { + JarFile jf = new JarFile(inFile); + byte[] buffer; + SerializationInjector si = new SerializationInjector(); + FileSystem fs = null; + boolean jarOut = outDir.getName().endsWith(".jar"); + Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxrwxrwx"); + FileAttribute<Set<PosixFilePermission>> attr = + PosixFilePermissions.asFileAttribute(perms); + Map<String, String> env = new HashMap<>(); + env.put("create", "true"); + if (jarOut) { + URI uri = URI.create("jar:file:" + outDir.getAbsolutePath()); + if (outDir.exists()) + outDir.delete(); + fs = FileSystems.newFileSystem(uri, env); + } else { + fs = FileSystems.getDefault(); + } + for (JarEntry je : Collections.list(jf.entries())) { + String fname = je.getName(); + if (fname.endsWith("/")) continue; + if (pattern.matcher(fname).matches()) { + buffer = readFully(jf.getInputStream(je)); + if (fname.endsWith(".class")) + buffer = si.transformClass(cl, buffer, options); + Path opath = jarOut ? fs.getPath(fname) : fs.getPath(outDir.getAbsolutePath(), fname); + Files.createDirectories(opath.getParent(), attr); + Files.write(opath, buffer, CREATE, TRUNCATE_EXISTING); + } + } + if (jarOut) + fs.close(); + } + + static String getRelativePath(File baseDir, File inFile) { + String bname = baseDir.getAbsolutePath(); + String fname = inFile.getAbsolutePath(); + return fname.substring(bname.length() + 1); + } + + static void doFile(File inDir, File outDir, File iFile, ClassLoader cl) throws Exception { + if (!iFile.getName().endsWith(".class")) { + Path p = new File(getRelativePath(inDir, iFile)).toPath(); + Files.copy(iFile.toPath(), p, COPY_ATTRIBUTES, REPLACE_EXISTING); + return; + } + byte[] classBuffer = Files.readAllBytes(iFile.toPath()); + ClassReader cr = new ClassReader(classBuffer); + File oFile = new File(outDir, cr.getClassName() + ".class"); + SerializationInjector si = new SerializationInjector(); + System.err.print("Transforming: " + iFile.getAbsolutePath()); + System.err.println(" to: " + oFile.getAbsolutePath()); + oFile.getParentFile().mkdirs(); + Files.write(oFile.toPath(), + si.transformClass(cl, classBuffer, EnumSet.noneOf(SerializationInjector.Options.class)), CREATE, TRUNCATE_EXISTING); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-ng/agent/src/com/oracle/lambda/SerializationInjector.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2012, 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 com.oracle.lambda; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Pattern; + +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Handle; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import static jdk.internal.org.objectweb.asm.Opcodes.*; +import jdk.internal.org.objectweb.asm.Type; + +import static java.nio.file.StandardOpenOption.*; + + +public class SerializationInjector { + static final String PREFIX = "com.oracle.lambda"; + static final String INCLUDES = "java.util.*|org.openjdk.tests.java.*"; // also should include java.util... + static final String SERIALIZE = ""; + + // properties that can be set + static final String INCLUDE_FILES = PREFIX + ".include.files"; + static final String SERIALIZE_FILE = PREFIX + ".serialize.file"; + static final String STORE_FRAMES = PREFIX + ".store.frames"; + static final String LOGFILE = PREFIX + ".log.file"; + static final String DEBUG = PREFIX + ".debug"; + static final String NROUNDS = PREFIX + ".nrounds"; + + static boolean storeFrames = true; + static boolean deserializationWorkaround; + + final DebugPrint debug; + final Pattern includePattern; + final Pattern serializePattern; + final boolean serializeOnly; + + public enum Options { SERIALIZE_ONLY, FORCE_SERIALIZE } + + public SerializationInjector() throws IOException { + includePattern = propToPattern(INCLUDE_FILES, INCLUDES); + serializePattern = fileToPattern(SERIALIZE_FILE, SERIALIZE); + debug = new DebugPrint(); + TestLambdaSerialization.initializeShutDownHook(); + storeFrames = Boolean.getBoolean(STORE_FRAMES); + deserializationWorkaround = false; + serializeOnly = false; + } + final Pattern propToPattern(String propKey, String defValue) { + return Pattern.compile(System.getProperty(propKey, defValue)); + } + final Pattern fileToPattern(String propKey, String defValue) throws IOException { + String fname = System.getProperty(propKey, null); + if (fname != null) { + File pFile = new File(fname); + if (pFile.canRead()) { + return fileToPattern(pFile); + } + } + return Pattern.compile(defValue); + } + final Pattern fileToPattern(File inFile) throws IOException { + List<String> serList = Files.readAllLines(inFile.toPath(), + Charset.defaultCharset()); + String regStr = ""; + boolean first = true; + for (String s : serList) { + String x = s.trim(); + if (x.startsWith("#")) { + continue; + } + regStr = regStr.concat((first ? "" : "|") + x); + first = false; + } + System.out.println("serialization: " + regStr); + return Pattern.compile(regStr); + } + + byte[] transformClass(ClassLoader cl, byte[] classBuffer, EnumSet<Options> options) { + byte[] xBuffer = classBuffer; + ClassReader classReader = new ClassReader(xBuffer); + String cname = classReader.getClassName(); + + if (options.contains(Options.FORCE_SERIALIZE) || serializePattern.matcher(cname).matches()) { + debug.println("Implementing Serialization: " + cname); + xBuffer = injectSerialization(xBuffer, cl); + } + if (serializeOnly || options.contains(Options.SERIALIZE_ONLY)) { + return xBuffer; + } + if (!includePattern.matcher(cname).matches()) { + debug.println("Excluding SAND: " + cname); + return xBuffer; + } + debug.println("Visiting: " + cname); + return injectSAND(xBuffer, cl); + } + + byte[] injectSAND(byte[] classBuffer, ClassLoader cl) { + Map<String, Integer> locals = getLocals(classBuffer); + ClassReader classReader = new ClassReader(classBuffer); + ClassWriter classWriter = new ClassWriter(classReader, + ClassWriter.COMPUTE_MAXS /*| ClassWriter.COMPUTE_FRAMES*/); + classReader.accept(makeSANDVisitor(classWriter, locals), 0); + byte[] byteArray = classWriter.toByteArray(); +// CheckClassAdapter.verify(new ClassReader(byteArray), cl, false, +// new PrintWriter(System.err)); + return byteArray; + } + + Map<String, Integer> getLocals(byte[] classBuffer) { + ClassReader cr = new ClassReader(classBuffer); + final Map<String, Integer> locals = new HashMap<>(); + cr.accept(new ClassVisitor(ASM4, null) { + String cname; + @Override + public void visit(int version, int access, String name, + String signature, String superName, + String[] interfaces) { + cname = name; + super.visit(version, access, name, signature, superName, interfaces); + } + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + final String mid = cname + "+" + name + "+" + desc + "+" + signature; + return new MethodVisitor(ASM4, + super.visitMethod(access, name, desc, signature, exceptions)) { + @Override + public void visitMaxs(int maxStack, int maxLocals) { + super.visitEnd(); + super.visitMaxs(maxStack, maxLocals); + //debug.println("Pre-Visiting:" + mid + ", stack=" + maxLocals); + locals.put(mid, maxLocals); + } + }; + } + }, 0); + + return locals; + } + + ClassVisitor makeSANDVisitor(ClassWriter cw, final Map<String, Integer>methodLocals) { + return new ClassVisitor(ASM4, cw) { + String cname = null; + @Override + public void visit(int version, int access, String name, + String signature, String superName, + String[] interfaces) { + cname = name; + super.visit(version, access, name, signature, superName, interfaces); + } + @Override + public MethodVisitor visitMethod(int access, final String name, + final String desc, final String signature, + String[] exceptions) { + if (name.equals("$deserializeLambda$")) { + return super.visitMethod(access, name, desc, signature, exceptions); + } + return new MethodVisitor(ASM4, + super.visitMethod(access, name, desc, signature, exceptions)) { + final String mid = cname + "+" + name + "+" + desc + "+" + signature; + int locals = methodLocals.containsKey(mid) ? methodLocals.get(mid) : 0; + + int saveArgs(Type[] types) { + final int n = locals + 1; + final int tlen = types.length; + int count = 0; + // store in LIFO order + for (int i = 0; i < tlen; i++) { + int pos = tlen - 1 - i; + super.visitVarInsn(types[pos].getOpcode(ISTORE), n + pos); + } + // restore back in FIFO order + for (int i = 0 ; i < tlen; i++) { + if (isNotPrimitive(types[i])) { + count++; + } + super.visitVarInsn(types[i].getOpcode(ILOAD), n + i); + } + return count; + } + boolean isNotPrimitive(Type t) { + int i = t.getSort(); + return i == Type.OBJECT || i == Type.ARRAY; + } + void restoreArgs(Type[] types, int count) { + final int n = locals + 1; + final int tlen = types.length; + // create the array + visitIntInsn(BIPUSH, count); + visitTypeInsn(ANEWARRAY, "java/lang/Object"); + int ncount = 0; + for (int i = 0 ; i < tlen ; i++ ) { + Type t = types[i]; + if (isNotPrimitive(t)) { + debug.println("injecting:" + t); + super.visitInsn(DUP); + super.visitIntInsn(BIPUSH, ncount); + super.visitVarInsn(t.getOpcode(ILOAD), n + i); + super.visitInsn(AASTORE); + ncount++; + } + } + } + @Override + public void visitInvokeDynamicInsn(String callsite, + String sig, Handle handle, Object... bsmArgs) { + // instrument only! lambda objects + final boolean needsTransform = handle.getName().equals("metafactory"); + final boolean injectSand = needsTransform || + handle.getName().equals("altMetafactory"); + if (!injectSand) { + super.visitInvokeDynamicInsn(callsite, sig, handle, bsmArgs); + return; + } + debug.println("indy:" + mid + ", locals = " + locals); + debug.println(" fixing: " + callsite + ":" + sig); + Handle nHandle = handle; + Object[] nbsmArgs = bsmArgs; + if (needsTransform) { + String ndesc = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"; + nHandle = new Handle(handle.getTag(), handle.getOwner(), + "altMetafactory", ndesc); + + ArrayList<Object> largs = new ArrayList<>(Arrays.asList(bsmArgs)); + largs.add(1); + largs.add(Type.getType(java.io.Serializable.class)); + nbsmArgs = new Object[largs.size()]; + largs.toArray(nbsmArgs); + debug.println("old: " + handle.toString()); + debug.println("new: " + nHandle.toString()); + } + Type[] types = Type.getArgumentTypes(sig); + int count = saveArgs(types); + super.visitInvokeDynamicInsn(callsite, sig, nHandle, nbsmArgs); + restoreArgs(types, count); + super.visitMethodInsn(INVOKESTATIC, + "com/oracle/lambda/TestLambdaSerialization", + (needsTransform && deserializationWorkaround) + ? "serializeOnly" + : "serializeAndDeserialize", + "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); + } + }; + } + }; + } + + void dumpClass(byte[] array, String prefix) { + try { + Path f = Files.createTempFile(new File("/tmp").toPath(), + prefix, ".class"); + System.out.println("dumping to:" + f.toString()); + Files.write(f, array, CREATE, TRUNCATE_EXISTING); + } catch (IOException ex) { + Logger.getLogger(SerializationInjector.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private byte[] injectSerialization(byte[] classBuffer, ClassLoader cl) { + ClassReader classReader = new ClassReader(classBuffer); + ClassWriter classWriter = new ClassWriter(classReader, + ClassWriter.COMPUTE_MAXS /*| ClassWriter.COMPUTE_FRAMES*/); + boolean hasNoArgCtor = hasNoArgCtor(classBuffer); + classReader.accept(makeSerializationVisitor(classWriter, !hasNoArgCtor), 0); + byte[] byteArray = classWriter.toByteArray(); + // Needed to comment this out because some classes are actually loaded during this verification + // and they fail the package name test (java.*) +// CheckClassAdapter.verify(new ClassReader(byteArray), cl, false, +// new PrintWriter(System.err)); + +// dumpClass(classBuffer, "pre-serialization"); +// dumpClass(byteArray, "post-serialization"); + return byteArray; + } + + private boolean hasNoArgCtor(byte[] buffer) { + final AtomicBoolean foundNoArg = new AtomicBoolean(); + new ClassReader(buffer).accept(new ClassVisitor(ASM4, null) { + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (name.equals("<init>") && desc.equals("()V")) + foundNoArg.set(true); + return super.visitMethod(access, name, desc, signature, exceptions); + } + }, 0); + return foundNoArg.get(); + } + + ClassVisitor makeSerializationVisitor(final ClassWriter cw, final boolean injectNoArg) { + return new ClassVisitor(ASM4, cw) { + @Override + public void visit(int version, int access, String name, + String signature, String superName, + String[] interfaces) { + String sername = java.io.Serializable.class.getName().replace('.', '/'); + boolean foundSer = false; + for (String x : interfaces) { + foundSer = foundSer || x.contains(sername); + } + if (!foundSer) { + debug.println("Injecting serialization: " + name); + int n = interfaces.length; + interfaces = Arrays.copyOf(interfaces, n + 1); + interfaces[n] = sername; + if (signature != null) + signature += "L" + sername + ";"; + } + super.visit(version, access, name, signature, superName, interfaces); + if (injectNoArg && (access & ACC_INTERFACE) == 0) { + debug.println("Injecting <init>: " + name); + MethodVisitor ctor = visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + ctor.visitCode(); + ctor.visitVarInsn(ALOAD, 0); + ctor.visitMethodInsn(INVOKESPECIAL, superName, "<init>", "()V"); + ctor.visitInsn(RETURN); + ctor.visitMaxs(-1, -1); + ctor.visitEnd(); + } + } + }; + } +} +class DebugPrint { + final PrintStream out; + final boolean mustPrint; + final boolean shouldClose; + + DebugPrint() throws IOException { + mustPrint = Boolean.getBoolean(SerializationInjector.DEBUG); + String output = System.getProperty(SerializationInjector.LOGFILE, "out"); + switch (output) { + case "out": + out = System.out; + shouldClose = false; + break; + case "err": + out = System.err; + shouldClose = false; + break; + default: + File f = new File(output); + if (f.exists()) { + File dir = f.getParentFile(); + f = File.createTempFile("agent", ".log", dir); + } + FileOutputStream fos = new FileOutputStream(f); + out = new PrintStream(new BufferedOutputStream(fos)); + shouldClose = true; + } + } + + void println(String s) { + if (mustPrint) + out.println(s); + } + void print(String s) { + if (mustPrint) + out.print(s); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-ng/agent/src/com/oracle/lambda/TestLambdaSerialization.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2012, 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 com.oracle.lambda; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +public class TestLambdaSerialization { + + private static final Map<String, SerializableStatus> nonSerializableClasses = + Collections.synchronizedMap(new HashMap<String, SerializableStatus>()); + private static final Map<String, SerializableStatus> serializableButFailedClasses = + Collections.synchronizedMap(new HashMap<String, SerializableStatus>()); + private static final Map<String, SerializableStatus> deserFailures = + Collections.synchronizedMap(new HashMap<String, SerializableStatus>()); + private static final CounterSet serCounters = new CounterSet(); + private static final CounterSet deserCounters = new CounterSet(); + private static final int nRounds = + Integer.parseInt(System.getProperty(SerializationInjector.NROUNDS, "1")); + + public static void printStats() { + PrintStream rpt = System.out; + try { + rpt.println("Lambda Serialization Test Status:"); + rpt.println(" serializations attempted: " + serCounters.attempted); + rpt.println(" serializations succeeded: " + serCounters.succeeded); + rpt.println(" serializations failed: " + serCounters.failed); + rpt.println(" serializations not attempted: " + serCounters.notAttempted); + rpt.println(" deserializations attempted: " + deserCounters.attempted); + rpt.println(" deserializations succeeded: " + deserCounters.succeeded); + rpt.println(" deserializations failed: " + deserCounters.failed); + rpt.println(" deserializations not attempted: " + deserCounters.notAttempted); + if (nonSerializableClasses.size() > 0) { + rpt.println("Non Serializable Class(es):"); + for (Entry<String, SerializableStatus> e : nonSerializableClasses.entrySet()) { + rpt.println(" " + e.getValue()); + } + } + if (serializableButFailedClasses.size() > 0) { + rpt.println("Serializable class(es) but failed serialization:"); + for (Entry<String, SerializableStatus> e : serializableButFailedClasses.entrySet()) { + rpt.println(" " + e.getValue()); + } + } + if (deserFailures.size() > 0) { + rpt.println("Failed deserialization:"); + for (Entry<String, SerializableStatus> e : deserFailures.entrySet()) { + rpt.println(" " + e.getValue()); + } + } + } finally { + rpt.flush(); + } + } + + public static Object serializeOnly(Object lambdaObj, Object... args) { + Object lObj = lambdaObj; + for (int i = 0 ; i < nRounds; i++) + lObj = serializeAndDeserialize0(true, lObj, args); + return lObj; + } + + public static Object serializeAndDeserialize(Object lambdaObj, Object... args) { + Object lObj = lambdaObj; + for (int i = 0; i < nRounds; i++) + lObj = serializeAndDeserialize0(false, lObj, args); + return lObj; + } + + private static Object serializeAndDeserialize0(boolean serializeonly, + Object lambdaObj, Object... args) { + boolean argsSerializable = true; + for (Object a : args) { + if (!(a instanceof java.io.Serializable)) { + argsSerializable = false; + String name = a.getClass().getName(); + SerializableStatus s = nonSerializableClasses.get(name); + if (s == null) { + s = new SerializableStatus("NonSerializableObject", name, + SerializationInjector.storeFrames + ? new Exception(name) : null); + nonSerializableClasses.put(name, s); + + } else { + s.incrementCount(); + } + } + } + + byte[] serBytes; + if (argsSerializable) { + try { + serCounters.attempted.incrementAndGet(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(lambdaObj); + oos.close(); + serBytes = bos.toByteArray(); + serCounters.succeeded.incrementAndGet(); + } catch (java.io.NotSerializableException e) { + serCounters.failed.incrementAndGet(); + String name = e.getMessage(); + SerializableStatus s = nonSerializableClasses.get(name); + if (s == null) { + s = new SerializableStatus("NonSerializableObject", name, + SerializationInjector.storeFrames ? e : null); + nonSerializableClasses.put(name, s); + } else { + s.incrementCount(); + } + return lambdaObj; + } catch (RuntimeException | IOException | Error e) { + serCounters.failed.incrementAndGet(); + String cname = lambdaObj.getClass().getName(); + SerializableStatus s = serializableButFailedClasses.get(cname); + if (s == null) { + serializableButFailedClasses.put(cname, new SerializableStatus("LambdaObject", + lambdaObj, SerializationInjector.storeFrames ? e : null)); + } else { + s.incrementCount(); + } + return lambdaObj; + } + } else { + serCounters.notAttempted.incrementAndGet(); + return lambdaObj; + } + + if (serializeonly) { + deserCounters.notAttempted.incrementAndGet(); + return lambdaObj; + } + + deserCounters.attempted.incrementAndGet(); + try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(serBytes))) { + Object nlambdaObj = ois.readObject(); + deserCounters.succeeded.incrementAndGet(); + return nlambdaObj; + } catch (Exception e) { + deserCounters.failed.incrementAndGet(); + String cname = lambdaObj.getClass().getName(); + SerializableStatus s = deserFailures.get(cname); + if (s == null) { + deserFailures.put(cname, new SerializableStatus("NonDeserializableObject", + lambdaObj, SerializationInjector.storeFrames ? e : null)); + } else { + s.incrementCount(); + } + return lambdaObj; + } + } + + /* + * a unit tester for this class + */ + + public static void main(String... args) { + String s = "Lambda"; + Object o = new Object(); + Object[] array = { + "one", + "two", + "three" + }; + serializeAndDeserialize(s, "Hi", array); + serializeAndDeserialize(s, "Bye", array); + } + + static AtomicBoolean initialized = new AtomicBoolean(); + static { + initializeShutDownHook(); + } + + static void initializeShutDownHook() { + if (initialized.compareAndSet(false, true)) { + Runtime.getRuntime().addShutdownHook( + new Thread() { + @Override + public void run() { + printStats(); + } + } + ); + } + } +} + +class CounterSet { + AtomicInteger notAttempted = new AtomicInteger(0); + AtomicInteger attempted = new AtomicInteger(0); + AtomicInteger succeeded = new AtomicInteger(0); + AtomicInteger failed = new AtomicInteger(0); +} + +class SerializableStatus { + String type; + Object obj; + Throwable exception; + AtomicInteger count = new AtomicInteger(1); + + public SerializableStatus(String type, Object obj, Throwable exception) { + this.type = type; + this.obj = obj; + this.exception = exception; + } + + public void incrementCount() { + count.incrementAndGet(); + } + + public String toString() { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + pw.printf("%s: %s [ %d ]%n", type, obj, count.get()); + if (exception != null) { + pw.printf(" Throwable: %s", exception); + exception.printStackTrace(pw); + } + return sw.toString(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-ng/build.xml Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,289 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + * Copyright (c) 2012, 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. +--> + +<project name="jdk" default="test"> + + <property name="build.dir" value="../../build/test-ng" /> + <property name="tests.classes.dir" value="${build.dir}/test-classes"/> + <property name="mangled.jdk.dir" value="${build.dir}/mangled-jdk"/> + <property name="mangled.bootlib.dir" value="${build.dir}/mangled-bootlib"/> + <property name="mangled.boottests.dir" value="${build.dir}/mangled-boottests"/> + <property name="mangled.tests.dir" value="${build.dir}/mangled-testclasses"/> + <property name="boottests.classes.dir" value="${build.dir}/boottest-classes"/> + <property name="bootlib.classes.dir" value="${build.dir}/test-bootlib"/> + <property name="test.reports.dir" value="${build.dir}/test-reports"/> + <property name="lambda.test.dir" value="../test/jdk/lambda" /> + <property name="spliterator.test.dir" value="../test/java/util/Spliterator" /> + <property name="tests.src.dir" value="../test/java/util/stream/test"/> + <property name="boottests.src.dir" value="../test/java/util/stream/boottest"/> + <property name="bootlib.src.dir" value="../test/java/util/stream/bootlib"/> + <property name="lib.dir" location="lib" /> + <property name="test.pattern" value="*" /> + <property name="lambda.metafactory" value="" /> + <property name="generated.dir" value="gen-separate"/> + <property name="agent.dir" location="${basedir}/agent"/> + <property name="agent.jar" value="${build.dir}/agent/dist/SerializationInjectorAgent.jar" /> + <property name="agent.serial.file" value="${agent.dir}/conf/serialize.list" /> + <property name="agent.rt.jar" value="${java.home}/lib/rt.jar" /> + <property name="agent.heap.size" value="-Xmx4g"/> + <property name="parallel.mode" value="classes" /> + <property name="parallel.suitePool" value="1" /> + <property name="parallel.providerPool" value="1" /> + + <property name="lib.testng.jar" value="${lib.dir}/testng.jar"/> + <property name="lib.tools.jar" value="${java.home}/../lib/tools.jar"/> + + <path id="compile.class.path"> + <pathelement location="${bootlib.classes.dir}" /> + <pathelement location="${tests.classes.dir}" /> + <pathelement location="${lib.testng.jar}"/> + <pathelement location="${lib.tools.jar}"/> + </path> + + <path id="test.class.path"> + <pathelement location="${tests.classes.dir}" /> + <pathelement location="${lib.testng.jar}"/> + <pathelement location="${lib.tools.jar}"/> + </path> + + <path id="mangled.class.path"> + <pathelement location="${mangled.tests.dir}" /> + <pathelement location="${lib.testng.jar}"/> + <pathelement location="${lib.tools.jar}"/> + </path> + + <taskdef name="testng" classpathref="test.class.path" classname="org.testng.TestNGAntTask" /> + + <target name="prepare"> + <mkdir dir="${build.dir}"/> + <mkdir dir="${mangled.jdk.dir}"/> + <mkdir dir="${tests.classes.dir}"/> + <mkdir dir="${boottests.classes.dir}"/> + <mkdir dir="${bootlib.classes.dir}"/> + <mkdir dir="${test.reports.dir}"/> + </target> + + <target name="test-compile" depends="prepare"> + <javac destdir="${bootlib.classes.dir}" debug="on" srcdir="${bootlib.src.dir}" fork="true" + classpathref="compile.class.path"> + <compilerarg value="-Xlint:all"/> + <compilerarg value="-XDignore.symbol.file"/> + </javac> + <javac destdir="${tests.classes.dir}" debug="on" srcdir="${tests.src.dir}:${lambda.test.dir}:${spliterator.test.dir}" fork="true" + classpathref="compile.class.path"> + <compilerarg value="-Xlint:all"/> + <compilerarg value="-XDignore.symbol.file"/> + </javac> + <javac destdir="${boottests.classes.dir}" debug="on" srcdir="${boottests.src.dir}" fork="true" + classpathref="compile.class.path"> + <compilerarg value="-Xlint:all"/> + <compilerarg value="-XDignore.symbol.file"/> + </javac> + </target> + + <target name="test" depends="test-compile" > + <echo>Results at: file:${test.reports.dir}/index.html</echo> + <testng classpathref="test.class.path" outputdir="${test.reports.dir}" + listener="org.testng.reporters.DotTestListener, org.testng.reporters.XMLReporter" useDefaultListeners="false"> + <classfileset dir="${tests.classes.dir}" includes="**/${test.pattern}.class"/> + <classfileset dir="${boottests.classes.dir}" includes="**/${test.pattern}.class"/> + <jvmarg value="-Xbootclasspath/p:${boottests.classes.dir}"/> + <jvmarg value="-Xbootclasspath/p:${bootlib.classes.dir}"/> + <jvmarg value="-Xbootclasspath/p:${lib.testng.jar}"/> + <jvmarg value="-ea"/> + <jvmarg value="-esa"/> + <jvmarg value="-Xverify:all"/> + <jvmarg value="-Xmx2g"/> + <sysproperty key="org.openjdk.java.util.stream.tripwire" value="true"/> + </testng> + </target> + + <target name="test-libs" depends="test-compile" > + <echo>Results at: file:${test.reports.dir}/index.html</echo> + <testng classpathref="test.class.path" outputdir="${test.reports.dir}" + listener="org.testng.reporters.XMLReporter" useDefaultListeners="false" + parallel="${parallel.mode}" suiteThreadPoolSize="${parallel.suitePool}" dataProviderThreadCount="${parallel.providerPool}"> + <classfileset dir="${tests.classes.dir}" includes="org/openjdk/tests/java/**/*.class"/> + <classfileset dir="${boottests.classes.dir}" includes="java/**/*.class"/> + <jvmarg value="-Xbootclasspath/p:${boottests.classes.dir}"/> + <jvmarg value="-Xbootclasspath/p:${bootlib.classes.dir}"/> + <jvmarg value="-Xbootclasspath/p:${lib.testng.jar}"/> + <jvmarg value="-ea" /> + <jvmarg value="-esa" /> + <jvmarg value="-Xverify:all" /> + <jvmarg value="-Xmx2500m" /> + <sysproperty key="org.openjdk.java.util.stream.tripwire" value="true"/> + </testng> + </target> + + <target name="test-notlibs" depends="test-compile" > + <echo>Results at: file:${test.reports.dir}/index.html</echo> + <testng classpathref="test.class.path" outputdir="${test.reports.dir}" + listener="org.testng.reporters.XMLReporter" useDefaultListeners="false" + parallel="${parallel.mode}" suiteThreadPoolSize="${parallel.suitePool}" dataProviderThreadCount="${parallel.providerPool}" > + <classfileset dir="${tests.classes.dir}" excludes="org/openjdk/tests/java/**/*.class"/> + <jvmarg value="-Xbootclasspath/p:${boottests.classes.dir}"/> + <jvmarg value="-Xbootclasspath/p:${bootlib.classes.dir}"/> + <jvmarg value="-Xbootclasspath/p:${lib.testng.jar}"/> + <jvmarg value="-ea" /> + <jvmarg value="-esa" /> + <jvmarg value="-Xverify:all" /> + <jvmarg value="-Xmx2500m" /> + <sysproperty key="org.openjdk.java.util.stream.tripwire" value="true"/> + </testng> + </target> + + <target name="clean" depends="clean-mangler"> + <delete includeEmptyDirs="true" failonerror="false"> + <fileset dir="${build.dir}" /> + <fileset dir="${generated.dir}" /> + </delete> + </target> + + <!-- mangler related start --> + <target name="clean-mangler"> + <ant antfile="${agent.dir}/make/build.xml" inheritAll="false" + target="clean"> + </ant> + </target> + + <target name="build-mangler"> + <ant antfile="${agent.dir}/make/build.xml" inheritAll="false" + target="dist"> + </ant> + </target> + + <target name="mangler-sanity"> + <condition property="agent.rt.jar.available"> + <available file="${agent.rt.jar}"/> + </condition> + <echo message="java.home : ${java.home}"/> + <echo message="agent.rt.jar : ${agent.rt.jar}"/> + <echo message="agent.heap.size : ${agent.heap.size}"/> + <fail message="agent.rt.jar was not found, please set it to a valid rt.jar" + unless="agent.rt.jar.available"/> + </target> + + <target name="premangle" depends="mangler-sanity, clean, build-mangler, test-compile"> + <!-- Mangle JDK to mangled.jdk.dir (serialization injection only, no SAND) --> + <delete dir="${mangled.jdk.dir}" /> + <mkdir dir="${mangled.jdk.dir}" /> + <java jar="${agent.jar}" fork="true" > + <jvmarg value="-Dcom.oracle.lambda.serialize.file=${agent.serial.file}"/> + <arg value="-s"/> + <arg value="-o"/> + <arg value="${mangled.jdk.dir}" /> + <arg value="-s"/> + <arg value="${agent.rt.jar}"/> + </java> + <!-- Mangle boot test classes to mangled.boottests.dir --> + <delete dir="${mangled.boottests.dir}" /> + <mkdir dir="${mangled.boottests.dir}" /> + <jar destfile="${build.dir}/tmpA.jar" basedir="${boottests.classes.dir}"/> + <java jar="${agent.jar}" fork="true" > + <jvmarg value="-Dcom.oracle.lambda.serialize.file=${agent.serial.file}"/> + <arg value="-o"/> + <arg value="${mangled.boottests.dir}" /> + <arg value="-s"/> + <arg value="${build.dir}/tmpA.jar"/> + </java> + <!-- Mangle boot test classes to mangled.bootlib.dir (serialization injection only, no SAND) --> + <delete dir="${mangled.bootlib.dir}" /> + <mkdir dir="${mangled.bootlib.dir}" /> + <jar destfile="${build.dir}/tmpB.jar" basedir="${bootlib.classes.dir}"/> + <java jar="${agent.jar}" fork="true" > + <jvmarg value="-Dcom.oracle.lambda.serialize.file=${agent.serial.file}"/> + <arg value="-s"/> + <arg value="-o"/> + <arg value="${mangled.bootlib.dir}" /> + <arg value="${build.dir}/tmpB.jar"/> + </java> + <!-- Mangle test classes to mangled.testdir --> + <delete dir="${mangled.tests.dir}" /> + <mkdir dir="${mangled.tests.dir}" /> + <jar destfile="${build.dir}/tmpC.jar" basedir="${tests.classes.dir}"/> + <java jar="${agent.jar}" fork="true" > + <jvmarg value="-Dcom.oracle.lambda.serialize.file=${agent.serial.file}"/> + <arg value="-o"/> + <arg value="${mangled.tests.dir}" /> + <arg value="${build.dir}/tmpC.jar"/> + </java> + </target> + + <!-- + Some tests are hostile to our serialization testing strategy; these are those where lambdas + have side-effects on captured arguments, and then we test properties of the captured arguments + separately. This is because serialization/deserialization severs the aliasing that this test + strategy depends on. So we mark and exclude these classes. + --> + <target name="test-mangled" depends="mangler-sanity, test-compile, build-mangler" > + <testng classpathref="mangled.class.path" outputdir="${test.reports.dir}" + excludedgroups="serialization-hostile"> + <classfileset dir="${mangled.tests.dir}" includes="org/openjdk/tests/java/**/*.class" + excludes="**/SerializedLambdaTest.class"/> + <classfileset dir="${mangled.boottests.dir}" includes="**/*.class"/> + <jvmarg value="-Xbootclasspath/p:${agent.jar}"/> + <jvmarg value="-Xbootclasspath/p:${mangled.jdk.dir}"/> + <jvmarg value="-Xbootclasspath/p:${mangled.bootlib.dir}"/> + <jvmarg value="-Xbootclasspath/p:${mangled.boottests.dir}"/> + <jvmarg value="-Xbootclasspath/p:${lib.testng.jar}"/> + <!--<jvmarg value="-Dsun.io.serialization.extendedDebugInfo=true" />--> + <jvmarg value="-ea" /> + <jvmarg value="-esa" /> + <jvmarg value="-Xverify:none" /> + <jvmarg value="${agent.heap.size}"/> + <sysproperty key="org.openjdk.java.util.stream.tripwire" value="true"/> + <sysproperty key="org.openjdk.java.util.stream.sand.mode" value="true"/> + <!-- properties specific to agent config --> + <jvmarg value="-Dcom.oracle.lambda.store.frames=false"/> + <jvmarg value="-Dcom.oracle.lambda.nrounds=1"/> + <jvmarg value="-javaagent:${agent.jar}"/> + </testng> + </target> + + <target name="test-mangledp" depends="test-compile, build-mangler" > + <testng classpathref="mangled.class.path" outputdir="${test.reports.dir}" + excludedgroups="serialization-hostile"> + <classfileset dir="${mangled.tests.dir}" includes="**/${test.pattern}.class"/> + <classfileset dir="${mangled.boottests.dir}" includes="**/${test.pattern}.class"/> + <jvmarg value="-Xbootclasspath/p:${agent.jar}"/> + <jvmarg value="-Xbootclasspath/p:${mangled.jdk.dir}"/> + <jvmarg value="-Xbootclasspath/p:${mangled.bootlib.dir}"/> + <jvmarg value="-Xbootclasspath/p:${mangled.boottests.dir}"/> + <jvmarg value="-Xbootclasspath/p:${lib.testng.jar}"/> + <!--<jvmarg value="-Dsun.io.serialization.extendedDebugInfo=true" />--> + <jvmarg value="-ea" /> + <jvmarg value="-esa" /> + <jvmarg value="-Xverify:none" /> + <jvmarg value="-Xmx2500m" /> + <sysproperty key="org.openjdk.java.util.stream.tripwire" value="true"/> + <sysproperty key="org.openjdk.java.util.stream.sand.mode" value="true"/> + <jvmarg value="-javaagent:${agent.jar}"/> + </testng> + </target> + <!-- mangler related, end --> +</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-ng/tests/lambda/TestInterfaceBridges.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2012, 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 lambda; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/** + * TestInterfaceBridges + * + * @author Brian Goetz + */ +@Test +public class TestInterfaceBridges { + public void testCovariantOverrideLambda() { + B b = () -> "Foo"; + assertEquals("Foo", b.make()); + assertEquals("Foo", ((A) b).make()); + } + + public void testCovariantOverrideIC() { + B b = new B() { + @Override + public String make() { + return "Foo"; + } + }; + assertEquals("Foo", b.make()); + assertEquals("Foo", ((A) b).make()); + } + + public static interface A { + Object make(); + } + + public static interface B extends A { + String make(); + } + + public void testCovariantMerge() { + D d = () -> "Foo"; + assertEquals("Foo", d.make()); + assertEquals("Foo", ((A) d).make()); + assertEquals("Foo", ((C) d).make()); + } + + public static interface C { + String make(); + } + + public static interface D extends A, C { + } +}
--- a/test/ProblemList.txt Tue Aug 13 10:42:37 2013 -0700 +++ b/test/ProblemList.txt Wed Aug 14 15:53:13 2013 -0700 @@ -210,6 +210,10 @@ # 8014719 sun/net/www/http/HttpClient/ProxyTest.java generic-all +# 7150552 +sun/net/www/protocol/http/B6299712.java macosx-all +java/net/CookieHandler/CookieManagerTest.java macosx-all + ############################################################################ # jdk_io
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/io/Serializable/defaultSVID/Child.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012, 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 Child implements Parent, java.io.Serializable { }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/io/Serializable/defaultSVID/DefaultSVID.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2012, 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 + * @summary Test default serial version id creation + * + * @compile ParentA.java Child.java + * @run main DefaultSVID moveParentA + * @compile ParentB.java + * @run main DefaultSVID + */ + +import java.io.IOException; +import java.io.ObjectStreamClass; +import java.nio.file.*; +import static java.nio.file.StandardCopyOption.*; + +public class DefaultSVID { + + static String testClasses = System.getProperty("test.classes"); + + public static class MyClassLoader extends ClassLoader { + final String suffix; + + public MyClassLoader(String suffix) { + super(null); + this.suffix = suffix; + } + + public Class<?> defineAndResolve(String name) { + try { + String file; + if ("Parent".equals(name)) { + file = name + suffix; + } else { + file = name; + } + byte[] classBytes = Files.readAllBytes(Paths.get(testClasses, file + ".class")); + Class<?> ret = defineClass(name, classBytes, 0, classBytes.length); + resolveClass(ret); + return ret; + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + } + + public static void main(String args[]) throws Exception { + if (args.length > 0) { + Files.move(Paths.get(testClasses, "Parent.class"), + Paths.get(testClasses, "ParentA.class"), + REPLACE_EXISTING); + return; + } + + Files.move(Paths.get(testClasses, "Parent.class"), + Paths.get(testClasses, "ParentB.class"), + REPLACE_EXISTING); + + MyClassLoader cl_nodefault = new MyClassLoader("A"); + MyClassLoader cl_default = new MyClassLoader("B"); + + cl_nodefault.defineAndResolve("Parent"); + cl_default.defineAndResolve("Parent"); + + Class<?> child_nodefault = cl_nodefault.defineAndResolve("Child"); + Class<?> child_default = cl_default.defineAndResolve("Child"); + + ObjectStreamClass osc_child_nodefault = ObjectStreamClass.lookup(child_nodefault); + ObjectStreamClass osc_child_default = ObjectStreamClass.lookup(child_default); + + long svid_nodefault = osc_child_nodefault.getSerialVersionUID(); + long svid_default = osc_child_default.getSerialVersionUID(); + + System.out.printf("svid_nodefault : %016X \n svid_default : %016X\n", + svid_nodefault, svid_default); + + if (svid_nodefault != svid_default) { + throw new AssertionError("serial version IDs differ"); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/io/Serializable/defaultSVID/ParentA.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2012, 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 Parent { +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/io/Serializable/defaultSVID/ParentB.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2012, 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 Parent { + default void parent() { } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/Double/DoubleReducersTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2012, 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 + * @summary + * @(#) DoubleReducersTest.java + * @author Tristan Yan + * @run testng DoubleReducersTest + */ + +import java.util.Random; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +public class DoubleReducersTest { + private final static int LOOP = 1 << 20; + + private final static Random rand = new Random(System.currentTimeMillis()); + + @Test + public void testMax() { + for(int index = 0; index < LOOP; index++){ + double d1 = rand.nextDouble(); + double d2 = rand.nextDouble(); + double expected = d1 > d2 ? d1 : d2; + assertEquals(Double.max(d1, d2), expected); + } + } + + @Test + public void testMin() { + for(int index = 0; index < LOOP; index++){ + double d1 = rand.nextDouble(); + double d2 = rand.nextDouble(); + double expected = d1 < d2 ? d1 : d2; + assertEquals(Double.min(d1, d2), expected); + } + } + + @Test + public void testSum() { + for(int index = 0; index < LOOP; index++){ + double d1 = rand.nextDouble(); + double d2 = rand.nextDouble(); + double expected = d1 + d2; + assertEquals(Double.sum(d1, d2), expected); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/Integer/IntegerReducersTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2012, 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 + * @summary + * @(#) IntegerReducersTest.java + * @author Tristan Yan + * @run testng IntegerReducersTest + */ + +import java.util.Random; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +public class IntegerReducersTest { + private final static int LOOP = 1 << 20; + + private final static Random rand = new Random(System.currentTimeMillis()); + + @Test + public void testMax() { + for(int index = 0; index < LOOP; index++){ + int i1 = rand.nextInt(); + int i2 = rand.nextInt(); + int expected = i1 > i2 ? i1 : i2; + assertEquals(Integer.max(i1, i2), expected); + } + } + + @Test + public void testMin() { + for(int index = 0; index < LOOP; index++){ + int i1 = rand.nextInt(); + int i2 = rand.nextInt(); + int expected = i1 < i2 ? i1 : i2; + assertEquals(Integer.min(i1, i2), expected); + } + } + + @Test + public void testSum() { + for(int index = 0; index < LOOP; index++){ + int i1 = rand.nextInt(); + int i2 = rand.nextInt(); + int expected = i1 + i2; + assertEquals(Integer.sum(i1, i2), expected); + } + } + + @Test + public void testUnsignedWithoutRadix(){ + for(int index = 0; index < LOOP; index++){ + int i = rand.nextInt(); + assertEquals(Integer.parseUnsignedInt(Integer.toUnsignedString(i)), i); + assertEquals(Integer.toUnsignedString(i, 10), Integer.toUnsignedString(i)); + if(i >= 0) + assertEquals(Integer.toUnsignedString(i), Integer.toString(i)); + } + } + + @Test + public void testUnsignedWithRadix(){ + for(int index = 0; index < LOOP; index++){ + int i = rand.nextInt(); + int radix = Character.MIN_RADIX + rand.nextInt(Character.MAX_RADIX - Character.MIN_RADIX + 1); + assertEquals(Integer.parseUnsignedInt(Integer.toUnsignedString(i, radix), radix), i); + if(i >= 0) + assertEquals(Integer.toUnsignedString(i, radix), Integer.toString(i, radix)); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/Long/LongReducersTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2012, 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 + * @summary + * @author Tristan Yan + * @run testng LongReducersTest + */ + +import java.util.Random; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +public class LongReducersTest { + private final static int LOOP = 1 << 20; + + private final static Random rand = new Random(System.currentTimeMillis()); + + @Test + public void testMax() { + for(int index = 0; index < LOOP; index++){ + long l1 = rand.nextLong(); + long l2 = rand.nextLong(); + long expected = l1 > l2 ? l1 : l2; + assertEquals(Long.max(l1, l2), expected); + } + } + + @Test + public void testMin() { + for(int index = 0; index < LOOP; index++){ + long l1 = rand.nextLong(); + long l2 = rand.nextLong(); + long expected = l1 < l2 ? l1 : l2; + assertEquals(Long.min(l1, l2), expected); + } + } + + @Test + public void testSum() { + for(int index = 0; index < LOOP; index++){ + long l1 = rand.nextLong(); + long l2 = rand.nextLong(); + long expected = l1 + l2; + assertEquals(Long.sum(l1, l2), expected); + } + } + + @Test + public void testUnsignedWithoutRadix(){ + for(int index = 0; index < LOOP; index++){ + long l = rand.nextLong(); + assertEquals(Long.parseUnsignedLong(Long.toUnsignedString(l)), l); + assertEquals(Long.toUnsignedString(l, 10), Long.toUnsignedString(l)); + if(l >= 0) + assertEquals(Long.toUnsignedString(l), Long.toString(l)); + } + } + + @Test + public void testUnsignedWithRadix(){ + for(int index = 0; index < LOOP; index++){ + long l = rand.nextLong(); + int radix = Character.MIN_RADIX + rand.nextInt(Character.MAX_RADIX - Character.MIN_RADIX + 1); + assertEquals(Long.parseUnsignedLong(Long.toUnsignedString(l, radix), radix), l); + if(l >= 0) + assertEquals(Long.toUnsignedString(l, radix), Long.toString(l, radix)); + } + } + + @Test + public void testCompareUnsigned() { + for(int index = 0; index < LOOP; index++){ + long l1 = rand.nextLong(); + long l2 = rand.nextLong(); + assertEquals(Long.compareUnsigned(l1, l2), + Long.compare(Long.MAX_VALUE + l1, Long.MAX_VALUE + l2)); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/String/JoinTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2012, 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 + * @summary test for String.join(CharSequence , CharSequence...) and + * String.join(CharSequence , Iterable<? extends CharSequence>). + * @(#) JoinTest.java + * @author Tristan Yan + * @run testng JoinTest + */ + +import java.util.*; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +public class JoinTest { + private final String[] LANGUAGES = { "ABAP", "ALGOL", "ActionScript", + "AppleScript","AspectJ", "Assembly language", "AutoLISP", "AWK", "Bash", + "C", "C Shell", "CICS", "COBOL", "Cool", "CSH", "DCL", "Delphi", + "Eiffel", "F#", "Formula", "Fortran", "FoxBase", "FoxPro", "Groovy", + "Haskell", "J#", "JCL", "JavaScript", "Ksh", "LINC", "Lisp", "Logo", + "MASM", "MATLAB", "NASM", "XML", "Objective-C", "Pascal", "Perl", + "PL/I", "PostScript", "PowerBuilder", "PowerShell", "Prolog", "Pro*C", + "Python", "QBasic", "REBOL", "Ruby", "Scheme", "Sed", "Smalltalk", + "SQL", "VBScript" }; + + + private final String[] DELIMETERS = {"`", "~", "!", "@", "#", "$", "%", "^", + "&", "*", "(", ")", "-", "_", "+", "=", "\\", "|", ",", ".", "/", ":", + ";", "\"", "\"", "\n", "\t", " ", "\t"}; + + @Test + public void testJoin() { + //HashSet sort data with hashCode(), iterator order by hashCode() + List<String> lanugageList = Arrays.asList(LANGUAGES); + Set<String> lanugageSet = new HashSet<>(lanugageList); + Random rnd = new Random(); + String delimeter = DELIMETERS[rnd.nextInt(DELIMETERS.length)]; + StringBuilder sb = new StringBuilder(); + Iterator<String> it = lanugageSet.iterator(); + int pos = 0; + while(it.hasNext()) { + sb.append(it.next()); + if(++pos < lanugageSet.size()) + sb.append(delimeter); + } + String result2 = String.join(delimeter, lanugageSet); + String result1 = sb.toString(); + assertEquals(result2, result1); + + Collections.shuffle(lanugageList, new Random(rnd.nextInt())); + String[] shuffledArray = new String[lanugageList.size()]; + lanugageList.toArray(shuffledArray); + result1 = String.join(delimeter, shuffledArray); + result2 = String.join(delimeter, lanugageList); + StringBuilder expectedSB = new StringBuilder(); + for(int index = 0; index < lanugageList.size() - 1; index++) + expectedSB.append(lanugageList.get(index)).append(delimeter); + expectedSB.append(lanugageList.get(lanugageList.size() - 1)); + assertEquals(expectedSB.toString(), result1); + assertEquals(result2, result1); + + String single = LANGUAGES[rnd.nextInt(LANGUAGES.length)]; + assertEquals(String.join(delimeter, new String[]{single}), single); + assertEquals(String.join(delimeter, new String[]{single}), single); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/StringBuilder/AddAllTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2012, 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 + * @summary test for StringBuilde/StringBuffer.addAll() + * @library /sqeutil + * @author Tristan Yan + * @run testng AddAllTest + */ + +import java.util.*; +import java.util.stream.*; +import static org.testng.Assert.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class AddAllTest { + private final static int LINE_NUM = 1 << 8; + + private final static int LINE_MIN_CHARACTER_LEN = 2; + + private final static int LINE_MAX_CHARACTER_LEN = 80; + + @Test(dataProvider = "generateData") + public void builderAddAll(String[] buff){ + String str = Arrays.stream(buff).collect(Collectors.joining()); + int start = 0; + for(int index = 0; index < buff.length; index++){ + assert(start + buff[index].length() <= str.length()); + assertEquals(str.substring(start, start + buff[index].length()), buff[index]); + start += buff[index].length(); + } + } + + @Test(dataProvider = "generateData") + public void bufferAddAll(String[] buff){ + StringBuffer sb = Arrays.stream(buff).collect(StringBuffer::new, StringBuffer::append, (s, o) -> s.append(o)); + String str = sb.toString(); + int start = 0; + for(int index = 0; index < buff.length; index++){ + assert(start + buff[index].length() <= str.length()); + assertEquals(str.substring(start, start + buff[index].length()), buff[index]); + start += buff[index].length(); + } + } + + @DataProvider + public Object[][] generateData(){ + Object[][] data = new Object[LINE_NUM][]; + for(int line = 0; line < LINE_NUM; line++) { + List<Object> l = new ArrayList<>(); + String[] buffer = new String[line]; + for(int row = 0; row < line; row++) + buffer[row] = StringUtilities.randomString(LINE_MAX_CHARACTER_LEN, + 0); + data[line] = new Object[]{buffer}; + } + return data; + } +}
--- a/test/java/lang/ThreadLocal/Basic.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/lang/ThreadLocal/Basic.java Wed Aug 14 15:53:13 2013 -0700 @@ -30,6 +30,7 @@ public class Basic { static ThreadLocal n = new ThreadLocal() { int i = 0; + @Override protected synchronized Object initialValue() { return new Integer(i++); } @@ -43,11 +44,12 @@ // Start the threads for(int i=0; i<threadCount; i++) { th[i] = new Thread() { + @Override public void run() { int threadId = ((Integer)(n.get())).intValue(); for (int j=0; j<threadId; j++) { x[threadId]++; - Thread.currentThread().yield(); + Thread.yield(); } } };
--- a/test/java/lang/ThreadLocal/ImmutableLocal.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/lang/ThreadLocal/ImmutableLocal.java Wed Aug 14 15:53:13 2013 -0700 @@ -33,15 +33,18 @@ * {@link ThreadLocal} guaranteed to always return the same reference. */ abstract public static class ImmutableThreadLocal extends ThreadLocal { + @Override public void set(final Object value) { throw new RuntimeException("ImmutableThreadLocal set called"); } // force override + @Override abstract protected Object initialValue(); } private static final ThreadLocal cache = new ImmutableThreadLocal() { + @Override public Object initialValue() { return Thread.currentThread().getName(); }
--- a/test/java/lang/ThreadLocal/InitialValue.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/lang/ThreadLocal/InitialValue.java Wed Aug 14 15:53:13 2013 -0700 @@ -34,8 +34,9 @@ public class MyLocal extends ThreadLocal<String> { String val; + @Override protected String initialValue() { - other = new ThreadLocal<String>(); + other = new ThreadLocal<>(); // This should reuse the map that the containing get() created // or visa versa (i.e. instead of a second map being created). other.set("Other"); @@ -43,6 +44,7 @@ } } + @Override public void run() { MyLocal l = new MyLocal(); // This should pick up the initial value
--- a/test/java/lang/ThreadLocal/TLRemoveTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/lang/ThreadLocal/TLRemoveTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -32,6 +32,7 @@ private static final int REMOVE_SET_VALUE = 102; static ThreadLocal<Integer> n = new ThreadLocal<Integer>() { + @Override protected synchronized Integer initialValue() { return INITIAL_VALUE; } @@ -51,11 +52,12 @@ for(int i = 0; i<threadCount; i++) { final int threadId = i; th[i] = new Thread() { + @Override public void run() { try{ n.set(threadId); // Sets threadId as threadlocal value... for (int j = 0; j<threadId; j++) - Thread.currentThread().yield(); + Thread.yield(); // To remove the ThreadLocal .... for(int removeId : removeNode)
--- a/test/java/lang/ThreadLocal/TestThreadId.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/lang/ThreadLocal/TestThreadId.java Wed Aug 14 15:53:13 2013 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2012, 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 @@ -49,8 +49,9 @@ } // Each child thread just publishes its id value for validation + @Override public void run() { - value = id.get(); + value = ThreadId.get(); } public static void main(String args[]) throws Throwable {
--- a/test/java/lang/invoke/lambda/LambdaAccessControlTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/lang/invoke/lambda/LambdaAccessControlTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -25,11 +25,12 @@ * @test * @bug 8003881 * @summary tests Lambda expression with a a security manager at top level - * @compile -XDignore.symbol.file LambdaAccessControlTest.java LUtils.java + * @compile -XDignore.symbol.file LambdaAccessControlDoPrivilegedTest.java LambdaAccessControlTest.java * * @run main/othervm LambdaAccessControlTest */ + public class LambdaAccessControlTest extends LUtils { public static void main(String... args) { System.setSecurityManager(new SecurityManager());
--- a/test/java/lang/invoke/lambda/LambdaSerialization.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/lang/invoke/lambda/LambdaSerialization.java Wed Aug 14 15:53:13 2013 -0700 @@ -25,6 +25,7 @@ @test @bug 8004970 @summary Lambda serialization + */ import java.io.*; @@ -65,11 +66,11 @@ } assertTrue(assertionCount == 3); } - + static void write(ObjectOutput out, LSI lamb) throws IOException { out.writeObject(lamb); } - + static void readAssert(ObjectInputStream in, String expected) throws IOException, ClassNotFoundException { LSI ls = (LSI) in.readObject(); String result = ls.convert("X");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/nio/Files/FilesLambdaTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2012, 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 + * @summary function test for Files.list()/find()/walk()/lines() + * @library /sqeutil + * @(#) FilesLambdaTest.java + * @author Tristan Yan + * @run testng/othervm FilesLambdaTest + */ + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.NotDirectoryException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import static org.testng.Assert.*; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class FilesLambdaTest { + private static final Random rand = new Random(System.nanoTime()); + + private static final String TEST_SRC = System.getProperty("test.src"); + + private static final String ROOT_NAME = "FilesLambdaTest" + System.nanoTime(); + + private static final String LINES_TEST_FILE = "lines" + System.nanoTime(); + + private static final int MAX_FILES_NUMBER = 1 << 6; + + private static final Charset UTF8 = Charset.forName("UTF-8"); + + private final static int MIN_LEN = 1 << 2; + + private final static int MAX_LEN = 1 << 8; + + private final static int LINES_NUM = 1 << 8; + + private static final String[][] folders = { + {"A01"}, + {"A01", "AA02"}, + {"A01", "AB02"}, + {"A01", "AC02"}, + {"B01"}, + {"B01", "BA02"}, + {"B01", "BA02", "BAA03"}, + {"B01", "BA02", "BAA03", "BAAA04", "BAAAA05", "BAAAAA06", "BAAAAAA07", + "BAAAAAAA08", "BAAAAAAAA09", "BAAAAAAAAA10", "BAAAAAAAAAA11"}, + {"C01"}, + {"C01", "CA02"}, + {"C01", "CD02"}, + {"D01", "DA02", "DAA03", "DAAA04", "DAAAA05", "DAAAAA06", "DAAAAAA07"}, + {"E01"}, + {"E01", "EA02"}, + {"E01", "EB02", "EBB03"}, + {"E01", "EC02", "ECC03", "ECCC04"}, + {"E01", "ED02", "EDD03", "EDDD04", "EDDDD05"}, + {"E01", "EE02", "EEE03", "EEEE04", "EEEEE05", "EEEEEE06"}, + {"E01", "EF02", "EFF03", "EFFF04", "EFFFF05", "EFFFFF06"}, + {"E01", "EG02", "EGG03", "EGGG04", "EGGGG05", "EGGGGG06", "EGGGGGG07"}, + {"E01", "EH02", "EHH03", "EHHH04", "EHHHH05", "EHHHHH06", "EHHHHHH07", + "EHHHHHHH08"}, + {"E01", "EI02", "EII03", "EIII04", "EIIII05", "EIIIII06", "EIIIIII07", + "EIIIIIII08", "EIIIIIIII09"}, + {"E01", "EJ02", "EJJ03", "EJJJ04", "EJJJJ05", "EJJJJJ06", "EJJJJJJ07", + "EJJJJJJJ08", "EJJJJJJJJ09", "EJJJJJJJJJ10"}, + {"E01", "EK02", "EKK03", "EKKK04", "EKKKK05", "EKKKKK06", "EKKKKKK07", + "EKKKKKKK08", "EKKKKKKKK09", "EKKKKKKKKK10", "EJJJJJJJJJJ11"} + }; + + private Path root; + + private Path testFile; + + private Path notExistPath; + + final Consumer<Path> writeReverseWithLink = path -> { + int filesCount = rand.nextInt(MAX_FILES_NUMBER); + for(int count = 0; count < filesCount; count++) { + String fileName = String.valueOf(rand.nextLong()); + Path file = path.resolve(fileName); + String linkName = String.valueOf(rand.nextLong()); + Path link = path.resolve(linkName); + try (BufferedWriter writer + = Files.newBufferedWriter(Files.createFile(file), UTF8)) { + writer.write(new StringBuilder(fileName).reverse().toString(), + 0, fileName.length()); + Files.createSymbolicLink(link, file); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + }; + + @BeforeClass + public void filesSetUp() throws IOException { + List<Path> paths = new ArrayList<>(); + root = Paths.get(TEST_SRC, ROOT_NAME); + paths.add(Files.createDirectory(root)); + for(int i = 0; i < folders.length; i++ ) { + String[] toBeCreated = folders[i]; + Path folder = Paths.get(ROOT_NAME, toBeCreated); + paths.add(Files.createDirectories(folder)); + } + paths.forEach(writeReverseWithLink); + + testFile = Paths.get(LINES_TEST_FILE); + try (BufferedWriter writer + = Files.newBufferedWriter(Files.createFile(testFile), UTF8)) { + for(int i = 0; i< LINES_NUM; i++) { + String line = StringUtilities.randomString(MAX_LEN, MIN_LEN); + writer.write(line, 0, line.length()); + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + String NOT_EXIST = "NOT_EXIST"; + notExistPath = Paths.get(TEST_SRC, NOT_EXIST); + } + + @AfterClass + public void filesTearDown() throws IOException { + if(root != null) { + Files.walkFileTree(root, new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) + throws IOException{ + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } + if(testFile != null) { + Files.delete(testFile); + } + } + + @Test + public void testFilesList() throws IOException { + checkFilesList(root); + } + + @Test + public void testWalk() throws IOException{ + String[] dir = folders[rand.nextInt(folders.length)]; + Path walkFolder = Paths.get(ROOT_NAME, dir); + final int maxDepth = rand.nextInt(11); + List<Path> expectedFullFileList = new ArrayList<>(); + List<Path> expectedMaxDepthFileList = new ArrayList<>(); + + List<Path> expectedFullSymList = new ArrayList<>(); + List<Path> expectedMaxDepthSymList = new ArrayList<>(); + + List<Path> expectedFullDirList = new ArrayList<>(); + List<Path> expectedMaxDepthDirList = new ArrayList<>(); + + Files.walkFileTree(walkFolder, new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + if (walkFolder.relativize(file).getNameCount() <= maxDepth) { + expectedMaxDepthFileList.add(file); + if(Files.isSymbolicLink(file)) { + expectedMaxDepthSymList.add(file); + } + } + expectedFullFileList.add(file); + if(Files.isSymbolicLink(file)) { + expectedFullSymList.add(file); + } + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) + throws IOException{ + if (walkFolder.relativize(dir).getNameCount() <= maxDepth + || walkFolder.equals(dir)) { + expectedMaxDepthDirList.add(dir); + } + expectedFullDirList.add(dir); + return FileVisitResult.CONTINUE; + } + }); + Collections.sort(expectedFullFileList); + Collections.sort(expectedMaxDepthFileList); + Collections.sort(expectedFullSymList); + Collections.sort(expectedMaxDepthSymList); + Collections.sort(expectedFullDirList); + Collections.sort(expectedMaxDepthDirList); + + assertEquals(expectedMaxDepthFileList, + Files.walk(walkFolder, maxDepth).filter(p -> !Files.isDirectory(p)). + sorted().collect(Collectors.toList())); + assertEquals(expectedMaxDepthSymList, + Files.walk(walkFolder, maxDepth).filter(p -> Files.isSymbolicLink(p)). + sorted().collect(Collectors.toList())); + assertEquals(expectedMaxDepthDirList, + Files.walk(walkFolder, maxDepth).filter(p -> Files.isDirectory(p)). + sorted().collect(Collectors.toList())); + assertEquals(expectedFullFileList, + Files.walk(walkFolder).filter(p -> !Files.isDirectory(p)). + sorted().collect(Collectors.toList())); + assertEquals(expectedFullSymList, + Files.walk(walkFolder).filter(p -> Files.isSymbolicLink(p)). + sorted().collect(Collectors.toList())); + assertEquals(expectedFullDirList, + Files.walk(walkFolder).filter(p -> Files.isDirectory(p)). + sorted().collect(Collectors.toList())); + } + + @Test + public void testFind() throws IOException{ + String[] dir = folders[rand.nextInt(folders.length)]; + Path walkFolder = Paths.get(ROOT_NAME, dir); + String walkFoderName = dir[dir.length - 1]; + + int walkFolderDepth = Integer.parseInt(walkFoderName.substring(walkFoderName.length() - 2)); + int maxDepth = rand.nextInt(11); + Stream<Path> stream + = Files.find(walkFolder, maxDepth, (p,bfa) -> Files.isSymbolicLink(p)); + stream.forEach(p -> { + assertTrue(Files.isSymbolicLink(p)); + assertFalse(Files.isDirectory(p)); + assertFalse(Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS)); + String parentName = p.getParent().toFile().getName(); + int depth = Integer.parseInt(parentName.substring(parentName.length() - 2)); + assertTrue(depth - walkFolderDepth < maxDepth); + }); + + assertEquals(Files.find(walkFolder, maxDepth, (p,bfa) -> Files.isSymbolicLink(p)).toArray(), + Files.find(walkFolder, maxDepth, (p,bfa) -> bfa.isSymbolicLink()).toArray()); + } + + @Test + public void testLines() throws IOException{ + Stream<String> stream = Files.lines(testFile, UTF8); + List<String> lines = Files.readAllLines(testFile, UTF8); + assertEquals(lines, stream.collect(Collectors.toList())); + } + + @Test(expectedExceptions = IOException.class) + public void testListNoReadAccess() throws IOException{ + Files.list(notExistPath); + } + + @Test(expectedExceptions = NotDirectoryException.class) + public void testListNotDirectory() throws IOException{ + Files.list(testFile); + } + + @Test(expectedExceptions = IOException.class) + public void testFindNoExist() throws IOException{ + Files.find(notExistPath, Integer.MAX_VALUE, (p, bfa) -> true); + } + + @Test(expectedExceptions = IOException.class) + public void testLinesNoExist() throws IOException{ + Files.lines(notExistPath, UTF8); + } + + @Test(expectedExceptions = IOException.class) + public void testWalkNoExist() throws IOException{ + Files.walk(notExistPath); + } + + @Test(expectedExceptions = IOException.class) + public void testWalkNoExistWithDepth() throws IOException{ + Files.walk(notExistPath, Integer.MAX_VALUE); + } + + public void checkFilesList(Path checkPath){ + try { + assert(Files.isDirectory(checkPath)); + Files.list(checkPath).filter(p -> Files.isDirectory(p)).forEach(p -> checkFilesList(p)); + assertEquals(Files.list(checkPath).filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS)).count(), + Files.list(checkPath).filter(p -> Files.isSymbolicLink(p)).count()); + assertTrue(Files.list(checkPath).filter(p -> Files.isRegularFile(p, LinkOption.NOFOLLOW_LINKS)).allMatch(file -> contentRevered(file))); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + private boolean contentRevered(Path path) { + try (BufferedReader reader = Files.newBufferedReader(path, UTF8)){ + String fileName = path.getName(path.getNameCount() -1).toString(); + String reversed = new StringBuilder(reader.readLine()).reverse().toString(); + return (fileName.equals(reversed)); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/nio/file/DirectoryStream/StreamTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,195 @@ +/* + * 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. + */ + +/* @test + * @bug 8006884 + * @summary Unit test for java.nio.file.DirectoyStream + * @library .. + * @run testng StreamTest + */ + +import java.nio.file.DirectoryStream; +import java.nio.file.DirectoryIteratorException; +import java.nio.file.NotDirectoryException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.io.IOException; +import java.util.TreeSet; +import java.util.Comparator; +import java.util.stream.Stream; +import java.util.Iterator; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.AfterClass; +import static org.testng.Assert.*; + +@Test(groups = "unit") +public class StreamTest { + /** + * Default test folder + * testFolder - empty + * - file + * - dir - d1 + * - f1 + * - lnDir2 (../dir2) + * - dir2 + * - linkDir (./dir) + * - linkFile(./file) + */ + static Path testFolder; + static boolean supportsLinks; + static Path[] level1; + static Path[] level2; + + @BeforeClass + void setupTestFolder() throws IOException { + testFolder = TestUtil.createTemporaryDirectory(); + supportsLinks = TestUtil.supportsLinks(testFolder); + TreeSet<Path> set = new TreeSet<>(); + + // Level 1 + Path empty = testFolder.resolve("empty"); + Path file = testFolder.resolve("file"); + Path dir = testFolder.resolve("dir"); + Path dir2 = testFolder.resolve("dir2"); + Files.createDirectory(empty); + Files.createFile(file); + Files.createDirectory(dir); + Files.createDirectory(dir2); + set.add(empty); + set.add(file); + set.add(dir); + set.add(dir2); + if (supportsLinks) { + Path tmp = testFolder.resolve("linkDir"); + Files.createSymbolicLink(tmp, dir); + set.add(tmp); + tmp = testFolder.resolve("linkFile"); + Files.createSymbolicLink(tmp, file); + set.add(tmp); + } + level1 = set.toArray(new Path[0]); + + set.clear(); + // Level 2 + Path tmp = dir.resolve("d1"); + Files.createDirectory(tmp); + set.add(tmp); + tmp = dir.resolve("f1"); + Files.createFile(tmp); + set.add(tmp); + if (supportsLinks) { + tmp = dir.resolve("lnDir2"); + Files.createSymbolicLink(tmp, dir2); + set.add(tmp); + } + level2 = set.toArray(new Path[0]); + } + + @AfterClass + void cleanupTestFolder() throws IOException { + TestUtil.removeAll(testFolder); + } + + public void testEntriesWithoutFilter() throws IOException { + try (DirectoryStream<Path> ds = Files.newDirectoryStream(testFolder)) { + Path[] actual = ds.stream().sorted(Comparator.naturalOrder()).toArray(Path[]::new); + assertEquals(actual, level1); + } + + // link to a directory + if (supportsLinks) { + try (DirectoryStream<Path> ds = Files.newDirectoryStream(testFolder.resolve("linkDir"))) { + Path[] actual = ds.stream().sorted(Comparator.naturalOrder()).toArray(Path[]::new); + assertEquals(actual.length, level2.length); + for (int i = 0; i < actual.length; i++) { + assertEquals(actual[i].getFileName(), level2[i].getFileName()); + } + } + } + } + + public void testEntriesWithFilter() throws IOException { + try (DirectoryStream<Path> ds = Files.newDirectoryStream(testFolder, "f*")) { + Path[] actual = ds.stream().toArray(Path[]::new); + assertEquals(actual.length, 1); + assertEquals(actual[0], testFolder.resolve("file")); + } + + try (DirectoryStream<Path> ds = Files.newDirectoryStream(testFolder, "z*")) { + int count = ds.stream().mapToInt(p -> 1).reduce(0, Integer::sum); + assertEquals(count, 0, "Expect empty stream."); + } + } + + public void testDirectoryIteratorException() throws IOException { + // check that an IOException thrown by a filter is propagated + DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { + public boolean accept(Path file) throws IOException { + throw new java.util.zip.ZipException(); + } + }; + + try (DirectoryStream<Path> ds = Files.newDirectoryStream(testFolder, filter)) { + Path[] actual = ds.stream().toArray(Path[]::new); + throw new RuntimeException("DirectoryIteratorException expected"); + } catch (DirectoryIteratorException x) { + IOException cause = x.getCause(); + if (!(cause instanceof java.util.zip.ZipException)) + throw new RuntimeException("Expected IOException not propagated"); + } + } + + public void testEmptyFolder() throws IOException { + try (DirectoryStream<Path> ds = Files.newDirectoryStream(testFolder.resolve("empty"))) { + int count = ds.stream().mapToInt(p -> 1).reduce(0, Integer::sum); + assertEquals(count, 0, "Expect empty stream."); + } + } + + public void testIllegalStateException() throws IOException { + try (DirectoryStream<Path> ds = Files.newDirectoryStream(testFolder)) { + Stream<Path> s = ds.stream(); + try { + ds.iterator(); + fail("Expect IllegalStateException from iterator() call."); + } catch (IllegalStateException ise1) {} + } + + try (DirectoryStream<Path> ds = Files.newDirectoryStream(testFolder)) { + Iterator<Path> it = ds.iterator(); + try { + ds.stream(); + fail("Expect IllegalStateException from stream() call."); + } catch (IllegalStateException ise2) {} + } + } + + public void testNotDirectoryException() throws IOException { + try { + Files.newDirectoryStream(testFolder.resolve("file")); + throw new RuntimeException("NotDirectoryException not thrown"); + } catch (NotDirectoryException x) { + } + } +} \ No newline at end of file
--- a/test/java/nio/file/Files/StreamTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/nio/file/Files/StreamTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -43,14 +43,13 @@ import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.util.Arrays; -import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.function.BiPredicate; -import java.util.stream.CloseableStream; +import java.util.stream.Stream; import java.util.stream.Collectors; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; @@ -138,14 +137,14 @@ } public void testBasic() { - try (CloseableStream<Path> s = Files.list(testFolder)) { - Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); + try (Stream<Path> s = Files.list(testFolder)) { + Object[] actual = s.sorted().toArray(); assertEquals(actual, level1); } catch (IOException ioe) { fail("Unexpected IOException"); } - try (CloseableStream<Path> s = Files.list(testFolder.resolve("empty"))) { + try (Stream<Path> s = Files.list(testFolder.resolve("empty"))) { int count = s.mapToInt(p -> 1).reduce(0, Integer::sum); assertEquals(count, 0, "Expect empty stream."); } catch (IOException ioe) { @@ -154,8 +153,8 @@ } public void testWalk() { - try (CloseableStream<Path> s = Files.walk(testFolder)) { - Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); + try (Stream<Path> s = Files.walk(testFolder)) { + Object[] actual = s.sorted().toArray(); assertEquals(actual, all); } catch (IOException ioe) { fail("Unexpected IOException"); @@ -163,9 +162,9 @@ } public void testWalkOneLevel() { - try (CloseableStream<Path> s = Files.walk(testFolder, 1)) { + try (Stream<Path> s = Files.walk(testFolder, 1)) { Object[] actual = s.filter(path -> ! path.equals(testFolder)) - .sorted(Comparator.naturalOrder()) + .sorted() .toArray(); assertEquals(actual, level1); } catch (IOException ioe) { @@ -176,8 +175,8 @@ public void testWalkFollowLink() { // If link is not supported, the directory structure won't have link. // We still want to test the behavior with FOLLOW_LINKS option. - try (CloseableStream<Path> s = Files.walk(testFolder, FileVisitOption.FOLLOW_LINKS)) { - Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); + try (Stream<Path> s = Files.walk(testFolder, FileVisitOption.FOLLOW_LINKS)) { + Object[] actual = s.sorted().toArray(); assertEquals(actual, all_folowLinks); } catch (IOException ioe) { fail("Unexpected IOException"); @@ -185,7 +184,7 @@ } private void validateFileSystemLoopException(Path start, Path... causes) { - try (CloseableStream<Path> s = Files.walk(start, FileVisitOption.FOLLOW_LINKS)) { + try (Stream<Path> s = Files.walk(start, FileVisitOption.FOLLOW_LINKS)) { try { int count = s.mapToInt(p -> 1).reduce(0, Integer::sum); fail("Should got FileSystemLoopException, but got " + count + "elements."); @@ -282,28 +281,28 @@ public void testFind() throws IOException { PathBiPredicate pred = new PathBiPredicate((path, attrs) -> true); - try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { + try (Stream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { Set<Path> result = s.collect(Collectors.toCollection(TreeSet::new)); assertEquals(pred.visited(), all); assertEquals(result.toArray(new Path[0]), pred.visited()); } pred = new PathBiPredicate((path, attrs) -> attrs.isSymbolicLink()); - try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { + try (Stream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> assertTrue(Files.isSymbolicLink(path))); assertEquals(pred.visited(), all); } pred = new PathBiPredicate((path, attrs) -> path.getFileName().toString().startsWith("e")); - try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { + try (Stream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> assertEquals(path.getFileName().toString(), "empty")); assertEquals(pred.visited(), all); } pred = new PathBiPredicate((path, attrs) -> path.getFileName().toString().startsWith("l") && attrs.isRegularFile()); - try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { + try (Stream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) { s.forEach(path -> fail("Expect empty stream")); assertEquals(pred.visited(), all); } @@ -317,14 +316,14 @@ try { // zero lines assertTrue(Files.size(tmpfile) == 0, "File should be empty"); - try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) { + try (Stream<String> s = Files.lines(tmpfile, US_ASCII)) { assertEquals(s.mapToInt(l -> 1).reduce(0, Integer::sum), 0, "No line expected"); } // one line byte[] hi = { (byte)'h', (byte)'i' }; Files.write(tmpfile, hi); - try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) { + try (Stream<String> s = Files.lines(tmpfile, US_ASCII)) { List<String> lines = s.collect(Collectors.toList()); assertTrue(lines.size() == 1, "One line expected"); assertTrue(lines.get(0).equals("hi"), "'Hi' expected"); @@ -334,7 +333,7 @@ List<String> expected = Arrays.asList("hi", "there"); Files.write(tmpfile, expected, US_ASCII); assertTrue(Files.size(tmpfile) > 0, "File is empty"); - try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) { + try (Stream<String> s = Files.lines(tmpfile, US_ASCII)) { List<String> lines = s.collect(Collectors.toList()); assertTrue(lines.equals(expected), "Unexpected lines"); } @@ -342,7 +341,7 @@ // MalformedInputException byte[] bad = { (byte)0xff, (byte)0xff }; Files.write(tmpfile, bad); - try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) { + try (Stream<String> s = Files.lines(tmpfile, US_ASCII)) { try { List<String> lines = s.collect(Collectors.toList()); throw new RuntimeException("UncheckedIOException expected"); @@ -378,7 +377,7 @@ fsp.setFaultyMode(false); Path fakeRoot = fs.getRoot(); try { - try (CloseableStream<Path> s = Files.list(fakeRoot)) { + try (Stream<Path> s = Files.list(fakeRoot)) { s.forEach(path -> assertEquals(path.getFileName().toString(), "DirectoryIteratorException")); } } catch (UncheckedIOException uioe) { @@ -398,7 +397,7 @@ } try { - try (CloseableStream<Path> s = Files.list(fakeRoot)) { + try (Stream<Path> s = Files.list(fakeRoot)) { s.forEach(path -> fail("should not get here")); } } catch (UncheckedIOException uioe) { @@ -427,12 +426,12 @@ try { fsp.setFaultyMode(false); Path fakeRoot = fs.getRoot(); - try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir2"))) { + try (Stream<Path> s = Files.list(fakeRoot.resolve("dir2"))) { // only one file s.forEach(path -> assertEquals(path.getFileName().toString(), "IOException")); } - try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("empty"))) { + try (Stream<Path> s = Files.walk(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); // ordered as depth-first @@ -440,13 +439,13 @@ } fsp.setFaultyMode(true); - try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir2"))) { + try (Stream<Path> s = Files.list(fakeRoot.resolve("dir2"))) { s.forEach(path -> fail("should have caused exception")); } catch (UncheckedIOException uioe) { assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException); } - try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("empty"))) { + try (Stream<Path> s = Files.walk(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); fail("should not reach here due to IOException"); @@ -454,7 +453,7 @@ assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException); } - try (CloseableStream<Path> s = Files.walk( + try (Stream<Path> s = Files.walk( fakeRoot.resolve("empty").resolve("IOException"))) { String[] result = s.map(path -> path.getFileName().toString()) @@ -502,20 +501,20 @@ fsp.setFaultyMode(false); Path fakeRoot = fs.getRoot(); // validate setting - try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("empty"))) { + try (Stream<Path> s = Files.list(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "SecurityException", "sample" }); } - try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("dir2"))) { + try (Stream<Path> s = Files.walk(fakeRoot.resolve("dir2"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "dir2", "SecurityException", "fileInSE", "file" }); } if (supportsLinks) { - try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir"))) { + try (Stream<Path> s = Files.list(fakeRoot.resolve("dir"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "d1", "f1", "lnDir2", "SecurityException", "lnDirSE", "lnFileSE" }); @@ -525,13 +524,13 @@ // execute test fsp.setFaultyMode(true); // ignore file cause SecurityException - try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("empty"))) { + try (Stream<Path> s = Files.walk(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "empty", "sample" }); } // skip folder cause SecurityException - try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("dir2"))) { + try (Stream<Path> s = Files.walk(fakeRoot.resolve("dir2"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "dir2", "file" }); @@ -539,14 +538,14 @@ if (supportsLinks) { // not following links - try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("dir"))) { + try (Stream<Path> s = Files.walk(fakeRoot.resolve("dir"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "dir", "d1", "f1", "lnDir2", "lnDirSE", "lnFileSE" }); } // following links - try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("dir"), FileVisitOption.FOLLOW_LINKS)) { + try (Stream<Path> s = Files.walk(fakeRoot.resolve("dir"), FileVisitOption.FOLLOW_LINKS)) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); // ?? Should fileInSE show up? @@ -556,19 +555,19 @@ } // list instead of walk - try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("empty"))) { + try (Stream<Path> s = Files.list(fakeRoot.resolve("empty"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "sample" }); } - try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir2"))) { + try (Stream<Path> s = Files.list(fakeRoot.resolve("dir2"))) { String[] result = s.map(path -> path.getFileName().toString()) .toArray(String[]::new); assertEqualsNoOrder(result, new String[] { "file" }); } // root cause SecurityException should be reported - try (CloseableStream<Path> s = Files.walk( + try (Stream<Path> s = Files.walk( fakeRoot.resolve("dir2").resolve("SecurityException"))) { String[] result = s.map(path -> path.getFileName().toString()) @@ -579,7 +578,7 @@ } // Walk a file cause SecurityException, we should get SE - try (CloseableStream<Path> s = Files.walk( + try (Stream<Path> s = Files.walk( fakeRoot.resolve("dir").resolve("SecurityException"))) { String[] result = s.map(path -> path.getFileName().toString()) @@ -590,7 +589,7 @@ } // List a file cause SecurityException, we should get SE as cannot read attribute - try (CloseableStream<Path> s = Files.list( + try (Stream<Path> s = Files.list( fakeRoot.resolve("dir2").resolve("SecurityException"))) { String[] result = s.map(path -> path.getFileName().toString()) @@ -600,7 +599,7 @@ assertTrue(se.getCause() instanceof FaultyFileSystem.FaultyException); } - try (CloseableStream<Path> s = Files.list( + try (Stream<Path> s = Files.list( fakeRoot.resolve("dir").resolve("SecurityException"))) { String[] result = s.map(path -> path.getFileName().toString()) @@ -627,7 +626,7 @@ } public void testConstructException() { - try (CloseableStream<String> s = Files.lines(testFolder.resolve("notExist"), Charset.forName("UTF-8"))) { + try (Stream<String> s = Files.lines(testFolder.resolve("notExist"), Charset.forName("UTF-8"))) { s.forEach(l -> fail("File is not even exist!")); } catch (IOException ioe) { assertTrue(ioe instanceof NoSuchFileException); @@ -635,24 +634,26 @@ } public void testClosedStream() throws IOException { - try (CloseableStream<Path> s = Files.list(testFolder)) { + try (Stream<Path> s = Files.list(testFolder)) { s.close(); - Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); - assertTrue(actual.length <= level1.length); - } - - try (CloseableStream<Path> s = Files.walk(testFolder)) { - s.close(); - Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); + Object[] actual = s.sorted().toArray(); fail("Operate on closed stream should throw IllegalStateException"); } catch (IllegalStateException ex) { // expected } - try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, + try (Stream<Path> s = Files.walk(testFolder)) { + s.close(); + Object[] actual = s.sorted().toArray(); + fail("Operate on closed stream should throw IllegalStateException"); + } catch (IllegalStateException ex) { + // expected + } + + try (Stream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, (p, attr) -> true)) { s.close(); - Object[] actual = s.sorted(Comparator.naturalOrder()).toArray(); + Object[] actual = s.sorted().toArray(); fail("Operate on closed stream should throw IllegalStateException"); } catch (IllegalStateException ex) { // expected
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/Arrays/ArraysLambdaTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,221 @@ +/* + * 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. + */ + +/** + * @test + * @summary Lambda unit test for Arrays. + * @library /sqeutil + * @(#) ArraysTest.java + * @author Tristan Yan + * @run testng ArraysLambdaTest + */ + +import java.util.Arrays; +import java.util.Random; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +public class ArraysLambdaTest { + private final static int ARRAY_SIZE = 1 << 8; + + private final static int MIN_LEN = 1 << 2; + + private final static int MAX_LEN = 1 << 6; + + private final static Random rand = new Random(System.currentTimeMillis()); + + private long[] generateLongData(int size) { + long[] array = new long[size]; + for(int i = 0; i < size; i++) { + array[i] = rand.nextLong(); + } + return array; + } + + private int[] generateIntData(int size) { + int[] array = new int[size]; + for(int i = 0; i < size; i++) { + array[i] = rand.nextInt(); + } + return array; + } + + private double[] generateDoubleData(int size) { + double[] array = new double[size]; + //Precision could be changed when do parallel calculation, we only use + //integer to test it to prevent precision lost issue + for(int i = 0; i < size; i++) { + array[i] = rand.nextInt(1024); + } + return array; + } + + private String[] generateStringData(int size) { + String[] array = new String[size]; + for(int i = 0; i < size; i++) { + array[i] = StringUtilities.randomString(MAX_LEN, MIN_LEN); + } + return array; + } + + private StringBuilder[] generateSBData(int size) { + StringBuilder[] array = new StringBuilder[size]; + for(int i = 0; i < size; i++) { + array[i] = new StringBuilder(StringUtilities.randomString(MAX_LEN, MIN_LEN)); + } + return array; + } + + @Test + public void testParallelPrefixForInt() { + int startIndex = rand.nextInt(ARRAY_SIZE); + int endIndex = startIndex + rand.nextInt(ARRAY_SIZE + 1 - startIndex); + + int[] intArr1 = generateIntData(ARRAY_SIZE); + int[] intArr2 = Arrays.copyOf(intArr1, ARRAY_SIZE); + int[] intBackArr = Arrays.copyOf(intArr1, ARRAY_SIZE); + Arrays.parallelPrefix(intArr1, LambdaUtilities.addIntBinaryOperator()); + assertEquals(intArr1.length, intBackArr.length); + int totalInt = 0; + for(int i = 0; i < ARRAY_SIZE; i++) { + totalInt += intBackArr[i]; + assertEquals(totalInt, intArr1[i]); + } + Arrays.parallelPrefix(intArr2, startIndex, endIndex, LambdaUtilities.addIntBinaryOperator()); + totalInt = 0; + for(int i = startIndex; i < endIndex; i++) { + totalInt += intBackArr[i]; + assertEquals(totalInt, intArr2[i]); + } + } + + @Test + public void testParallelPrefixForLong() { + int startIndex = rand.nextInt(ARRAY_SIZE); + int endIndex = startIndex + rand.nextInt(ARRAY_SIZE + 1 - startIndex); + long[] longArr1 = generateLongData(ARRAY_SIZE); + long[] longArr2 = Arrays.copyOf(longArr1, ARRAY_SIZE); + long[] longBackArr = Arrays.copyOf(longArr1, ARRAY_SIZE); + Arrays.parallelPrefix(longArr1, LambdaUtilities.addLongBinaryOperator()); + assertEquals(longArr1.length, longBackArr.length); + long totalLong = 0; + for(int i = 0; i < ARRAY_SIZE; i++) { + totalLong += longBackArr[i]; + assertEquals(totalLong, longArr1[i]); + } + Arrays.parallelPrefix(longArr2, startIndex, endIndex, LambdaUtilities.addLongBinaryOperator()); + totalLong = 0; + for(int i = startIndex; i < endIndex; i++) { + totalLong += longBackArr[i]; + assertEquals(totalLong, longArr2[i]); + } + } + + @Test + public void testParallelPrefixForDouble() { + int startIndex = rand.nextInt(ARRAY_SIZE); + int endIndex = startIndex + rand.nextInt(ARRAY_SIZE + 1 - startIndex); + double[] doubleArr1 = generateDoubleData(ARRAY_SIZE); + double[] doubleArr2 = Arrays.copyOf(doubleArr1, ARRAY_SIZE); + double[] doubleBackArr = Arrays.copyOf(doubleArr1, ARRAY_SIZE); + Arrays.parallelPrefix(doubleArr1, LambdaUtilities.addDoubleBinaryOperator()); + assertEquals(doubleArr1.length, doubleBackArr.length); + double totalDouble = 0; + for(int i = 0; i < ARRAY_SIZE; i++) { + totalDouble += doubleBackArr[i]; + assertEquals(totalDouble, doubleArr1[i]); + } + Arrays.parallelPrefix(doubleArr2, startIndex, endIndex, LambdaUtilities.addDoubleBinaryOperator()); + totalDouble = 0; + for(int i = startIndex; i < endIndex; i++) { + totalDouble += doubleBackArr[i]; + assertEquals(totalDouble, doubleArr2[i]); + } + } + + @Test + public void testParallelPrefix() { + int startIndex = rand.nextInt(ARRAY_SIZE); + int endIndex = startIndex + rand.nextInt(ARRAY_SIZE + 1 - startIndex); + StringBuilder[] sbArr1 = generateSBData(ARRAY_SIZE); + StringBuilder[] sbArr2 = Arrays.copyOf(sbArr1, ARRAY_SIZE); + StringBuilder[] sbBackArr = Arrays.copyOf(sbArr1, ARRAY_SIZE); + Arrays.parallelPrefix(sbArr1, LambdaUtilities.appendSBBinaryOperator()); + assertEquals(sbArr1.length, sbBackArr.length); + StringBuilder sbTotal = new StringBuilder(); + for(int i = 0; i < ARRAY_SIZE; i++) { + sbTotal.append(sbBackArr[i]); + assertEquals(sbTotal.toString(), sbArr1[i].toString()); + } + Arrays.parallelPrefix(sbArr2, startIndex, endIndex, LambdaUtilities.appendSBBinaryOperator()); + sbTotal = new StringBuilder(); + for(int i = startIndex; i < endIndex; i++) { + sbTotal.append(sbBackArr[i]); + assertEquals(sbTotal.toString(), sbArr2[i].toString()); + } + } + + @Test + public void testSetAll() { + boolean[] both = new boolean[]{true, false}; + for(boolean isParallel:both) { + //test Arrays.setAll(double[], IntFunction) + int[] iArray = new int[ARRAY_SIZE]; + if (isParallel) + Arrays.parallelSetAll(iArray, i -> 2 * i); + else + Arrays.setAll(iArray, i -> 2 * i); + for(int i = 0; i < iArray.length; i++) + assertEquals(iArray[i], 2 * i); + + //test Arrays.setAll(long[], IntFunction) + long[] lArray = new long[ARRAY_SIZE]; + if (isParallel) + Arrays.parallelSetAll(lArray, i -> (long)i << 32); + else + Arrays.setAll(lArray, i -> (long)i << 32); + for(int i = 0; i < lArray.length; i++) + assertEquals(lArray[i] >> 32, i); + + //test Arrays.setAll(double[], IntToDoubleFunction) + double[] dArray = new double[ARRAY_SIZE]; + if (isParallel) + Arrays.parallelSetAll(dArray, i -> Math.sin(i)); + else + Arrays.setAll(dArray, i -> Math.sin(i)); + for(int i = 0; i < dArray.length; i++) + assertEquals(dArray[i], Math.sin(i)); + + //test Arrays.setAll(T[], IntFunction<? extends T>) + //This is a other version of Arrays.copy() + String[] sArrExpected = generateStringData(ARRAY_SIZE); + String[] sArr = new String[ARRAY_SIZE]; + if (isParallel) + Arrays.parallelSetAll(sArr, i -> sArrExpected[i]); + else + Arrays.setAll(sArr, i -> sArrExpected[i]); + for(int i = 0; i < sArr.length; i++) + assertEquals(sArrExpected[i], sArrExpected[i]); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/Arrays/ParallelSet.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,157 @@ +/* + * 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. + */ + +/** + * @test + * @summary unit test for Arrays.ParallelSet()/set(). + * @author Tristan Yan + * @run testng ParallelSet + */ + +import java.util.Arrays; +import java.util.function.IntFunction; +import java.util.function.IntToDoubleFunction; +import java.util.function.IntToLongFunction; +import java.util.function.IntUnaryOperator; +import static org.testng.Assert.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class ParallelSet { + //Array size less than MIN_PARTITION + private final static int SMALL_ARRAY_SIZE = 1 << 3; + + //Array size equals MIN_PARTITION + private final static int THRESHOLD_ARRAY_SIZE = 1 << 4; + + //Array size greater than MIN_PARTITION + private final static int MEDIUM_ARRAY_SIZE = 1 << 8; + + //Array size much greater than MIN_PARTITION + private final static int LARGE_ARRAY_SIZE = 1 << 12; + + private final static int[] ARRAY_SIZE_COLLECTION = new int[]{ + SMALL_ARRAY_SIZE, THRESHOLD_ARRAY_SIZE,MEDIUM_ARRAY_SIZE, LARGE_ARRAY_SIZE}; + + @DataProvider + public static Object[][] intSet(){ + return genericData("Arrays.parrallelSetAll/setAll(new int[%d],IntUnaryOperator)", + int[]::<Integer>new, new IntUnaryOperator[]{i -> i * 2, i -> i * 2 + 1}); + } + + @DataProvider + public static Object[][] longSet(){ + return genericData("Arrays.parrallelSetAll/setAll(new long[%d],IntToLongeFunction)", + long[]::<Integer>new, new IntToLongFunction[]{ i -> i * 2, i -> i * 2 + 1}); + } + + @DataProvider + public static Object[][] doubleSet(){ + return genericData("Arrays.parallelSetAll/setAll(new double[%d],IntToDoubleFunction)", + double[]::<Integer>new, new IntToDoubleFunction[]{i -> i * 2, i -> i * 2 + 1}); + } + + @DataProvider + public static Object[][] stringSet(){ + return genericData("Arrays.parallelSetAll/setAll(new String[%d],IntFunction<String>)", + String[]::<Integer>new, new IntFunction[]{Integer::toString}); + } + + private static <T, OPS> Object[][] genericData(String description, IntFunction<T> generateFunc, OPS[] funcs) { + //test arrays which size is equals n-1, n, n+1 + Object[][] data = new Object[ARRAY_SIZE_COLLECTION.length * 3 * funcs.length][4]; + for(int n = 0; n < ARRAY_SIZE_COLLECTION.length; n++ ) { + for(int testValue = -1 ; testValue <= 1; testValue++) { + int array_size = ARRAY_SIZE_COLLECTION[n] + testValue; + for(int funcN = 0; funcN < funcs.length; funcN++) { + int index = n * 3 * funcs.length + (testValue + 1) * funcs.length + funcN; + data[index][0] = String.format(description, array_size); + data[index][1] = array_size; + data[index][2] = generateFunc; + data[index][3] = funcs[funcN]; + } + } + } + return data; + } + + @Test(dataProvider="intSet") + public void testSetAllForInt(String description,int size, IntFunction<int[]> dataFunc, IntUnaryOperator iuo) { + int[] expectedResult = dataFunc.apply(size); + for(int i = 0; i < size; i++) + expectedResult[i] = iuo.applyAsInt(i); + + int[] sequentialResult = dataFunc.apply(size); + Arrays.setAll(sequentialResult, iuo); + assertEquals(sequentialResult, expectedResult); + + int[] parallelResult = dataFunc.apply(size); + Arrays.setAll(parallelResult, iuo); + assertEquals(sequentialResult, expectedResult); + } + + @Test(dataProvider="longSet") + public void testSetAllForLong(String description, int size, IntFunction<long[]> dataFunc, IntToLongFunction itlf) { + long[] expectedResult = dataFunc.apply(size); + for(int i = 0; i < size; i++) + expectedResult[i] = itlf.applyAsLong(i); + + long[] sequentialResult = dataFunc.apply(size); + Arrays.setAll(sequentialResult, itlf); + assertEquals(sequentialResult, expectedResult); + + long[] parallelResult = dataFunc.apply(size); + Arrays.setAll(parallelResult, itlf); + assertEquals(sequentialResult, expectedResult); + } + + @Test(dataProvider="doubleSet") + public void testSetAllForDouble(String description,int size, IntFunction<double[]> dataFunc, IntToDoubleFunction itdf) { + double[] expectedResult = dataFunc.apply(size); + for(int i = 0; i < size; i++) + expectedResult[i] = itdf.applyAsDouble(i); + + double[] sequentialResult = dataFunc.apply(size); + Arrays.setAll(sequentialResult, itdf); + assertEquals(sequentialResult, expectedResult); + + double[] parallelResult = dataFunc.apply(size); + Arrays.setAll(parallelResult, itdf); + assertEquals(sequentialResult, expectedResult); + } + + @Test(dataProvider="stringSet") + public void testSetAllForString(String description,int size, IntFunction<String[]> dataFunc, IntFunction<String> itsf) { + String[] expectedResult = dataFunc.apply(size); + for(int i = 0; i < size; i++) + expectedResult[i] = itsf.apply(i); + + String[] sequentialResult = dataFunc.apply(size); + Arrays.setAll(sequentialResult, itsf); + assertEquals(sequentialResult, expectedResult); + + String[] parallelResult = dataFunc.apply(size); + Arrays.setAll(parallelResult, itsf); + assertEquals(sequentialResult, expectedResult); + } +}
--- a/test/java/util/Arrays/SetAllTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/Arrays/SetAllTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -167,13 +167,13 @@ public void testStringSetNulls() { String[] ar = new String[2]; try { - Arrays.setAll(null, i -> "X"); + Arrays.setAll(null, (IntFunction<String>) i -> "X"); fail("Arrays.setAll(null, foo) should throw NPE"); } catch (NullPointerException npe) { // expected } try { - Arrays.parallelSetAll(null, i -> "X"); + Arrays.parallelSetAll(null, (IntFunction<String>) i -> "X"); fail("Arrays.parallelSetAll(null, foo) should throw NPE"); } catch (NullPointerException npe) { // expected
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/Collection/IntRemovePredicateAllTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2012, 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 + * @summary sanity tests for Collection.removeIf()/forEach() + * @library /sqeutil + * @(#) IntRemovePredicateAllTest.java + * @author Tristan Yan + * @run testng IntRemovePredicateAllTest + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import static org.testng.Assert.*; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +public class IntRemovePredicateAllTest <T extends Collection<Integer>> { + private final static Random rand = new Random(System.currentTimeMillis()); + + //1K data is best we can optimize for now. + private final static int MAX_SIZE = 1 << 10; + + //At least we should have 4 elements in the list + private final static int MIN_SIZE = 1 << 2; + + T t; + + public IntRemovePredicateAllTest(T typeObject1){ + t = typeObject1; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static Collection<Integer> + generateData(Class<? extends Collection<Integer>> cls, + int... initValue) throws InstantiationException, + IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException{ + Collection<Integer> c; + int t1Size = 0; + if(initValue.length == 0) { + c = cls.newInstance(); + t1Size= rand.nextInt(MAX_SIZE); + if(t1Size < MIN_SIZE) t1Size = MIN_SIZE; + } else { + Constructor con = cls.getConstructor(int.class); + c = (Collection<Integer>)con.newInstance(initValue[0]); + t1Size = initValue.length == 2 ? initValue[1] : initValue[0]; + } + for(int i = 0; i < t1Size; i++) + c.add(rand.nextInt()); + return c; + } + + @Factory + @SuppressWarnings({"rawtypes", "unchecked"}) + public static Object[] create() throws Exception{ + List<IntRemovePredicateAllTest> result = new ArrayList<>(); + Class[] defaultConstructorClazz = { + (Class<ArrayDeque<Integer>>)(Class<?>)(ArrayDeque.class), + (Class<ArrayList<Integer>>)(Class<?>)(ArrayList.class), + (Class<ConcurrentLinkedDeque<Integer>>)(Class<?>)(ConcurrentLinkedDeque.class), + (Class<ConcurrentLinkedQueue<Integer>>)(Class<?>)(ConcurrentLinkedQueue.class), + (Class<ConcurrentSkipListSet<Integer>>)(Class<?>)(ConcurrentSkipListSet.class), + (Class<HashSet<Integer>>)(Class<?>)(HashSet.class), + (Class<LinkedBlockingDeque<Integer>>)(Class<?>)(LinkedBlockingDeque.class), + (Class<LinkedBlockingQueue<Integer>>)(Class<?>)(LinkedBlockingQueue.class), + (Class<LinkedHashSet<Integer>>)(Class<?>)(LinkedHashSet.class), + (Class<LinkedList<Integer>>)(Class<?>)(LinkedList.class), + (Class<LinkedTransferQueue<Integer>>)(Class<?>)(LinkedTransferQueue.class), + (Class<PriorityBlockingQueue<Integer>>)(Class<?>)(PriorityBlockingQueue.class), + (Class<PriorityQueue<Integer>>)(Class<?>)(PriorityQueue.class), + (Class<Stack<Integer>>)(Class<?>)(Stack.class), + (Class<TreeSet<Integer>>)(Class<?>)(TreeSet.class), + (Class<Vector<Integer>>)(Class<?>)(Vector.class) + }; + + Class[] capacityConstructorClazz = { + (Class<ArrayBlockingQueue<Integer>>)(Class<?>)(ArrayBlockingQueue.class), + (Class<ArrayDeque<Integer>>)(Class<?>)(ArrayDeque.class), + (Class<ArrayList<Integer>>)(Class<?>)(ArrayList.class), + (Class<HashSet<Integer>>)(Class<?>)(HashSet.class), + (Class<LinkedBlockingDeque<Integer>>)(Class<?>)(LinkedBlockingDeque.class), + (Class<LinkedBlockingQueue<Integer>>)(Class<?>)(LinkedBlockingQueue.class), + (Class<LinkedHashSet<Integer>>)(Class<?>)(LinkedHashSet.class), + (Class<PriorityBlockingQueue<Integer>>)(Class<?>)(PriorityBlockingQueue.class), + (Class<PriorityQueue<Integer>>)(Class<?>)(PriorityQueue.class), + (Class<Vector<Integer>>)(Class<?>)(Vector.class) + }; + + for(int i = 0; i < defaultConstructorClazz.length + + capacityConstructorClazz.length; i++) { + int initSize1 = rand.nextInt(MAX_SIZE); + if(initSize1 < MIN_SIZE) initSize1 = MIN_SIZE; + Collection<Integer> c = i < defaultConstructorClazz.length ? + generateData(defaultConstructorClazz[i]) : + generateData(capacityConstructorClazz[i - defaultConstructorClazz.length], + initSize1); + result.add(new IntRemovePredicateAllTest(c)); + } + return result.toArray(); + } + + @Test + public void testRemoveIf() { + int limit = rand.nextInt(); + boolean isUP = rand.nextBoolean(); + Predicate<Integer> p = LambdaUtilities.randomIntegerPredicate(isUP, limit); + t.removeIf(p); + assertTrue(verifyMatch(t, limit, !isUP, true)); + } + + @Test + public void testForEach() { + final AtomicInteger accumulator = new AtomicInteger(0); + t.forEach(t -> accumulator.addAndGet(t)); + + Optional<Integer> opi = t.stream().reduce((i1, i2) -> i1 + i2); + assertTrue(opi.isPresent()); + assertEquals(accumulator.get(), opi.get().intValue()); + } + + private boolean verifyMatch(Collection<Integer> c, int limit, boolean isUP, + boolean all) { + Iterator<Integer> it = c.iterator(); + while(it.hasNext()) { + int current = it.next(); + if(isUP) { + if(all) { + if(current < limit) return false; + } else { + if(current >= limit) return true; + } + } else { + if(all) { + if(current >= limit) return false; + } else { + if(current < limit) return true; + } + } + } + return all; + } +}
--- a/test/java/util/Collection/MOAT.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/Collection/MOAT.java Wed Aug 14 15:53:13 2013 -0700 @@ -51,10 +51,29 @@ * simultaneously test all other implementations. */ -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.NotSerializableException; +import java.io.Serializable; import java.util.*; import java.util.concurrent.*; -import static java.util.Collections.*; +import java.util.function.*; + +import static java.util.Collections.EMPTY_LIST; +import static java.util.Collections.EMPTY_MAP; +import static java.util.Collections.EMPTY_SET; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.emptySet; +import static java.util.Collections.nCopies; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static java.util.Collections.unmodifiableList; public class MOAT { public static void realMain(String[] args) { @@ -118,9 +137,9 @@ testCollection(emptyArray); testEmptyList(emptyArray); THROWS(IndexOutOfBoundsException.class, - new Fun(){void f(){ emptyArray.set(0,1); }}); + new Fun(){public void f(){ emptyArray.set(0,1); }}); THROWS(UnsupportedOperationException.class, - new Fun(){void f(){ emptyArray.add(0,1); }}); + new Fun(){public void f(){ emptyArray.add(0,1); }}); List<Integer> noOne = nCopies(0,1); testCollection(noOne); @@ -205,11 +224,11 @@ check(! it.hasNext()); THROWS(NoSuchElementException.class, - new Fun(){void f(){ it.next(); }}); + new Fun(){public void f(){ it.next(); }}); try { it.remove(); } - catch (IllegalStateException _) { pass(); } - catch (UnsupportedOperationException _) { pass(); } + catch (IllegalStateException pass) { pass(); } + catch (UnsupportedOperationException pass) { pass(); } catch (Throwable t) { unexpected(t); } if (rnd.nextBoolean()) @@ -232,15 +251,15 @@ private static void testImmutableCollection(final Collection<Integer> c) { THROWS(UnsupportedOperationException.class, - new Fun(){void f(){ c.add(99); }}, - new Fun(){void f(){ c.addAll(singleton(99)); }}); + new Fun(){public void f(){ c.add(99); }}, + new Fun(){public void f(){ c.addAll(singleton(99)); }}); if (! c.isEmpty()) { final Integer first = c.iterator().next(); THROWS(UnsupportedOperationException.class, - new Fun(){void f(){ c.clear(); }}, - new Fun(){void f(){ c.remove(first); }}, - new Fun(){void f(){ c.removeAll(singleton(first)); }}, - new Fun(){void f(){ c.retainAll(emptyList()); }} + new Fun(){public void f(){ c.clear(); }}, + new Fun(){public void f(){ c.remove(first); }}, + new Fun(){public void f(){ c.removeAll(singleton(first)); }}, + new Fun(){public void f(){ c.retainAll(emptyList()); }} ); } } @@ -253,15 +272,15 @@ testList(c); testImmutableCollection(c); THROWS(UnsupportedOperationException.class, - new Fun(){void f(){ c.set(0,42); }}, - new Fun(){void f(){ c.add(0,42); }}, - new Fun(){void f(){ c.addAll(0,singleton(86)); }}); + new Fun(){public void f(){ c.set(0,42); }}, + new Fun(){public void f(){ c.add(0,42); }}, + new Fun(){public void f(){ c.addAll(0,singleton(86)); }}); if (! c.isEmpty()) THROWS(UnsupportedOperationException.class, - new Fun(){void f(){ + new Fun(){public void f(){ Iterator<Integer> it = c.iterator(); it.next(); it.remove();}}, - new Fun(){void f(){ + new Fun(){public void f(){ ListIterator<Integer> it = c.listIterator(); it.next(); it.remove();}}); } @@ -281,28 +300,28 @@ testEmptyCollection(m.values()); try { check(! m.containsValue(null)); } - catch (NullPointerException _) { /* OK */ } + catch (NullPointerException pass) { /* OK */ } try { check(! m.containsKey(null)); } - catch (NullPointerException _) { /* OK */ } + catch (NullPointerException pass) { /* OK */ } check(! m.containsValue(1)); check(! m.containsKey(1)); } private static void testImmutableMap(final Map<Integer,Integer> m) { THROWS(UnsupportedOperationException.class, - new Fun(){void f(){ m.put(1,1); }}, - new Fun(){void f(){ m.putAll(singletonMap(1,1)); }}); + new Fun(){public void f(){ m.put(1,1); }}, + new Fun(){public void f(){ m.putAll(singletonMap(1,1)); }}); if (! m.isEmpty()) { final Integer first = m.keySet().iterator().next(); THROWS(UnsupportedOperationException.class, - new Fun(){void f(){ m.remove(first); }}, - new Fun(){void f(){ m.clear(); }}); + new Fun(){public void f(){ m.remove(first); }}, + new Fun(){public void f(){ m.clear(); }}); final Map.Entry<Integer,Integer> me = m.entrySet().iterator().next(); Integer key = me.getKey(); Integer val = me.getValue(); THROWS(UnsupportedOperationException.class, - new Fun(){void f(){ me.setValue(3); }}); + new Fun(){public void f(){ me.setValue(3); }}); equal(key, me.getKey()); equal(val, me.getValue()); } @@ -494,9 +513,9 @@ // insert, query, remove element at head if (isEmpty) { THROWS(NoSuchElementException.class, - new Fun(){void f(){ deq.getFirst(); }}, - new Fun(){void f(){ deq.element(); }}, - new Fun(){void f(){ deq.iterator().next(); }}); + new Fun(){public void f(){ deq.getFirst(); }}, + new Fun(){public void f(){ deq.element(); }}, + new Fun(){public void f(){ deq.iterator().next(); }}); check(deq.peekFirst() == null); check(deq.peek() == null); } else { @@ -548,9 +567,9 @@ } if (isEmpty) { THROWS(NoSuchElementException.class, - new Fun(){void f(){ deq.getFirst(); }}, - new Fun(){void f(){ deq.element(); }}, - new Fun(){void f(){ deq.iterator().next(); }}); + new Fun(){public void f(){ deq.getFirst(); }}, + new Fun(){public void f(){ deq.element(); }}, + new Fun(){public void f(){ deq.iterator().next(); }}); check(deq.peekFirst() == null); check(deq.peek() == null); } else { @@ -574,7 +593,7 @@ if (isEmpty) { check(deq.peekLast() == null); THROWS(NoSuchElementException.class, - new Fun(){void f(){ deq.getLast(); }}); + new Fun(){public void f(){ deq.getLast(); }}); } else { check(deq.peekLast() != e); check(deq.getLast() != e); @@ -618,7 +637,7 @@ if (isEmpty) { check(deq.peekLast() == null); THROWS(NoSuchElementException.class, - new Fun(){void f(){ deq.getLast(); }}); + new Fun(){public void f(){ deq.getLast(); }}); } else { check(deq.peekLast() != e); check(deq.getLast() != e); @@ -651,17 +670,17 @@ if (isList) { check(!asList.listIterator().hasPrevious()); THROWS(NoSuchElementException.class, - new Fun(){void f(){ asList.listIterator().previous(); }}); + new Fun(){public void f(){ asList.listIterator().previous(); }}); } THROWS(NoSuchElementException.class, - new Fun(){void f(){ deq.iterator().next(); }}, - new Fun(){void f(){ deq.element(); }}, - new Fun(){void f(){ deq.getFirst(); }}, - new Fun(){void f(){ deq.getLast(); }}, - new Fun(){void f(){ deq.pop(); }}, - new Fun(){void f(){ deq.remove(); }}, - new Fun(){void f(){ deq.removeFirst(); }}, - new Fun(){void f(){ deq.removeLast(); }}); + new Fun(){public void f(){ deq.iterator().next(); }}, + new Fun(){public void f(){ deq.element(); }}, + new Fun(){public void f(){ deq.getFirst(); }}, + new Fun(){public void f(){ deq.getLast(); }}, + new Fun(){public void f(){ deq.pop(); }}, + new Fun(){public void f(){ deq.remove(); }}, + new Fun(){public void f(){ deq.removeFirst(); }}, + new Fun(){public void f(){ deq.removeLast(); }}); check(deq.poll() == null); check(deq.pollFirst() == null); @@ -713,8 +732,8 @@ l.addAll(-1, Collections.<Integer>emptyList()); fail("Expected IndexOutOfBoundsException not thrown"); } - catch (UnsupportedOperationException _) {/* OK */} - catch (IndexOutOfBoundsException _) {/* OK */} + catch (UnsupportedOperationException pass) {/* OK */} + catch (IndexOutOfBoundsException pass) {/* OK */} catch (Throwable t) { unexpected(t); } // equal(l instanceof Serializable, @@ -789,12 +808,7 @@ catch (Throwable t) { unexpected(t); } oneElement(c); - try { - c.removeAll(null); - fail("Expected NullPointerException"); - } - catch (NullPointerException e) { pass(); } - catch (Throwable t) { unexpected(t); } + THROWS( NullPointerException.class, (Fun) () -> { c.removeAll((Collection<?>)null);}); clear(c); try { @@ -805,28 +819,16 @@ catch (Throwable t) { unexpected(t); } oneElement(c); - try { - c.retainAll(null); - fail("Expected NullPointerException"); - } - catch (NullPointerException e) { pass(); } - catch (Throwable t) { unexpected(t); } + THROWS( NullPointerException.class, (Fun) () -> {c.retainAll((Collection<?>)null);}); oneElement(c); - try { - c.addAll(null); - fail("Expected NullPointerException"); - } - catch (NullPointerException e) { pass(); } - catch (Throwable t) { unexpected(t); } + THROWS( NullPointerException.class, (Fun) () -> {c.addAll((Collection<Integer>)null);}); oneElement(c); - try { - c.containsAll(null); - fail("Expected NullPointerException"); - } - catch (NullPointerException e) { pass(); } - catch (Throwable t) { unexpected(t); } + THROWS( NullPointerException.class, (Fun) () -> {c.containsAll((Collection<?>)null);}); + + oneElement(c); + THROWS( NullPointerException.class, (Fun) () -> {c.removeIf((Predicate<Integer>)null);}); } } @@ -984,22 +986,22 @@ ? (ConcurrentMap<T,Integer>) m : null; List<Fun> fs = new ArrayList<Fun>(); - fs.add(new Fun(){void f(){ check(! m.containsKey(null));}}); - fs.add(new Fun(){void f(){ equal(m.remove(null), null);}}); - fs.add(new Fun(){void f(){ equal(m.get(null), null);}}); + fs.add(() -> { check(! m.containsKey(null));}); + fs.add(new Fun(){public void f(){ equal(m.remove(null), null);}}); + fs.add(new Fun(){public void f(){ equal(m.get(null), null);}}); if (cm != null) { - fs.add(new Fun(){void f(){ check(! cm.remove(null,null));}});} + fs.add(new Fun(){public void f(){ check(! cm.remove(null,null));}});} throwsConsistently(NullPointerException.class, fs); fs.clear(); final Map<T,Integer> sm = singletonMap(null,1); - fs.add(new Fun(){void f(){ equal(m.put(null,1), null); m.clear();}}); - fs.add(new Fun(){void f(){ m.putAll(sm); m.clear();}}); + fs.add(new Fun(){public void f(){ equal(m.put(null,1), null); m.clear();}}); + fs.add(new Fun(){public void f(){ m.putAll(sm); m.clear();}}); if (cm != null) { - fs.add(new Fun(){void f(){ check(! cm.remove(null,null));}}); - fs.add(new Fun(){void f(){ equal(cm.putIfAbsent(null,1), 1);}}); - fs.add(new Fun(){void f(){ equal(cm.replace(null,1), null);}}); - fs.add(new Fun(){void f(){ equal(cm.replace(null,1, 1), 1);}}); + fs.add(new Fun(){public void f(){ check(! cm.remove(null,null));}}); + fs.add(new Fun(){public void f(){ equal(cm.putIfAbsent(null,1), 1);}}); + fs.add(new Fun(){public void f(){ equal(cm.replace(null,1), null);}}); + fs.add(new Fun(){public void f(){ equal(cm.replace(null,1, 1), 1);}}); } throwsConsistently(NullPointerException.class, fs); } @@ -1161,7 +1163,7 @@ equalNext(it, 1); check(! it.hasNext()); THROWS(NoSuchElementException.class, - new Fun(){void f(){it.next();}}); + new Fun(){public void f(){it.next();}}); } { @@ -1172,7 +1174,7 @@ check(it.hasNext()); equal(it.next().getKey(), 1); check(! it.hasNext()); THROWS(NoSuchElementException.class, - new Fun(){void f(){it.next();}}); + new Fun(){public void f(){it.next();}}); } } @@ -1206,7 +1208,7 @@ equalNext(it, 1); check(! it.hasNext()); THROWS(NoSuchElementException.class, - new Fun(){void f(){it.next();}}); + new Fun(){public void f(){it.next();}}); } } @@ -1227,7 +1229,7 @@ System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); if (failed > 0) throw new Exception("Some tests failed"); } - private static abstract class Fun {abstract void f() throws Throwable;} + private interface Fun {void f() throws Throwable;} private static void THROWS(Class<? extends Throwable> k, Fun... fs) { for (Fun f : fs) try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/Collections/Wrappers.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,132 @@ +/* + * 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. + */ + +/** + * @test + * @run testng Wrappers + * @summary Unit tests for wrapping classes should delegate to default methods + */ + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +import static org.testng.Assert.assertFalse; + +@Test(groups = "unit") +public class Wrappers { + static Object[][] collections; + + @DataProvider(name="collections") + public static Object[][] collectionCases() { + if (collections != null) { + return collections; + } + + List<Object[]> cases = new ArrayList<>(); + LinkedList<Integer> seedList = new LinkedList<>(); + TreeSet<Integer> seedSet = new TreeSet<>(); + TreeMap<Integer, Integer> seedMap = new TreeMap<>(); + + for (int i = 1; i <= 10; i++) { + seedList.add(i); + seedSet.add(i); + seedMap.put(i, i); + } + + cases.add(new Object[] { Collections.unmodifiableCollection(seedList) }); + cases.add(new Object[] { Collections.unmodifiableList(seedList) }); + cases.add(new Object[] { Collections.unmodifiableSet(seedSet) }); + cases.add(new Object[] { Collections.unmodifiableSortedSet(seedSet) }); + + // As sets from map also need to be unmodifiable, thus a wrapping + // layer exist and should not have default methods + cases.add(new Object[] { Collections.unmodifiableMap(seedMap).entrySet() }); + cases.add(new Object[] { Collections.unmodifiableMap(seedMap).keySet() }); + cases.add(new Object[] { Collections.unmodifiableMap(seedMap).values() }); + cases.add(new Object[] { Collections.unmodifiableSortedMap(seedMap).entrySet() }); + cases.add(new Object[] { Collections.unmodifiableSortedMap(seedMap).keySet() }); + cases.add(new Object[] { Collections.unmodifiableSortedMap(seedMap).values() }); + + // Synchronized + cases.add(new Object[] { Collections.synchronizedCollection(seedList) }); + cases.add(new Object[] { Collections.synchronizedList(seedList) }); + cases.add(new Object[] { Collections.synchronizedSet(seedSet) }); + cases.add(new Object[] { Collections.synchronizedSortedSet(seedSet) }); + + // As sets from map also need to be synchronized on the map, thus a + // wrapping layer exist and should not have default methods + cases.add(new Object[] { Collections.synchronizedMap(seedMap).entrySet() }); + cases.add(new Object[] { Collections.synchronizedMap(seedMap).keySet() }); + cases.add(new Object[] { Collections.synchronizedMap(seedMap).values() }); + cases.add(new Object[] { Collections.synchronizedSortedMap(seedMap).entrySet() }); + cases.add(new Object[] { Collections.synchronizedSortedMap(seedMap).keySet() }); + cases.add(new Object[] { Collections.synchronizedSortedMap(seedMap).values() }); + + // Checked + cases.add(new Object[] { Collections.checkedCollection(seedList, Integer.class) }); + cases.add(new Object[] { Collections.checkedList(seedList, Integer.class) }); + cases.add(new Object[] { Collections.checkedSet(seedSet, Integer.class) }); + cases.add(new Object[] { Collections.checkedSortedSet(seedSet, Integer.class) }); + cases.add(new Object[] { Collections.checkedQueue(seedList, Integer.class) }); + + // asLifoQueue is another wrapper + cases.add(new Object[] { Collections.asLifoQueue(seedList) }); + + collections = cases.toArray(new Object[0][]); + return collections; + } + + static Method[] defaultMethods; + + static { + ArrayList<Method> list = new ArrayList<>(); + Method[] methods = Collection.class.getMethods(); + for (Method m: methods) { + if (m.isDefault()) { + list.add(m); + } + } + defaultMethods = list.toArray(new Method[0]); + } + + @Test(dataProvider = "collections") + public static void testAllDefaultMethodsOverridden(Collection c) throws NoSuchMethodException { + Class cls = c.getClass(); + for (Method m: defaultMethods) { + Method m2 = cls.getMethod(m.getName(), m.getParameterTypes()); + // default had been override + assertFalse(m2.isDefault(), cls.getCanonicalName()); + } + } +} +
--- a/test/java/util/Comparator/BasicTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/Comparator/BasicTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -211,8 +211,8 @@ }; public void testComparatorDefaultMethods() { - Comparator<People> cmp = Comparator.comparing((Function<People, String>) People::getFirstName); - Comparator<People> cmp2 = Comparator.comparing((Function<People, String>) People::getLastName); + Comparator<People> cmp = Comparator.comparing(People::getFirstName); + Comparator<People> cmp2 = Comparator.comparing(People::getLastName); // reverseOrder assertComparison(cmp.reversed(), people[1], people[0]); // thenComparing(Comparator) @@ -235,7 +235,7 @@ public void testNullsFirst() { Comparator<String> strcmp = Comparator.nullsFirst(Comparator.naturalOrder()); - Comparator<People> cmp = Comparator.<People, String>comparing(People::getLastName, strcmp) + Comparator<People> cmp = Comparator.comparing(People::getLastName, strcmp) .thenComparing(People::getFirstName, strcmp); // Mary.null vs Mary.Cook - solve by last name assertComparison(cmp, people[6], people[5]); @@ -243,7 +243,7 @@ assertComparison(cmp, people[7], people[6]); // More than one thenComparing - strcmp = Comparator.nullsFirst(Comparator.comparing((ToIntFunction<String>) String::length) + strcmp = Comparator.nullsFirst(Comparator.comparing(String::length) .thenComparing(String.CASE_INSENSITIVE_ORDER)); assertComparison(strcmp, null, "abc"); assertComparison(strcmp, "ab", "abc"); @@ -273,7 +273,7 @@ public void testNullsLast() { Comparator<String> strcmp = Comparator.nullsLast(Comparator.naturalOrder()); - Comparator<People> cmp = Comparator.<People, String>comparing(People::getLastName, strcmp) + Comparator<People> cmp = Comparator.comparing(People::getLastName, strcmp) .thenComparing(People::getFirstName, strcmp); // Mary.null vs Mary.Cook - solve by last name assertComparison(cmp, people[5], people[6]); @@ -281,7 +281,7 @@ assertComparison(cmp, people[7], people[6]); // More than one thenComparing - strcmp = Comparator.nullsLast(Comparator.comparing((ToIntFunction<String>) String::length) + strcmp = Comparator.nullsLast(Comparator.comparing(String::length) .thenComparing(String.CASE_INSENSITIVE_ORDER)); assertComparison(strcmp, "abc", null); assertComparison(strcmp, "ab", "abc"); @@ -345,7 +345,7 @@ fail("comparing(null, cmp) should throw NPE"); } catch (NullPointerException npe) {} try { - Comparator<People> cmp = Comparator.comparing((Function<People, String>) People::getFirstName, null); + Comparator<People> cmp = Comparator.comparing(People::getFirstName, null); fail("comparing(f, null) should throw NPE"); } catch (NullPointerException npe) {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/List/ExtensionsTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2012, 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 + * @summary Basic tests for List's extension methods + * @library /sqeutil + * @(#) ListTest.java + * @author Tristan Yan + * @run testng ExtensionsTest + */ + +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Stream; +import static org.testng.Assert.*; +import org.testng.annotations.*; + +public class ExtensionsTest<T extends List<Integer>> { + protected Class<T> typeObject; + + protected boolean hasIni; + + protected int initSize; + + protected final static int START = -1 << 6; + + protected final static int END = 1 << 6; + + protected final static Random rand = new Random(System.currentTimeMillis()); + + @SuppressWarnings({"rawtypes", "unchecked"}) + protected static Class[] defaultConstructorClazz = { + (Class<ArrayList<Integer>>)(Class<?>)(ArrayList.class), + (Class<CopyOnWriteArrayList<Integer>>)(Class<?>)(CopyOnWriteArrayList.class), + (Class<LinkedList<Integer>>)(Class<?>)(LinkedList.class), + (Class<Stack<Integer>>)(Class<?>)(Stack.class), + (Class<Vector<Integer>>)(Class<?>)(Vector.class) + }; + + @SuppressWarnings({"rawtypes", "unchecked"}) + protected static Class[] capacityConstructorClazz = { + (Class<ArrayList<Integer>>)(Class<?>)(ArrayList.class), + (Class<Vector<Integer>>)(Class<?>)(Vector.class) + }; + + public ExtensionsTest(Class<T> clazz, int... initSizes){ + this.typeObject = clazz; + assert(initSizes.length <= 1); + if(initSizes.length == 1) { + hasIni = true; + this.initSize = initSizes[0]; + } + } + + protected List<Integer> generateList(int start, int end, int size) + throws Exception{ + List<Integer> list = hasIni ? + (List<Integer>)LambdaUtilities.create(typeObject, initSize) : + (List<Integer>)LambdaUtilities.create(typeObject); + for(int i = 0; i < size; i++) { + int element = start + rand.nextInt(end - start); + list.add(element); + } + return list; + } + + protected List<Integer> generateList(int start, int end) throws Exception{ + return generateList(start, end, end - start); + } + + @Factory + @SuppressWarnings({"rawtypes", "unchecked"}) + public static Object[] create(){ + List<ExtensionsTest> result = new ArrayList<>(); + Stream<Class> stream1 = Arrays.stream(defaultConstructorClazz); + Stream<Class> stream2 = Arrays.stream(capacityConstructorClazz); + stream1.forEach(clazz -> result.add(new ExtensionsTest(clazz))); + stream2.forEach(clazz -> result.add(new ExtensionsTest(clazz, + END - START + rand.nextInt(END - START)))); + return result.toArray(); + } + + @Test + public void testReplaceAll() throws Exception { + List<Integer> list = generateList(START, END); + List<Integer> listCloned = hasIni ? + (List<Integer>)LambdaUtilities.create(typeObject, initSize) : + (List<Integer>)LambdaUtilities.create(typeObject); + listCloned.addAll(list); + EnumSet<LambdaUtilities.IntOp> set = EnumSet.allOf(LambdaUtilities.IntOp.class); + LambdaUtilities.IntOp[] ops = new LambdaUtilities.IntOp[set.size()]; + set.toArray(ops); + LambdaUtilities.IntOp op = ops[rand.nextInt(ops.length)]; + int value = START + rand.nextInt(END - START); + if( op == LambdaUtilities.IntOp.DIVIDE || op == LambdaUtilities.IntOp.MOD) + while(value == 0) + value = START + rand.nextInt(END - START); + list.replaceAll(LambdaUtilities.opIntegerUnaryOperator(op, value)); + checkReplace(listCloned, list, op, value); + } + + @Test + @SuppressWarnings({"rawtypes", "unchecked"}) + public void testSort() throws Exception{ + Comparator[] cs = { Comparator.<Integer>naturalOrder(), + Comparator.<Integer>reverseOrder(), + Comparator.comparing(LambdaUtilities.highestPosValueIntFunction() ), + Comparator.comparing(LambdaUtilities.lowestPosValueIntFunction() ), + }; + + Iterator<Comparator> csit = Arrays.stream(cs).iterator(); + while(csit.hasNext()) + testComparator((Comparator<Integer>)csit.next()); + } + + @Test + public void testRemoveAll() throws Exception { + List<Integer> list = generateList(START, END); + int limit = rand.nextInt(2 * (END - START)) + 2 * START; + boolean isUP = rand.nextBoolean(); + list.removeIf(LambdaUtilities.randomIntegerPredicate(isUP, limit)); + assertTrue(verifyMatch(list, limit, !isUP, true)); + } + + @Test + public void testForEach() throws Exception { + List<Integer> list = generateList(START, END); + List<Integer> listCloned = hasIni ? + (List<Integer>)LambdaUtilities.create(typeObject, initSize) : + (List<Integer>)LambdaUtilities.create(typeObject); + list.forEach(LambdaUtilities.copyConsumer(listCloned)); + assertEquals(list, listCloned); + } + private void checkReplace(List<Integer> orig, List<Integer> replaced, + LambdaUtilities.IntOp op, int value) { + assertEquals(orig.size(), replaced.size()); + for(int index = 0; index < orig.size(); index++) + switch(op) { + case ADD: + assertTrue(orig.get(index) + value == replaced.get(index)); + break; + case SUBTRACT: + assertTrue(orig.get(index) - value == replaced.get(index)); + break; + case MULTIPLY: + assertTrue(orig.get(index) * value == replaced.get(index)); + break; + case DIVIDE: + assertTrue(orig.get(index) / value == replaced.get(index)); + break; + default: + assertTrue(orig.get(index) % value == replaced.get(index)); + break; + } + } + + private void testComparator(Comparator<Integer> c) throws Exception { + List<Integer> list = generateList(START, END); + List<Integer> listCloned = hasIni ? + (List<Integer>)LambdaUtilities.create(typeObject, initSize) : + (List<Integer>)LambdaUtilities.create(typeObject); + listCloned.addAll(list); + list.sort(c); + for(int i = 0; i < list.size() - 1; i++) + assertTrue(c.compare(list.get(i), list.get(i+1)) <= 0); + //assertEquals(list, listCloned); + } + + private boolean verifyMatch(Collection<Integer> c, int limit, boolean isUP, + boolean all) { + Iterator<Integer> it = c.iterator(); + while(it.hasNext()) { + int current = it.next(); + if(isUP) { + if(all) { + if(current < limit) return false; + } else { + if(current >= limit) return true; + } + } else { + if(all) { + if(current >= limit) return false; + } else { + if(current < limit) return true; + } + } + } + return all; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/List/SortAndReplaceAllTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,189 @@ +/* + * 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. + */ + +/** + * @test + * @summary test for List default methods replaceAll() and sort(); + * @(#) SortAndReplaceAllTest.java + * @library /sqeutil + * @author Tristan Yan + * @run testng SortAndReplaceAllTest + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.Stack; +import java.util.Vector; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Function; +import java.util.stream.Stream; +import static org.testng.Assert.*; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +public class SortAndReplaceAllTest <T extends List<Integer>>{ + protected Class<T> typeObject; + + protected boolean hasIni; + + protected int initSize; + + protected final static int START = -1 << 6; + + protected final static int END = 1 << 6; + + protected final static Random rand = new Random(System.currentTimeMillis()); + + @SuppressWarnings({"rawtypes", "unchecked"}) + protected static Class[] defaultConstructorClazz = { + (Class<ArrayList<Integer>>)(Class<?>)(ArrayList.class), + (Class<CopyOnWriteArrayList<Integer>>)(Class<?>)(CopyOnWriteArrayList.class), + (Class<LinkedList<Integer>>)(Class<?>)(LinkedList.class), + (Class<Stack<Integer>>)(Class<?>)(Stack.class), + (Class<Vector<Integer>>)(Class<?>)(Vector.class) + }; + + @SuppressWarnings({"rawtypes", "unchecked"}) + protected static Class[] capacityConstructorClazz = { + (Class<ArrayList<Integer>>)(Class<?>)(ArrayList.class), + (Class<Vector<Integer>>)(Class<?>)(Vector.class) + }; + + public SortAndReplaceAllTest(Class<T> clazz, int... initSizes){ + this.typeObject = clazz; + assert(initSizes.length <= 1); + if(initSizes.length == 1) { + hasIni = true; + this.initSize = initSizes[0]; + } + } + + private List<Integer> generateList(int start, int end, int size) + throws Exception{ + List<Integer> list = hasIni ? + (List<Integer>)LambdaUtilities.create(typeObject, initSize) : + (List<Integer>)LambdaUtilities.create(typeObject); + for(int i = 0; i < size; i++) { + int element = start + rand.nextInt(end - start); + list.add(element); + } + return list; + } + + protected List<Integer> generateList(int start, int end) throws Exception{ + return generateList(start, end, end - start); + } + + @Factory + @SuppressWarnings({"rawtypes", "unchecked"}) + public static Object[] create(){ + List<SortAndReplaceAllTest> result = new ArrayList<>(); + Stream<Class> stream1 = Arrays.stream(defaultConstructorClazz); + Stream<Class> stream2 = Arrays.stream(capacityConstructorClazz); + stream1.forEach(clazz -> result.add(new SortAndReplaceAllTest(clazz))); + stream2.forEach(clazz -> result.add(new SortAndReplaceAllTest(clazz, + END - START + rand.nextInt(END - START)))); + return result.toArray(); + } + + @Test + public void testReplaceAll() throws Exception { + List<Integer> list = generateList(START, END); + List<Integer> listCloned = hasIni ? + (List<Integer>)LambdaUtilities.create(typeObject, initSize) : + (List<Integer>)LambdaUtilities.create(typeObject); + listCloned.addAll(list); + EnumSet<LambdaUtilities.IntOp> set = EnumSet.allOf(LambdaUtilities.IntOp.class); + LambdaUtilities.IntOp[] ops = new LambdaUtilities.IntOp[set.size()]; + set.toArray(ops); + LambdaUtilities.IntOp op = ops[rand.nextInt(ops.length)]; + int value = START + rand.nextInt(END - START); + if( op == LambdaUtilities.IntOp.DIVIDE || op == LambdaUtilities.IntOp.MOD) { + while(value == 0) { + value = START + rand.nextInt(END - START); + } + } + list.replaceAll(LambdaUtilities.opIntegerUnaryOperator(op, value)); + checkReplace(listCloned, list, op, value); + } + + @Test + @SuppressWarnings({"rawtypes", "unchecked"}) + public void testSort() throws Exception{ + final Function<Integer, String> i2s = i -> String.valueOf(i); + Comparator[] cs = { Comparator.<Integer>naturalOrder(), + Comparator.<Integer>reverseOrder(), + Comparator.comparing(LambdaUtilities.highestPosValueIntFunction()), + Comparator.comparing(LambdaUtilities.lowestPosValueIntFunction()), + Comparator.comparing(i2s, Comparator.<String>naturalOrder()), + Comparator.comparing(i2s, Comparator.<String>reverseOrder()), + }; + + Iterator<Comparator> csit = Arrays.stream(cs).iterator(); + while(csit.hasNext()) { + testComparator((Comparator<Integer>)csit.next()); + } + } + + private void checkReplace(List<Integer> orig, List<Integer> replaced, + LambdaUtilities.IntOp op, int value) { + assertEquals(orig.size(), replaced.size()); + for(int index = 0; index < orig.size(); index++) { + switch(op) { + case ADD: + assertTrue(orig.get(index) + value == replaced.get(index)); + break; + case SUBTRACT: + assertTrue(orig.get(index) - value == replaced.get(index)); + break; + case MULTIPLY: + assertTrue(orig.get(index) * value == replaced.get(index)); + break; + case DIVIDE: + assertTrue(orig.get(index) / value == replaced.get(index)); + break; + default: + assertTrue(orig.get(index) % value == replaced.get(index)); + break; + } + } + } + + private void testComparator(Comparator<Integer> c) throws Exception { + List<Integer> list = generateList(START, END); + List<Integer> listCloned = hasIni ? + (List<Integer>)LambdaUtilities.create(typeObject, initSize) : + (List<Integer>)LambdaUtilities.create(typeObject); + listCloned.addAll(list); + list.sort(c); + for(int i = 0; i < list.size() - 1; i++) { + assertTrue(c.compare(list.get(i), list.get(i+1)) <= 0); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/Map/LambdaTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2012, 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 + * @summary sanity test forEach on Map<String,Integer> + * @library /sqeutil + * @(#)LambdaTest.java + * @author Yong Lu/Tristan Yan + * @run testng LambdaTest + */ + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.stream.Stream; +import static org.testng.Assert.*; +import org.testng.ITest; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class LambdaTest<T extends Map<String, Integer>> implements ITest { + private final static Random rand = new Random(System.currentTimeMillis()); + private Class<T> typeObject; + private boolean hasIni; + private int initSize; + + private final static int START = -1 << 6; + private final static int END = 1 << 6; + + private static Class[] defaultConstructorClazz = { + (Class<ConcurrentHashMap<String, Integer>>) (Class<?>) (ConcurrentHashMap.class), + (Class<ConcurrentSkipListMap<String, Integer>>) (Class<?>) (ConcurrentSkipListMap.class), + (Class<HashMap<String, Integer>>) (Class<?>) (HashMap.class), + (Class<Hashtable<String, Integer>>) (Class<?>) (Hashtable.class), + (Class<LinkedHashMap<String, Integer>>) (Class<?>) (LinkedHashMap.class), + (Class<IdentityHashMap<String, Integer>>) (Class<?>) (IdentityHashMap.class), + (Class<WeakHashMap<String, Integer>>) (Class<?>) (WeakHashMap.class) + }; + + public LambdaTest(Class<T> clazz, int... initSizes) { + this.typeObject = clazz; + assert (initSizes.length <= 1); + if (initSizes.length == 1) { + hasIni = true; + this.initSize = initSizes[0]; + } + } + + protected Map<String, Integer> generateData(int start, int end, int size) throws Exception { + Map<String, Integer> m = LambdaUtilities.createMap(typeObject); + for (int i = 0; i < size; i++) { + int element = start + rand.nextInt(end - start); + if (element != Integer.SIZE) { + m.put(String.valueOf(element), new Integer(element)); + } + } + return m; + } + + protected Map<String, Integer> generateData(int start, int end) throws Exception { + return generateData(start, end, end - start); + } + + @Factory + public static Object[] create(){ + List<LambdaTest> result = new ArrayList<>(); + Stream<Class> stream = Arrays.stream(defaultConstructorClazz); + stream.forEach(clazz -> result.add(new LambdaTest(clazz))); + return result.toArray(); + } + + @Test + public void testForEach() throws Exception{ + Map<String, Integer> m1 = generateData(START, END); + m1.forEach((x,y) -> assertTrue(m1.get(x).equals(y))); + + Map<String, Integer> m2 = LambdaUtilities.createMap(typeObject); + m1.forEach((x,y) -> m2.put(x, y)); + m2.forEach((x,y) -> assertTrue(m1.get(x).equals(y))); + + m1.forEach((x,y) -> { + m1.put(x, Integer.SIZE); + assertFalse(m1.containsValue(y)); + }); + } + + @Test + public void testReplaceAll() throws Exception{ + //ConcurrentSkipListMap doesn't replaceAll + if(typeObject.getName().endsWith("ConcurrentSkipListMap")) + return; + Map<String, Integer> m = generateData(START, END); + Map<String, Integer> mCloned = LambdaUtilities.createMap(typeObject); + mCloned.putAll(m); + EnumSet<LambdaUtilities.IntOp> set = EnumSet.allOf(LambdaUtilities.IntOp.class); + LambdaUtilities.IntOp[] ops = new LambdaUtilities.IntOp[set.size()]; + set.toArray(ops); + LambdaUtilities.IntOp op = ops[rand.nextInt(ops.length)]; + int value = START + rand.nextInt(END - START); + while((value = START + rand.nextInt(END - START)) == 0); + m.replaceAll(LambdaUtilities.opBiFunction(op, value)); + checkReplace(mCloned, m, op, value); + } + + @Test + public void testCompute() throws Exception{ + Map<String, Integer> m = generateData(START, END); + Map<String, Integer> mCloned = LambdaUtilities.createMap(typeObject); + mCloned.putAll(m); + final int value = START + rand.nextInt(END - START); + EnumSet<LambdaUtilities.IntOp> set = EnumSet.allOf(LambdaUtilities.IntOp.class); + LambdaUtilities.IntOp[] ops = new LambdaUtilities.IntOp[set.size()]; + set.toArray(ops); + LambdaUtilities.IntOp op = ops[rand.nextInt(ops.length)]; + mCloned.keySet().forEach(t -> m.compute(t, LambdaUtilities.opBiFunction(op, value))); + checkReplace(mCloned, m, op, value); + } + + @Test + public void testMerge() throws Exception{ + Map<String, Integer> m = generateData(START, END); + Map<String, Integer> mCloned = LambdaUtilities.createMap(typeObject); + mCloned.putAll(m); + final int value = START + rand.nextInt(END - START); + EnumSet<LambdaUtilities.IntOp> set = EnumSet.allOf(LambdaUtilities.IntOp.class); + LambdaUtilities.IntOp[] ops = new LambdaUtilities.IntOp[set.size()]; + set.toArray(ops); + LambdaUtilities.IntOp op = ops[rand.nextInt(ops.length)]; + mCloned.keySet().forEach(t -> m.merge(t, value, LambdaUtilities.opBiFunction(op))); + checkReplace(mCloned, m, op, value); + } + + @Test + public void testReplace() throws Exception{ + Map<String, Integer> m = generateData(START, END); + Map<String, Integer> mCloned = LambdaUtilities.createMap(typeObject); + mCloned.putAll(m); + final int value = START + rand.nextInt(END - START); + EnumSet<LambdaUtilities.IntOp> set = EnumSet.allOf(LambdaUtilities.IntOp.class); + LambdaUtilities.IntOp[] ops = new LambdaUtilities.IntOp[set.size()]; + set.toArray(ops); + LambdaUtilities.IntOp op = ops[rand.nextInt(ops.length)]; + mCloned.keySet().forEach(t -> m.replace(t, mCloned.get(t), LambdaUtilities.opBiFunction(op).apply(mCloned.get(t), value))); + checkReplace(mCloned, m, op, value); + } + + private void checkReplace(Map<String, Integer> orig, Map<String, Integer> replaced, + LambdaUtilities.IntOp op, int value) { + assertEquals(orig.size(), replaced.size()); + Iterator<String> it = orig.keySet().iterator(); + while(it.hasNext()) { + String k = it.next(); + if(value == 0){ + assertEquals(orig.get(k), replaced.get(k)); + continue; + } + switch(op) { + case ADD: + assertTrue(orig.get(k) + value == replaced.get(k)); + break; + case SUBTRACT: + assertTrue(orig.get(k) - value == replaced.get(k)); + break; + case MULTIPLY: + assertTrue(orig.get(k) * value == replaced.get(k)); + break; + case DIVIDE: + assertTrue(orig.get(k) / value == replaced.get(k)); + break; + default: + assertTrue(orig.get(k) % value == replaced.get(k)); + break; + } + } + } + + @Test + public void testRemove() throws Exception { + Map<String, Integer> m = generateData(START, END); + Map<String, Integer> mCloned = LambdaUtilities.createMap(typeObject); + mCloned.putAll(m); + mCloned.keySet().forEach(t -> m.remove(t)); + assertTrue(m.isEmpty()); + } + + @Test + public void testComputeIfAbsent() throws Exception { + Map<String, Integer> m = generateData(START, END); + Map<String, Integer> mCloned = LambdaUtilities.createMap(typeObject); + final int value = START + rand.nextInt(END - START); + EnumSet<LambdaUtilities.IntOp> set = EnumSet.allOf(LambdaUtilities.IntOp.class); + LambdaUtilities.IntOp[] ops = new LambdaUtilities.IntOp[set.size()]; + set.toArray(ops); + LambdaUtilities.IntOp op = ops[rand.nextInt(ops.length)]; + m.keySet().forEach(t -> mCloned.computeIfAbsent(t, LambdaUtilities.mappingFunction(m, op, value))); + checkReplace(m, mCloned, op, value); + } + + @Override + public String getTestName() { + return typeObject.getName() + "<String,Integer>"; + } +}
--- a/test/java/util/Random/RandomStreamTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/Random/RandomStreamTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -82,13 +82,6 @@ assertEquals(destination.size(), count); } - @Test(dataProvider = "suppliers") - public void testRandomGaussianStream(final Random random, final int count) { - final List<Double> destination = new ArrayList<>(count); - random.gaussians().limit(count).forEach(destination::add); - assertEquals(destination.size(), count); - } - @Test public void testIntStream() { final long seed = System.currentTimeMillis(); @@ -132,20 +125,6 @@ } @Test - public void testGaussianStream() { - final long seed = System.currentTimeMillis(); - final Random r1 = new Random(seed); - final double[] a = new double[SIZE]; - for (int i=0; i < SIZE; i++) { - a[i] = r1.nextGaussian(); - } - - final Random r2 = new Random(seed); // same seed - final double[] b = r2.gaussians().limit(SIZE).toArray(); - assertEquals(a, b); - } - - @Test public void testThreadLocalIntStream() throws InterruptedException, ExecutionException, TimeoutException { ThreadLocalRandom tlr = ThreadLocalRandom.current(); testRandomResultSupplierConcurrently(() -> tlr.ints().limit(SIZE).boxed().collect(toList())); @@ -163,12 +142,6 @@ testRandomResultSupplierConcurrently(() -> tlr.doubles().limit(SIZE).boxed().collect(toList())); } - @Test - public void testThreadLocalGaussianStream() throws InterruptedException, ExecutionException, TimeoutException { - ThreadLocalRandom tlr = ThreadLocalRandom.current(); - testRandomResultSupplierConcurrently(() -> tlr.gaussians().limit(SIZE).boxed().collect(toList())); - } - <T> void testRandomResultSupplierConcurrently(Supplier<T> s) throws InterruptedException, ExecutionException, TimeoutException { // Produce 10 completable future tasks final int tasks = 10;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/Random/RandomTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,518 @@ +/* + * Copyright (c) 2012, 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 org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAdder; + +import static org.testng.Assert.*; + +/** + * @test + * @run testng RandomTest + * @summary test methods on Random + */ +@Test +public class RandomTest { + + // Note: this test was adapted from the 166 TCK ThreadLocalRandomTest test + // and modified to be a TestNG test + + /* + * Testing coverage notes: + * + * We don't test randomness properties, but only that repeated + * calls, up to NCALLS tries, produce at least one different + * result. For bounded versions, we sample various intervals + * across multiples of primes. + */ + + // max numbers of calls to detect getting stuck on one value + static final int NCALLS = 10000; + + // max sampled int bound + static final int MAX_INT_BOUND = (1 << 28); + + // max sampled long bound + static final long MAX_LONG_BOUND = (1L << 42); + + // Number of replications for other checks + static final int REPS = 20; + + /** + * Repeated calls to nextInt produce at least two distinct results + */ + public void testNextInt() { + Random r = new Random(); + int f = r.nextInt(); + int i = 0; + while (i < NCALLS && r.nextInt() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextLong produce at least two distinct results + */ + public void testNextLong() { + Random r = new Random(); + long f = r.nextLong(); + int i = 0; + while (i < NCALLS && r.nextLong() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextBoolean produce at least two distinct results + */ + public void testNextBoolean() { + Random r = new Random(); + boolean f = r.nextBoolean(); + int i = 0; + while (i < NCALLS && r.nextBoolean() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextFloat produce at least two distinct results + */ + public void testNextFloat() { + Random r = new Random(); + float f = r.nextFloat(); + int i = 0; + while (i < NCALLS && r.nextFloat() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextDouble produce at least two distinct results + */ + public void testNextDouble() { + Random r = new Random(); + double f = r.nextDouble(); + int i = 0; + while (i < NCALLS && r.nextDouble() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextGaussian produce at least two distinct results + */ + public void testNextGaussian() { + Random r = new Random(); + double f = r.nextGaussian(); + int i = 0; + while (i < NCALLS && r.nextGaussian() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * nextInt(negative) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextIntBoundedNeg() { + Random r = new Random(); + int f = r.nextInt(-17); + } + + /** + * nextInt(least >= bound) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextIntBadBounds() { + Random r = new Random(); + int f = r.nextInt(17, 2); + } + + /** + * nextInt(bound) returns 0 <= value < bound; repeated calls produce at + * least two distinct results + */ + public void testNextIntBounded() { + Random r = new Random(); + // sample bound space across prime number increments + for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) { + int f = r.nextInt(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + int j; + while (i < NCALLS && + (j = r.nextInt(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextInt(least, bound) returns least <= value < bound; repeated calls + * produce at least two distinct results + */ + public void testNextIntBounded2() { + Random r = new Random(0); + for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) { + for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) { + int f = r.nextInt(least, bound); + assertTrue(least <= f && f < bound, String.format("[%d, %d] %d", least, bound, f)); + int i = 0; + int j; + while (i < NCALLS && + (j = r.nextInt(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextLong(negative) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextLongBoundedNeg() { + Random r = new Random(); + long f = r.nextLong(-17); + } + + /** + * nextLong(least >= bound) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextLongBadBounds() { + Random r = new Random(); + long f = r.nextLong(17, 2); + } + + /** + * nextLong(bound) returns 0 <= value < bound; repeated calls produce at + * least two distinct results + */ + public void testNextLongBounded() { + Random r = new Random(); + for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) { + long f = r.nextLong(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = r.nextLong(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextLong(least, bound) returns least <= value < bound; repeated calls + * produce at least two distinct results + */ + public void testNextLongBounded2() { + Random r = new Random(); + for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) { + for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) { + long f = r.nextLong(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = r.nextLong(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextDouble(least, bound) returns least <= value < bound; repeated calls + * produce at least two distinct results + */ + public void testNextDoubleBounded2() { + Random r = new Random(); + for (double least = 0.0001; least < 1.0e20; least *= 8) { + for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) { + double f = r.nextDouble(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + double j; + while (i < NCALLS && + (j = r.nextDouble(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * Invoking sized ints, long, doubles, with negative sizes throws + * IllegalArgumentException + */ + public void testBadStreamSize() { + Random r = new Random(); + executeAndCatchIAE(() -> r.ints(-1L)); + executeAndCatchIAE(() -> r.ints(-1L, 2, 3)); + executeAndCatchIAE(() -> r.longs(-1L)); + executeAndCatchIAE(() -> r.longs(-1L, -1L, 1L)); + executeAndCatchIAE(() -> r.doubles(-1L)); + executeAndCatchIAE(() -> r.doubles(-1L, .5, .6)); + } + + /** + * Invoking bounded ints, long, doubles, with illegal bounds throws + * IllegalArgumentException + */ + public void testBadStreamBounds() { + Random r = new Random(); + executeAndCatchIAE(() -> r.ints(2, 1)); + executeAndCatchIAE(() -> r.ints(10, 42, 42)); + executeAndCatchIAE(() -> r.longs(-1L, -1L)); + executeAndCatchIAE(() -> r.longs(10, 1L, -2L)); + executeAndCatchIAE(() -> r.doubles(0.0, 0.0)); + executeAndCatchIAE(() -> r.doubles(10, .5, .4)); + } + + private void executeAndCatchIAE(Runnable r) { + executeAndCatch(IllegalArgumentException.class, r); + } + + private void executeAndCatch(Class<? extends Exception> expected, Runnable r) { + Exception caught = null; + try { + r.run(); + } + catch (Exception e) { + caught = e; + } + + assertNotNull(caught, + String.format("No Exception was thrown, expected an Exception of %s to be thrown", + expected.getName())); + Assert.assertTrue(expected.isInstance(caught), + String.format("Exception thrown %s not an instance of %s", + caught.getClass().getName(), expected.getName())); + } + + /** + * A sequential sized stream of ints generates the given number of values + */ + public void testIntsCount() { + LongAdder counter = new LongAdder(); + Random r = new Random(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.ints(size).forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * A sequential sized stream of longs generates the given number of values + */ + public void testLongsCount() { + LongAdder counter = new LongAdder(); + Random r = new Random(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.longs(size).forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * A sequential sized stream of doubles generates the given number of values + */ + public void testDoublesCount() { + LongAdder counter = new LongAdder(); + Random r = new Random(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.doubles(size).forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * Each of a sequential sized stream of bounded ints is within bounds + */ + public void testBoundedInts() { + AtomicInteger fails = new AtomicInteger(0); + Random r = new Random(); + long size = 12345L; + for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) { + for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) { + final int lo = least, hi = bound; + r.ints(size, lo, hi). + forEach(x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); + }); + } + } + assertEquals(fails.get(), 0); + } + + /** + * Each of a sequential sized stream of bounded longs is within bounds + */ + public void testBoundedLongs() { + AtomicInteger fails = new AtomicInteger(0); + Random r = new Random(); + long size = 123L; + for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) { + for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) { + final long lo = least, hi = bound; + r.longs(size, lo, hi). + forEach(x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); + }); + } + } + assertEquals(fails.get(), 0); + } + + /** + * Each of a sequential sized stream of bounded doubles is within bounds + */ + public void testBoundedDoubles() { + AtomicInteger fails = new AtomicInteger(0); + Random r = new Random(); + long size = 456; + for (double least = 0.00011; least < 1.0e20; least *= 9) { + for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) { + final double lo = least, hi = bound; + r.doubles(size, lo, hi). + forEach(x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); + }); + } + } + assertEquals(fails.get(), 0); + } + + /** + * A parallel unsized stream of ints generates at least 100 values + */ + public void testUnsizedIntsCount() { + LongAdder counter = new LongAdder(); + Random r = new Random(); + long size = 100; + r.ints().limit(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + + /** + * A parallel unsized stream of longs generates at least 100 values + */ + public void testUnsizedLongsCount() { + LongAdder counter = new LongAdder(); + Random r = new Random(); + long size = 100; + r.longs().limit(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + + /** + * A parallel unsized stream of doubles generates at least 100 values + */ + public void testUnsizedDoublesCount() { + LongAdder counter = new LongAdder(); + Random r = new Random(); + long size = 100; + r.doubles().limit(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + + /** + * A sequential unsized stream of ints generates at least 100 values + */ + public void testUnsizedIntsCountSeq() { + LongAdder counter = new LongAdder(); + Random r = new Random(); + long size = 100; + r.ints().limit(size).forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + + /** + * A sequential unsized stream of longs generates at least 100 values + */ + public void testUnsizedLongsCountSeq() { + LongAdder counter = new LongAdder(); + Random r = new Random(); + long size = 100; + r.longs().limit(size).forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + + /** + * A sequential unsized stream of doubles generates at least 100 values + */ + public void testUnsizedDoublesCountSeq() { + LongAdder counter = new LongAdder(); + Random r = new Random(); + long size = 100; + r.doubles().limit(size).forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + +}
--- a/test/java/util/Spliterator/SpliteratorCharacteristics.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/Spliterator/SpliteratorCharacteristics.java Wed Aug 14 15:53:13 2013 -0700 @@ -32,80 +32,134 @@ import java.util.Arrays; import java.util.Collection; import java.util.Comparator; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; import java.util.Spliterator; import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.ConcurrentSkipListSet; import static org.testng.Assert.*; @Test public class SpliteratorCharacteristics { - public void testTreeMap() { - TreeMap<Integer, String> tm = new TreeMap<>(); - tm.put(1, "4"); - tm.put(2, "3"); - tm.put(3, "2"); - tm.put(4, "1"); + // TreeMap - assertCharacteristics(tm.keySet(), - Spliterator.SIZED | Spliterator.DISTINCT | - Spliterator.SORTED | Spliterator.ORDERED); - assertNullComparator(tm.keySet()); - - assertCharacteristics(tm.values(), - Spliterator.SIZED | Spliterator.ORDERED); - assertISEComparator(tm.values()); - - assertCharacteristics(tm.entrySet(), - Spliterator.SIZED | Spliterator.DISTINCT | - Spliterator.SORTED | Spliterator.ORDERED); - assertNotNullComparator(tm.entrySet()); + public void testTreeMap() { + assertSortedMapCharacteristics(new TreeMap<>(), + Spliterator.SIZED | Spliterator.DISTINCT | + Spliterator.SORTED | Spliterator.ORDERED); } public void testTreeMapWithComparator() { - TreeMap<Integer, String> tm = new TreeMap<>(Comparator.<Integer>reverseOrder()); - tm.put(1, "4"); - tm.put(2, "3"); - tm.put(3, "2"); - tm.put(4, "1"); - - assertCharacteristics(tm.keySet(), - Spliterator.SIZED | Spliterator.DISTINCT | - Spliterator.SORTED | Spliterator.ORDERED); - assertNotNullComparator(tm.keySet()); - - assertCharacteristics(tm.values(), - Spliterator.SIZED | Spliterator.ORDERED); - assertISEComparator(tm.values()); - - assertCharacteristics(tm.entrySet(), - Spliterator.SIZED | Spliterator.DISTINCT | - Spliterator.SORTED | Spliterator.ORDERED); - assertNotNullComparator(tm.entrySet()); + assertSortedMapCharacteristics(new TreeMap<>(Comparator.reverseOrder()), + Spliterator.SIZED | Spliterator.DISTINCT | + Spliterator.SORTED | Spliterator.ORDERED); } - public void testTreeSet() { - TreeSet<Integer> ts = new TreeSet<>(); - ts.addAll(Arrays.asList(1, 2, 3, 4)); + + // TreeSet - assertCharacteristics(ts, - Spliterator.SIZED | Spliterator.DISTINCT | - Spliterator.SORTED | Spliterator.ORDERED); - assertNullComparator(ts); + public void testTreeSet() { + assertSortedSetCharacteristics(new TreeSet<>(), + Spliterator.SIZED | Spliterator.DISTINCT | + Spliterator.SORTED | Spliterator.ORDERED); } public void testTreeSetWithComparator() { - TreeSet<Integer> ts = new TreeSet<>(Comparator.reverseOrder()); - ts.addAll(Arrays.asList(1, 2, 3, 4)); + assertSortedSetCharacteristics(new TreeSet<>(Comparator.reverseOrder()), + Spliterator.SIZED | Spliterator.DISTINCT | + Spliterator.SORTED | Spliterator.ORDERED); + } + + + // ConcurrentSkipListMap + + public void testConcurrentSkipListMap() { + assertSortedMapCharacteristics(new ConcurrentSkipListMap<>(), + Spliterator.CONCURRENT | Spliterator.NONNULL | + Spliterator.DISTINCT | Spliterator.SORTED | + Spliterator.ORDERED); + } - assertCharacteristics(ts, - Spliterator.SIZED | Spliterator.DISTINCT | - Spliterator.SORTED | Spliterator.ORDERED); - assertNotNullComparator(ts); + public void testConcurrentSkipListMapWithComparator() { + assertSortedMapCharacteristics(new ConcurrentSkipListMap<>(Comparator.<Integer>reverseOrder()), + Spliterator.CONCURRENT | Spliterator.NONNULL | + Spliterator.DISTINCT | Spliterator.SORTED | + Spliterator.ORDERED); + } + + + // ConcurrentSkipListSet + + public void testConcurrentSkipListSet() { + assertSortedSetCharacteristics(new ConcurrentSkipListSet<>(), + Spliterator.CONCURRENT | Spliterator.NONNULL | + Spliterator.DISTINCT | Spliterator.SORTED | + Spliterator.ORDERED); + } + + public void testConcurrentSkipListSetWithComparator() { + assertSortedSetCharacteristics(new ConcurrentSkipListSet<>(Comparator.reverseOrder()), + Spliterator.CONCURRENT | Spliterator.NONNULL | + Spliterator.DISTINCT | Spliterator.SORTED | + Spliterator.ORDERED); } + // + + void assertSortedMapCharacteristics(SortedMap<Integer, String> m, int keyCharacteristics) { + initMap(m); + + boolean hasComparator = m.comparator() != null; + + Set<Integer> keys = m.keySet(); + assertCharacteristics(keys, keyCharacteristics); + if (hasComparator) { + assertNotNullComparator(keys); + } + else { + assertNullComparator(keys); + } + + assertCharacteristics(m.values(), + keyCharacteristics & ~(Spliterator.DISTINCT | Spliterator.SORTED)); + assertISEComparator(m.values()); + + assertCharacteristics(m.entrySet(), keyCharacteristics); + assertNotNullComparator(m.entrySet()); + } + + void assertSortedSetCharacteristics(SortedSet<Integer> s, int keyCharacteristics) { + initSet(s); + + boolean hasComparator = s.comparator() != null; + + assertCharacteristics(s, keyCharacteristics); + if (hasComparator) { + assertNotNullComparator(s); + } + else { + assertNullComparator(s); + } + } + + void initMap(Map<Integer, String> m) { + m.put(1, "4"); + m.put(2, "3"); + m.put(3, "2"); + m.put(4, "1"); + } + + void initSet(Set<Integer> s) { + s.addAll(Arrays.asList(1, 2, 3, 4)); + } + void assertCharacteristics(Collection<?> c, int expectedCharacteristics) { assertCharacteristics(c.spliterator(), expectedCharacteristics); }
--- a/test/java/util/Spliterator/SpliteratorCollisions.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/Spliterator/SpliteratorCollisions.java Wed Aug 14 15:53:13 2013 -0700 @@ -148,7 +148,6 @@ List<Object[]> data = new ArrayList<>(); for (int size : SIZES) { List<HashableInteger> exp = listIntRange(size, true); - exp.add(0, null); SpliteratorDataBuilder<HashableInteger> db = new SpliteratorDataBuilder<>(data, exp); // Maps
--- a/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -513,6 +513,12 @@ @Test(dataProvider = "Spliterator<Integer>") @SuppressWarnings({"unchecked", "rawtypes"}) + public void testMixedTraverseAndSplit(String description, Collection exp, Supplier<Spliterator> s) { + testMixedTraverseAndSplit(exp, s, (Consumer<Object> b) -> b); + } + + @Test(dataProvider = "Spliterator<Integer>") + @SuppressWarnings({"unchecked", "rawtypes"}) public void testSplitAfterFullTraversal(String description, Collection exp, Supplier<Spliterator> s) { testSplitAfterFullTraversal(s, (Consumer<Object> b) -> b); } @@ -669,6 +675,11 @@ } @Test(dataProvider = "Spliterator.OfInt") + public void testIntMixedTraverseAndSplit(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) { + testMixedTraverseAndSplit(exp, s, intBoxingConsumer()); + } + + @Test(dataProvider = "Spliterator.OfInt") public void testIntSplitAfterFullTraversal(String description, Collection<Integer> exp, Supplier<Spliterator.OfInt> s) { testSplitAfterFullTraversal(s, intBoxingConsumer()); } @@ -829,6 +840,11 @@ } @Test(dataProvider = "Spliterator.OfLong") + public void testLongMixedTraverseAndSplit(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) { + testMixedTraverseAndSplit(exp, s, longBoxingConsumer()); + } + + @Test(dataProvider = "Spliterator.OfLong") public void testLongSplitAfterFullTraversal(String description, Collection<Long> exp, Supplier<Spliterator.OfLong> s) { testSplitAfterFullTraversal(s, longBoxingConsumer()); } @@ -989,6 +1005,11 @@ } @Test(dataProvider = "Spliterator.OfDouble") + public void testDoubleMixedTraverseAndSplit(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) { + testMixedTraverseAndSplit(exp, s, doubleBoxingConsumer()); + } + + @Test(dataProvider = "Spliterator.OfDouble") public void testDoubleSplitAfterFullTraversal(String description, Collection<Double> exp, Supplier<Spliterator.OfDouble> s) { testSplitAfterFullTraversal(s, doubleBoxingConsumer()); } @@ -1097,6 +1118,53 @@ } } + private static <T, S extends Spliterator<T>> void testMixedTraverseAndSplit( + Collection<T> exp, + Supplier<S> supplier, + UnaryOperator<Consumer<T>> boxingAdapter) { + S spliterator = supplier.get(); + long sizeIfKnown = spliterator.getExactSizeIfKnown(); + boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED); + + ArrayList<T> dest = new ArrayList<>(); + spliterator = supplier.get(); + Consumer<T> b = boxingAdapter.apply(dest::add); + + Spliterator<T> spl1, spl2, spl3; + spliterator.tryAdvance(b); + spl2 = spliterator.trySplit(); + if (spl2 != null) { + spl2.tryAdvance(b); + spl1 = spl2.trySplit(); + if (spl1 != null) { + spl1.tryAdvance(b); + spl1.forEachRemaining(b); + } + spl2.tryAdvance(b); + spl2.forEachRemaining(b); + } + spliterator.tryAdvance(b); + spl3 = spliterator.trySplit(); + if (spl3 != null) { + spl3.tryAdvance(b); + spl3.forEachRemaining(b); + } + spliterator.tryAdvance(b); + spliterator.forEachRemaining(b); + + if (sizeIfKnown >= 0) { + assertEquals(sizeIfKnown, dest.size()); + } + assertEquals(dest.size(), exp.size()); + + if (isOrdered) { + assertEquals(dest, exp); + } + else { + assertContentsUnordered(dest, exp); + } + } + private static <T, S extends Spliterator<T>> void testSplitAfterFullTraversal( Supplier<S> supplier, UnaryOperator<Consumer<T>> boxingAdapter) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/SplittableRandom/SplittableRandomTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2012, 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 org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.SplittableRandom; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAdder; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.AssertJUnit.assertTrue; + +/** + * @test + * @run testng SplittableRandomTest + * @summary test methods on SplittableRandom + */ +@Test +public class SplittableRandomTest { + + // Note: this test was copied from the 166 TCK SplittableRandomTest test + // and modified to be a TestNG test + + /* + * Testing coverage notes: + * + * 1. Many of the test methods are adapted from ThreadLocalRandomTest. + * + * 2. These tests do not check for random number generator quality. + * But we check for minimal API compliance by requiring that + * repeated calls to nextX methods, up to NCALLS tries, produce at + * least two distinct results. (In some possible universe, a + * "correct" implementation might fail, but the odds are vastly + * less than that of encountering a hardware failure while running + * the test.) For bounded nextX methods, we sample various + * intervals across multiples of primes. In other tests, we repeat + * under REPS different values. + */ + + // max numbers of calls to detect getting stuck on one value + static final int NCALLS = 10000; + + // max sampled int bound + static final int MAX_INT_BOUND = (1 << 28); + + // max sampled long bound + static final long MAX_LONG_BOUND = (1L << 42); + + // Number of replications for other checks + static final int REPS = 20; + + /** + * Repeated calls to nextInt produce at least two distinct results + */ + public void testNextInt() { + SplittableRandom sr = new SplittableRandom(); + int f = sr.nextInt(); + int i = 0; + while (i < NCALLS && sr.nextInt() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextLong produce at least two distinct results + */ + public void testNextLong() { + SplittableRandom sr = new SplittableRandom(); + long f = sr.nextLong(); + int i = 0; + while (i < NCALLS && sr.nextLong() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextDouble produce at least two distinct results + */ + public void testNextDouble() { + SplittableRandom sr = new SplittableRandom(); + double f = sr.nextDouble(); + int i = 0; + while (i < NCALLS && sr.nextDouble() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Two SplittableRandoms created with the same seed produce the + * same values for nextLong. + */ + public void testSeedConstructor() { + for (long seed = 2; seed < MAX_LONG_BOUND; seed += 15485863) { + SplittableRandom sr1 = new SplittableRandom(seed); + SplittableRandom sr2 = new SplittableRandom(seed); + for (int i = 0; i < REPS; ++i) + assertEquals(sr1.nextLong(), sr2.nextLong()); + } + } + + /** + * A SplittableRandom produced by split() of a default-constructed + * SplittableRandom generates a different sequence + */ + public void testSplit1() { + SplittableRandom sr = new SplittableRandom(); + for (int reps = 0; reps < REPS; ++reps) { + SplittableRandom sc = sr.split(); + int i = 0; + while (i < NCALLS && sr.nextLong() == sc.nextLong()) + ++i; + assertTrue(i < NCALLS); + } + } + + /** + * A SplittableRandom produced by split() of a seeded-constructed + * SplittableRandom generates a different sequence + */ + public void testSplit2() { + SplittableRandom sr = new SplittableRandom(12345); + for (int reps = 0; reps < REPS; ++reps) { + SplittableRandom sc = sr.split(); + int i = 0; + while (i < NCALLS && sr.nextLong() == sc.nextLong()) + ++i; + assertTrue(i < NCALLS); + } + } + + /** + * nextInt(negative) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextIntBoundedNeg() { + SplittableRandom sr = new SplittableRandom(); + int f = sr.nextInt(-17); + } + + /** + * nextInt(least >= bound) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextIntBadBounds() { + SplittableRandom sr = new SplittableRandom(); + int f = sr.nextInt(17, 2); + } + + /** + * nextInt(bound) returns 0 <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextIntBounded() { + SplittableRandom sr = new SplittableRandom(); + // sample bound space across prime number increments + for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) { + int f = sr.nextInt(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + int j; + while (i < NCALLS && + (j = sr.nextInt(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextInt(least, bound) returns least <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextIntBounded2() { + SplittableRandom sr = new SplittableRandom(); + for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) { + for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) { + int f = sr.nextInt(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + int j; + while (i < NCALLS && + (j = sr.nextInt(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextLong(negative) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextLongBoundedNeg() { + SplittableRandom sr = new SplittableRandom(); + long f = sr.nextLong(-17); + } + + /** + * nextLong(least >= bound) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextLongBadBounds() { + SplittableRandom sr = new SplittableRandom(); + long f = sr.nextLong(17, 2); + } + + /** + * nextLong(bound) returns 0 <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextLongBounded() { + SplittableRandom sr = new SplittableRandom(); + for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) { + long f = sr.nextLong(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = sr.nextLong(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextLong(least, bound) returns least <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextLongBounded2() { + SplittableRandom sr = new SplittableRandom(); + for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) { + for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) { + long f = sr.nextLong(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = sr.nextLong(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextDouble(least, bound) returns least <= value < bound; + * repeated calls produce at least two distinct results + */ + public void testNextDoubleBounded2() { + SplittableRandom sr = new SplittableRandom(); + for (double least = 0.0001; least < 1.0e20; least *= 8) { + for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) { + double f = sr.nextDouble(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + double j; + while (i < NCALLS && + (j = sr.nextDouble(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * Invoking sized ints, long, doubles, with negative sizes throws + * IllegalArgumentException + */ + public void testBadStreamSize() { + SplittableRandom r = new SplittableRandom(); + executeAndCatchIAE(() -> r.ints(-1L)); + executeAndCatchIAE(() -> r.ints(-1L, 2, 3)); + executeAndCatchIAE(() -> r.longs(-1L)); + executeAndCatchIAE(() -> r.longs(-1L, -1L, 1L)); + executeAndCatchIAE(() -> r.doubles(-1L)); + executeAndCatchIAE(() -> r.doubles(-1L, .5, .6)); + } + + /** + * Invoking bounded ints, long, doubles, with illegal bounds throws + * IllegalArgumentException + */ + public void testBadStreamBounds() { + SplittableRandom r = new SplittableRandom(); + executeAndCatchIAE(() -> r.ints(2, 1)); + executeAndCatchIAE(() -> r.ints(10, 42, 42)); + executeAndCatchIAE(() -> r.longs(-1L, -1L)); + executeAndCatchIAE(() -> r.longs(10, 1L, -2L)); + executeAndCatchIAE(() -> r.doubles(0.0, 0.0)); + executeAndCatchIAE(() -> r.doubles(10, .5, .4)); + } + + private void executeAndCatchIAE(Runnable r) { + executeAndCatch(IllegalArgumentException.class, r); + } + + private void executeAndCatch(Class<? extends Exception> expected, Runnable r) { + Exception caught = null; + try { + r.run(); + } + catch (Exception e) { + caught = e; + } + + assertNotNull(caught, + String.format("No Exception was thrown, expected an Exception of %s to be thrown", + expected.getName())); + Assert.assertTrue(expected.isInstance(caught), + String.format("Exception thrown %s not an instance of %s", + caught.getClass().getName(), expected.getName())); + } + + /** + * A parallel sized stream of ints generates the given number of values + */ + public void testIntsCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.ints(size).parallel().forEach(x -> {counter.increment();}); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * A parallel sized stream of longs generates the given number of values + */ + public void testLongsCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.longs(size).parallel().forEach(x -> {counter.increment();}); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * A parallel sized stream of doubles generates the given number of values + */ + public void testDoublesCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.doubles(size).parallel().forEach(x -> {counter.increment();}); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * Each of a parallel sized stream of bounded ints is within bounds + */ + public void testBoundedInts() { + AtomicInteger fails = new AtomicInteger(0); + SplittableRandom r = new SplittableRandom(); + long size = 12345L; + for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) { + for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) { + final int lo = least, hi = bound; + r.ints(size, lo, hi).parallel(). + forEach(x -> {if (x < lo || x >= hi) + fails.getAndIncrement(); }); + } + } + assertEquals(fails.get(), 0); + } + + /** + * Each of a parallel sized stream of bounded longs is within bounds + */ + public void testBoundedLongs() { + AtomicInteger fails = new AtomicInteger(0); + SplittableRandom r = new SplittableRandom(); + long size = 123L; + for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) { + for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) { + final long lo = least, hi = bound; + r.longs(size, lo, hi).parallel(). + forEach(x -> {if (x < lo || x >= hi) + fails.getAndIncrement(); }); + } + } + assertEquals(fails.get(), 0); + } + + /** + * Each of a parallel sized stream of bounded doubles is within bounds + */ + public void testBoundedDoubles() { + AtomicInteger fails = new AtomicInteger(0); + SplittableRandom r = new SplittableRandom(); + long size = 456; + for (double least = 0.00011; least < 1.0e20; least *= 9) { + for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) { + final double lo = least, hi = bound; + r.doubles(size, lo, hi).parallel(). + forEach(x -> {if (x < lo || x >= hi) + fails.getAndIncrement(); }); + } + } + assertEquals(fails.get(), 0); + } + + /** + * A parallel unsized stream of ints generates at least 100 values + */ + public void testUnsizedIntsCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.ints().limit(size).parallel().forEach(x -> {counter.increment();}); + assertEquals(counter.sum(), size); + } + + /** + * A parallel unsized stream of longs generates at least 100 values + */ + public void testUnsizedLongsCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.longs().limit(size).parallel().forEach(x -> {counter.increment();}); + assertEquals(counter.sum(), size); + } + + /** + * A parallel unsized stream of doubles generates at least 100 values + */ + public void testUnsizedDoublesCount() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.doubles().limit(size).parallel().forEach(x -> {counter.increment();}); + assertEquals(counter.sum(), size); + } + + /** + * A sequential unsized stream of ints generates at least 100 values + */ + public void testUnsizedIntsCountSeq() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.ints().limit(size).forEach(x -> {counter.increment();}); + assertEquals(counter.sum(), size); + } + + /** + * A sequential unsized stream of longs generates at least 100 values + */ + public void testUnsizedLongsCountSeq() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.longs().limit(size).forEach(x -> {counter.increment();}); + assertEquals(counter.sum(), size); + } + + /** + * A sequential unsized stream of doubles generates at least 100 values + */ + public void testUnsizedDoublesCountSeq() { + LongAdder counter = new LongAdder(); + SplittableRandom r = new SplittableRandom(); + long size = 100; + r.doubles().limit(size).forEach(x -> {counter.increment();}); + assertEquals(counter.sum(), size); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/StringJoiner/BasicTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2012, 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 + * @summary Sanity Test for StringJoiner + * @(#) BasicTest.java + * @library /sqeutil + * @author Tristan Yan + * @run testng BasicTest + */ + +import java.util.*; +import java.util.stream.Collectors; +import static org.testng.Assert.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class BasicTest { + protected final static int STRING_ARR_SIIE = 1 << 8; + + protected final static int STRING_LEN = 1 << 10; + + protected final static int TIMES = 1 << 4; + + protected final static Random rnd = new Random(System.currentTimeMillis()); + + protected String[] randomStringArray(){ + String[] arr = new String[STRING_ARR_SIIE]; + for(int index = 0; index < STRING_ARR_SIIE; index++) + arr[index] = StringUtilities.randomAlphabetic(rnd.nextInt(STRING_LEN)); + return arr; + } + + @DataProvider(name = "Connector-Provider") + public Object[][] notAlphanumeric(){ + Object[][] data = new Object[TIMES][3]; + for(int i = 0; i < TIMES; i++) { + List<String> list = new ArrayList<>(); + for(char c=32; c < 127; c++) { + if(!Character.isAlphabetic(c) && !Character.isDigit(c)) + list.add(String.valueOf(c)); + } + Collections.shuffle(list); + int infixPos = rnd.nextInt(list.size()/2); + if(infixPos < 1) infixPos = 1; + int prefixPos = infixPos + rnd.nextInt(list.size() - infixPos); + if(prefixPos == infixPos) prefixPos = infixPos + 1; + data[i] = new String[]{ list.subList(0, infixPos).stream().collect(Collectors.joining("")).toString(), + list.subList(infixPos, prefixPos).stream().collect(Collectors.joining("")).toString(), + list.subList(prefixPos, list.size()).stream().collect(Collectors.joining("")).toString() + }; + } + return data; + } + + @Test(dataProvider = "Connector-Provider") + public void constructorTest(String INFIX, String PREFIX, String SUFFIX) { + //Test constructor with only infix + //String[] sj = notAlphanumeric(); + String[] rndStrings = randomStringArray(); + final StringJoiner nofixsj = new StringJoiner(INFIX); + Arrays.stream(rndStrings).forEach(t -> {nofixsj.add(t); }); + String longStr1 = nofixsj.toString(); + int index = 0; + int pos = 0; + while(index < rndStrings.length) { + if(index < rndStrings.length - 1) { + assertEquals(longStr1.substring(pos, pos + rndStrings[index].length()), rndStrings[index]); + pos += rndStrings[index++].length(); + assertEquals(longStr1.substring(pos, pos + INFIX.length()), INFIX); + pos += INFIX.length(); + } else + assertEquals(longStr1.substring(pos, pos + rndStrings[index].length()), rndStrings[index++]); + } + + //Test constructor with infix, prefix and suffix + final StringJoiner hasfixsj = new StringJoiner(INFIX, PREFIX, SUFFIX); + Arrays.stream(rndStrings).forEach(t -> {hasfixsj.add(t); }); + String longStr2 = hasfixsj.toString(); + index = 0; + pos = 0; + assertEquals(longStr2.substring(pos, pos + PREFIX.length()), PREFIX); + pos += PREFIX.length(); + while(index < rndStrings.length) { + if(index < rndStrings.length - 1) { + assertEquals(longStr2.substring(pos, pos + rndStrings[index].length()), rndStrings[index]); + pos += rndStrings[index++].length(); + assertEquals(longStr2.substring(pos, pos + INFIX.length()), INFIX); + pos += INFIX.length(); + } else { + assertEquals(longStr2.substring(pos, pos + rndStrings[index].length()), rndStrings[index]); + pos += rndStrings[index++].length(); + } + } + assertEquals(longStr2.substring(pos, pos + SUFFIX.length()), SUFFIX); + } + + @Test(dataProvider = "Connector-Provider") + public void addTest(String INFIX, String PREFIX, String SUFFIX) { + StringJoiner nofixsj = new StringJoiner(INFIX); + String randAddStr1 = StringUtilities.randomAlphabetic(rnd.nextInt(STRING_LEN)); + assertEquals(nofixsj.add(randAddStr1).toString(), randAddStr1); + String randAddStr2 = StringUtilities.randomAlphabetic(rnd.nextInt(STRING_LEN)); + assertEquals(nofixsj.add(randAddStr2).toString(), randAddStr1 + INFIX + randAddStr2); + + //Test constructor with infix, prefix and suffix + StringJoiner hasfixsj = new StringJoiner(INFIX, PREFIX, SUFFIX); + assertEquals(hasfixsj.add(randAddStr1).toString(), PREFIX + randAddStr1 + SUFFIX); + assertEquals(hasfixsj.add(randAddStr2).toString(), PREFIX + randAddStr1 + INFIX + randAddStr2 + SUFFIX); + } + + @Test(dataProvider = "Connector-Provider") + public void testsetEmptyValue(String INFIX, String PREFIX, String SUFFIX) { + StringJoiner nofixsj = new StringJoiner(INFIX); + String emptyExpectedStr = StringUtilities.randomAlphabetic(rnd.nextInt(STRING_LEN)); + assertEquals(nofixsj.toString().length(), 0); + nofixsj.setEmptyValue(emptyExpectedStr); + assertEquals(nofixsj.toString(), emptyExpectedStr); + String randAddStr = StringUtilities.randomAlphabetic(rnd.nextInt(STRING_LEN)); + assertEquals(nofixsj.add(randAddStr).toString(), randAddStr); + + //Test constructor with infix, prefix and suffix + StringJoiner hasfixsj = new StringJoiner(INFIX, PREFIX, SUFFIX); + assertEquals(hasfixsj.toString(), PREFIX + SUFFIX); + hasfixsj.setEmptyValue(emptyExpectedStr); + assertEquals(hasfixsj.toString(), emptyExpectedStr); + + assertEquals(hasfixsj.add(randAddStr).toString(), PREFIX + randAddStr + SUFFIX); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/concurrent/AtomicInteger/LambdaTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,175 @@ +/* + * 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. + */ + +/** + * @test + * @summary test for AtomicInteger.updateAndGet()/getAndUpdate() and + * AtomicInteger.accumulateAndGet()/getAndAccumulate(); + * @(#) LambdaTest.java + * @library /sqeutil + * @author Tristan Yan + * @bug 8001666 + * @run testng LambdaTest + */ + +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.concurrent.atomic.AtomicInteger; +import static org.testng.Assert.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class LambdaTest { + private static enum Type{updateAndGet, getAndUpdate, accumulateAndGet, getAndAccumulate}; + + private static Random rand = new Random(System.currentTimeMillis()); + + private final static int MAX_VALUE = 1 << 24; + + private final static int MIN_VALUE = -1 << 24; + + private final static int OP_TIMES = 1 << 10; + + private final static int THREAD_NUM = 1 << 4; + + @DataProvider + public Object[][] booleanProvider(){ + Object[][] providersParam = new Object[Type.values().length][]; + Object[] a = Type.values(); + for(int i = 0; i < a.length; i++) + providersParam[i] = new Object[]{a[i]}; + return providersParam; + } + + @Test(dataProvider = "booleanProvider") + @SuppressWarnings({"rawtypes", "unchecked"}) + public void test(final Type type) throws InterruptedException, + ExecutionException { + int initValue = MIN_VALUE + rand.nextInt(MAX_VALUE - MIN_VALUE); + final AtomicInteger ai = new AtomicInteger(initValue); + final int randPlus = MIN_VALUE + rand.nextInt(MAX_VALUE - MIN_VALUE); + final int randMinus = MIN_VALUE + rand.nextInt(MAX_VALUE - MIN_VALUE); + switch(type) { + case updateAndGet: + assertEquals(ai.updateAndGet(LambdaUtilities.addIntUnaryOperator(randPlus)) + , initValue + randPlus); + assertEquals(ai.updateAndGet(LambdaUtilities.subIntUnaryOperator(randMinus)) + , initValue + randPlus - randMinus); + break; + case getAndUpdate: + assertEquals(ai.getAndUpdate(LambdaUtilities.addIntUnaryOperator(randPlus)) + , initValue); + assertEquals(ai.getAndUpdate(LambdaUtilities.subIntUnaryOperator(randMinus)) + , initValue + randPlus); + break; + case accumulateAndGet: + assertEquals(ai.accumulateAndGet(randPlus,LambdaUtilities.addIntBinaryOperator()) + , initValue + randPlus); + assertEquals(ai.accumulateAndGet(randMinus,LambdaUtilities.subIntBinaryOperator()) + , initValue + randPlus - randMinus); + break; + case getAndAccumulate: + assertEquals(ai.getAndAccumulate(randPlus,LambdaUtilities.addIntBinaryOperator()) + , initValue); + assertEquals(ai.getAndAccumulate(randMinus,LambdaUtilities.subIntBinaryOperator()) + , initValue + randPlus); + break; + } + assertEquals(ai.get(), initValue + randPlus - randMinus); + + ai.set(initValue); + Callable<Integer> ac = new Callable<Integer>(){ + @Override + public Integer call() throws Exception { + int plusTimes1 = 0; + for(int index = 0; index < OP_TIMES; index++) { + boolean plus = rand.nextBoolean(); + if(plus) plusTimes1++; + switch(type) { + case updateAndGet: + ai.updateAndGet(plus ? + LambdaUtilities.addIntUnaryOperator(randPlus) : + LambdaUtilities.subIntUnaryOperator(randMinus)); + break; + case getAndUpdate: + ai.getAndUpdate(plus ? + LambdaUtilities.addIntUnaryOperator(randPlus) : + LambdaUtilities.subIntUnaryOperator(randMinus)); + break; + case accumulateAndGet: + ai.accumulateAndGet(plus ? randPlus : randMinus, + plus ? LambdaUtilities.addIntBinaryOperator() : + LambdaUtilities.subIntBinaryOperator()); + break; + case getAndAccumulate: + ai.getAndAccumulate(plus ? randPlus : randMinus, + plus ? LambdaUtilities.addIntBinaryOperator() : + LambdaUtilities.subIntBinaryOperator()); + } + } + return plusTimes1; + } + + }; + FutureTask[] futures = new FutureTask[THREAD_NUM -1]; + for(int i = 0; i < THREAD_NUM -1; i++) { + futures[i] = new FutureTask<Integer>(ac); + new Thread(futures[i]).start(); + } + + int plusTimes = 0; + for(int index = 0; index < OP_TIMES; index++) { + boolean plus = rand.nextBoolean(); + if(plus) plusTimes++; + switch(type) { + case updateAndGet: + ai.updateAndGet(plus ? + LambdaUtilities.addIntUnaryOperator(randPlus) : + LambdaUtilities.subIntUnaryOperator(randMinus)); + break; + case getAndUpdate: + ai.getAndUpdate(plus ? + LambdaUtilities.addIntUnaryOperator(randPlus) : + LambdaUtilities.subIntUnaryOperator(randMinus)); + break; + case accumulateAndGet: + ai.accumulateAndGet(plus ? randPlus : randMinus, + plus ? LambdaUtilities.addIntBinaryOperator() : + LambdaUtilities.subIntBinaryOperator()); + break; + case getAndAccumulate: + ai.getAndAccumulate(plus ? randPlus : randMinus, + plus ? LambdaUtilities.addIntBinaryOperator() : + LambdaUtilities.subIntBinaryOperator()); + } + } + for(int i = 0; i < THREAD_NUM -1; i++) { + plusTimes += ((FutureTask<Integer>)futures[i]).get(); + } + int expected = initValue + plusTimes * randPlus - + (THREAD_NUM * OP_TIMES - plusTimes) * randMinus; + assertEquals(expected, ai.get()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/concurrent/AtomicIntegerFieldUpdater/LambdaTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,182 @@ +/* + * 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. + */ + + +/** + * @test + * @summary test for AtomicIntegerUpdateField.updateAndGet()/getAndUpdate() and + * AtomicIntegerUpdateField.accumulateAndGet()/getAndAccumulate(); + * @(#) LambdaTest.java + * @library /sqeutil + * @author Tristan Yan + * @run testng LambdaTest + * @bug 8001666 + */ + +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; +import static org.testng.Assert.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class LambdaTest { + private static enum Type{updateAndGet, getAndUpdate, accumulateAndGet, getAndAccumulate}; + + private static Random rand = new Random(System.currentTimeMillis()); + + private final static int MAX_VALUE = 1 << 24; + + private final static int MIN_VALUE = -1 << 24; + + private final static int OP_TIMES = 1 << 10; + + private final static int THREAD_NUM = 1 << 4; + + @DataProvider + public Object[][] booleanProvider(){ + Object[][] providersParam = new Object[Type.values().length][]; + Object[] a = Type.values(); + for(int i = 0; i < a.length; i++) + providersParam[i] = new Object[]{a[i]}; + return providersParam; + } + + @Test(dataProvider = "booleanProvider") + @SuppressWarnings({"rawtypes", "unchecked"}) + public void test(final Type type) throws InterruptedException, + ExecutionException { + final Updater u = new Updater(); + final AtomicIntegerFieldUpdater<Updater> accAIFU = AtomicIntegerFieldUpdater.<Updater>newUpdater((Class<Updater>)(Class<?>)u.getClass(), "accumulation"); + final int randPlus = MIN_VALUE + rand.nextInt(MAX_VALUE - MIN_VALUE); + final int randMinus = MIN_VALUE + rand.nextInt(MAX_VALUE - MIN_VALUE); + switch(type) { + case updateAndGet: + assertEquals(accAIFU.updateAndGet(u, + LambdaUtilities.addIntUnaryOperator(randPlus)), randPlus); + assertEquals(accAIFU.updateAndGet(u, + LambdaUtilities.subIntUnaryOperator(randMinus)), randPlus - randMinus); + break; + case getAndUpdate: + assertEquals(accAIFU.getAndUpdate(u, + LambdaUtilities.addIntUnaryOperator(randPlus)), 0); + assertEquals(accAIFU.getAndUpdate(u, + LambdaUtilities.subIntUnaryOperator(randMinus)), randPlus); + break; + case accumulateAndGet: + assertEquals(accAIFU.accumulateAndGet(u, randPlus, + LambdaUtilities.addIntBinaryOperator()), randPlus); + assertEquals(accAIFU.accumulateAndGet(u, randMinus, + LambdaUtilities.subIntBinaryOperator()), randPlus - randMinus); + break; + case getAndAccumulate: + assertEquals(accAIFU.getAndAccumulate(u, randPlus, + LambdaUtilities.addIntBinaryOperator()), 0); + assertEquals(accAIFU.getAndAccumulate(u, randMinus, + LambdaUtilities.subIntBinaryOperator()), randPlus); + break; + } + assertEquals(u.accumulation, randPlus - randMinus); + + u.accumulation = 0; + + Callable<Integer> ac = new Callable<Integer>(){ + @Override + public Integer call() throws Exception { + int plusTimes = 0; + for(int index = 0; index < OP_TIMES; index++) { + boolean plus = rand.nextBoolean(); + if(plus) plusTimes++; + switch(type) { + case updateAndGet: + accAIFU.updateAndGet(u, plus ? + LambdaUtilities.addIntUnaryOperator(randPlus) : + LambdaUtilities.subIntUnaryOperator(randMinus)); + break; + case getAndUpdate: + accAIFU.getAndUpdate(u, plus ? + LambdaUtilities.addIntUnaryOperator(randPlus) : + LambdaUtilities.subIntUnaryOperator(randMinus)); + break; + case accumulateAndGet: + accAIFU.accumulateAndGet(u, plus ? randPlus : randMinus, + plus ? LambdaUtilities.addIntBinaryOperator() : + LambdaUtilities.subIntBinaryOperator()); + break; + case getAndAccumulate: + accAIFU.getAndAccumulate(u, plus ? randPlus : randMinus, + plus ? LambdaUtilities.addIntBinaryOperator() : + LambdaUtilities.subIntBinaryOperator()); + break; + } + } + return plusTimes; + } + }; + + FutureTask[] futures = new FutureTask[THREAD_NUM -1]; + for(int i = 0; i < THREAD_NUM -1; i++) { + futures[i] = new FutureTask<Integer>(ac); + new Thread(futures[i]).start(); + } + int plusTimes = 0; + for(int index = 0; index < OP_TIMES; index++) { + boolean plus = rand.nextBoolean(); + if(plus) plusTimes++; + switch(type) { + case updateAndGet: + accAIFU.updateAndGet(u, plus ? + LambdaUtilities.addIntUnaryOperator(randPlus) : + LambdaUtilities.subIntUnaryOperator(randMinus)); + break; + case getAndUpdate: + accAIFU.getAndUpdate(u, plus ? + LambdaUtilities.addIntUnaryOperator(randPlus) : + LambdaUtilities.subIntUnaryOperator(randMinus)); + break; + case accumulateAndGet: + accAIFU.accumulateAndGet(u, plus ? randPlus : randMinus, + plus ? LambdaUtilities.addIntBinaryOperator() : + LambdaUtilities.subIntBinaryOperator()); + break; + case getAndAccumulate: + accAIFU.getAndAccumulate(u, plus ? randPlus : randMinus, + plus ? LambdaUtilities.addIntBinaryOperator() : + LambdaUtilities.subIntBinaryOperator()); + break; + } + } + for(int i = 0; i < THREAD_NUM -1; i++) { + plusTimes += ((FutureTask<Integer>)futures[i]).get(); + } + int expected = plusTimes * randPlus - + (THREAD_NUM * OP_TIMES - plusTimes) * randMinus; + assertEquals(expected, u.accumulation); + } + + static class Updater{ + public volatile int accumulation; + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/concurrent/AtomicLong/LambdaTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,245 @@ +/* + * 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. + */ + +/** + * @test + * @summary test AtomicLong.updateAndGet()/getAndUpdate() and + * AtomicLong.accumulateAndGet()/getAndAccumulate(); + * @(#) LambdaTest.java + * @library /sqeutil + * @author Tristan Yan + * @run testng LambdaTest + * @bug 8001666 + */ + +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicLong; +import static org.testng.Assert.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + + +public class LambdaTest { + private static enum Type{updateAndGet, getAndUpdate, accumulateAndGet, getAndAccumulate}; + + private static Random rand = new Random(System.currentTimeMillis()); + + private final static int MAX_VALUE = 1 << 24; + + private final static int MIN_VALUE = -1 << 24; + + private final static int OP_TIMES = 1 << 10; + + private final static int THREAD_NUM = 1 << 4; + + @DataProvider + public Object[][] booleanProvider(){ + Object[][] providersParam = new Object[Type.values().length][]; + Object[] a = Type.values(); + for(int i = 0; i < a.length; i++) + providersParam[i] = new Object[]{a[i]}; + return providersParam; + } + + @Test(dataProvider = "booleanProvider") + public void test(final Type type) throws InterruptedException, + ExecutionException { + long initValue = rand.nextLong(); + final AtomicLong al = new AtomicLong(initValue); + long tl1 = rand.nextLong(); + if(((byte)(tl1 >> 63) & (byte)(initValue >> 63)) == 0){ + if(initValue > 0) { + if(tl1 > Long.MAX_VALUE - initValue) + tl1 = - tl1; + } else { + if(tl1 < Long.MIN_VALUE - initValue) + tl1 = - tl1; + } + } + final long randPlus = tl1; + + long tl2 = rand.nextLong(); + if(((byte)(tl2 >> 63) & (byte)(initValue + randPlus >> 63)) == 1){ + if(initValue + randPlus > 0) { + if(-tl2 > Long.MAX_VALUE - (initValue + randPlus)) + tl2 = - tl2; + } else { + if(tl2 < Long.MIN_VALUE + (initValue + randPlus)) + tl2 = - tl2; + } + } + final long randMinus = tl2; + switch(type) { + case updateAndGet: + assertEquals(al.updateAndGet(LambdaUtilities.addLongUnaryOperator(randPlus)) + , initValue + randPlus); + assertEquals(al.updateAndGet(LambdaUtilities.subLongUnaryOperator(randMinus)) + , initValue + randPlus - randMinus); + break; + case getAndUpdate: + assertEquals(al.getAndUpdate(LambdaUtilities.addLongUnaryOperator(randPlus)) + , initValue); + assertEquals(al.getAndUpdate(LambdaUtilities.subLongUnaryOperator(randMinus)) + , initValue + randPlus); + break; + case accumulateAndGet: + assertEquals(al.accumulateAndGet(randPlus, LambdaUtilities.addLongBinaryOperator()) + , initValue + randPlus); + assertEquals(al.accumulateAndGet(randMinus, LambdaUtilities.subLongBinaryOperator()) + , initValue + randPlus - randMinus); + break; + case getAndAccumulate: + assertEquals(al.getAndAccumulate(randPlus, LambdaUtilities.addLongBinaryOperator()) + , initValue); + assertEquals(al.getAndAccumulate(randMinus, LambdaUtilities.subLongBinaryOperator()) + , initValue + randPlus); + break; + } + assertEquals(al.get() , initValue + randPlus - randMinus); + + final long randPlus1 = rand.nextBoolean() ? (long)rand.nextInt(MAX_VALUE) : -(long)rand.nextInt(MAX_VALUE); + final long randMinus1 = rand.nextBoolean() ? (long)rand.nextInt(MAX_VALUE) : -(long)rand.nextInt(MAX_VALUE); + al.set(initValue); + final int mainPLusTimes = 0; + int minusPLusTimes = 0; + + class UpdateThread extends Thread{ + OpTimes ops; + CountDownLatch latch; + UpdateThread(OpTimes ops, CountDownLatch latch){ + this.ops = ops; + this.latch = latch; + } + @Override + public void run() { + int plusTimes = 0; + int minusTimes = 0; + for(int index = 0; index < OP_TIMES; index++) { + boolean plus = rand.nextBoolean(); + if(plus) { + if (randPlus1 > 0 && al.get() > Long.MAX_VALUE - randPlus1 + || randPlus1 < 0 && al.get() < Long.MIN_VALUE - randPlus1) + continue; + } + else { + if (randPlus1 > 0 && al.get() < Long.MIN_VALUE + randPlus1 + || randPlus1 > 0 && al.get() > Long.MAX_VALUE - randPlus1) + continue; + } + switch(type) { + case updateAndGet: + al.updateAndGet(plus ? + LambdaUtilities.addLongUnaryOperator(randPlus1) : + LambdaUtilities.subLongUnaryOperator(randMinus1)); + break; + case getAndUpdate: + al.getAndUpdate(plus ? + LambdaUtilities.addLongUnaryOperator(randPlus1) : + LambdaUtilities.subLongUnaryOperator(randMinus1)); + break; + case accumulateAndGet: + al.accumulateAndGet(plus ? randPlus1 : randMinus1, + plus ? LambdaUtilities.addLongBinaryOperator() : + LambdaUtilities.subLongBinaryOperator()); + break; + case getAndAccumulate: + al.getAndAccumulate(plus ? randPlus1 : randMinus1, + plus ? LambdaUtilities.addLongBinaryOperator() : + LambdaUtilities.subLongBinaryOperator()); + break; + } + if(plus) + plusTimes++; + else + minusTimes++; + } + ops.plus = plusTimes; + ops.minus = minusTimes; + latch.countDown(); + } + + }; + OpTimes[] opts = new OpTimes[THREAD_NUM -1]; + CountDownLatch latch = new CountDownLatch(THREAD_NUM -1); + for(int i = 0; i < THREAD_NUM -1; i++) { + opts[i] = new OpTimes(); + new UpdateThread(opts[i], latch).start(); + } + int plusTimes = 0; + int minusTimes = 0; + for(int index = 0; index < OP_TIMES; index++) { + boolean plus = rand.nextBoolean(); + if(plus) { + if (randPlus1 > 0 && al.get() > Long.MAX_VALUE - randPlus1 + || randPlus1 < 0 && al.get() < Long.MIN_VALUE - randPlus1) + continue; + } + else { + if (randPlus1 > 0 && al.get() < Long.MIN_VALUE + randPlus1 + || randPlus1 > 0 && al.get() > Long.MAX_VALUE - randPlus1) + continue; + } + switch(type) { + case updateAndGet: + al.updateAndGet(plus ? + LambdaUtilities.addLongUnaryOperator(randPlus1) : + LambdaUtilities.subLongUnaryOperator(randMinus1)); + break; + case getAndUpdate: + al.getAndUpdate(plus ? + LambdaUtilities.addLongUnaryOperator(randPlus1) : + LambdaUtilities.subLongUnaryOperator(randMinus1)); + break; + case accumulateAndGet: + al.accumulateAndGet(plus ? randPlus1 : randMinus1, + plus ? LambdaUtilities.addLongBinaryOperator() : + LambdaUtilities.subLongBinaryOperator()); + break; + case getAndAccumulate: + al.getAndAccumulate(plus ? randPlus1 : randMinus1, + plus ? LambdaUtilities.addLongBinaryOperator() : + LambdaUtilities.subLongBinaryOperator()); + break; + } + if(plus) + plusTimes++; + else + minusTimes++; + } + latch.await(); + for(int i = 0; i < THREAD_NUM -1; i++) { + plusTimes += opts[i].plus; + minusTimes += opts[i].minus; + } + long expected = initValue + plusTimes * randPlus1 - + minusTimes * randMinus1; + assertEquals(expected, al.get()); + } +} +class OpTimes{ + public int plus; + + public int minus; +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/concurrent/AtomicLongFieldUpdater/LambdaTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,213 @@ +/* + * 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. + */ + +/** + * @test + * @summary test AtomicLongFieldUpdater.updateAndGet()/getAndUpdate() and + * AtomicLongFieldUpdater.accumulateAndGet()/getAndAccumulate(); + * @(#) LambdaTest.java + * @library /sqeutil + * @author Tristan Yan + * @run testng LambdaTest + * @bug 8001666 + */ + +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; +import static org.testng.Assert.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class LambdaTest { + private static enum Type{updateAndGet, getAndUpdate, accumulateAndGet, getAndAccumulate}; + + private static Random rand = new Random(System.currentTimeMillis()); + + private final static int OP_TIMES = 1 << 10; + + private final static int THREAD_NUM = 1 << 4; + + @DataProvider + public Object[][] booleanProvider(){ + Object[][] providersParam = new Object[Type.values().length][]; + Object[] a = Type.values(); + for(int i = 0; i < a.length; i++) + providersParam[i] = new Object[]{a[i]}; + return providersParam; + } + + @Test(dataProvider = "booleanProvider") + @SuppressWarnings({"rawtypes", "unchecked"}) + public void test(final Type type) throws InterruptedException, + ExecutionException { + long initValue = rand.nextLong(); + final Updater u = new Updater(); + u.accumulation = initValue; + final AtomicLongFieldUpdater<Updater> accAIFU + = AtomicLongFieldUpdater.<Updater>newUpdater((Class<Updater>)(Class<?>)u.getClass(), "accumulation"); + final long randPlus = rand.nextLong(); + final long randMinus = rand.nextLong(); + switch(type) { + case updateAndGet: + assertEquals(accAIFU.updateAndGet(u, + LambdaUtilities.addLongUnaryOperator(randPlus)), + initValue + randPlus); + assertEquals(accAIFU.updateAndGet(u, + LambdaUtilities.subLongUnaryOperator(randMinus)), + initValue + randPlus - randMinus); + break; + case getAndUpdate: + assertEquals(accAIFU.getAndUpdate(u, + LambdaUtilities.addLongUnaryOperator(randPlus)), + initValue); + assertEquals(accAIFU.getAndUpdate(u, + LambdaUtilities.subLongUnaryOperator(randMinus)), + initValue + randPlus); + break; + case accumulateAndGet: + assertEquals(accAIFU.accumulateAndGet(u, randPlus, + LambdaUtilities.addLongBinaryOperator()), + initValue + randPlus); + assertEquals(accAIFU.accumulateAndGet(u, randMinus, + LambdaUtilities.subLongBinaryOperator()), + initValue + randPlus - randMinus); + break; + case getAndAccumulate: + assertEquals(accAIFU.getAndAccumulate(u, randPlus, + LambdaUtilities.addLongBinaryOperator()), + initValue); + assertEquals(accAIFU.getAndAccumulate(u, randMinus, + LambdaUtilities.subLongBinaryOperator()), + initValue + randPlus); + break; + } + assertEquals(u.accumulation, initValue + randPlus - randMinus); + + class UpdateThread extends Thread{ + OpTimes ops; + + CountDownLatch latch; + UpdateThread(OpTimes ops, CountDownLatch latch){ + this.ops = ops; + this.latch = latch; + } + @Override + public void run() { + int plusTimes = 0; + int minusTimes = 0; + for(int index = 0; index < OP_TIMES; index++) { + boolean plus = rand.nextBoolean(); + switch(type) { + case updateAndGet: + accAIFU.updateAndGet(u, plus ? + LambdaUtilities.addLongUnaryOperator(randPlus) : + LambdaUtilities.subLongUnaryOperator(randMinus)); + break; + case getAndUpdate: + accAIFU.getAndUpdate(u, plus ? + LambdaUtilities.addLongUnaryOperator(randPlus) : + LambdaUtilities.subLongUnaryOperator(randMinus)); + break; + case accumulateAndGet: + accAIFU.accumulateAndGet(u, plus ? randPlus : randMinus, + plus ? LambdaUtilities.addLongBinaryOperator() : + LambdaUtilities.subLongBinaryOperator()); + break; + case getAndAccumulate: + accAIFU.getAndAccumulate(u, plus ? randPlus : randMinus, + plus ? LambdaUtilities.addLongBinaryOperator() : + LambdaUtilities.subLongBinaryOperator()); + break; + } + if(plus) + plusTimes++; + else + minusTimes++; + } + ops.plus = plusTimes; + ops.minus = minusTimes; + latch.countDown(); + } + }; + u.accumulation = initValue; + + int plusTimes = 0; + int minusTimes = 0; + OpTimes[] opts = new OpTimes[THREAD_NUM -1]; + CountDownLatch latch = new CountDownLatch(THREAD_NUM -1); + for(int i = 0; i < THREAD_NUM -1; i++) { + opts[i] = new OpTimes(); + new UpdateThread(opts[i], latch).start(); + } + + for(int index = 0; index < OP_TIMES; index++) { + boolean plus = rand.nextBoolean(); + switch(type) { + case updateAndGet: + accAIFU.updateAndGet(u, plus ? + LambdaUtilities.addLongUnaryOperator(randPlus) : + LambdaUtilities.subLongUnaryOperator(randMinus)); + break; + case getAndUpdate: + accAIFU.getAndUpdate(u, plus ? + LambdaUtilities.addLongUnaryOperator(randPlus) : + LambdaUtilities.subLongUnaryOperator(randMinus)); + break; + case accumulateAndGet: + accAIFU.accumulateAndGet(u, plus ? randPlus : randMinus, + plus ? LambdaUtilities.addLongBinaryOperator() : + LambdaUtilities.subLongBinaryOperator()); + break; + case getAndAccumulate: + accAIFU.getAndAccumulate(u, plus ? randPlus : randMinus, + plus ? LambdaUtilities.addLongBinaryOperator() : + LambdaUtilities.subLongBinaryOperator()); + break; + } + if(plus) + plusTimes++; + else + minusTimes++; + } + latch.await(); + for(int i = 0; i < THREAD_NUM -1; i++) { + plusTimes += opts[i].plus; + minusTimes += opts[i].minus; + } + long expected = initValue + plusTimes * randPlus - + minusTimes * randMinus; + assertEquals(expected, u.accumulation); + } + + static class Updater{ + public volatile long accumulation; + } + + static class OpTimes{ + public int plus; + + public int minus; + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/concurrent/AtomicReference/LambdaTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,221 @@ +/* + * 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. + */ + +/** + * @test + * @summary test for AtomicReference.updateAndGet()/getAndUpdate() and + * AtomicReference.accumulateAndGet()/getAndAccumulate(); + * @(#) LambdaTest.java + * @library ../../ + * @author Tristan Yan + * @run testng LambdaTest + * @bug 8001666 + */ + +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BinaryOperator; +import java.util.function.UnaryOperator; +import static org.testng.Assert.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class LambdaTest { + private static enum Type{updateAndGet, getAndUpdate, accumulateAndGet, getAndAccumulate}; + + private static Random rand = new Random(System.currentTimeMillis()); + + private final static int OP_TIMES = 1 << 10; + + private final static int THREAD_NUM = 1 << 4; + + @DataProvider + public Object[][] booleanProvider(){ + Object[][] providersParam = new Object[Type.values().length][]; + Object[] a = Type.values(); + for(int i = 0; i < a.length; i++) + providersParam[i] = new Object[]{a[i]}; + return providersParam; + } + + @Test(dataProvider = "booleanProvider") + public void test(final Type type) throws InterruptedException{ + int initPlus = rand.nextInt(); + int initMinus = rand.nextInt(); + final AtomicReference<OpValues> ar + = new AtomicReference<>(new OpValues(initPlus, initMinus)); + final int oprnd = rand.nextInt(); + boolean startFromPlus = rand.nextBoolean(); + switch(type) { + case updateAndGet: + assertEquals(ar.updateAndGet(opUnaryOperator(startFromPlus, oprnd)) + , startFromPlus ? new OpValues(initPlus + oprnd, initMinus) : + new OpValues(initPlus, initMinus - oprnd)); + assertEquals(ar.updateAndGet(opUnaryOperator(!startFromPlus, oprnd)) + , new OpValues(initPlus + oprnd, initMinus - oprnd)); + break; + case getAndUpdate: + assertEquals(ar.getAndUpdate(opUnaryOperator(startFromPlus, oprnd)) + , new OpValues(initPlus, initMinus)); + assertEquals(ar.getAndUpdate(opUnaryOperator(!startFromPlus, oprnd)) + , startFromPlus ? new OpValues(initPlus + oprnd, initMinus) : + new OpValues(initPlus, initMinus - oprnd)); + break; + case accumulateAndGet: + assertEquals(ar.accumulateAndGet( + new OpValues(oprnd, oprnd), + opBinaryOperator(startFromPlus)) + , startFromPlus ? new OpValues(initPlus + oprnd, initMinus) : + new OpValues(initPlus, initMinus - oprnd)); + assertEquals(ar.accumulateAndGet( + new OpValues(oprnd, oprnd), + opBinaryOperator(!startFromPlus)) + , new OpValues(initPlus + oprnd, initMinus - oprnd)); + break; + case getAndAccumulate: + assertEquals(ar.getAndAccumulate( + new OpValues(oprnd, oprnd), + opBinaryOperator(startFromPlus)) + , new OpValues(initPlus, initMinus)); + assertEquals(ar.getAndAccumulate( + new OpValues(oprnd, oprnd), + opBinaryOperator(!startFromPlus)) + , startFromPlus ? new OpValues(initPlus + oprnd, initMinus) : + new OpValues(initPlus, initMinus - oprnd)); + break; + } + class UpdateThread extends Thread{ + CountDownLatch latch; + private int plusTimes = 0; + private int minusTimes = 0; + + UpdateThread(CountDownLatch latch){ + this.latch = latch; + } + @Override + public void run() { + for(int index = 0; index < OP_TIMES; index++) { + boolean plus = rand.nextBoolean(); + switch(type) { + case updateAndGet: + ar.updateAndGet(opUnaryOperator(plus, oprnd)); + break; + case getAndUpdate: + ar.getAndUpdate(opUnaryOperator(plus, oprnd)); + break; + case accumulateAndGet: + ar.accumulateAndGet(new OpValues(oprnd, oprnd), + opBinaryOperator(plus)); + break; + case getAndAccumulate: + ar.getAndAccumulate(new OpValues(oprnd, oprnd), + opBinaryOperator(plus)); + break; + } + if(plus) + plusTimes++; + else + minusTimes++; + } + latch.countDown(); + } + int plusTimes(){ return plusTimes; } + int minusTimes(){ return minusTimes; } + }; + UpdateThread[] threads = new UpdateThread[THREAD_NUM -1]; + CountDownLatch latch = new CountDownLatch(THREAD_NUM -1); + for(int i = 0; i < THREAD_NUM -1; i++) { + threads[i] = new UpdateThread(latch); + threads[i].start(); + } + int plusTimes = 0; + int minusTimes = 0; + for(int index = 0; index < OP_TIMES; index++) { + boolean isPlus = rand.nextBoolean(); + switch(type) { + case updateAndGet: + ar.updateAndGet(opUnaryOperator(isPlus, oprnd)); + break; + case getAndUpdate: + ar.getAndUpdate(opUnaryOperator(isPlus, oprnd)); + break; + case accumulateAndGet: + ar.accumulateAndGet(new OpValues(oprnd, oprnd), + opBinaryOperator(isPlus)); + break; + case getAndAccumulate: + ar.getAndAccumulate(new OpValues(oprnd, oprnd), + opBinaryOperator(isPlus)); + break; + } + if(isPlus) + plusTimes++; + else + minusTimes++; + } + latch.await(); + for(int i = 0; i < THREAD_NUM -1; i++) { + plusTimes += threads[i].plusTimes(); + minusTimes += threads[i].minusTimes(); + } + assertEquals(ar.get().getPlusAccumulated(), initPlus + plusTimes * oprnd + oprnd); + assertEquals(ar.get().getMinusAccumulated(), initMinus - minusTimes * oprnd - oprnd); + } + + static final class OpValues{ + final int plusAccumulated; + + final int minusAccumulated; + + OpValues(int plusAccumulated, int minusAccumulated){ + this.plusAccumulated = plusAccumulated; + this.minusAccumulated = minusAccumulated; + } + + OpValues operate(boolean isPlus, int value) { + if(isPlus) + return new OpValues(plusAccumulated + value, minusAccumulated); + else + return new OpValues(plusAccumulated, minusAccumulated - value); + } + + int getPlusAccumulated(){ return plusAccumulated; } + int getMinusAccumulated(){ return minusAccumulated; } + + @Override + public boolean equals(Object o){ + OpValues opv = (OpValues)o; + return (opv.plusAccumulated == plusAccumulated) + && (opv.minusAccumulated == minusAccumulated); + } + } + + public static UnaryOperator<OpValues> opUnaryOperator(boolean isPlus, int value) { + return t -> t.operate(isPlus, value); + } + + public static BinaryOperator<OpValues> opBinaryOperator(boolean isPlus) { + return (t1, t2) -> t1.operate(isPlus, isPlus ? t2.getPlusAccumulated() : t2.getMinusAccumulated()); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/concurrent/AtomicReferenceFieldUpdater/LambdaTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,215 @@ +/* + * 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. + */ + +/** + * @test + * @summary test for AtomicReferenceFieldUpdater.updateAndGet()/getAndUpdate() + * and AtomicReferenceFieldUpdater.accumulateAndGet()/getAndAccumulate(); + * @(#) LambdaTest.java + * @library ../../ + * @author Tristan Yan + * @run testng LambdaTest + * @bug 8001666 + */ + +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import java.util.function.BinaryOperator; +import java.util.function.UnaryOperator; +import static org.testng.Assert.*; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public class LambdaTest { + private static enum Type{updateAndGet, getAndUpdate, accumulateAndGet, getAndAccumulate}; + + private static Random rand = new Random(System.currentTimeMillis()); + + private final static int OP_TIMES = 1 << 10; + + private final static int THREAD_NUM = 1 << 4; + + @DataProvider + public Object[][] booleanProvider(){ + Object[][] providersParam = new Object[Type.values().length][]; + Object[] a = Type.values(); + for(int i = 0; i < a.length; i++) + providersParam[i] = new Object[]{a[i]}; + return providersParam; + } + + @Test(dataProvider = "booleanProvider") + @SuppressWarnings({"rawtypes", "unchecked"}) + public void test(final Type type) throws InterruptedException{ + int initValue = rand.nextInt(); + final Updater u = new Updater(); + u.accumulation = new OpValues(initValue); + final AtomicReferenceFieldUpdater<Updater, OpValues> accARFU + = AtomicReferenceFieldUpdater.<Updater, OpValues>newUpdater((Class<Updater>)(Class<?>)u.getClass(), (Class<OpValues>)(Class<?>)u.accumulation.getClass(), "accumulation"); + + final int oprnd = rand.nextInt(); + boolean startFromPlus = rand.nextBoolean(); + switch(type) { + case updateAndGet: + assertEquals(accARFU.updateAndGet(u,opUnaryOperator(startFromPlus, oprnd)) + , new OpValues(startFromPlus ? initValue + oprnd : initValue - oprnd)); + assertEquals(accARFU.updateAndGet(u,opUnaryOperator(!startFromPlus, oprnd)) + , new OpValues(initValue)); + break; + case getAndUpdate: + assertEquals(accARFU.getAndUpdate(u,opUnaryOperator(startFromPlus, oprnd)) + , new OpValues(initValue)); + assertEquals(accARFU.getAndUpdate(u,opUnaryOperator(!startFromPlus, oprnd)) + , new OpValues(startFromPlus ? initValue + oprnd : initValue - oprnd)); + break; + case accumulateAndGet: + assertEquals(accARFU.accumulateAndGet(u, new OpValues(oprnd), + opBinaryOperator(startFromPlus)) + , new OpValues(startFromPlus ? initValue + oprnd : initValue - oprnd)); + assertEquals(accARFU.accumulateAndGet(u, new OpValues(oprnd), + opBinaryOperator(!startFromPlus)) + , new OpValues(initValue)); + break; + case getAndAccumulate: + assertEquals(accARFU.getAndAccumulate(u, new OpValues(oprnd), + opBinaryOperator(startFromPlus)) + , new OpValues(initValue)); + assertEquals(accARFU.getAndAccumulate(u, new OpValues(oprnd), + opBinaryOperator(!startFromPlus)) + , new OpValues(startFromPlus ? initValue + oprnd : initValue - oprnd)); + break; + } + class UpdateThread extends Thread{ + CountDownLatch latch; + private int plusTimes = 0; + private int minusTimes = 0; + + UpdateThread(CountDownLatch latch){ + this.latch = latch; + } + @Override + public void run() { + for(int index = 0; index < OP_TIMES; index++) { + boolean plus = rand.nextBoolean(); + switch(type) { + case updateAndGet: + accARFU.updateAndGet(u,opUnaryOperator(plus, oprnd)); + break; + case getAndUpdate: + accARFU.getAndUpdate(u,opUnaryOperator(plus, oprnd)); + break; + case accumulateAndGet: + accARFU.accumulateAndGet(u, new OpValues(oprnd), + opBinaryOperator(plus)); + break; + case getAndAccumulate: + accARFU.getAndAccumulate(u, new OpValues(oprnd), + opBinaryOperator(plus)); + break; + } + if(plus) + plusTimes++; + else + minusTimes++; + } + latch.countDown(); + } + int plusTimes(){ return plusTimes; } + int minusTimes(){ return minusTimes; } + }; + UpdateThread[] threads = new UpdateThread[THREAD_NUM -1]; + CountDownLatch latch = new CountDownLatch(THREAD_NUM -1); + for(int i = 0; i < THREAD_NUM -1; i++) { + threads[i] = new UpdateThread(latch); + threads[i].start(); + } + int plusTimes = 0; + int minusTimes = 0; + for(int index = 0; index < OP_TIMES; index++) { + boolean isPlus = rand.nextBoolean(); + switch(type) { + case updateAndGet: + accARFU.updateAndGet(u,opUnaryOperator(isPlus, oprnd)); + break; + case getAndUpdate: + accARFU.getAndUpdate(u,opUnaryOperator(isPlus, oprnd)); + break; + case accumulateAndGet: + accARFU.accumulateAndGet(u, new OpValues(oprnd), + opBinaryOperator(isPlus)); + break; + case getAndAccumulate: + accARFU.getAndAccumulate(u, new OpValues(oprnd), + opBinaryOperator(isPlus)); + break; + } + if(isPlus) + plusTimes++; + else + minusTimes++; + } + latch.await(); + for(int i = 0; i < THREAD_NUM -1; i++) { + plusTimes += threads[i].plusTimes(); + minusTimes += threads[i].minusTimes(); + } + assertEquals(accARFU.get(u).getAccumulated(), initValue + plusTimes * oprnd - minusTimes * oprnd); + } + + static final class OpValues{ + int accumulated; + + OpValues(int accumulated){ + this.accumulated = accumulated; + } + + OpValues operate(boolean isPlus, int value) { + if(isPlus) + return new OpValues(accumulated + value); + else + return new OpValues(accumulated - value); + } + + int getAccumulated(){ return accumulated; } + + @Override + public boolean equals(Object o){ + OpValues opv = (OpValues)o; + return (opv.accumulated == accumulated); + } + } + + + static class Updater{ + public volatile OpValues accumulation; + } + + public static UnaryOperator<OpValues> opUnaryOperator(boolean isPlus, int value) { + return t -> t.operate(isPlus, value); + } + + public static BinaryOperator<OpValues> opBinaryOperator(boolean isPlus) { + return (t1, t2) -> t1.operate(isPlus, t2.getAccumulated()); + } +} \ No newline at end of file
--- a/test/java/util/concurrent/CompletableFuture/Basic.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/concurrent/CompletableFuture/Basic.java Wed Aug 14 15:53:13 2013 -0700 @@ -66,6 +66,7 @@ try { equalAnyOf(cf.get(), values); } catch (Throwable x) { unexpected(x); } try { equalAnyOf(cf.get(0L, SECONDS), values); } catch (Throwable x) { unexpected(x); } check(cf.isDone(), "Expected isDone to be true, got:" + cf); + check(!cf.isCompletedExceptionally(), "Expected isCompletedExceptionally to return false"); check(!cf.isCancelled(), "Expected isCancelled to be false"); check(!cf.cancel(true), "Expected cancel to return false"); check(cf.toString().contains("[Completed normally]")); @@ -97,6 +98,7 @@ catch (CancellationException x) { if (cancelled) pass(); else fail(); } catch (ExecutionException x) { if (cancelled) check(x.getCause() instanceof CancellationException); else pass(); } check(cf.isDone(), "Expected isDone to be true, got:" + cf); + check(cf.isCompletedExceptionally(), "Expected isCompletedExceptionally"); check(cf.isCancelled() == cancelled, "Expected isCancelled: " + cancelled + ", got:" + cf.isCancelled()); check(cf.cancel(true) == cancelled, "Expected cancel: " + cancelled + ", got:" + cf.cancel(true)); check(cf.toString().contains("[Completed exceptionally]")); // ## TODO: 'E'xceptionally @@ -805,6 +807,49 @@ cf2 = cf1.handle((x,t) -> { check(t.getCause() == ex); return 2;}); checkCompletedExceptionally(cf1); checkCompletedNormally(cf2, 2); + + cf1 = supplyAsync(() -> 1); + cf2 = cf1.handleAsync((x,t) -> x+1); + checkCompletedNormally(cf1, 1); + checkCompletedNormally(cf2, 2); + + cf1 = supplyAsync(() -> { throw ex; }); + cf2 = cf1.handleAsync((x,t) -> { check(t.getCause() == ex); return 2;}); + checkCompletedExceptionally(cf1); + checkCompletedNormally(cf2, 2); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // whenComplete tests + //---------------------------------------------------------------- + try { + AtomicInteger count = new AtomicInteger(); + CompletableFuture<Integer> cf2; + CompletableFuture<Integer> cf1 = supplyAsync(() -> 1); + cf2 = cf1.whenComplete((x,t) -> count.getAndIncrement()); + checkCompletedNormally(cf1, 1); + checkCompletedNormally(cf2, 1); + check(count.get() == 1, "action count should be incremented"); + + final RuntimeException ex = new RuntimeException(); + cf1 = supplyAsync(() -> { throw ex; }); + cf2 = cf1.whenComplete((x,t) -> count.getAndIncrement()); + checkCompletedExceptionally(cf1); + checkCompletedExceptionally(cf2); + check(count.get() == 2, "action count should be incremented"); + + cf1 = supplyAsync(() -> 1); + cf2 = cf1.whenCompleteAsync((x,t) -> count.getAndIncrement()); + checkCompletedNormally(cf1, 1); + checkCompletedNormally(cf2, 1); + check(count.get() == 3, "action count should be incremented"); + + cf1 = supplyAsync(() -> { throw ex; }); + cf2 = cf1.whenCompleteAsync((x,t) -> count.getAndIncrement()); + checkCompletedExceptionally(cf1); + checkCompletedExceptionally(cf2); + check(count.get() == 4, "action count should be incremented"); + } catch (Throwable t) { unexpected(t); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/concurrent/ThreadLocalRandom/ThreadLocalRandomTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,511 @@ +/* + * Copyright (c) 2012, 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 org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.LongAdder; + +import static org.testng.Assert.*; + +/** + * @test + * @run testng ThreadLocalRandomTest + * @summary test methods on ThreadLocalRandom + */ +@Test +public class ThreadLocalRandomTest { + + // Note: this test was copied from the 166 TCK ThreadLocalRandomTest test + // and modified to be a TestNG test + + /* + * Testing coverage notes: + * + * We don't test randomness properties, but only that repeated + * calls, up to NCALLS tries, produce at least one different + * result. For bounded versions, we sample various intervals + * across multiples of primes. + */ + + // max numbers of calls to detect getting stuck on one value + static final int NCALLS = 10000; + + // max sampled int bound + static final int MAX_INT_BOUND = (1 << 28); + + // max sampled long bound + static final long MAX_LONG_BOUND = (1L << 42); + + // Number of replications for other checks + static final int REPS = 20; + + /** + * setSeed throws UnsupportedOperationException + */ + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testSetSeed() { + ThreadLocalRandom.current().setSeed(17); + } + + /** + * Repeated calls to nextInt produce at least two distinct results + */ + public void testNextInt() { + int f = ThreadLocalRandom.current().nextInt(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextInt() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextLong produce at least two distinct results + */ + public void testNextLong() { + long f = ThreadLocalRandom.current().nextLong(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextLong() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextBoolean produce at least two distinct results + */ + public void testNextBoolean() { + boolean f = ThreadLocalRandom.current().nextBoolean(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextBoolean() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextFloat produce at least two distinct results + */ + public void testNextFloat() { + float f = ThreadLocalRandom.current().nextFloat(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextFloat() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextDouble produce at least two distinct results + */ + public void testNextDouble() { + double f = ThreadLocalRandom.current().nextDouble(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextDouble() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * Repeated calls to nextGaussian produce at least two distinct results + */ + public void testNextGaussian() { + double f = ThreadLocalRandom.current().nextGaussian(); + int i = 0; + while (i < NCALLS && ThreadLocalRandom.current().nextGaussian() == f) + ++i; + assertTrue(i < NCALLS); + } + + /** + * nextInt(negative) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextIntBoundedNeg() { + int f = ThreadLocalRandom.current().nextInt(-17); + } + + /** + * nextInt(least >= bound) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextIntBadBounds() { + int f = ThreadLocalRandom.current().nextInt(17, 2); + } + + /** + * nextInt(bound) returns 0 <= value < bound; repeated calls produce at + * least two distinct results + */ + public void testNextIntBounded() { + // sample bound space across prime number increments + for (int bound = 2; bound < MAX_INT_BOUND; bound += 524959) { + int f = ThreadLocalRandom.current().nextInt(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + int j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextInt(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextInt(least, bound) returns least <= value < bound; repeated calls + * produce at least two distinct results + */ + public void testNextIntBounded2() { + for (int least = -15485863; least < MAX_INT_BOUND; least += 524959) { + for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 49979687) { + int f = ThreadLocalRandom.current().nextInt(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + int j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextInt(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextLong(negative) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextLongBoundedNeg() { + long f = ThreadLocalRandom.current().nextLong(-17); + } + + /** + * nextLong(least >= bound) throws IllegalArgumentException + */ + @Test(expectedExceptions = IllegalArgumentException.class) + public void testNextLongBadBounds() { + long f = ThreadLocalRandom.current().nextLong(17, 2); + } + + /** + * nextLong(bound) returns 0 <= value < bound; repeated calls produce at + * least two distinct results + */ + public void testNextLongBounded() { + for (long bound = 2; bound < MAX_LONG_BOUND; bound += 15485863) { + long f = ThreadLocalRandom.current().nextLong(bound); + assertTrue(0 <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextLong(bound)) == f) { + assertTrue(0 <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + + /** + * nextLong(least, bound) returns least <= value < bound; repeated calls + * produce at least two distinct results + */ + public void testNextLongBounded2() { + for (long least = -86028121; least < MAX_LONG_BOUND; least += 982451653L) { + for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) { + long f = ThreadLocalRandom.current().nextLong(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + long j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextLong(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * nextDouble(least, bound) returns least <= value < bound; repeated calls + * produce at least two distinct results + */ + public void testNextDoubleBounded2() { + for (double least = 0.0001; least < 1.0e20; least *= 8) { + for (double bound = least * 1.001; bound < 1.0e20; bound *= 16) { + double f = ThreadLocalRandom.current().nextDouble(least, bound); + assertTrue(least <= f && f < bound); + int i = 0; + double j; + while (i < NCALLS && + (j = ThreadLocalRandom.current().nextDouble(least, bound)) == f) { + assertTrue(least <= j && j < bound); + ++i; + } + assertTrue(i < NCALLS); + } + } + } + + /** + * Invoking sized ints, long, doubles, with negative sizes throws + * IllegalArgumentException + */ + public void testBadStreamSize() { + ThreadLocalRandom r = ThreadLocalRandom.current(); + executeAndCatchIAE(() -> r.ints(-1L)); + executeAndCatchIAE(() -> r.ints(-1L, 2, 3)); + executeAndCatchIAE(() -> r.longs(-1L)); + executeAndCatchIAE(() -> r.longs(-1L, -1L, 1L)); + executeAndCatchIAE(() -> r.doubles(-1L)); + executeAndCatchIAE(() -> r.doubles(-1L, .5, .6)); + } + + /** + * Invoking bounded ints, long, doubles, with illegal bounds throws + * IllegalArgumentException + */ + public void testBadStreamBounds() { + ThreadLocalRandom r = ThreadLocalRandom.current(); + executeAndCatchIAE(() -> r.ints(2, 1)); + executeAndCatchIAE(() -> r.ints(10, 42, 42)); + executeAndCatchIAE(() -> r.longs(-1L, -1L)); + executeAndCatchIAE(() -> r.longs(10, 1L, -2L)); + executeAndCatchIAE(() -> r.doubles(0.0, 0.0)); + executeAndCatchIAE(() -> r.doubles(10, .5, .4)); + } + + private void executeAndCatchIAE(Runnable r) { + executeAndCatch(IllegalArgumentException.class, r); + } + + private void executeAndCatch(Class<? extends Exception> expected, Runnable r) { + Exception caught = null; + try { + r.run(); + } + catch (Exception e) { + caught = e; + } + + assertNotNull(caught, + String.format("No Exception was thrown, expected an Exception of %s to be thrown", + expected.getName())); + Assert.assertTrue(expected.isInstance(caught), + String.format("Exception thrown %s not an instance of %s", + caught.getClass().getName(), expected.getName())); + } + + /** + * A parallel sized stream of ints generates the given number of values + */ + public void testIntsCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.ints(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * A parallel sized stream of longs generates the given number of values + */ + public void testLongsCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.longs(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * A parallel sized stream of doubles generates the given number of values + */ + public void testDoublesCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 0; + for (int reps = 0; reps < REPS; ++reps) { + counter.reset(); + r.doubles(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + size += 524959; + } + } + + /** + * Each of a parallel sized stream of bounded ints is within bounds + */ + public void testBoundedInts() { + AtomicInteger fails = new AtomicInteger(0); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 12345L; + for (int least = -15485867; least < MAX_INT_BOUND; least += 524959) { + for (int bound = least + 2; bound > least && bound < MAX_INT_BOUND; bound += 67867967) { + final int lo = least, hi = bound; + r.ints(size, lo, hi).parallel(). + forEach(x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); + }); + } + } + assertEquals(fails.get(), 0); + } + + /** + * Each of a parallel sized stream of bounded longs is within bounds + */ + public void testBoundedLongs() { + AtomicInteger fails = new AtomicInteger(0); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 123L; + for (long least = -86028121; least < MAX_LONG_BOUND; least += 1982451653L) { + for (long bound = least + 2; bound > least && bound < MAX_LONG_BOUND; bound += Math.abs(bound * 7919)) { + final long lo = least, hi = bound; + r.longs(size, lo, hi).parallel(). + forEach(x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); + }); + } + } + assertEquals(fails.get(), 0); + } + + /** + * Each of a parallel sized stream of bounded doubles is within bounds + */ + public void testBoundedDoubles() { + AtomicInteger fails = new AtomicInteger(0); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 456; + for (double least = 0.00011; least < 1.0e20; least *= 9) { + for (double bound = least * 1.0011; bound < 1.0e20; bound *= 17) { + final double lo = least, hi = bound; + r.doubles(size, lo, hi).parallel(). + forEach(x -> { + if (x < lo || x >= hi) + fails.getAndIncrement(); + }); + } + } + assertEquals(fails.get(), 0); + } + + /** + * A parallel unsized stream of ints generates at least 100 values + */ + public void testUnsizedIntsCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.ints().limit(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + + /** + * A parallel unsized stream of longs generates at least 100 values + */ + public void testUnsizedLongsCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.longs().limit(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + + /** + * A parallel unsized stream of doubles generates at least 100 values + */ + public void testUnsizedDoublesCount() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.doubles().limit(size).parallel().forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + + /** + * A sequential unsized stream of ints generates at least 100 values + */ + public void testUnsizedIntsCountSeq() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.ints().limit(size).forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + + /** + * A sequential unsized stream of longs generates at least 100 values + */ + public void testUnsizedLongsCountSeq() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.longs().limit(size).forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + + /** + * A sequential unsized stream of doubles generates at least 100 values + */ + public void testUnsizedDoublesCountSeq() { + LongAdder counter = new LongAdder(); + ThreadLocalRandom r = ThreadLocalRandom.current(); + long size = 100; + r.doubles().limit(size).forEach(x -> { + counter.increment(); + }); + assertEquals(counter.sum(), size); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/concurrent/forkjoin/ParallelSorting.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,2069 @@ +/* + * Copyright (c) 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. + * + * 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. + */ + +/* Adapted from test/java/util/Arrays/Sorting.java + * + * Where that test checks Arrays.sort against manual quicksort routines, + * this test checks parallelSort against either Arrays.sort or manual + * quicksort routines. + */ + +/* +/* + * @test + * @summary Exercise ForkJoinUtils.parallelSort (adapted from test Sorting) + * @build ParallelSorting + * @run main ParallelSorting -shortrun + * + * @author Vladimir Yaroslavskiy + * @author Jon Bentley + * @author Josh Bloch + * + */ + +import java.util.Arrays; +import java.util.Random; +import java.io.PrintStream; +import java.util.Comparator; + +public class ParallelSorting { + private static final PrintStream out = System.out; + private static final PrintStream err = System.err; + + // Array lengths used in a long run (default) + private static final int[] LONG_RUN_LENGTHS = { + 1, 2, 3, 5, 8, 13, 21, 34, 55, 100, 1000, 10000, 100000, 1000000 }; + + // Array lengths used in a short run + private static final int[] SHORT_RUN_LENGTHS = { + 1, 2, 3, 21, 55, 1000, 10000 }; + + // Random initial values used in a long run (default) + private static final long[] LONG_RUN_RANDOMS = { 666, 0xC0FFEE, 999 }; + + // Random initial values used in a short run + private static final long[] SHORT_RUN_RANDOMS = { 666 }; + + public static void main(String[] args) { + boolean shortRun = args.length > 0 && args[0].equals("-shortrun"); + long start = System.currentTimeMillis(); + + if (shortRun) { + testAndCheck(SHORT_RUN_LENGTHS, SHORT_RUN_RANDOMS); + } else { + testAndCheck(LONG_RUN_LENGTHS, LONG_RUN_RANDOMS); + } + long end = System.currentTimeMillis(); + + out.format("PASSED in %d sec.\n", Math.round((end - start) / 1E3)); + } + + private static void testAndCheck(int[] lengths, long[] randoms) { + testEmptyAndNullIntArray(); + testEmptyAndNullLongArray(); + testEmptyAndNullShortArray(); + testEmptyAndNullCharArray(); + testEmptyAndNullByteArray(); + testEmptyAndNullFloatArray(); + testEmptyAndNullDoubleArray(); + + for (int length : lengths) { + testMergeSort(length); + testAndCheckRange(length); + testAndCheckSubArray(length); + } + for (long seed : randoms) { + for (int length : lengths) { + testAndCheckWithInsertionSort(length, new MyRandom(seed)); + testAndCheckWithCheckSum(length, new MyRandom(seed)); + testAndCheckWithScrambling(length, new MyRandom(seed)); + testAndCheckFloat(length, new MyRandom(seed)); + testAndCheckDouble(length, new MyRandom(seed)); + testStable(length, new MyRandom(seed)); + } + } + } + + private static void testEmptyAndNullIntArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new int[]{}); + Arrays.parallelSort(new int[]{}, 0, 0); + + try { + Arrays.parallelSort((int[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((int[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("ForkJoinUtils.parallelSort(int[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("ForkJoinUtils.parallelSort(int[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullLongArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new long[]{}); + Arrays.parallelSort(new long[]{}, 0, 0); + + try { + Arrays.parallelSort((long[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((long[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("ForkJoinUtils.parallelSort(long[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("ForkJoinUtils.parallelSort(long[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullShortArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new short[]{}); + Arrays.parallelSort(new short[]{}, 0, 0); + + try { + Arrays.parallelSort((short[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((short[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("ForkJoinUtils.parallelSort(short[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("ForkJoinUtils.parallelSort(short[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullCharArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new char[]{}); + Arrays.parallelSort(new char[]{}, 0, 0); + + try { + Arrays.parallelSort((char[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((char[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("ForkJoinUtils.parallelSort(char[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("ForkJoinUtils.parallelSort(char[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullByteArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new byte[]{}); + Arrays.parallelSort(new byte[]{}, 0, 0); + + try { + Arrays.parallelSort((byte[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((byte[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("ForkJoinUtils.parallelSort(byte[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("ForkJoinUtils.parallelSort(byte[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullFloatArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new float[]{}); + Arrays.parallelSort(new float[]{}, 0, 0); + + try { + Arrays.parallelSort((float[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((float[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("ForkJoinUtils.parallelSort(float[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("ForkJoinUtils.parallelSort(float[]) shouldn't catch null array"); + } + + private static void testEmptyAndNullDoubleArray() { + ourDescription = "Check empty and null array"; + Arrays.parallelSort(new double[]{}); + Arrays.parallelSort(new double[]{}, 0, 0); + + try { + Arrays.parallelSort((double[]) null); + } catch (NullPointerException expected) { + try { + Arrays.parallelSort((double[]) null, 0, 0); + } catch (NullPointerException expected2) { + return; + } + failed("ForkJoinUtils.parallelSort(double[],fromIndex,toIndex) shouldn't " + + "catch null array"); + } + failed("ForkJoinUtils.parallelSort(double[]) shouldn't catch null array"); + } + + private static void testAndCheckSubArray(int length) { + ourDescription = "Check sorting of subarray"; + int[] golden = new int[length]; + boolean newLine = false; + + for (int m = 1; m < length / 2; m *= 2) { + newLine = true; + int fromIndex = m; + int toIndex = length - m; + + prepareSubArray(golden, fromIndex, toIndex, m); + int[] test = golden.clone(); + + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'subarray': " + converter + + " length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + Object convertedTest = converter.convert(test); + sortSubArray(convertedTest, fromIndex, toIndex); + checkSubArray(convertedTest, fromIndex, toIndex, m); + } + } + if (newLine) { + out.println(); + } + } + + private static void testAndCheckRange(int length) { + ourDescription = "Check range check"; + int[] golden = new int[length]; + + for (int m = 1; m < 2 * length; m *= 2) { + for (int i = 1; i <= length; i++) { + golden[i - 1] = i % m + m % i; + } + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'range': " + converter + + ", length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + checkRange(convertedGolden, m); + } + } + out.println(); + } + + private static void testStable(int length, MyRandom random) { + ourDescription = "Check if sorting is stable"; + Pair[] a = build(length, random); + + out.println("Test 'stable': " + "random = " + random.getSeed() + + ", length = " + length); + Arrays.parallelSort(a); + checkSorted(a); + checkStable(a); + out.println(); + + a = build(length, random); + + out.println("Test 'stable' comparator: " + "random = " + random.getSeed() + + ", length = " + length); + Arrays.parallelSort(a, pairCmp); + checkSorted(a); + checkStable(a); + out.println(); + + } + + private static void checkSorted(Pair[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i].getKey() > a[i + 1].getKey()) { + failedSort(i, "" + a[i].getKey(), "" + a[i + 1].getKey()); + } + } + } + + private static void checkStable(Pair[] a) { + for (int i = 0; i < a.length / 4; ) { + int key1 = a[i].getKey(); + int value1 = a[i++].getValue(); + int key2 = a[i].getKey(); + int value2 = a[i++].getValue(); + int key3 = a[i].getKey(); + int value3 = a[i++].getValue(); + int key4 = a[i].getKey(); + int value4 = a[i++].getValue(); + + if (!(key1 == key2 && key2 == key3 && key3 == key4)) { + failed("On position " + i + " keys are different " + + key1 + ", " + key2 + ", " + key3 + ", " + key4); + } + if (!(value1 < value2 && value2 < value3 && value3 < value4)) { + failed("Sorting is not stable at position " + i + + ". Second values have been changed: " + value1 + ", " + + value2 + ", " + value3 + ", " + value4); + } + } + } + + private static Pair[] build(int length, Random random) { + Pair[] a = new Pair[length * 4]; + + for (int i = 0; i < a.length; ) { + int key = random.nextInt(); + a[i++] = new Pair(key, 1); + a[i++] = new Pair(key, 2); + a[i++] = new Pair(key, 3); + a[i++] = new Pair(key, 4); + } + return a; + } + + private static Comparator<Pair> pairCmp = new Comparator<Pair>() { + public int compare(Pair p1, Pair p2) { + return p1.compareTo(p2); + } + }; + + private static final class Pair implements Comparable<Pair> { + Pair(int key, int value) { + myKey = key; + myValue = value; + } + + int getKey() { + return myKey; + } + + int getValue() { + return myValue; + } + + public int compareTo(Pair pair) { + if (myKey < pair.myKey) { + return -1; + } + if (myKey > pair.myKey) { + return 1; + } + return 0; + } + + @Override + public String toString() { + return "(" + myKey + ", " + myValue + ")"; + } + + private int myKey; + private int myValue; + } + + + private static void testAndCheckWithInsertionSort(int length, MyRandom random) { + if (length > 1000) { + return; + } + ourDescription = "Check sorting with insertion sort"; + int[] golden = new int[length]; + + for (int m = 1; m < 2 * length; m *= 2) { + for (UnsortedBuilder builder : UnsortedBuilder.values()) { + builder.build(golden, m, random); + int[] test = golden.clone(); + + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'insertion sort': " + converter + + " " + builder + "random = " + random.getSeed() + + ", length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + Object convertedTest1 = converter.convert(test); + Object convertedTest2 = converter.convert(test); + sort(convertedTest1); + sortByInsertionSort(convertedTest2); + compare(convertedTest1, convertedTest2); + } + } + } + out.println(); + } + + private static void testMergeSort(int length) { + if (length < 1000) { + return; + } + ourDescription = "Check merge sorting"; + int[] golden = new int[length]; + int period = 67; // java.util.DualPivotQuicksort.MAX_RUN_COUNT + + for (int m = period - 2; m <= period + 2; m++) { + for (MergeBuilder builder : MergeBuilder.values()) { + builder.build(golden, m); + int[] test = golden.clone(); + + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'merge sort': " + converter + " " + + builder + "length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + sort(convertedGolden); + checkSorted(convertedGolden); + } + } + } + out.println(); + } + + private static void testAndCheckWithCheckSum(int length, MyRandom random) { + ourDescription = "Check sorting with check sum"; + int[] golden = new int[length]; + + for (int m = 1; m < 2 * length; m *= 2) { + for (UnsortedBuilder builder : UnsortedBuilder.values()) { + builder.build(golden, m, random); + int[] test = golden.clone(); + + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'check sum': " + converter + + " " + builder + "random = " + random.getSeed() + + ", length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + Object convertedTest = converter.convert(test); + sort(convertedTest); + checkWithCheckSum(convertedTest, convertedGolden); + } + } + } + out.println(); + } + + private static void testAndCheckWithScrambling(int length, MyRandom random) { + ourDescription = "Check sorting with scrambling"; + int[] golden = new int[length]; + + for (int m = 1; m <= 7; m++) { + if (m > length) { + break; + } + for (SortedBuilder builder : SortedBuilder.values()) { + builder.build(golden, m); + int[] test = golden.clone(); + scramble(test, random); + + for (TypeConverter converter : TypeConverter.values()) { + out.println("Test 'scrambling': " + converter + + " " + builder + "random = " + random.getSeed() + + ", length = " + length + ", m = " + m); + Object convertedGolden = converter.convert(golden); + Object convertedTest = converter.convert(test); + sort(convertedTest); + compare(convertedTest, convertedGolden); + } + } + } + out.println(); + } + + private static void testAndCheckFloat(int length, MyRandom random) { + ourDescription = "Check float sorting"; + float[] golden = new float[length]; + final int MAX = 10; + boolean newLine = false; + + for (int a = 0; a <= MAX; a++) { + for (int g = 0; g <= MAX; g++) { + for (int z = 0; z <= MAX; z++) { + for (int n = 0; n <= MAX; n++) { + for (int p = 0; p <= MAX; p++) { + if (a + g + z + n + p > length) { + continue; + } + if (a + g + z + n + p < length) { + continue; + } + for (FloatBuilder builder : FloatBuilder.values()) { + out.println("Test 'float': random = " + random.getSeed() + + ", length = " + length + ", a = " + a + ", g = " + + g + ", z = " + z + ", n = " + n + ", p = " + p); + builder.build(golden, a, g, z, n, p, random); + float[] test = golden.clone(); + scramble(test, random); + sort(test); + compare(test, golden, a, n, g); + } + newLine = true; + } + } + } + } + } + if (newLine) { + out.println(); + } + } + + private static void testAndCheckDouble(int length, MyRandom random) { + ourDescription = "Check double sorting"; + double[] golden = new double[length]; + final int MAX = 10; + boolean newLine = false; + + for (int a = 0; a <= MAX; a++) { + for (int g = 0; g <= MAX; g++) { + for (int z = 0; z <= MAX; z++) { + for (int n = 0; n <= MAX; n++) { + for (int p = 0; p <= MAX; p++) { + if (a + g + z + n + p > length) { + continue; + } + if (a + g + z + n + p < length) { + continue; + } + for (DoubleBuilder builder : DoubleBuilder.values()) { + out.println("Test 'double': random = " + random.getSeed() + + ", length = " + length + ", a = " + a + ", g = " + + g + ", z = " + z + ", n = " + n + ", p = " + p); + builder.build(golden, a, g, z, n, p, random); + double[] test = golden.clone(); + scramble(test, random); + sort(test); + compare(test, golden, a, n, g); + } + newLine = true; + } + } + } + } + } + if (newLine) { + out.println(); + } + } + + private static void prepareSubArray(int[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + a[i] = 0xDEDA; + } + int middle = (fromIndex + toIndex) >>> 1; + int k = 0; + + for (int i = fromIndex; i < middle; i++) { + a[i] = k++; + } + for (int i = middle; i < toIndex; i++) { + a[i] = k--; + } + for (int i = toIndex; i < a.length; i++) { + a[i] = 0xBABA; + } + } + + private static void scramble(int[] a, Random random) { + for (int i = 0; i < a.length * 7; i++) { + swap(a, random.nextInt(a.length), random.nextInt(a.length)); + } + } + + private static void scramble(float[] a, Random random) { + for (int i = 0; i < a.length * 7; i++) { + swap(a, random.nextInt(a.length), random.nextInt(a.length)); + } + } + + private static void scramble(double[] a, Random random) { + for (int i = 0; i < a.length * 7; i++) { + swap(a, random.nextInt(a.length), random.nextInt(a.length)); + } + } + + private static void swap(int[] a, int i, int j) { + int t = a[i]; + a[i] = a[j]; + a[j] = t; + } + + private static void swap(float[] a, int i, int j) { + float t = a[i]; + a[i] = a[j]; + a[j] = t; + } + + private static void swap(double[] a, int i, int j) { + double t = a[i]; + a[i] = a[j]; + a[j] = t; + } + + private static enum TypeConverter { + INT { + Object convert(int[] a) { + return a.clone(); + } + }, + LONG { + Object convert(int[] a) { + long[] b = new long[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (long) a[i]; + } + return b; + } + }, + BYTE { + Object convert(int[] a) { + byte[] b = new byte[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (byte) a[i]; + } + return b; + } + }, + SHORT { + Object convert(int[] a) { + short[] b = new short[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (short) a[i]; + } + return b; + } + }, + CHAR { + Object convert(int[] a) { + char[] b = new char[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (char) a[i]; + } + return b; + } + }, + FLOAT { + Object convert(int[] a) { + float[] b = new float[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (float) a[i]; + } + return b; + } + }, + DOUBLE { + Object convert(int[] a) { + double[] b = new double[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = (double) a[i]; + } + return b; + } + }, + INTEGER { + Object convert(int[] a) { + Integer[] b = new Integer[a.length]; + + for (int i = 0; i < a.length; i++) { + b[i] = new Integer(a[i]); + } + return b; + } + }; + + abstract Object convert(int[] a); + + @Override public String toString() { + String name = name(); + + for (int i = name.length(); i < 9; i++) { + name += " "; + } + return name; + } + } + + private static enum FloatBuilder { + SIMPLE { + void build(float[] x, int a, int g, int z, int n, int p, Random random) { + int fromIndex = 0; + float negativeValue = -random.nextFloat(); + float positiveValue = random.nextFloat(); + + writeValue(x, negativeValue, fromIndex, n); + fromIndex += n; + + writeValue(x, -0.0f, fromIndex, g); + fromIndex += g; + + writeValue(x, 0.0f, fromIndex, z); + fromIndex += z; + + writeValue(x, positiveValue, fromIndex, p); + fromIndex += p; + + writeValue(x, Float.NaN, fromIndex, a); + } + }; + + abstract void build(float[] x, int a, int g, int z, int n, int p, Random random); + } + + private static enum DoubleBuilder { + SIMPLE { + void build(double[] x, int a, int g, int z, int n, int p, Random random) { + int fromIndex = 0; + double negativeValue = -random.nextFloat(); + double positiveValue = random.nextFloat(); + + writeValue(x, negativeValue, fromIndex, n); + fromIndex += n; + + writeValue(x, -0.0d, fromIndex, g); + fromIndex += g; + + writeValue(x, 0.0d, fromIndex, z); + fromIndex += z; + + writeValue(x, positiveValue, fromIndex, p); + fromIndex += p; + + writeValue(x, Double.NaN, fromIndex, a); + } + }; + + abstract void build(double[] x, int a, int g, int z, int n, int p, Random random); + } + + private static void writeValue(float[] a, float value, int fromIndex, int count) { + for (int i = fromIndex; i < fromIndex + count; i++) { + a[i] = value; + } + } + + private static void compare(float[] a, float[] b, int numNaN, int numNeg, int numNegZero) { + for (int i = a.length - numNaN; i < a.length; i++) { + if (a[i] == a[i]) { + failed("On position " + i + " must be NaN instead of " + a[i]); + } + } + final int NEGATIVE_ZERO = Float.floatToIntBits(-0.0f); + + for (int i = numNeg; i < numNeg + numNegZero; i++) { + if (NEGATIVE_ZERO != Float.floatToIntBits(a[i])) { + failed("On position " + i + " must be -0.0 instead of " + a[i]); + } + } + for (int i = 0; i < a.length - numNaN; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void writeValue(double[] a, double value, int fromIndex, int count) { + for (int i = fromIndex; i < fromIndex + count; i++) { + a[i] = value; + } + } + + private static void compare(double[] a, double[] b, int numNaN, int numNeg, int numNegZero) { + for (int i = a.length - numNaN; i < a.length; i++) { + if (a[i] == a[i]) { + failed("On position " + i + " must be NaN instead of " + a[i]); + } + } + final long NEGATIVE_ZERO = Double.doubleToLongBits(-0.0d); + + for (int i = numNeg; i < numNeg + numNegZero; i++) { + if (NEGATIVE_ZERO != Double.doubleToLongBits(a[i])) { + failed("On position " + i + " must be -0.0 instead of " + a[i]); + } + } + for (int i = 0; i < a.length - numNaN; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static enum SortedBuilder { + REPEATED { + void build(int[] a, int m) { + int period = a.length / m; + int i = 0; + int k = 0; + + while (true) { + for (int t = 1; t <= period; t++) { + if (i >= a.length) { + return; + } + a[i++] = k; + } + if (i >= a.length) { + return; + } + k++; + } + } + }, + ORGAN_PIPES { + void build(int[] a, int m) { + int i = 0; + int k = m; + + while (true) { + for (int t = 1; t <= m; t++) { + if (i >= a.length) { + return; + } + a[i++] = k; + } + } + } + }; + + abstract void build(int[] a, int m); + + @Override public String toString() { + String name = name(); + + for (int i = name.length(); i < 12; i++) { + name += " "; + } + return name; + } + } + + private static enum MergeBuilder { + ASCENDING { + void build(int[] a, int m) { + int period = a.length / m; + int v = 1, i = 0; + + for (int k = 0; k < m; k++) { + v = 1; + for (int p = 0; p < period; p++) { + a[i++] = v++; + } + } + for (int j = i; j < a.length - 1; j++) { + a[j] = v++; + } + a[a.length - 1] = 0; + } + }, + DESCENDING { + void build(int[] a, int m) { + int period = a.length / m; + int v = -1, i = 0; + + for (int k = 0; k < m; k++) { + v = -1; + for (int p = 0; p < period; p++) { + a[i++] = v--; + } + } + for (int j = i; j < a.length - 1; j++) { + a[j] = v--; + } + a[a.length - 1] = 0; + } + }; + + abstract void build(int[] a, int m); + + @Override public String toString() { + String name = name(); + + for (int i = name.length(); i < 12; i++) { + name += " "; + } + return name; + } + } + + private static enum UnsortedBuilder { + RANDOM { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = random.nextInt(); + } + } + }, + ASCENDING { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = m + i; + } + } + }, + DESCENDING { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = a.length - m - i; + } + } + }, + ALL_EQUAL { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = m; + } + } + }, + SAW { + void build(int[] a, int m, Random random) { + int incCount = 1; + int decCount = a.length; + int i = 0; + int period = m--; + + while (true) { + for (int k = 1; k <= period; k++) { + if (i >= a.length) { + return; + } + a[i++] = incCount++; + } + period += m; + + for (int k = 1; k <= period; k++) { + if (i >= a.length) { + return; + } + a[i++] = decCount--; + } + period += m; + } + } + }, + REPEATED { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = i % m; + } + } + }, + DUPLICATED { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = random.nextInt(m); + } + } + }, + ORGAN_PIPES { + void build(int[] a, int m, Random random) { + int middle = a.length / (m + 1); + + for (int i = 0; i < middle; i++) { + a[i] = i; + } + for (int i = middle; i < a.length; i++) { + a[i] = a.length - i - 1; + } + } + }, + STAGGER { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = (i * m + i) % a.length; + } + } + }, + PLATEAU { + void build(int[] a, int m, Random random) { + for (int i = 0; i < a.length; i++) { + a[i] = Math.min(i, m); + } + } + }, + SHUFFLE { + void build(int[] a, int m, Random random) { + int x = 0, y = 0; + for (int i = 0; i < a.length; i++) { + a[i] = random.nextBoolean() ? (x += 2) : (y += 2); + } + } + }; + + abstract void build(int[] a, int m, Random random); + + @Override public String toString() { + String name = name(); + + for (int i = name.length(); i < 12; i++) { + name += " "; + } + return name; + } + } + + private static void checkWithCheckSum(Object test, Object golden) { + checkSorted(test); + checkCheckSum(test, golden); + } + + private static void failed(String message) { + err.format("\n*** TEST FAILED - %s.\n\n%s.\n\n", ourDescription, message); + throw new RuntimeException("Test failed - see log file for details"); + } + + private static void failedSort(int index, String value1, String value2) { + failed("Array is not sorted at " + index + "-th position: " + + value1 + " and " + value2); + } + + private static void failedCompare(int index, String value1, String value2) { + failed("On position " + index + " must be " + value2 + " instead of " + value1); + } + + private static void compare(Object test, Object golden) { + if (test instanceof int[]) { + compare((int[]) test, (int[]) golden); + } else if (test instanceof long[]) { + compare((long[]) test, (long[]) golden); + } else if (test instanceof short[]) { + compare((short[]) test, (short[]) golden); + } else if (test instanceof byte[]) { + compare((byte[]) test, (byte[]) golden); + } else if (test instanceof char[]) { + compare((char[]) test, (char[]) golden); + } else if (test instanceof float[]) { + compare((float[]) test, (float[]) golden); + } else if (test instanceof double[]) { + compare((double[]) test, (double[]) golden); + } else if (test instanceof Integer[]) { + compare((Integer[]) test, (Integer[]) golden); + } else { + failed("Unknow type of array: " + test + " of class " + + test.getClass().getName()); + } + } + + private static void compare(int[] a, int[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(long[] a, long[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(short[] a, short[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(byte[] a, byte[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(char[] a, char[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(float[] a, float[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(double[] a, double[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void compare(Integer[] a, Integer[] b) { + for (int i = 0; i < a.length; i++) { + if (a[i].compareTo(b[i]) != 0) { + failedCompare(i, "" + a[i], "" + b[i]); + } + } + } + + private static void checkSorted(Object object) { + if (object instanceof int[]) { + checkSorted((int[]) object); + } else if (object instanceof long[]) { + checkSorted((long[]) object); + } else if (object instanceof short[]) { + checkSorted((short[]) object); + } else if (object instanceof byte[]) { + checkSorted((byte[]) object); + } else if (object instanceof char[]) { + checkSorted((char[]) object); + } else if (object instanceof float[]) { + checkSorted((float[]) object); + } else if (object instanceof double[]) { + checkSorted((double[]) object); + } else if (object instanceof Integer[]) { + checkSorted((Integer[]) object); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void checkSorted(int[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(long[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(short[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(byte[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(char[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(float[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(double[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkSorted(Integer[] a) { + for (int i = 0; i < a.length - 1; i++) { + if (a[i].intValue() > a[i + 1].intValue()) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + } + + private static void checkCheckSum(Object test, Object golden) { + if (checkSumXor(test) != checkSumXor(golden)) { + failed("Original and sorted arrays are not identical [xor]"); + } + if (checkSumPlus(test) != checkSumPlus(golden)) { + failed("Original and sorted arrays are not identical [plus]"); + } + } + + private static int checkSumXor(Object object) { + if (object instanceof int[]) { + return checkSumXor((int[]) object); + } else if (object instanceof long[]) { + return checkSumXor((long[]) object); + } else if (object instanceof short[]) { + return checkSumXor((short[]) object); + } else if (object instanceof byte[]) { + return checkSumXor((byte[]) object); + } else if (object instanceof char[]) { + return checkSumXor((char[]) object); + } else if (object instanceof float[]) { + return checkSumXor((float[]) object); + } else if (object instanceof double[]) { + return checkSumXor((double[]) object); + } else if (object instanceof Integer[]) { + return checkSumXor((Integer[]) object); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + return -1; + } + } + + private static int checkSumXor(Integer[] a) { + int checkSum = 0; + + for (Integer e : a) { + checkSum ^= e.intValue(); + } + return checkSum; + } + + private static int checkSumXor(int[] a) { + int checkSum = 0; + + for (int e : a) { + checkSum ^= e; + } + return checkSum; + } + + private static int checkSumXor(long[] a) { + long checkSum = 0; + + for (long e : a) { + checkSum ^= e; + } + return (int) checkSum; + } + + private static int checkSumXor(short[] a) { + short checkSum = 0; + + for (short e : a) { + checkSum ^= e; + } + return (int) checkSum; + } + + private static int checkSumXor(byte[] a) { + byte checkSum = 0; + + for (byte e : a) { + checkSum ^= e; + } + return (int) checkSum; + } + + private static int checkSumXor(char[] a) { + char checkSum = 0; + + for (char e : a) { + checkSum ^= e; + } + return (int) checkSum; + } + + private static int checkSumXor(float[] a) { + int checkSum = 0; + + for (float e : a) { + checkSum ^= (int) e; + } + return checkSum; + } + + private static int checkSumXor(double[] a) { + int checkSum = 0; + + for (double e : a) { + checkSum ^= (int) e; + } + return checkSum; + } + + private static int checkSumPlus(Object object) { + if (object instanceof int[]) { + return checkSumPlus((int[]) object); + } else if (object instanceof long[]) { + return checkSumPlus((long[]) object); + } else if (object instanceof short[]) { + return checkSumPlus((short[]) object); + } else if (object instanceof byte[]) { + return checkSumPlus((byte[]) object); + } else if (object instanceof char[]) { + return checkSumPlus((char[]) object); + } else if (object instanceof float[]) { + return checkSumPlus((float[]) object); + } else if (object instanceof double[]) { + return checkSumPlus((double[]) object); + } else if (object instanceof Integer[]) { + return checkSumPlus((Integer[]) object); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + return -1; + } + } + + private static int checkSumPlus(int[] a) { + int checkSum = 0; + + for (int e : a) { + checkSum += e; + } + return checkSum; + } + + private static int checkSumPlus(long[] a) { + long checkSum = 0; + + for (long e : a) { + checkSum += e; + } + return (int) checkSum; + } + + private static int checkSumPlus(short[] a) { + short checkSum = 0; + + for (short e : a) { + checkSum += e; + } + return (int) checkSum; + } + + private static int checkSumPlus(byte[] a) { + byte checkSum = 0; + + for (byte e : a) { + checkSum += e; + } + return (int) checkSum; + } + + private static int checkSumPlus(char[] a) { + char checkSum = 0; + + for (char e : a) { + checkSum += e; + } + return (int) checkSum; + } + + private static int checkSumPlus(float[] a) { + int checkSum = 0; + + for (float e : a) { + checkSum += (int) e; + } + return checkSum; + } + + private static int checkSumPlus(double[] a) { + int checkSum = 0; + + for (double e : a) { + checkSum += (int) e; + } + return checkSum; + } + + private static int checkSumPlus(Integer[] a) { + int checkSum = 0; + + for (Integer e : a) { + checkSum += e.intValue(); + } + return checkSum; + } + + private static void sortByInsertionSort(Object object) { + if (object instanceof int[]) { + sortByInsertionSort((int[]) object); + } else if (object instanceof long[]) { + sortByInsertionSort((long[]) object); + } else if (object instanceof short[]) { + sortByInsertionSort((short[]) object); + } else if (object instanceof byte[]) { + sortByInsertionSort((byte[]) object); + } else if (object instanceof char[]) { + sortByInsertionSort((char[]) object); + } else if (object instanceof float[]) { + sortByInsertionSort((float[]) object); + } else if (object instanceof double[]) { + sortByInsertionSort((double[]) object); + } else if (object instanceof Integer[]) { + sortByInsertionSort((Integer[]) object); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void sortByInsertionSort(int[] a) { + for (int j, i = 1; i < a.length; i++) { + int ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(long[] a) { + for (int j, i = 1; i < a.length; i++) { + long ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(short[] a) { + for (int j, i = 1; i < a.length; i++) { + short ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(byte[] a) { + for (int j, i = 1; i < a.length; i++) { + byte ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(char[] a) { + for (int j, i = 1; i < a.length; i++) { + char ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(float[] a) { + for (int j, i = 1; i < a.length; i++) { + float ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(double[] a) { + for (int j, i = 1; i < a.length; i++) { + double ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sortByInsertionSort(Integer[] a) { + for (int j, i = 1; i < a.length; i++) { + Integer ai = a[i]; + for (j = i - 1; j >= 0 && ai < a[j]; j--) { + a[j + 1] = a[j]; + } + a[j + 1] = ai; + } + } + + private static void sort(Object object) { + if (object instanceof int[]) { + Arrays.parallelSort((int[]) object); + } else if (object instanceof long[]) { + Arrays.parallelSort((long[]) object); + } else if (object instanceof short[]) { + Arrays.parallelSort((short[]) object); + } else if (object instanceof byte[]) { + Arrays.parallelSort((byte[]) object); + } else if (object instanceof char[]) { + Arrays.parallelSort((char[]) object); + } else if (object instanceof float[]) { + Arrays.parallelSort((float[]) object); + } else if (object instanceof double[]) { + Arrays.parallelSort((double[]) object); + } else if (object instanceof Integer[]) { + Arrays.parallelSort((Integer[]) object); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void sortSubArray(Object object, int fromIndex, int toIndex) { + if (object instanceof int[]) { + Arrays.parallelSort((int[]) object, fromIndex, toIndex); + } else if (object instanceof long[]) { + Arrays.parallelSort((long[]) object, fromIndex, toIndex); + } else if (object instanceof short[]) { + Arrays.parallelSort((short[]) object, fromIndex, toIndex); + } else if (object instanceof byte[]) { + Arrays.parallelSort((byte[]) object, fromIndex, toIndex); + } else if (object instanceof char[]) { + Arrays.parallelSort((char[]) object, fromIndex, toIndex); + } else if (object instanceof float[]) { + Arrays.parallelSort((float[]) object, fromIndex, toIndex); + } else if (object instanceof double[]) { + Arrays.parallelSort((double[]) object, fromIndex, toIndex); + } else if (object instanceof Integer[]) { + Arrays.parallelSort((Integer[]) object, fromIndex, toIndex); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void checkSubArray(Object object, int fromIndex, int toIndex, int m) { + if (object instanceof int[]) { + checkSubArray((int[]) object, fromIndex, toIndex, m); + } else if (object instanceof long[]) { + checkSubArray((long[]) object, fromIndex, toIndex, m); + } else if (object instanceof short[]) { + checkSubArray((short[]) object, fromIndex, toIndex, m); + } else if (object instanceof byte[]) { + checkSubArray((byte[]) object, fromIndex, toIndex, m); + } else if (object instanceof char[]) { + checkSubArray((char[]) object, fromIndex, toIndex, m); + } else if (object instanceof float[]) { + checkSubArray((float[]) object, fromIndex, toIndex, m); + } else if (object instanceof double[]) { + checkSubArray((double[]) object, fromIndex, toIndex, m); + } else if (object instanceof Integer[]) { + checkSubArray((Integer[]) object, fromIndex, toIndex, m); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void checkSubArray(Integer[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i].intValue() != 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i].intValue() > a[i + 1].intValue()) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i].intValue() != 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(int[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(byte[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (byte) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (byte) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(long[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (long) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (long) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(char[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (char) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (char) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(short[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (short) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (short) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(float[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (float) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (float) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkSubArray(double[] a, int fromIndex, int toIndex, int m) { + for (int i = 0; i < fromIndex; i++) { + if (a[i] != (double) 0xDEDA) { + failed("Range sort changes left element on position " + i + + ": " + a[i] + ", must be " + 0xDEDA); + } + } + + for (int i = fromIndex; i < toIndex - 1; i++) { + if (a[i] > a[i + 1]) { + failedSort(i, "" + a[i], "" + a[i + 1]); + } + } + + for (int i = toIndex; i < a.length; i++) { + if (a[i] != (double) 0xBABA) { + failed("Range sort changes right element on position " + i + + ": " + a[i] + ", must be " + 0xBABA); + } + } + } + + private static void checkRange(Object object, int m) { + if (object instanceof int[]) { + checkRange((int[]) object, m); + } else if (object instanceof long[]) { + checkRange((long[]) object, m); + } else if (object instanceof short[]) { + checkRange((short[]) object, m); + } else if (object instanceof byte[]) { + checkRange((byte[]) object, m); + } else if (object instanceof char[]) { + checkRange((char[]) object, m); + } else if (object instanceof float[]) { + checkRange((float[]) object, m); + } else if (object instanceof double[]) { + checkRange((double[]) object, m); + } else if (object instanceof Integer[]) { + checkRange((Integer[]) object, m); + } else { + failed("Unknow type of array: " + object + " of class " + + object.getClass().getName()); + } + } + + private static void checkRange(Integer[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(int[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(long[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(byte[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(short[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(char[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(float[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void checkRange(double[] a, int m) { + try { + Arrays.parallelSort(a, m + 1, m); + + failed("ParallelSort does not throw IllegalArgumentException " + + " as expected: fromIndex = " + (m + 1) + + " toIndex = " + m); + } + catch (IllegalArgumentException iae) { + try { + Arrays.parallelSort(a, -m, a.length); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: fromIndex = " + (-m)); + } + catch (ArrayIndexOutOfBoundsException aoe) { + try { + Arrays.parallelSort(a, 0, a.length + m); + + failed("ParallelSort does not throw ArrayIndexOutOfBoundsException " + + " as expected: toIndex = " + (a.length + m)); + } + catch (ArrayIndexOutOfBoundsException aie) { + return; + } + } + } + } + + private static void outArray(Object[] a) { + for (int i = 0; i < a.length; i++) { + out.print(a[i] + " "); + } + out.println(); + } + + private static void outArray(int[] a) { + for (int i = 0; i < a.length; i++) { + out.print(a[i] + " "); + } + out.println(); + } + + private static void outArray(float[] a) { + for (int i = 0; i < a.length; i++) { + out.print(a[i] + " "); + } + out.println(); + } + + private static void outArray(double[] a) { + for (int i = 0; i < a.length; i++) { + out.print(a[i] + " "); + } + out.println(); + } + + private static class MyRandom extends Random { + MyRandom(long seed) { + super(seed); + mySeed = seed; + } + + long getSeed() { + return mySeed; + } + + private long mySeed; + } + + private static String ourDescription; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/function/PredicateTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2012, 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. + */ + +/* + * @test + * @bug 8004561 + * @run testng PredicateTest + */ + +import org.testng.annotations.Test; + +import java.util.HashSet; +import java.util.function.Predicate; +import java.util.function.DoublePredicate; +import java.util.function.IntPredicate; +import java.util.function.LongPredicate; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertFalse; + +/** + * Unit tests for helper methods in Comparators + */ +@Test(groups = "unit") +public class PredicateTest { + + public void testIntPredicate() { + IntPredicate pOdd = (t) -> 0 != (t & 1); + IntPredicate pEven = (t) -> 0 == (t & 1); + + assertFalse(pOdd.test(0)); + assertFalse(pOdd.test(Integer.valueOf(0))); + assertFalse(pOdd.test(new Integer(0))); + assertFalse(pOdd.test(0)); + + assertTrue(pOdd.test(1)); + assertTrue(pOdd.test(Integer.valueOf(1))); + assertTrue(pOdd.test(new Integer(1))); + assertTrue(pOdd.test(1)); + + assertTrue(pEven.test(0)); + assertTrue(pEven.test(Integer.valueOf(0))); + assertTrue(pEven.test(new Integer(0))); + assertTrue(pEven.test(0)); + + assertFalse(pEven.test(1)); + assertFalse(pEven.test(Integer.valueOf(1))); + assertFalse(pEven.test(new Integer(1))); + assertFalse(pEven.test(1)); + + int magic[] = { 8675390 }; + IntPredicate pMagic = (t) -> t == magic[0]; + + assertFalse(pMagic.test(3927704 )); + assertFalse(pMagic.test(Integer.valueOf(3927704))); + assertFalse(pMagic.test(new Integer(3927704))); + assertFalse(pMagic.test(3927704)); + + assertTrue(pMagic.test(8675390)); + assertTrue(pMagic.test(Integer.valueOf(8675390))); + assertTrue(pMagic.test(new Integer(8675390))); + assertTrue(pMagic.test(8675390)); + + magic[0] = 3927704; + + assertTrue(pMagic.test(3927704)); + assertTrue(pMagic.test(Integer.valueOf(3927704))); + assertTrue(pMagic.test(new Integer(3927704))); + assertTrue(pMagic.test(3927704)); + + assertFalse(pMagic.test(8675390)); + assertFalse(pMagic.test(Integer.valueOf(8675390))); + assertFalse(pMagic.test(new Integer(8675390))); + assertFalse(pMagic.test(8675390)); + } + + public void testLongPredicate() { + LongPredicate pOdd = (t) -> 0 != (t & 1); + LongPredicate pEven = (t) -> 0 == (t & 1); + + assertFalse(pOdd.test(0L)); + assertFalse(pOdd.test(Long.valueOf(0L))); + assertFalse(pOdd.test(new Long(0L))); + assertFalse(pOdd.test(0L)); + + assertTrue(pOdd.test(1L)); + assertTrue(pOdd.test(Long.valueOf(1L))); + assertTrue(pOdd.test(new Long(1L))); + assertTrue(pOdd.test(1L)); + + assertTrue(pEven.test(0L)); + assertTrue(pEven.test(Long.valueOf(0L))); + assertTrue(pEven.test(new Long(0L))); + assertTrue(pEven.test(0L)); + + assertFalse(pEven.test(1L)); + assertFalse(pEven.test(Long.valueOf(1L))); + assertFalse(pEven.test(new Long(1L))); + assertFalse(pEven.test(1L)); + + long magic[] = { 8675390L }; + LongPredicate pMagic = (t) -> t == magic[0]; + + assertFalse(pMagic.test(3927704L)); + assertFalse(pMagic.test(Long.valueOf(3927704L))); + assertFalse(pMagic.test(new Long(3927704L))); + assertFalse(pMagic.test(3927704L)); + + assertTrue(pMagic.test(8675390L)); + assertTrue(pMagic.test(Long.valueOf(8675390L))); + assertTrue(pMagic.test(new Long(8675390L))); + assertTrue(pMagic.test(8675390L)); + + magic[0] = 3927704L; + + assertTrue(pMagic.test(3927704L)); + assertTrue(pMagic.test(Long.valueOf(3927704L))); + assertTrue(pMagic.test(new Long(3927704L))); + assertTrue(pMagic.test(3927704L)); + + assertFalse(pMagic.test(8675390L)); + assertFalse(pMagic.test(Long.valueOf(8675390L))); + assertFalse(pMagic.test(new Long(8675390L))); + assertFalse(pMagic.test(8675390L)); + } + + public void testDoublePredicate() { + DoublePredicate pInfinite = (t) -> Double.isInfinite(t); + DoublePredicate pFinite = (t) -> Double.isFinite(t); + + assertFalse(pInfinite.test(0.0)); + assertFalse(pInfinite.test(Double.valueOf(0.0))); + assertFalse(pInfinite.test(new Double(0.0))); + assertFalse(pInfinite.test(0.0)); + + assertTrue(pInfinite.test(Double.POSITIVE_INFINITY)); + assertTrue(pInfinite.test(Double.valueOf(Double.POSITIVE_INFINITY))); + assertTrue(pInfinite.test(new Double(Double.POSITIVE_INFINITY))); + assertTrue(pInfinite.test(Double.POSITIVE_INFINITY)); + + assertTrue(pFinite.test(0.0)); + assertTrue(pFinite.test(Double.valueOf(0.0))); + assertTrue(pFinite.test(new Double(0.0))); + assertTrue(pFinite.test(0.0)); + + assertFalse(pFinite.test(Double.POSITIVE_INFINITY)); + assertFalse(pFinite.test(Double.valueOf(Double.POSITIVE_INFINITY))); + assertFalse(pFinite.test(new Double(Double.POSITIVE_INFINITY))); + assertFalse(pFinite.test(Double.POSITIVE_INFINITY)); + + double magic[] = { 1.8675390 }; + DoublePredicate pMagic = (t) -> t == magic[0]; + + assertFalse(pMagic.test(1.3927704)); + assertFalse(pMagic.test(Double.valueOf(1.3927704))); + assertFalse(pMagic.test(new Double(1.3927704))); + assertFalse(pMagic.test(1.3927704)); + + assertTrue(pMagic.test(1.8675390)); + assertTrue(pMagic.test(Double.valueOf(1.8675390))); + assertTrue(pMagic.test(new Double(1.8675390))); + assertTrue(pMagic.test(1.8675390)); + + magic[0] = 1.3927704; + + assertTrue(pMagic.test(1.3927704)); + assertTrue(pMagic.test(Double.valueOf(1.3927704))); + assertTrue(pMagic.test(new Double(1.3927704))); + assertTrue(pMagic.test(1.3927704)); + + assertFalse(pMagic.test(1.8675390)); + assertFalse(pMagic.test(Double.valueOf(1.8675390))); + assertFalse(pMagic.test(new Double(1.8675390))); + assertFalse(pMagic.test(1.8675390)); + } + + @Test + public void testIsEqual() { + HashSet<Long> s1 = new HashSet<>(); + HashSet<Long> s2 = (HashSet<Long>) s1.clone(); + assertTrue(Predicate.isEqual(null).test(null)); + assertTrue(Predicate.isEqual(s2).test(s1)); + } + +}
--- a/test/java/util/regex/RegExTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/regex/RegExTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -40,6 +40,7 @@ import java.util.Random; import java.io.*; import java.util.*; +import java.util.function.Predicate; import java.nio.CharBuffer; import java.util.function.Predicate;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/Stream/BaseStreamTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,26 @@ +/* + * 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. + */ + +public class BaseStreamTest { + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/Stream/EmployeeStreamTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,307 @@ +/* + * 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. + */ + +/** + * @test + * @summary Basic test for Stream<Employee> + * @library /sqeutil + * @(#) EmployeeStreamTest.java + * @author Eric Wang/Tristan Yan + * @run testng/othervm -mx1g EmployeeStreamTest + */ + +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.ToIntFunction; +import org.testng.annotations.Factory; + +class Employee implements Comparable<Employee>, Cloneable { + static final int ID_LENGTH = 6; + static final int MIN_BIRTHDAY = 1900; + static final int MAX_BIRTHDAY = 2050; + static final int MIN_PAY = 1000; + static final int MAX_PAY = 100000; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getBirthday() { + return birthday; + } + + public boolean isMale() { + return male; + } + + public double getPay() { + return pay; + } + + public void setPay(double pay) { + this.pay = pay; + } + + public Title getTitle() { + return title; + } + + public void setTitle(Title title) { + this.title = title; + } + public Employee(String id, int birthday, boolean male, double pay, Title title) { + this.id = id; + this.birthday = birthday; + this.male = male; + this.pay = pay; + this.title = title; + } + + static enum Title { + SE, SSE, PSE, CSE, ARCHITECT, M1, M2, M3, M4 + } + + static enum Rule { + ID { + @Override + Function<Employee, String> empFunc() { + return Employee::getId; + } + @Override + String funcName(){ + return "Employee::getId"; + } + }, + BIRTHDAY { + @Override + Function<Employee, Integer> empFunc() { + return Employee::getBirthday; + } + @Override + String funcName(){ + return "Employee::getBirthday"; + } + }, + MALE { + @Override + Function<Employee, Boolean> empFunc() { + return Employee::isMale; + } + @Override + String funcName(){ + return "Employee::isMale"; + } + }, + PAY { + @Override + Function<Employee, Double> empFunc() { + return Employee::getPay; + } + @Override + String funcName(){ + return "Employee::getPay"; + } + }, + TITLE { + @Override + Function<Employee, Title> empFunc(){ + return Employee::getTitle; + } + + @Override + String funcName(){ + return "Employee::getTitle"; + } + }; + + Comparator<Employee> getComparator() { + return Comparator.comparing(empFunc()); + } + + Object getValue(Employee e) { return empFunc().apply(e); } + + abstract String funcName(); + + abstract Function<Employee, ? extends Comparable> empFunc(); + } + + private String id; + private final int birthday; + private final boolean male; + private double pay; + private Title title; + + @Override + public boolean equals(Object other) { + if (other instanceof Employee) { + Employee compared = (Employee) other; + return id.equals(compared.getId()) && birthday == compared.getBirthday() + && male == compared.isMale() && pay == compared.getPay() + && title == compared.getTitle(); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(this.id); + } + + @Override + public int compareTo(Employee t) { + return id.compareTo(t.getId()); + } + + @Override + public Employee clone() { + try { + return (Employee)super.clone(); + } catch (CloneNotSupportedException ex) { + throw new RuntimeException(ex); + } + } + + @Override + public String toString() { + return "id=" + getId() + ", birthday=" + getBirthday() + ". male=" + isMale() + + ", pay=" + getPay() + ", title=" + getTitle(); + } +} + +public class EmployeeStreamTest<T extends Collection<Employee>> extends + GenericStreamTest<Employee, Collection<Employee>> { + public EmployeeStreamTest(Class clazz, + ParallelType parallelType, int initSize) { + super(clazz, parallelType, Comparator.naturalOrder(), initSize); + } + + public EmployeeStreamTest(Class clazz, + ParallelType parallelType) { + super(clazz, parallelType, Comparator.naturalOrder()); + } + + @Factory + public static Object[] create(){ + TripleFunction<Class,ParallelType,Integer, EmployeeStreamTest> supplierWithInitSize = + EmployeeStreamTest<Collection<Employee>>::<Class,ParallelType,Integer>new; + BiFunction<Class,ParallelType,EmployeeStreamTest> supplier = + EmployeeStreamTest<Collection<Employee>>::<Class,ParallelType>new; + return GenericStreamTest.create(supplierWithInitSize, supplier, true); + } + + @Override + public String getTestName() { + return typeObject.getName() + "<Employee>"; + } + + @Override + protected Object[] predicateAndDesc(int size) { + Employee limit = createEmployee(); + boolean isUp = rand.nextBoolean(); + Employee.Rule rule = Arrays.stream(Employee.Rule.values()).findAny().get(); + return new Object[]{ LambdaUtilities.randomGenericPredicate(isUp, limit, rule.getComparator()), + String.format("emp -> Comparator.comparing(%s).compare(emp, %s) %s 0", rule.funcName(), isUp ? ">=" : "<", limit)}; + } + + //Function<U, ? extends Comparable>, descrption for Function<? extends Comparable> + @Override + protected Object[][] functionAndDesc(){ + return Arrays.stream(Employee.Rule.values()).map(rule -> new Object[]{rule.empFunc(), rule.funcName()}) + .toArray(Object[][]::new); + } + + //BinaryOperator, descrption, stateless + @Override + protected Object[][] binaryOperatorAndDesc() { + BinaryOperator<Employee> maxOp = (e1, e2) -> naturalOrderComparator.compare(e1, e2) < 0 ? e2 : e1; + BinaryOperator<Employee> minOp = (e1, e2) -> naturalOrderComparator.compare(e1, e2) > 0? e2 : e1; + return new Object[][]{ + {maxOp, "(e1, e2) -> naturalOrderComparator.compare(e1, e2) < 0 ? e2 : e1", true}, + {minOp, "(e1, e2) -> naturalOrderComparator.compare(e1, e2) > 0? e2 : e1", true}, + }; + } + + @Override + protected Employee[] bases(int size){ + return new Employee[]{createEmployee()}; + } + + @Override + protected Collection<Employee> generate(int size) { + Collection<Employee> col = createEmptyCollection(); + for (int i = 0; i < size; i++) { + col.add(createEmployee(String.valueOf(i + 1))); + } + return col; + } + + private Employee createEmployee(String id) { + BiFunction<Integer, Integer, Integer> RANDOM_INT_BETWEEN + = (start, end) -> start + rand.nextInt(end - start); + int birthday = RANDOM_INT_BETWEEN.apply(Employee.MIN_BIRTHDAY, Employee.MAX_BIRTHDAY); + Boolean gender = rand.nextBoolean(); + double pay = RANDOM_INT_BETWEEN.apply(Employee.MIN_PAY, Employee.MAX_PAY); + Employee.Title title = Employee.Title.values()[rand.nextInt(Employee.Title.values().length)]; + return new Employee(id, birthday, gender, pay, title); + } + + private Employee createEmployee() { + return this.createEmployee(StringUtilities.randomNumeric(Employee.ID_LENGTH)); + } + + @Override + protected Object[] validArrayGeneratorAndDesc() { + return new Object[]{ + new IntFunction[]{(IntFunction)Employee[]::new, (IntFunction)Object[]::new}, + "{Employee[]::new, Object[]::new}" + }; + } + + @Override + protected Object[][] invalidArrayGeneratorAndDesc() { + return new Object[][]{ + {(IntFunction)Employee.Rule[]::new, "Employee.Rule[]::new"}, + {(IntFunction)String[]::new, "String[]::new"}, + }; + } + + @Override + protected IntFunction<Employee> createFunction() { + return i -> createEmployee(String.valueOf(i)); + } + + @Override + protected ToIntFunction<Employee> intValueFunction() { + return e -> Integer.parseInt(e.getId()) ; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/Stream/GenericStreamTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,854 @@ +/* + * Copyright (c) 2012, 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.*; +import java.util.concurrent.*; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.function.ToIntFunction; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import org.testng.ITest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +public abstract class GenericStreamTest<U, V extends Collection<U>> implements ITest{ + protected enum ParallelType { Parallel, Sequential, Default } + + protected enum Selection { EMPTY, SINGLE, TRIANGLE, COPY_64} + + //Test data size + protected final static int VERY_SMALL_DATA_SIZE = 1; + + protected final static int SMALL_DATA_SIZE = 1 << 2; + + //MIN_PARTITION + protected final static int PARTITION_DATA_SIZE = 1 << 4; + + protected final static int MEDIUM_DATA_SIZE = 1 << 7; + + protected final static int SIXTY_FOUR = 64; + + protected final static int DEFAULT_INIT_SIZE = -1; + + protected final static Random rand = new Random(433494437); + + private final static Class[] defaultConstructorClazz = { + ArrayDeque.class, + ArrayList.class, + ConcurrentLinkedDeque.class, + ConcurrentLinkedQueue.class, + CopyOnWriteArrayList.class, + HashSet.class, + LinkedBlockingDeque.class, + LinkedBlockingQueue.class, + LinkedHashSet.class, + LinkedList.class, + LinkedTransferQueue.class, + Stack.class, + Vector.class + }; + + private final static Class[] capacityConstructorClazz = { + ArrayBlockingQueue.class, + ArrayDeque.class, + ArrayList.class, + HashSet.class, + LinkedBlockingDeque.class, + LinkedBlockingQueue.class, + LinkedHashSet.class, + Vector.class + }; + + private final static Class[] defaultComparableConstructorClazz = { + //Sorted collection only supports Comparable data type + PriorityBlockingQueue.class, + PriorityQueue.class, + ConcurrentSkipListSet.class, + TreeSet.class, + }; + + private final static Class[] capacityComparableConstructorClazz = { + //Sorted collection only supports Comparable data type + PriorityBlockingQueue.class, + PriorityQueue.class, + }; + + protected final Class<V> typeObject; + + private final int initSize; + + protected final ParallelType parallelType; + + protected final Comparator<U> naturalOrderComparator; + + protected Stream<U> streamFor(Collection<U> c1) { + return (parallelType == ParallelType.Parallel) ? + c1.parallelStream(): (parallelType == ParallelType.Sequential) + ? c1.parallelStream().sequential() : c1.stream(); + } + protected Collection<U> createEmptyCollection() { + try { + Collection<U> emptyCol = (initSize == DEFAULT_INIT_SIZE) ? LambdaUtilities.create(typeObject) : + LambdaUtilities.create(typeObject, initSize); + return emptyCol; + } catch ( ReflectiveOperationException roe) { + throw new RuntimeException(roe); + } + } + + @FunctionalInterface + public interface TripleFunction<L, M, N, I> { + public I apply(L l, M m, N n); + } + + public static Object[] create(TripleFunction<Class, ParallelType, Integer,? extends GenericStreamTest> + testCreatorWithInitSize, BiFunction<Class, ParallelType, ? extends GenericStreamTest> testCreator,boolean comparable) { + List<GenericStreamTest> result = new ArrayList<>(); + + for(final ParallelType parallelType : ParallelType.values()) { + Arrays.stream(defaultConstructorClazz).forEach( + clazz -> { result.add(testCreator.apply(clazz, parallelType)); } + ); + if(comparable) + Arrays.stream(defaultComparableConstructorClazz).forEach( + clazz -> result.add(testCreator.apply(clazz, parallelType))); + + Arrays.stream(capacityConstructorClazz).forEach( + clazz -> { result.add(testCreatorWithInitSize.apply(clazz, parallelType, MEDIUM_DATA_SIZE)); } + ); + if(comparable) + Arrays.stream(capacityComparableConstructorClazz).forEach( + clazz -> result.add(testCreatorWithInitSize.apply(clazz, parallelType, MEDIUM_DATA_SIZE))); + } + return result.toArray(); + } + + protected GenericStreamTest(Class<V> clazz, ParallelType parallelType, + Comparator<U> naturalOrderComparator, int initSize) { + this.typeObject = clazz; + this.parallelType = parallelType; + this.naturalOrderComparator = naturalOrderComparator; + this.initSize = initSize; + } + + protected GenericStreamTest(Class<V> clazz, ParallelType parallelType, + Comparator<U> naturalOrderComparator) { + this(clazz, parallelType, naturalOrderComparator, DEFAULT_INIT_SIZE); + } + + protected static <T> void assertEqualContents(Collection<T> result1, + Collection<T> result2, Comparator<T> comparator) { + assertEquals(result1.size(), result2.size()); + ArrayList<T> a = new ArrayList<>(result1); + ArrayList<T> b = new ArrayList<>(result2); + Collections.sort(a, comparator); + Collections.sort(b, comparator); + + for(int i = 0; i < result1.size(); i++) + assertEquals(comparator.compare(a.get(i), b.get(i)), 0); + } + + private U getMaxOrMinWithIterator(Collection<U> col, Comparator<U> c, boolean isMax) { + assert(!col.isEmpty()); + Iterator<U> it = col.iterator(); + U choosen = it.next(); + while(it.hasNext()) { + U next = it.next(); + if (!(c.compare(choosen, next) < 0 ^ isMax)) choosen = next; + } + return choosen; + } + + private U getMaxOrMinByCollectorBy(Collection<U> col, Comparator<U> c, boolean isMax) { + assert(!col.isEmpty()); + U choosen = isMax ? streamFor(col).collect(Collectors.<U>maxBy(c)).get() : + streamFor(col).collect(Collectors.<U>minBy(c)).get(); + return choosen; + } + + private U getMaxOrMinByReduce(Collection<U> col, Comparator<U> c, boolean isMax) { + assert (!col.isEmpty()); + Optional<U> choosen = col.stream().reduce(isMax ? + LambdaUtilities.maxGenericBinaryOperator(c) : + LambdaUtilities.minGenericBinaryOperator(c) ); + assert(choosen.isPresent()); + return choosen.get(); + } + + private U getMaxOrMinReduceWithBase(Collection<U> col, Comparator<U> c, boolean isMax) { + assert (!col.isEmpty()); + U any = col.iterator().next(); + U choosen = col.stream().reduce(any, + isMax ? LambdaUtilities.maxGenericFunction(c) : LambdaUtilities.minGenericFunction(c), + isMax ? LambdaUtilities.maxGenericBinaryOperator(c) : LambdaUtilities.minGenericBinaryOperator(c)); + return choosen; + } + + private U getMaxOrMinCollectWithReducing(Collection<U> col, Comparator<U> c, boolean isMax) { + assert (!col.isEmpty()); + U choosen = col.stream().collect(Collectors.<U>reducing(isMax ? + LambdaUtilities.maxGenericBinaryOperator(c) : + LambdaUtilities.minGenericBinaryOperator(c))).get(); + return choosen; + } + + private void verifySlice(Collection<U> col, Collection<U> subL, int skip, int limit, boolean ordered) { + int toIndex = skip + limit < col.size() ? skip + limit : col.size(); + if (ordered) { + assertEquals(new ArrayList(col).subList(skip, toIndex), subL); + } else { + // Unordered: verify that all items from the slice are + // present in the original collection + assertTrue(col.containsAll(subL)); + assertEquals(subL.size(), toIndex - skip); + } + } + + private Function<U, Stream<U>> genFlatMapper(Selection selection) { + switch(selection) { + case EMPTY: return e -> Stream.empty(); + case SINGLE: return e -> Collections.singletonList(e).stream(); + case TRIANGLE: + return e -> triangleStream(e); + case COPY_64: + default: + return e -> Stream.generate(() -> e).limit(SIXTY_FOUR); + } + } + private void verifyFlatMap(Collection<U> orig, Collection<U> result, Selection selection) { + switch (selection) { + case EMPTY: + assertEquals(result.size(), 0); + break; + case SINGLE: + assertEqualContents(new ArrayList<>(orig), result, naturalOrderComparator); + break; + case TRIANGLE: + List<U> list2 = new ArrayList<>(); + orig.stream().forEach((current) -> { + triangleStream(current).forEach(u -> list2.add(u)); + }); + assertEqualContents(list2, result, naturalOrderComparator); + break; + case COPY_64: + default: + List<U> list3 = new ArrayList<>(); + orig.stream().forEach((current) -> { + IntStream.range(0, SIXTY_FOUR).forEach(i -> list3.add(current)); + }); + assertEquals(list3, result); + break; + } + } + + private boolean verifyMatch(Collection<U> col, Predicate<U> predicate, boolean checkAll) { + for(U current : col) { + if (checkAll ^ predicate.test(current) ) + return !checkAll; + } + return checkAll; + } + + private void verifyAfterMap(Collection<U> origCol, Object[] result, Function<U, ? extends Object> mapFunc) { + int index = 0; + assertEquals(origCol.size(), result.length); + for(U element : origCol) + assertEquals(mapFunc.apply(element), result[index++]); + } + + protected abstract Collection<U> generate(int size); + + //Predicate<U>, descrption for Predicate<U> + protected abstract Object[] predicateAndDesc(int size); + + //BinaryOperator<U>, descrption for BinaryOperator<U>, stateless + protected abstract Object[][] binaryOperatorAndDesc(); + + //Function<U, ? extends Comparable>, descrption for Function<? extends Comparable> + protected abstract Object[][] functionAndDesc(); + + //IntFunction<W>[], descrption for all IntFunction<W> + protected abstract Object[] validArrayGeneratorAndDesc(); + + //IntFunction<W>[], descrption for IntFunction<W> + protected abstract Object[][] invalidArrayGeneratorAndDesc(); + + //IntFunction<U> + protected abstract IntFunction<U> createFunction(); + + //ToIntFunction<U> + protected abstract ToIntFunction<U> intValueFunction(); + + //base object based on size + protected abstract U[] bases(int size); + + protected Object[] builders() { + return new Object[]{ + generate(MEDIUM_DATA_SIZE), + generate(SMALL_DATA_SIZE), + generate(PARTITION_DATA_SIZE), + generate(VERY_SMALL_DATA_SIZE) + }; + } + + protected Object[] smallSizeBuilders() { + return new Object[]{ + generate(SMALL_DATA_SIZE), + generate(PARTITION_DATA_SIZE), + generate(VERY_SMALL_DATA_SIZE) + }; + } + + //Test only needs Collection<U> + @DataProvider + protected Iterator<Object[]> collectionData() { + return Arrays.stream(builders()).map(data -> + new Object[]{String.format("%s(%d)", typeObject.getName(), ((Collection<U>)data).size()),data}).iterator(); + } + + //Test only needs Collection<U> + @DataProvider + protected Iterator<Object[]> collectionStartEndData() { + List<Object[]> dataList= new ArrayList<>(); + long[][] exceptionStartEnd = new long[][]{ + {-1, 1}, //startInclusive is negative + {1, -1}, //endInclusive is negative + {1, 0} //startInclusive is greater than endExclusive + }; + Arrays.stream(builders()).forEach(data -> { + Collection<U> col = (Collection<U>)data; + Arrays.stream(exceptionStartEnd).forEach(startEnd -> { + dataList.add(new Object[]{String.format("%s(%d), %d, %d", typeObject.getName(), col.size(), startEnd[0], + startEnd[1]), col, startEnd[0], startEnd[1]}); + }); + }); + return dataList.iterator(); + } + + //Test needs Collection<U> and one Predicate + @DataProvider + protected Iterator<Object[]> collectionPredicateData() { + List<Object[]> dataList= new ArrayList<>(); + Arrays.stream(builders()).forEach(data -> { + Collection<U> col = (Collection<U>)data; + Object[] predicateAndDesc = predicateAndDesc(col.size()); + Predicate<U> p = (Predicate<U>)predicateAndDesc[0]; + String desc = String.format("%s(%d), %s", typeObject.getName(), col.size(), (String)predicateAndDesc[1]); + dataList.add(new Object[]{desc, data, p}); + }); + return dataList.iterator(); + } + + //Test needs Collection<U> and two Predicate + @DataProvider + protected Iterator<Object[]> collectionDualPredicateData() { + List<Object[]> dataList = new ArrayList<>(); + Arrays.stream(builders()).forEach(data -> { + Collection<U> col = (Collection<U>)data; + Object[] predicateAndDesc1 = predicateAndDesc(col.size()); + Object[] predicateAndDesc2 = predicateAndDesc(col.size()); + Predicate<U> p1 = (Predicate<U>)predicateAndDesc1[0]; + Predicate<U> p2 = (Predicate<U>)predicateAndDesc2[0]; + String desc = String.format("%s(%d), %s, %s", typeObject.getName(), col.size(), + (String)predicateAndDesc1[1], (String)predicateAndDesc2[1]); + dataList.add(new Object[]{desc, col, p1, p2}); + }); + return dataList.iterator(); + } + + //Test needs Collection<U>, U base and and BinaryOperator<U>, stateless + @DataProvider + protected Iterator<Object[]> collectionBinaryOpeatorData(){ + List<Object[]> dataList = new ArrayList<>(); + Arrays.stream(builders()).forEach(data -> { + Collection<U> col = (Collection<U>)data; + Arrays.stream(binaryOperatorAndDesc()).forEach((Object[] bpDesc) -> { + BinaryOperator<U> bo = (BinaryOperator<U>)bpDesc[0]; + String boDesc = (String)bpDesc[1]; + boolean stateless = (Boolean)bpDesc[2]; + Arrays.stream(bases(col.size())).forEach(base -> { + dataList.add(new Object[]{String.format("%s(%d), %s, %s, %s", typeObject.getName(), col.size(), base, + boDesc, stateless), col, base, bo, stateless}); + }); + }); + }); + return dataList.iterator(); + } + + //Test needs Collection<U>, Function<U, ? extends Object> + @DataProvider + protected Iterator<Object[]> collectionFunctionData(){ + List<Object[]> dataList = new ArrayList<>(); + Arrays.stream(builders()).forEach(data -> { + Collection<Integer> col = (Collection<Integer>)data; + Arrays.stream(functionAndDesc()).forEach((Object[] funcDesc) -> { + Function<U, Object> func = (Function<U, Object>)funcDesc[0]; + String desc = (String)funcDesc[1]; + dataList.add(new Object[]{String.format("%s(%d), %s", typeObject.getName(), col.size(), desc), + col, func}); + }); + }); + return dataList.iterator(); + } + + //Test needs Collection<U>, Function<U, K> keyFunc, Function<U, V> valueFunc + //toMap need keep key's identical, using Function.identity() + @DataProvider + protected Iterator<Object[]> collectionKeyValueFunctionData(){ + List<Object[]> dataList = new ArrayList<>(); + Arrays.stream(builders()).forEach(data -> { + Collection<Integer> col = (Collection<Integer>)data; + Arrays.stream(functionAndDesc()).forEach((Object[] valueFuncDesc) -> { + Function<U, Object> valueFunc = (Function<U, Object>)valueFuncDesc[0]; + String valuedesc = (String)valueFuncDesc[1]; + dataList.add(new Object[]{String.format("%s(%d), %s, %s", typeObject.getName(), col.size(), + "Function.identity()", valuedesc), col, Function.identity(), valueFunc}); + }); + }); + return dataList.iterator(); + } + + //testToArray needs Collection<U>, IntFunction<W>[] + @DataProvider + protected Iterator<Object[]> collectionArrayGeneratorData(){ + List<Object[]> dataList = new ArrayList<>(); + Arrays.stream(builders()).forEach(data -> { + Collection<Integer> col = (Collection<Integer>)data; + Object[] validArrayGenerators = validArrayGeneratorAndDesc(); + IntFunction[] generators = (IntFunction[])validArrayGenerators[0]; + String desc = (String)validArrayGenerators[1]; + dataList.add(new Object[]{String.format("%s(%d), %s", typeObject.getName(), col.size(), desc), col, generators}); + }); + return dataList.iterator(); + } + + //testToArrayException needs Collection<U>, IntFunction<W>, + //This for test toArray's ArrayStoreException + @DataProvider + protected Iterator<Object[]> collectionArrayInvalidGeneratorData(){ + List<Object[]> dataList = new ArrayList<>(); + Arrays.stream(builders()).forEach(data -> { + Collection<Integer> col = (Collection<Integer>)data; + Arrays.stream(invalidArrayGeneratorAndDesc()).forEach(invalidArrayGenerators -> { + IntFunction generator = (IntFunction)invalidArrayGenerators[0]; + String desc = (String)invalidArrayGenerators[1]; + dataList.add(new Object[]{String.format("%s(%d), %s", typeObject.getName(), col.size(), desc), col, generator}); + }); + }); + return dataList.iterator(); + } + + //Test needs Collection<U>, Collection<U> + @DataProvider + protected Iterator<Object[]> concatCollectionData(){ + List<Object[]> dataList = new ArrayList<>(); + Arrays.stream(builders()).forEach(data1 -> + Arrays.stream(builders()).forEach(data2 -> { + String desc = String.format("%s(%d), %s(%d)", typeObject.getName(), ((Collection<U>)data1).size(), + typeObject.getName(), ((Collection<U>)data2).size()); + dataList.add(new Object[]{desc, data1, data2}); } + ) + ); + return dataList.iterator(); + } + + @DataProvider + protected Iterator<Object[]> exceptionTestData() throws Exception { + Collection<U> col = generate(SMALL_DATA_SIZE); + return Arrays.stream(new Object[][]{ + {String.format("%s(%d)", typeObject.getName(), col.size()), col}, + }).iterator(); + } + + @DataProvider + protected Iterator<Object[]> smallSizeCollectionData() { + List<Object[]> dataList = new ArrayList<>(); + Arrays.stream(smallSizeBuilders()).forEach(data -> { + Collection<U> col = (Collection<U>)data; + dataList.add(new Object[]{String.format("%s(%d)", typeObject.getName(), col.size()), col}); + }); + return dataList.iterator(); + } + + @DataProvider + protected Iterator<Object[]> collectionDataWithLimit() { + List<Object[]> dataList = new ArrayList<>(); + Arrays.stream(builders()).forEach(dataCol -> { + Collection<U> col = (Collection<U>)dataCol; + int withBoundryLimit = rand.nextInt(col.size()); + int beyondBoundryLimit = col.size() + rand.nextInt(Integer.MAX_VALUE - col.size()); + dataList.add(new Object[]{String.format("%s(%d), %s", typeObject.getName(), col.size(), withBoundryLimit, + beyondBoundryLimit), col, withBoundryLimit, beyondBoundryLimit}); + }); + return dataList.iterator(); + } + + @DataProvider + protected Iterator<Object[]> collectionDataWithSelection() { + List<Object[]> dataList = new ArrayList<>(); + Arrays.stream(smallSizeBuilders()).forEach(data -> { + Collection<U> col = (Collection<U>)data; + Arrays.stream(Selection.values()).forEach(selection -> { + dataList.add(new Object[]{String.format("%s(%d), %s", typeObject.getName(), col.size(), selection), + col, selection}); + }); + }); + return dataList.iterator(); + } + + @Test(dataProvider="collectionFunctionData") + public void testCollectByGroupingBy(String desc, Collection<U> col, Function<U, ? extends Object> mapFunc) { + Map<Object, List<U>> result = streamFor(col). collect(Collectors.<U, Object>groupingBy(mapFunc)); + result.keySet().stream().forEach((key) -> { + result.get(key).stream().forEach((u) -> { + assertEquals(key, mapFunc.apply(u)); + }); + }); + assertTrue(streamFor(createEmptyCollection()).collect(Collectors.<U, Object>groupingBy(mapFunc)).isEmpty()); + } + + @Test(dataProvider="collectionPredicateData") + public void testCollectByPartition(String desc, Collection<U> col, Predicate<U> mapFunc) { + Map<Boolean, List<U>> result = streamFor(col). collect(Collectors.<U>partitioningBy(mapFunc)); + for(boolean key : result.keySet()) { + result.get(key).stream().forEach((u) -> { + assertEquals(mapFunc.test(u), key); + }); + } + assertTrue(streamFor(createEmptyCollection()).collect(Collectors.<U>partitioningBy(mapFunc)).get(true).isEmpty()); + assertTrue(streamFor(createEmptyCollection()).collect(Collectors.<U>partitioningBy(mapFunc)).get(false).isEmpty()); + } + + @Test(dataProvider="collectionKeyValueFunctionData") + public <K, V> void testCollectToMap(String desc, Collection<U> col, Function<U, K> keyFunc, Function<U, V> valueFunc) { + Map<K, V> keyToValue = streamFor(col).collect(Collectors.<U, K, V>toMap(keyFunc, valueFunc)); + assertEquals(keyToValue.size(), streamFor(col).map(keyFunc).distinct().count()); + col.stream().forEach((u) -> { + assertEquals(valueFunc.apply(u), keyToValue.get(keyFunc.apply(u))); + }); + } + + @Test(dataProvider="collectionData") + public void testFind(String desc, Collection<U> col) throws Exception{ + Stream<U> stream = streamFor(col); + Optional<U> opAny = stream.findAny(); + Optional<U> opFirst = streamFor(col).findFirst(); + if(!stream.isParallel()) + assertEquals(opAny, opFirst); + assertTrue(opAny.isPresent()); + assertTrue(opFirst.isPresent()); + + assertFalse(streamFor(createEmptyCollection()).findAny().isPresent()); + assertFalse(streamFor(createEmptyCollection()).findFirst().isPresent()); + } + + @Test(dataProvider="collectionPredicateData") + public void testXxxMatch(String desc, Collection<U> col, Predicate<U> predicate) throws Exception { + assertEquals(streamFor(col).allMatch(predicate), + verifyMatch(col, predicate, true)); + assertEquals(streamFor(col).anyMatch(predicate), + verifyMatch(col, predicate, false)); + assertEquals(streamFor(col).noneMatch(predicate), + verifyMatch(col, predicate.negate(), true)); + + assertTrue(streamFor(createEmptyCollection()).allMatch(predicate)); + assertFalse(streamFor(createEmptyCollection()).anyMatch(predicate)); + assertTrue(streamFor(createEmptyCollection()).noneMatch(predicate)); + } + + @Test(dataProvider="concatCollectionData") + public void testConcat(String desc, Collection<U> tobeConcated, Collection<U> concat) throws Exception { + //We're using List to store expected result because Set could merge object, + Collection<U> expected = new ArrayList<>(tobeConcated); + expected.addAll(concat); + + //test concat with parallel/non-parallel Stream + for(ParallelType concatType : ParallelType.values()) { + Stream<U> concatStream = (concatType == ParallelType.Parallel) ? + concat.parallelStream(): (concatType == ParallelType.Sequential) + ? concat.parallelStream().sequential() : concat.stream(); + List<U> calculated = Stream.concat(streamFor(tobeConcated), concatStream) + .collect(Collectors.<U>toList()); + assertEqualContents(calculated, expected, naturalOrderComparator); + } + + List<U> result3 = Stream.concat(tobeConcated.stream(), streamFor(createEmptyCollection())) + .collect(Collectors.<U>toList()); + List<U> result4 = Stream.concat(tobeConcated.stream(), Stream.empty()) + .collect(Collectors.<U>toList()); + assertEqualContents(tobeConcated, result3, naturalOrderComparator); + assertEqualContents(tobeConcated, result4, naturalOrderComparator); + } + + @Test(dataProvider="collectionData") + public void testCount(String desc, Collection<U> col) throws Exception{ + assertEquals(streamFor(col).count(), col.size()); + assertEquals(streamFor(col).count(), streamFor(col).mapToLong(u -> 1).sum()); + assertEquals(streamFor(createEmptyCollection()).count(), 0); + } + + @Test(dataProvider="collectionDualPredicateData") + public void testFilter(String desc, Collection<U> col, Predicate<U> p1, Predicate<U> p2) throws Exception { + //Filter the data, check if it works as expected. + Collection<U> filteredColByP1 = streamFor(col).filter(p1) + .collect(Collectors.<U, List<U>>toCollection(LinkedList::new)); + assertTrue(verifyMatch(filteredColByP1, p1, true)); + + //filter on parallel stream can cause IllegalStateException + Collection<U> filteredColByAlwaysTrue = streamFor(col).filter(p1).filter(e -> true) + .collect(Collectors.<U, List<U>>toCollection(LinkedList::new)); + assertTrue(verifyMatch(filteredColByAlwaysTrue, p1, true)); + + //filter with false will get nothing + Collection<U> filteredColByAlwaysFalse = streamFor(col).filter(p1).filter(e -> false) + .collect(Collectors.<U, List<U>>toCollection(LinkedList::new)); + assertTrue(filteredColByAlwaysFalse.isEmpty()); + + //The reason conver l to sorted is CopyOnWriteArrayList doesn't support + //sort, we use ArrayList sort data instead + Collection<U> filteredColByP2AfterP1 = streamFor(col).filter(p1).filter(p2) + .collect(Collectors.<U, List<U>>toCollection(ArrayList::new)); + + List<U> filteredColByrP1AndP2 = streamFor(col).filter(p1.and(p2)) + .collect(Collectors.<U, List<U>>toCollection(ArrayList::new)); + assertEqualContents(filteredColByP2AfterP1, filteredColByrP1AndP2, naturalOrderComparator); + + List<U> filteredColByrP1OrP2 = streamFor(col).filter(p1.or(p2)) + .collect(Collectors.<U, List<U>>toCollection(ArrayList::new)); + + List<U> filteredExpected = new ArrayList<>(filteredColByrP1AndP2); + filteredExpected.addAll(filteredColByrP1OrP2); + + //concat stream.filter(p1) with stream.filter(p2) should be same as stream.filter(p1 && p2) combine with + //stream.filter(p1 || p2) + List<U> filteredColByP1CancatfilteredColByP2 = Stream.concat(streamFor(col).filter(p1), streamFor(col).filter(p2)) + .collect(Collectors.<U, List<U>>toCollection(ArrayList::new)); + assertEqualContents(filteredColByP1CancatfilteredColByP2, filteredExpected, naturalOrderComparator); + + assertEquals(streamFor(createEmptyCollection()).filter(p1).count(), 0); + } + + @Test(dataProvider="collectionData") + public void testForEach(String desc, Collection<U> col) throws Exception{ + List<U> expected= new ArrayList<>(col); + //Unordered streams could play parallel executions, which we need a + //thread-safe queue to check the data + ConcurrentLinkedQueue<U> forEachQueue = new ConcurrentLinkedQueue<>(); + streamFor(col).forEach(forEachQueue::add); + assertEqualContents(forEachQueue, expected, naturalOrderComparator); + + List<U> forEachOrderedList = new ArrayList<>(); + streamFor(col).forEachOrdered(forEachOrderedList::add); + assertEquals(forEachOrderedList, expected); + + List<U> expectedEmpty = new ArrayList<>(); + streamFor(createEmptyCollection()).forEach(expectedEmpty::add); + assertTrue(expectedEmpty.isEmpty()); + } + + @Test(dataProvider="collectionDataWithLimit") + public void testLimit(String desc, Collection<U> col, int withBoundryLimit, int beyondBoundryLimit) { + Collection<U> resultWithBoundary = streamFor(col).limit(withBoundryLimit) + .collect(Collectors.<U,List<U>>toCollection(LinkedList::new)); + assertEquals(resultWithBoundary.size(), withBoundryLimit); + + Collection<U> resultBeyondBoundary = streamFor(col).limit(beyondBoundryLimit) + .collect(Collectors.<U,List<U>>toCollection(LinkedList::new)); + assertEquals(resultBeyondBoundary.size(), resultBeyondBoundary.size()); + + assertEquals(streamFor(createEmptyCollection()).limit(withBoundryLimit).count(), 0); + assertEquals(streamFor(createEmptyCollection()).limit(beyondBoundryLimit).count(), 0); + } + + @Test(dataProvider="exceptionTestData",expectedExceptions = IllegalArgumentException.class) + public void testLimitException(String desc, Collection<U> col){ + streamFor(col).limit(-1).toArray(); + } + + @Test(dataProvider="collectionDataWithSelection") + public void testFlatMap(String desc, Collection<U> col, Selection selection) { + List<U> result = streamFor(col) + .flatMap(genFlatMapper(selection)) + .collect(Collectors.<U>toList()); + verifyFlatMap(col, result, selection); + + assertEquals(streamFor(createEmptyCollection()).flatMap(genFlatMapper(selection)).count(), 0); + } + + @Test(dataProvider="collectionFunctionData") + public void testMaxAndMin(String desc, Collection<U> col, Function<U, ? extends Comparable> mapFunc) { + Comparator<U> comp = Comparator.comparing(mapFunc); + Optional<U> optionaMax = streamFor(col).max(comp) ; + Optional<U> optionaMin = streamFor(col).min(comp) ; + assertTrue(optionaMax.isPresent()); + assertTrue(optionaMin.isPresent()); + + assertEquals(comp.compare(optionaMax.get(), getMaxOrMinWithIterator(col, comp, true)), 0); + assertEquals(comp.compare(optionaMin.get(), getMaxOrMinWithIterator(col, comp, false)), 0); + + assertEquals(comp.compare(optionaMax.get(), getMaxOrMinByCollectorBy(col, comp, true)), 0); + assertEquals(comp.compare(optionaMin.get(), getMaxOrMinWithIterator(col, comp, false)), 0); + + assertEquals(comp.compare(optionaMax.get(), getMaxOrMinByReduce(col, comp, true)), 0); + assertEquals(comp.compare(optionaMin.get(), getMaxOrMinByReduce(col, comp, false)), 0); + + assertEquals(comp.compare(optionaMax.get(), getMaxOrMinReduceWithBase(col, comp, true)), 0); + assertEquals(comp.compare(optionaMin.get(), getMaxOrMinReduceWithBase(col, comp, false)), 0); + + assertEquals(comp.compare(optionaMax.get(), getMaxOrMinCollectWithReducing(col, comp, true)), 0); + assertEquals(comp.compare(optionaMin.get(), getMaxOrMinCollectWithReducing(col, comp, false)), 0); + + assertFalse(streamFor(createEmptyCollection()).max(comp).isPresent()); + assertFalse(streamFor(createEmptyCollection()).max(comp).isPresent()); + } + + @Test(dataProvider="collectionBinaryOpeatorData") + public void testReduce(String desc, Collection<U> col, U base, BinaryOperator<U> op, boolean stateless) { + Optional<U> reduceResult = streamFor(col).reduce( op); + assertTrue(reduceResult.isPresent()); + Iterator<U> iter = col.iterator(); + U expected = iter.next(); + while(iter.hasNext()) + expected = op.apply(expected, iter.next()); + assertEquals(reduceResult.get(), expected); + + assertEquals(streamFor(createEmptyCollection()).reduce(base,op), base); + assertFalse(streamFor(createEmptyCollection()).reduce(op).isPresent()); + + if(stateless || !streamFor(col).isParallel()) { + U reduceWithBaseResult = streamFor(col).reduce(base, op); + U expectedWithBase = base; + for(U aCol : col) + expectedWithBase = op.apply(expectedWithBase, aCol); + assertEquals(reduceWithBaseResult, expectedWithBase); + } + } + + @Test(dataProvider="collectionData") + public void testSubstream(String desc, Collection<U> col) { + BiFunction<Integer,Integer,Integer> bf = LambdaUtilities.randBetweenIntegerFunction(); + int skip = rand.nextInt(col.size()); + int limit1 = bf.apply(0, col.size() - skip); + Collection<U> sliceCol = streamFor(col).substream(skip, skip + limit1). + collect(Collectors.<U>toList()); + verifySlice(col, sliceCol, skip, limit1, !col.getClass().equals(HashSet.class)); + + int limitExceeded = bf.apply(col.size() - skip, Integer.MAX_VALUE); + Collection<U> sliceCol2 = streamFor(col).substream(skip, skip + limitExceeded). + collect(Collectors.<U>toList()); + //We couldn't predicate unordered collection exact behavior for substream, only check the length and if data + // exists in original collection + verifySlice(col, sliceCol2, skip, limitExceeded, !col.getClass().equals(HashSet.class)); + + assertEquals(streamFor(col).substream(skip, skip).count(), 0); + + //skip exceed collection size cause empty stream + Stream<U> stream3 = streamFor(col); + int skipExceeded = bf.apply(col.size(), Integer.MAX_VALUE); + assertEquals(stream3.substream(skipExceeded, skipExceeded + 1).count(), 0); + + assertEquals(streamFor(createEmptyCollection()).substream(skip, skip + limit1).count(), 0); + } + + @Test(dataProvider="collectionStartEndData",expectedExceptions = IllegalArgumentException.class) + public void testSubstreamException(String desc, Collection<U> col, long startInclusive, long endExclusive){ + streamFor(col).substream(startInclusive).count(); + streamFor(col).substream(startInclusive, endExclusive).count(); + } + + @Test(dataProvider="collectionFunctionData") + public void testSorted(String desc, Collection<U> col, Function<U, ? extends Comparable> mapFunc) { + // SortedSet instance's stream can't be reordered, we use ArrayList to do reorder + Comparator<U> comp = Comparator.comparing(mapFunc); + List<U> sorted = new ArrayList<>(col); + List<U> reversed = streamFor(col).sorted(comp).collect(Collectors.<U>toList()); + Collections.sort(sorted, comp); + assertEquals(sorted, reversed); + + assertEquals(streamFor(createEmptyCollection()).sorted(comp).count(), 0); + } + + //Note non-Comparable U need override this method + @Test(dataProvider="collectionData") + public void testSortedSpecial(String desc, Collection<U> col) { + // SortedSet instance's stream can't be reordered, we use ArrayList to do reorder + List<U> sorted = new ArrayList<>(col); + List<U> reversed = streamFor(col).sorted().collect(Collectors.<U>toList()); + Collections.sort(sorted, naturalOrderComparator); + assertEquals(sorted, reversed); + + assertEquals(streamFor(createEmptyCollection()).sorted().count(), 0); + } + + @Test(dataProvider="collectionFunctionData") + public void testPeek(String desc, Collection<U> col, Function<U, ? extends Object> mapFunc) { + //Note ArrayList is not a concurrent data type, if we don't set initial + //size, we could get AIOOBE + List<U> peekList = new ArrayList<>(col.size()); + Object[] result = streamFor(col).peek(peekList::add).map(mapFunc).toArray(); + verifyAfterMap(col, result, mapFunc); + + List<U> emptyList = new ArrayList<>(); + assertEquals(streamFor(createEmptyCollection()).peek(emptyList::add).count(), 0); + assertTrue(emptyList.isEmpty()); + } + + @Test(dataProvider="collectionArrayGeneratorData") + public void testToArray(String desc, Collection<U> col, IntFunction[] generators) { + Object[] expectedArr = col.toArray(); + assertEquals(streamFor(col).toArray(), expectedArr); + assertEquals(streamFor(createEmptyCollection()).toArray().length, 0); + + Arrays.stream(generators).forEach(generator -> { + assertEquals(streamFor(col).toArray(generator), expectedArr); + assertEquals(streamFor(createEmptyCollection()).toArray(generator).length, 0); + }); + } + + @Test(dataProvider="collectionArrayInvalidGeneratorData",expectedExceptions = ArrayStoreException.class) + public void testToArrayException(String desc, Collection<U> col, IntFunction generator){ + streamFor(col).toArray(generator); + } + + @Test(dataProvider="smallSizeCollectionData") + public void testDistinct(String desc, Collection<U> col) { + Set<U> set = new HashSet<>(col); + List<U> list = streamFor(col).flatMap(genFlatMapper(Selection.COPY_64)).distinct().collect(Collectors.<U>toList()); + assertEqualContents(list, set, naturalOrderComparator); + + assertEquals(streamFor(createEmptyCollection()).distinct().count(), 0); + } + + private Stream<U> triangleStream(U u) { + int e = intValueFunction().applyAsInt(u); + return IntStream.range(0, e).mapToObj(i -> createFunction().apply(e * (e - 1) / 2 + i)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/Stream/IntStreamTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2012, 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 + * @summary Basic test for IntStream + * @library /sqeutil + * @(#) IntStreamTest.java + * @author Tristan Yan + * @run testng IntStreamTest + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; +import java.util.OptionalInt; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.IntFunction; +import java.util.function.IntPredicate; +import java.util.function.IntUnaryOperator; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +public class IntStreamTest { + private final static int[] ARRAY_SIZES = {1, 1 << 2, 1 << 4, 1 << 7, 1 << 12}; + + protected enum Selection { EMPTY, SINGLE, TRIANGLE, COPY_64}; + + private final static int[] EMPTY_ARRAY = new int[0]; + + private IntStream streamFor(ParallelType p, int[] array) { + return (p == ParallelType.Parallel) ? Arrays.stream(array).parallel() + : (p == ParallelType.Sequential) ? Arrays.stream(array).sequential() : Arrays.stream(array); + } + + private IntStream streamFor(ParallelType p, int[] array, int startIndex, int endIndex) { + return (p == ParallelType.Parallel) ? Arrays.stream(array, startIndex, endIndex).parallel() + : (p == ParallelType.Sequential) ? Arrays.stream(array, startIndex, endIndex).sequential() : + Arrays.stream(array, startIndex, endIndex); + } + + static enum ParallelType { Parallel, Sequential, Default } + + private final static Random rand = new Random(1968721); + + private boolean verifyMatch(int[] array, IntPredicate predicate, boolean checkAll) { + for(int element : array) { + if(predicate.test(element) != checkAll) + return !checkAll; + } + return checkAll; + } + + private final ParallelType parallelType; + + private IntStreamTest(final ParallelType parallelType) { + this.parallelType = parallelType; + } + + @Factory + public static Object[] create(){ + return Arrays.stream(ParallelType.values()).map(p -> new IntStreamTest(p)).toArray(); + } + + final static Object[][] arrays = Arrays.stream(ARRAY_SIZES).mapToObj(size -> + new Object[]{IntStream.range(0, size).toArray()}).toArray(Object[][]::new); + + @DataProvider + public static Iterator<Object[]> intArrays(){ + ArrayList<Object[]> list = new ArrayList<>(); + String format = "<int[%d]>"; + Arrays.stream(arrays).forEach(data -> { + int[] array = (int[])data[0]; + list.add(new Object[]{String.format(format, array.length), array}); + }); + return list.iterator(); + } + + @DataProvider + public static Iterator<Object[]> intArraysWithSkipLimit(){ + ArrayList<Object[]> list = new ArrayList<>(); + String format = "<int[%d], %d, %d>"; + Arrays.stream(arrays).forEach(data -> { + int[] array = (int[])data[0]; + list.add(new Object[]{String.format(format, array.length, 0, array.length), array, 0, array.length}); + list.add(new Object[]{String.format(format, array.length, array.length / 3, array.length /2), array, array.length / 3, array.length /2}); + list.add(new Object[]{String.format(format, array.length, array.length /2, array.length + 1), array, array.length /2, array.length + 1}); + }); + return list.iterator(); + } + + @DataProvider + public static Iterator<Object[]> intArraysWithLimit(){ + ArrayList<Object[]> list = new ArrayList<>(); + String format = "<int[%d], %d>"; + Arrays.stream(arrays).forEach(data -> { + int[] array = (int[])data[0]; + list.add(new Object[]{String.format(format, array.length, 0), array, 0}); + list.add(new Object[]{String.format(format, array.length, array.length / 3), array, array.length / 3}); + list.add(new Object[]{String.format(format, array.length, array.length /2), array, array.length /2}); + list.add(new Object[]{String.format(format, array.length, array.length + 1), array, array.length + 1}); + }); + return list.iterator(); + } + + @DataProvider + public static Iterator<Object[]> intArraysWithPredicate(){ + ArrayList<Object[]> list = new ArrayList<>(); + String format = "<int[%d], %s>"; + Arrays.stream(arrays).forEach(data -> { + int[] array = (int[])data[0]; + list.add(new Object[]{String.format(format, array.length, "i -> i < " + array.length / 2), array, (IntPredicate) i -> i < array.length / 2}); + list.add(new Object[]{String.format(format, array.length, "i -> i < 0"), array, (IntPredicate) i -> i < 0}); + list.add(new Object[]{String.format(format, array.length, "i -> i >= 0"), array, (IntPredicate) i -> i >= 0}); + list.add(new Object[]{String.format(format, array.length, "i -> i >= " + array.length), array, (IntPredicate) i -> i >= array.length}); + }); + return list.iterator(); + } + + @DataProvider + public static Iterator<Object[]> intArraysWithOperator(){ + ArrayList<Object[]> list = new ArrayList<>(); + String format = "<int[%d], %s>"; + Arrays.stream(arrays).forEach(data -> { + int[] array = (int[])data[0]; + list.add(new Object[]{String.format(format, array.length, "i -> i + 1"), array, (IntUnaryOperator)i -> i + 1}); + list.add(new Object[]{String.format(format, array.length, " i -> i << 2"), array, (IntUnaryOperator) i -> i << 2}); + list.add(new Object[]{String.format(format, array.length, "Math::abs"), array, (IntUnaryOperator)Math::abs}); + }); + return list.iterator(); + } + + @DataProvider + public static Iterator<Object[]> intArraysWithSelection(){ + ArrayList<Object[]> list = new ArrayList<>(); + String format = "<int[%d], %s>"; + Arrays.stream(arrays).forEach(data -> { + int[] array = (int[])data[0]; + Arrays.stream(Selection.values()).forEach(selection -> + list.add(new Object[]{String.format(format, array.length, selection), array, selection})); + }); + return list.iterator(); + } + + @Test(dataProvider="intArraysWithPredicate") + public void testXxxMatch(String desc, int[] array, IntPredicate predicate) { + assertEquals(streamFor(parallelType, array) .allMatch(predicate), verifyMatch(array, predicate, true)); + assertEquals(streamFor(parallelType, array) .anyMatch(predicate), verifyMatch(array, predicate, false)); + assertEquals(streamFor(parallelType, array) .noneMatch(predicate), verifyMatch(array, predicate.negate(), true)); + + assertEquals(streamFor(parallelType, array, 0, array.length).allMatch(predicate), + streamFor(parallelType, array).allMatch(predicate)); + assertEquals(streamFor(parallelType, array, 0, array.length).anyMatch(predicate), + streamFor(parallelType, array).anyMatch(predicate)); + assertEquals(streamFor(parallelType, array, 0, array.length).noneMatch(predicate), + streamFor(parallelType, array).noneMatch(predicate)); + + assertTrue(streamFor(parallelType, EMPTY_ARRAY).allMatch(predicate)); + assertFalse(streamFor(parallelType, EMPTY_ARRAY).anyMatch(predicate)); + assertTrue(streamFor(parallelType, EMPTY_ARRAY).noneMatch(predicate)); + } + + @Test(dataProvider="intArrays") + public void testAveargeAndSum(String desc, int[] array) throws Exception { + //for calculate average, we need bigger scale data type calculate total, this could prevent int overflow + double total = 0; + int intTotal = 0; + for(int index = 0; index < array.length; index++) { + total += array[index]; + intTotal += array[index]; + } + assertEquals(streamFor(parallelType, array).average(). getAsDouble(), total / array.length); + assertEquals(streamFor(parallelType, array).sum(), intTotal); + + assertEquals(streamFor(parallelType, array).average(), streamFor(parallelType, array, 0, array.length).average()); + assertEquals(streamFor(parallelType, array).sum(), streamFor(parallelType, array, 0, array.length).sum()); + + assertFalse(streamFor(parallelType, EMPTY_ARRAY).average().isPresent()); + assertEquals(streamFor(parallelType, EMPTY_ARRAY).sum(), 0); + } + + @Test(dataProvider="intArrays") + public void testBoxed(String desc, int[] array) throws Exception { + Object[] boxedArr1 = streamFor(parallelType, array).boxed().toArray(); + Object[] boxedArr2 = streamFor(parallelType, array).mapToObj(Integer::new).toArray(); + assertEquals(boxedArr1, boxedArr2); + + assertEquals(streamFor(parallelType, EMPTY_ARRAY).boxed().count(), 0); + } + + @Test(dataProvider="intArraysWithPredicate") + public void testFilter(String desc, int[] array/*, int startIndex, int endIndex*/, IntPredicate predicate) throws Exception{ + assertTrue(verifyMatch(streamFor(parallelType, array).filter(predicate).toArray(), predicate, true)); + assertEquals(streamFor(parallelType, array, 0, array.length).filter(predicate).toArray(), + streamFor(parallelType, array).filter(predicate).toArray()); + + assertEquals(streamFor(parallelType, EMPTY_ARRAY).filter(predicate).count(), 0); + } + + @Test(dataProvider="intArrays") + public void testFind(String desc, int[] array) throws Exception{ + IntStream streamFindAny = streamFor(parallelType, array); + OptionalInt opAny = streamFindAny.findAny(); + OptionalInt opFirst = streamFor(parallelType, array).findFirst(); + assertTrue(opAny.isPresent()); + assertTrue(opFirst.isPresent()); + if(!streamFindAny.isParallel()) + assertEquals(opAny, opFirst); + + assertFalse(streamFor(parallelType, EMPTY_ARRAY).findAny().isPresent()); + assertFalse(streamFor(parallelType, EMPTY_ARRAY).findFirst().isPresent()); + } + + @Test(dataProvider="intArrays") + public void testForEach(String desc, int[] array) throws Exception { + IntStream stream = streamFor(parallelType, array); + Arrays.parallelSort(array); + stream.forEach(t -> { assertTrue(Arrays.binarySearch(array, t) >= 0); }); + + ArrayList<Integer> touch = new ArrayList<>(); + streamFor(parallelType, EMPTY_ARRAY).forEach(t -> { touch.add(t); }); + assertTrue(touch.isEmpty()); + } + + @Test(dataProvider="intArraysWithLimit") + public void testLimit(String desc, int[] array, int limit) { + if(limit > array.length) + assertEquals(streamFor(parallelType, array).limit(limit).count(), array.length); + else + assertEquals(streamFor(parallelType, array).limit(limit).count(), limit); + } + + @Test(dataProvider="intArraysWithSelection") + public void testFlatMap(String desc, int[] array, Selection selection) { + int[] flatArray = streamFor(parallelType, array).flatMap(genIntFlatMapper(selection)).sorted().toArray(); + verifyMultifunction(array, flatArray, selection); + + assertEquals(streamFor(parallelType, EMPTY_ARRAY).flatMap(genIntFlatMapper(selection)).toArray(),EMPTY_ARRAY); + } + + @Test(dataProvider="intArraysWithOperator") + public void testMap(String desc, int[] array, IntUnaryOperator mapper) { + int[] mapped = streamFor(parallelType, array).map(mapper).toArray(); + for(int index = 0; index < array.length; index++) { + assertEquals(mapped[index], mapper.applyAsInt(array[index])); + } + + IntFunction<Object> objMapper = i -> new Integer(mapper.applyAsInt(i)); + Object[] objArray1 = streamFor(parallelType, array).map(mapper).boxed().toArray(); + Object[] objArray2 = streamFor(parallelType, array).mapToObj(objMapper).toArray(); + assertEquals(objArray1, objArray2); + } + + @Test(dataProvider="intArrays") + public void testMaxAndMin(String desc, int[] array) { + boolean[] maxOrMin = {true, false}; + for(boolean testMax : maxOrMin) { + OptionalInt testOpt = testMax ? streamFor(parallelType, array).max() + : streamFor(parallelType, array).min(); + assertEquals(testOpt.getAsInt(), getMaxOrMin(array, testMax)); + assertEquals(testOpt.getAsInt(), getMaxOrMinWithReduceOp(array, testMax)); + assertEquals(testOpt.getAsInt(), getMaxOrMinWithReduceOpAndBase(array, testMax)); + + assertFalse(testMax ? streamFor(parallelType, EMPTY_ARRAY).max().isPresent() + : streamFor(parallelType, EMPTY_ARRAY).min().isPresent()); + } + } + + @Test(dataProvider="intArrays") + public void testSpliterator(String desc, int[] array) { + int[] calArrParallel = StreamSupport.intStream(streamFor(parallelType, array).spliterator(), true).toArray(); + int[] calArrSequential = StreamSupport.intStream(streamFor(parallelType, array).spliterator(), false).toArray(); + for(int index = 0; index < array.length; index++) { + assertEquals(array[index], calArrParallel[index]); + assertEquals(array[index], calArrSequential[index]); + } + } + + @Test(dataProvider="intArrays") + public void testReduceWithBase(String desc, int[] array) { + if(parallelType == ParallelType.Parallel) + return; + int base = rand.nextInt(); + int oiPlus = streamFor(parallelType, array).reduce(base, + LambdaUtilities.addIntBinaryOperator()); + int total = 0; + for(int index = 0; index < array.length; index++) + total += array[index]; + assertEquals(oiPlus, total + base); + + int oiSubtract = streamFor(parallelType, array).reduce(base, + LambdaUtilities.subIntBinaryOperator()); + int subTotal = base; + for(int index = 0; index < array.length; index++) + subTotal -= array[index]; + assertEquals(oiSubtract, subTotal); + + int emptyByBase = streamFor(parallelType, EMPTY_ARRAY).reduce(base, rand.nextBoolean() ? + LambdaUtilities.addIntBinaryOperator() : + LambdaUtilities.subIntBinaryOperator()); + assertEquals(emptyByBase, base); + } + + @Test(dataProvider="intArrays") + public void testReduceWithoutBase(String desc, int[] array) { + OptionalInt oiPlus = streamFor(parallelType, array).reduce(Integer::sum); + assertTrue(oiPlus.isPresent()); + int total = 0; + for(int index = 0; index < array.length; index++) + total += array[index]; + assertTrue(oiPlus.getAsInt() == total); + + //Can't predict Parallel Substract reduce result, give up testing on + //parallel substract test + if(parallelType == ParallelType.Parallel) + return; + + OptionalInt oiSubtract = streamFor(parallelType, array). + reduce(LambdaUtilities.subIntBinaryOperator()); + assertTrue(oiSubtract.isPresent()); + int subTotal = array[0]; + for(int index = 1; index < array.length; index++) + subTotal -= array[index]; + assertEquals(oiSubtract.getAsInt(), subTotal); + + OptionalInt emptyOp = streamFor(parallelType, EMPTY_ARRAY). + reduce(rand.nextBoolean() ? LambdaUtilities.addIntBinaryOperator() : + LambdaUtilities.subIntBinaryOperator()); + assertFalse(emptyOp.isPresent()); + } + + @Test(dataProvider="intArraysWithSkipLimit") + public void testSubstream(String desc, int[] array, int skip, int limit) { + assert(limit >= skip); + int[] toArray = streamFor(parallelType, array).substream(skip, limit).toArray(); + if(skip < array.length) { + if(limit < array.length) { + verifySlice(array, toArray, skip, limit); + } else { + verifySlice(array, toArray, skip, array.length); + } + } + else { + assertEquals(toArray.length, 0); + } + assertEquals(streamFor(parallelType, EMPTY_ARRAY).substream(skip, limit).count(), 0); + + } + + @Test(dataProvider="intArrays") + public void testSorted(String desc, int[] array){ + checkSorted(streamFor(parallelType, array).sorted().toArray(), Comparator.naturalOrder()); + assertEquals(streamFor(parallelType, array).sorted().toArray(), + streamFor(parallelType, array, 0, array.length).sorted().toArray()); + assertEquals(streamFor(parallelType, EMPTY_ARRAY).sorted().toArray().length, 0); + } + + @Test(dataProvider="intArrays") + public void testPeek(String desc, int[] array){ + AtomicInteger ai1 = new AtomicInteger(0); + IntStream stream = streamFor(parallelType, array).peek(LambdaUtilities.addIntConsumer(ai1)); + //Lazy operation checking + assertEquals(ai1.get(), 0); + int sum = stream.sum(); + assertEquals(ai1.get(), sum); + + assertEquals(streamFor(parallelType,EMPTY_ARRAY).peek(LambdaUtilities.addIntConsumer(ai1)).count(), 0); + } + + @Test(dataProvider="intArrays") + public void testToArray(String desc, int[] array){ + assertEquals(streamFor(parallelType, array).toArray(), array); + assertEquals(streamFor(parallelType, array, 0, array.length).toArray(), array); + assertEquals(streamFor(parallelType, EMPTY_ARRAY).toArray().length, 0); + } + + @Test(dataProvider="intArrays") + public void testDistrinct(String desc, int[] array){ + int[] toArray = streamFor(parallelType, array).flatMap(genIntFlatMapper(Selection.COPY_64)).distinct().toArray(); + assertEquals(toArray, array); + + assertEquals(streamFor(parallelType, EMPTY_ARRAY).flatMap(genIntFlatMapper(Selection.COPY_64)). + distinct().toArray().length, 0); + } + + private void verifyMultifunction(int[] orig, int[] result, Selection selection) { + switch(selection) { + case EMPTY: + assertEquals(result.length, 0); + break; + case SINGLE: + assertEquals(orig, result); + break; + case TRIANGLE: + int expectedSize = orig.length * (orig.length + 1) / 2; + int[] expected = IntStream.range(0, expectedSize).toArray(); + assertEquals(result, expected); + break; + case COPY_64: + Stream<Integer> sToBeConcated = Stream.empty(); + for(int current : orig) { + sToBeConcated = Stream.concat(sToBeConcated, + IntStream.generate(() -> current). + limit(1 << 6).boxed()); + } + assertEquals(sToBeConcated.toArray(), result); + break; + default: + throw new RuntimeException("Invalid selection " + selection); + } + } + + private int getMaxOrMin(int[] array, boolean isMax) { + assert(array.length > 0); + int pos = 0; + int choosen = array[pos++]; + while(pos < array.length) { + int current = array[pos++]; + if((choosen < current) == isMax) choosen = current; + } + return choosen; + } + + private int getMaxOrMinWithReduceOp(int[] array, boolean isMax) { + assert(array.length > 0); + OptionalInt choosen = Arrays.stream(array).reduce(isMax ? + LambdaUtilities.maxIntBinaryOperator() : + LambdaUtilities.minIntBinaryOperator()); + return choosen.getAsInt(); + } + + private int getMaxOrMinWithReduceOpAndBase(int[] array, boolean isMax) { + assert(array.length > 0); + int choosen = Arrays.stream(array).reduce(array[0], isMax ? + LambdaUtilities.maxIntBinaryOperator() : + LambdaUtilities.minIntBinaryOperator()); + return choosen; + } + + private void verifySlice(int[] array, int[] toArray, int skip, int limit){ + for(int index = skip; index < array.length && index < limit; index++) + assertTrue(array[index] <= toArray[index - skip]); + } + + private void checkSorted(int[] array, Comparator<Integer> comp){ + assert(array.length > 0); + for(int index = 0; index < array.length - 1; index++) + assertTrue(comp.compare(array[index] , array[index + 1]) <= 0); + } + + private static IntFunction<IntStream> genIntFlatMapper(Selection selection) { + switch (selection) { + case EMPTY: + //Generate a empty collection + return e -> IntStream.empty(); + case SINGLE: + return e -> Arrays.stream(new int[]{ e }); + case TRIANGLE: + //Generate a triangle has different value + return e -> IntStream.range(0, e + 1).map(i -> e * (e + 1) / 2 + i); + case COPY_64: + //Generate 64 folded flat map + return e -> IntStream.range(0, 1 << 6).map(i -> e); + default: + throw new RuntimeException("Invalid selection " + selection); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/Stream/IntegerStreamTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2012, 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 + * @summary Basic test for Stream<Integer> + * @library /sqeutil + * @(#) IntegerStreamTest.java + * @author Tristan Yan + * @run testng IntegerStreamTest + */ + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.Comparator; +import java.util.IntSummaryStatistics; +import java.util.Optional; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.ToIntFunction; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import static org.testng.Assert.assertEquals; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +public class IntegerStreamTest<T extends Collection<Integer>> extends GenericStreamTest<Integer, Collection<Integer>> { + private IntegerStreamTest(Class clazz, + ParallelType parallelType, int initSize) { + super(clazz, parallelType, Comparator.naturalOrder(), initSize); + } + + private IntegerStreamTest(Class clazz, + ParallelType parallelType) { + super(clazz, parallelType, Comparator.naturalOrder()); + } + + @Factory + public static Object[] create(){ + TripleFunction<Class,ParallelType,Integer, IntegerStreamTest> supplierWithInitSize = + IntegerStreamTest<Collection<Integer>>::<Class,ParallelType,Integer>new; + BiFunction<Class,ParallelType, IntegerStreamTest> supplier = + IntegerStreamTest<Collection<Integer>>::<Class,ParallelType>new; + return GenericStreamTest.create(supplierWithInitSize, supplier, true); + } + + @Override + public String getTestName() { + return typeObject.getName() + "<Integer>"; + } + + @Test(dataProvider="collectionFunctionData") + public void testCollectSummarizingInt(String desc, Collection<Integer> col, Function<Integer, ? extends Object> mapFunc) { + //test summarizingInt + IntSummaryStatistics iss = streamFor(col).collect(Collectors.<Integer>summarizingInt(Integer::intValue)); + assertEquals(iss.getCount(), col.size()); + assertEquals(iss.getMax(), streamFor(col).max(naturalOrderComparator).get().intValue()); + assertEquals(iss.getMin(), streamFor(col).min(naturalOrderComparator).get().intValue()); + assertEquals(iss.getSum(), streamFor(col).mapToInt(Integer::intValue).sum()); + } + + @Test(dataProvider="collectionData") + public void testReduceIdentitiyWithoutBase(String desc, Collection<Integer> col) throws Exception{ + Stream<Integer> streamTotal = streamFor(col); + Optional<Integer> total = streamTotal.reduce(LambdaUtilities.addIntegerBinaryOperator()); + double avg = (double) total.get().intValue() / col.size(); + Stream<Integer> streamStanDeviation = streamFor(col); + BigDecimal deviationSquare = streamStanDeviation.reduce(new BigDecimal(0.0), + LambdaUtilities.deviationSequareFunction(avg), + LambdaUtilities.addBigDecimalBinaryOperator()); + double stdev = deviationSquare.doubleValue() / col.size(); + + int totalExpected = 0; + for (Integer aCol : col) + totalExpected += aCol; + assertEquals(totalExpected, total.get().intValue()); + double avgExpected = (double)totalExpected / col.size(); + assertEquals(avgExpected, avg); + + BigDecimal deviationSquareExpected = new BigDecimal(0.0); + for (Integer aCol : col) + deviationSquareExpected = deviationSquareExpected + .add(new BigDecimal(avg - aCol).pow(2)); + assertEquals(deviationSquare, deviationSquareExpected); + + double stddvExpected = deviationSquareExpected.doubleValue() / col.size(); + assertEquals(stddvExpected, stdev); + } + + @Override + protected Collection<Integer> generate(int size) { + Collection<Integer> col = createEmptyCollection(); + IntStream.range(0, size).forEach(i -> col.add(i)); + return col; + } + + @Override + protected Object[] predicateAndDesc(int size){ + boolean isUp = rand.nextBoolean(); + int limit = rand.nextInt(size); + return new Object[]{ LambdaUtilities.randomIntegerPredicate(isUp, limit), + String.format("i -> i %s %d", isUp ? ">=" : "<", limit)}; + } + + //BinaryOperator, descrption, stateless + @Override + protected Object[][] binaryOperatorAndDesc() { + return new Object[][]{ + {(BinaryOperator<Integer>)Integer::sum, "Integer::sum", false}, + {(BinaryOperator<Integer>)Integer::max, "Integer::sum", true}, + {(BinaryOperator<Integer>)Integer::max, "Integer::sum", true}, + }; + } + + //Function<U, ? extends Comparable>, descrption for Function<? extends Comparable> + @Override + protected Object[][] functionAndDesc(){ + Function<Integer, String> toStr = String::valueOf; + return new Object[][]{ + {(Function<Integer, Integer>)i -> i++, "i -> i++"}, + {toStr, "String::valueOf"}, + }; + } + + @Override + protected Integer[] bases(int size){ + return new Integer[]{-1, 0, 1, size /2, size - 1, size, size + 1}; + } + + @Override + protected Object[] validArrayGeneratorAndDesc() { + return new Object[]{ + new IntFunction[]{(IntFunction)Integer[]::new, (IntFunction)Number[]::new}, + "{Integer[]::new, Number[]::new}" + }; + } + + @Override + protected Object[][] invalidArrayGeneratorAndDesc() { + return new Object[][]{ + {(IntFunction)Double[]::new, "Double[]::new"}, + {(IntFunction)String[]::new, "String[]::new"}, + }; + } + + @Override + protected IntFunction<Integer> createFunction() { + return Integer::valueOf; + } + + @Override + protected ToIntFunction<Integer> intValueFunction() { + return Integer::intValue; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/Stream/StringBuilderStreamTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,170 @@ +/* + * 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. + */ + +/** + * @test + * @summary Basic test for Stream<StringBuilder> + * @library /sqeutil + * @(#) StringBuilderStreamTest.java + * @author Eric Wang/Tristan Yan + * @run testng/othervm -mx1g StringBuilderStreamTest + */ + +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.function.ToIntFunction; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import static org.testng.Assert.assertEquals; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +public class StringBuilderStreamTest<T extends Collection<StringBuilder>> extends + GenericStreamTest<StringBuilder, Collection<StringBuilder>> { + public StringBuilderStreamTest(Class clazz, + ParallelType parallelType, int initSize) { + super(clazz, parallelType, Comparator.comparing((Function<StringBuilder, + String>)StringBuilder::toString), initSize); + } + + public StringBuilderStreamTest(Class clazz, + ParallelType parallelType) { + super(clazz, parallelType, Comparator.comparing((Function<StringBuilder, + String>)StringBuilder::toString)); + } + + @Factory + public static Object[] create(){ + TripleFunction<Class,ParallelType,Integer,StringBuilderStreamTest> supplierWithInitSize = + StringBuilderStreamTest<Collection<StringBuilder>>::<Class,ParallelType,Integer>new; + BiFunction<Class,ParallelType,StringBuilderStreamTest> supplier = + StringBuilderStreamTest<Collection<StringBuilder>>::<Class,ParallelType>new; + return GenericStreamTest.create(supplierWithInitSize, supplier, false); + } + + @Override + public String getTestName() { + return typeObject.getName() + "<StringBuilder>"; + } + + @Test(dataProvider="collectionFunctionData") + public void testCollectJoining(String desc, Collection<StringBuilder> col, Function<StringBuilder, ? extends Object> mapFunc) { + //test joining + String joined = streamFor(col).collect(Collectors.joining()); + int pos = 0; + for(StringBuilder sb : col) { + String toString = sb.toString(); + assertEquals(joined.substring(pos, pos + toString.length()), toString); + pos += toString.length(); + } + } + + //StringBuilder is not Comparable + //Not single element stream won't throw ClassCastException + @Test(dataProvider="exceptionTestData", expectedExceptions = ClassCastException.class) + @Override + public void testSortedSpecial(String desc, Collection<StringBuilder> col) { + streamFor(col).sorted().findFirst(); + System.out.println(col.size()); + } + + @Override + protected Object[] predicateAndDesc(int size){ + LambdaUtilities.CharType charType = Arrays.stream(LambdaUtilities.CharType.values()).findAny().get(); + Boolean isFirst = rand.nextBoolean(); + Predicate<StringBuilder> p =(Predicate<StringBuilder>) LambdaUtilities.randomSBPredicate(charType, isFirst); + String funcName = ""; + switch (charType) { + case DIGIT: funcName = "isDigit"; break; + case LOWERCASE: funcName = "isLowerCase"; break; + case UPPERCASE: funcName = "isUpperCase"; break; + default: funcName = "isLetterOrDigit"; break; + } + return new Object[]{ p , String.format("sb -> Character.%s(%s)", funcName, isFirst ? "0" : "sb.toString().length() - 1")}; + + } + + //BinaryOperator, descrption, stateless + @Override + protected Object[][] binaryOperatorAndDesc() { + return new Object[][]{ + {(BinaryOperator<StringBuilder>)StringBuilder::append, "StringBuilder::append", false}, + {LambdaUtilities.maxGenericBinaryOperator(naturalOrderComparator), "(t1, t2) -> t1 < t2 ? t2 : t1", true}, + {LambdaUtilities.minGenericBinaryOperator(naturalOrderComparator), " (t1, t2) -> t1 < t2 ? t1 : t2", true}, + }; + } + + //Function<U, ? extends Comparable>, descrption for Function<? extends Comparable> + @Override + protected Object[][] functionAndDesc(){ + return new Object[][]{ + {(Function<StringBuilder, String>)StringBuilder::toString, "StringBuilder::toString"}, + }; + } + + @Override + protected Collection<StringBuilder> generate(int size) { + Collection<StringBuilder> col = createEmptyCollection(); + IntStream.range(0, size).mapToObj(i -> new StringBuilder().append(i) ).forEach(sb -> col.add(sb)); + return col; + } + + @Override + protected StringBuilder[] bases(int size){ + return new StringBuilder[]{new StringBuilder(), new StringBuilder(rand.nextInt(size)), new StringBuilder(size)}; + } + + //StringBuilder[] can be converted into StringBuilder[]::new,CharSequence[]::new, Object[]::new + @Override + protected Object[] validArrayGeneratorAndDesc() { + return new Object[]{ + new IntFunction[]{(IntFunction)StringBuilder[]::new, (IntFunction)CharSequence[]::new, (IntFunction)Object[]::new}, + "{StringBuilder[]::new, CharSequence[]::new}, Object[]::new}" + }; + } + + //StringBuilder[] can't be converted into StringBuffer[]::new,Integer[]::new + @Override + protected Object[][] invalidArrayGeneratorAndDesc() { + return new Object[][]{ + {(IntFunction)StringBuffer[]::new, "StringBuffer[]::new"}, + {(IntFunction)Integer[]::new, "Integer[]::new"}, + }; + } + + @Override + protected IntFunction<StringBuilder> createFunction() { + return i -> new StringBuilder().append(i); + } + + @Override + protected ToIntFunction<StringBuilder> intValueFunction() { + return sb -> Integer.parseInt(sb.toString()) ; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/Streams/BasicTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2012, 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 + * @summary Basic test for Streams's API + * @library /sqeutil + * @(#) BasicTest.java + * @author Tristan Yan + * @run testng BasicTest + */ + +import java.util.Arrays; +import java.util.Iterator; +import java.util.PrimitiveIterator; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.IntConsumer; +import java.util.function.IntUnaryOperator; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import java.util.Spliterators; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +public class BasicTest { + private final static int ARRAY_SIZE = 1 << 12; + + private final static Random rand = new Random(8675309); + + private final static String HELLO_WORLD = "Hello world"; + + @Test + public void testEmpty() { + final AtomicInteger accumulator = new AtomicInteger(0); + IntConsumer ic = i -> { + accumulator.getAndIncrement(); + }; + Spliterators.emptyIntSpliterator().forEachRemaining(ic); + assertEquals(accumulator.get(), 0); + + assertEquals(IntStream.empty().toArray().length, 0); + + assertEquals(Stream.empty().toArray().length, 0); + + assertEquals(Spliterators.emptyIntSpliterator().estimateSize(), 0); + + assertEquals(Spliterators.<String>emptySpliterator().estimateSize(), 0); + } + + @Test + public void testGenerate() { + final int randomValue = rand.nextInt(); + final int randomSize= rand.nextInt(1 << 8); + IntStream is = IntStream.generate(() -> randomValue); + assertEquals(is.spliterator().getExactSizeIfKnown(), -1); + + IntStream is1 = IntStream.generate(() -> randomValue); + IntStream is2 = IntStream.generate(() -> randomValue); + assertEquals(is1.limit(randomSize).max(), is2.limit(randomSize).min()); + + Stream<String> ss = Stream.generate(() -> HELLO_WORLD); + assertEquals(ss.spliterator().getExactSizeIfKnown(), -1); + Stream<String> ss1 = Stream.generate(() -> HELLO_WORLD); + Stream<String> ss2 = Stream.generate(() -> HELLO_WORLD); + assertEquals(ss1.limit(randomSize).findAny(), ss2.limit(randomSize).findAny()); + + LambdaUtilities.IntOp op = LambdaUtilities.IntOp.values()[rand.nextInt(LambdaUtilities.IntOp.values().length)]; + final int randomInit = rand.nextInt(); + final int randomOpnd = rand.nextInt(); + IntUnaryOperator iuo = LambdaUtilities.opIntUnaryOperator(op, randomOpnd); + IntStream isi = IntStream.iterate(randomInit, iuo); + int limitation = rand.nextInt( 1 << 10 ); + PrimitiveIterator.OfInt pi = isi.limit(limitation).iterator(); + + int next = randomInit; + + while(pi.hasNext()) { + assertEquals(pi.nextInt(), next); + next = iuo.applyAsInt(next); + } + + //below is time-consuming, need watch out if it's gonna affect our test + int start = rand.nextInt(1 << 12); + int end = rand.nextInt(1 << 12); + + if(start > end) { + start = start ^ end; + end = end ^ start; + start = end ^ start; + } + PrimitiveIterator.OfInt it1 = IntStream.range(start, end).iterator(); + while(it1.hasNext()) + assertEquals(it1.nextInt(), start++); + assertEquals(start, end); + } + + @Test + public void testIterator() { + int seed = rand.nextInt(); + int opRnd = rand.nextInt(); + int selected = rand.nextInt(LambdaUtilities.IntOp.values().length); + LambdaUtilities.IntOp op = LambdaUtilities.IntOp.values()[selected]; + int limitRnd = rand.nextInt(1 << 20); + IntUnaryOperator iuo = LambdaUtilities.opIntUnaryOperator(op, opRnd); + int[] arr = IntStream.iterate(seed, iuo).limit(limitRnd).toArray(); + assertEquals(arr.length, limitRnd); + } +}
--- a/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestScenario.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestScenario.java Wed Aug 14 15:53:13 2013 -0700 @@ -40,7 +40,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public enum DoubleStreamTestScenario implements OpTestCase.BaseStreamTestScenario { - STREAM_FOR_EACH(false) { + STREAM_FOR_EACH_WITH_CLOSE(false) { <T, S_IN extends BaseStream<T, S_IN>> void _run(TestData<T, S_IN> data, DoubleConsumer b, Function<S_IN, DoubleStream> m) { DoubleStream s = m.apply(data.stream()); @@ -48,6 +48,7 @@ s = s.sequential(); } s.forEach(b); + s.close(); } },
--- a/test/java/util/stream/bootlib/java/util/stream/IntStreamTestScenario.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/bootlib/java/util/stream/IntStreamTestScenario.java Wed Aug 14 15:53:13 2013 -0700 @@ -40,7 +40,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public enum IntStreamTestScenario implements OpTestCase.BaseStreamTestScenario { - STREAM_FOR_EACH(false) { + STREAM_FOR_EACH_WITH_CLOSE(false) { <T, S_IN extends BaseStream<T, S_IN>> void _run(TestData<T, S_IN> data, IntConsumer b, Function<S_IN, IntStream> m) { IntStream s = m.apply(data.stream()); @@ -48,6 +48,7 @@ s = s.sequential(); } s.forEach(b); + s.close(); } },
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/bootlib/java/util/stream/LambdaTestMode.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,71 @@ +/* + * 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. + */ +package java.util.stream; + +/** + * Runtime modes of test execution. + */ +public enum LambdaTestMode { + /** + * Execution mode with no particular runtime constraints. + */ + NORMAL, + + /** + * Execution mode where tests are executed for testing lambda serialization + * and deserialization. + * + * <p>This mode may be queried by tests or data supplied by data + * providers, which cannot otherwise be assigned to the test group + * <em>serialization-hostile</em>, to not execute or declare + * serialization-hostile code or data. + * + * <p>This mode is enabled if the boolean system property + * {@code org.openjdk.java.util.stream.sand.mode} is declared with a + * {@code true} value. + */ + SERIALIZATION; + + /** + * {@code true} if tests are executed in the mode for testing lambda + * Serialization ANd Deserialization (SAND). + */ + private static final boolean IS_LAMBDA_SERIALIZATION_MODE = + Boolean.getBoolean("org.openjdk.java.util.stream.sand.mode"); + + /** + * + * @return the mode of test execution. + */ + public static LambdaTestMode getMode() { + return IS_LAMBDA_SERIALIZATION_MODE ? SERIALIZATION : NORMAL; + } + + /** + * + * @return {@code true} if normal test mode. + */ + public static boolean isNormalMode() { + return getMode() == NORMAL; + } +}
--- a/test/java/util/stream/bootlib/java/util/stream/LoggingTestCase.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/bootlib/java/util/stream/LoggingTestCase.java Wed Aug 14 15:53:13 2013 -0700 @@ -37,6 +37,7 @@ /** * LoggingTestCase * + * @author Brian Goetz */ @Test public class LoggingTestCase extends Assert {
--- a/test/java/util/stream/bootlib/java/util/stream/LongStreamTestScenario.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/bootlib/java/util/stream/LongStreamTestScenario.java Wed Aug 14 15:53:13 2013 -0700 @@ -40,7 +40,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public enum LongStreamTestScenario implements OpTestCase.BaseStreamTestScenario { - STREAM_FOR_EACH(false) { + STREAM_FOR_EACH_WITH_CLOSE(false) { <T, S_IN extends BaseStream<T, S_IN>> void _run(TestData<T, S_IN> data, LongConsumer b, Function<S_IN, LongStream> m) { LongStream s = m.apply(data.stream()); @@ -48,6 +48,7 @@ s = s.sequential(); } s.forEach(b); + s.close(); } },
--- a/test/java/util/stream/bootlib/java/util/stream/OpTestCase.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/bootlib/java/util/stream/OpTestCase.java Wed Aug 14 15:53:13 2013 -0700 @@ -30,7 +30,6 @@ import java.util.Collections; import java.util.EnumMap; import java.util.EnumSet; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -41,7 +40,6 @@ import java.util.function.Consumer; import java.util.function.Function; -import org.testng.Assert; import org.testng.annotations.Test; /** @@ -397,14 +395,68 @@ // Exercise terminal operations - static enum TerminalTestScenario { - SINGLE_SEQUENTIAL, - SINGLE_SEQUENTIAL_SHORT_CIRCUIT, - SINGLE_PARALLEL, - ALL_SEQUENTIAL, - ALL_SEQUENTIAL_SHORT_CIRCUIT, - ALL_PARALLEL, - ALL_PARALLEL_SEQUENTIAL, + interface BaseTerminalTestScenario<U, R, S_OUT extends BaseStream<U, S_OUT>> { + boolean requiresSingleStageSource(); + + boolean requiresParallelSource(); + + default R run(Function<S_OUT, R> terminalF, S_OUT source, StreamShape shape) { + return terminalF.apply(source); + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + static enum TerminalTestScenario implements BaseTerminalTestScenario { + SINGLE_SEQUENTIAL(true, false), + + SINGLE_SEQUENTIAL_SHORT_CIRCUIT(true, false) { + @Override + public Object run(Function terminalF, BaseStream source, StreamShape shape) { + source = (BaseStream) chain(source, new ShortCircuitOp(shape)); + return terminalF.apply(source); + } + }, + + SINGLE_PARALLEL(true, true), + + ALL_SEQUENTIAL(false, false), + + ALL_SEQUENTIAL_SHORT_CIRCUIT(false, false) { + @Override + public Object run(Function terminalF, BaseStream source, StreamShape shape) { + source = (BaseStream) chain(source, new ShortCircuitOp(shape)); + return terminalF.apply(source); + } + }, + + ALL_PARALLEL(false, true), + + ALL_PARALLEL_SEQUENTIAL(false, false) { + @Override + public Object run(Function terminalF, BaseStream source, StreamShape shape) { + return terminalF.apply(source.sequential()); + } + }, + ; + + private final boolean requiresSingleStageSource; + private final boolean isParallel; + + TerminalTestScenario(boolean requiresSingleStageSource, boolean isParallel) { + this.requiresSingleStageSource = requiresSingleStageSource; + this.isParallel = isParallel; + } + + @Override + public boolean requiresSingleStageSource() { + return requiresSingleStageSource; + } + + @Override + public boolean requiresParallelSource() { + return isParallel; + } + } @SuppressWarnings({"rawtypes", "unchecked"}) @@ -415,8 +467,6 @@ R refResult; - Set<TerminalTestScenario> testSet = EnumSet.allOf(TerminalTestScenario.class); - ResultAsserter<R> resultAsserter = (act, exp, ord, par) -> LambdaTestHelpers.assertContentsEqual(act, exp); private ExerciseDataTerminalBuilder(TestData<T, S_IN> data, Function<S_IN, S_OUT> streamF, Function<S_OUT, R> terminalF) { @@ -442,27 +492,6 @@ return this; } - public ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT> without(TerminalTestScenario... tests) { - return without(Arrays.asList(tests)); - } - - public ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT> without(Collection<TerminalTestScenario> tests) { - testSet.removeAll(tests); - if (testSet.isEmpty()) { - throw new IllegalStateException("Terminal test scenario set is empty"); - } - return this; - } - - public ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT> with(TerminalTestScenario... tests) { - return with(Arrays.asList(tests)); - } - - public ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT> with(Collection<TerminalTestScenario> tests) { - testSet.addAll(tests); - return this; - } - // Build method public R exercise() { @@ -471,70 +500,36 @@ boolean isOrdered = StreamOpFlag.ORDERED.isKnown(ap.getStreamFlags()); StreamShape shape = ap.getOutputShape(); + EnumSet<TerminalTestScenario> tests = EnumSet.allOf(TerminalTestScenario.class); + // Sequentially collect the output that will be input to the terminal op Node<U> node = ap.evaluateToArrayNode(size -> (U[]) new Object[size]); if (refResult == null) { - // Sequentially collect the output that will be input to the terminal op - refResult = terminalF.apply((S_OUT) createPipeline(shape, node.spliterator(), - StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SIZED, - false)); - } else if (testSet.contains(TerminalTestScenario.SINGLE_SEQUENTIAL)) { - S_OUT source = (S_OUT) createPipeline(shape, node.spliterator(), - StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SIZED, - false); - R result = terminalF.apply(source); - LambdaTestHelpers.launderAssertion(() -> resultAsserter.assertResult(result, refResult, isOrdered, false), - () -> String.format("Single sequential: %s != %s", refResult, result)); - } - - if (testSet.contains(TerminalTestScenario.SINGLE_SEQUENTIAL_SHORT_CIRCUIT)) { + // Induce the reference result S_OUT source = (S_OUT) createPipeline(shape, node.spliterator(), StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SIZED, false); - // Force short-circuit - source = (S_OUT) chain(source, new ShortCircuitOp<U>(shape)); - R result = terminalF.apply(source); - LambdaTestHelpers.launderAssertion(() -> resultAsserter.assertResult(result, refResult, isOrdered, false), - () -> String.format("Single sequential pull: %s != %s", refResult, result)); - } - if (testSet.contains(TerminalTestScenario.SINGLE_PARALLEL)) { - S_OUT source = (S_OUT) createPipeline(shape, node.spliterator(), - StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SIZED, - true); - R result = terminalF.apply(source); - LambdaTestHelpers.launderAssertion(() -> resultAsserter.assertResult(result, refResult, isOrdered, true), - () -> String.format("Single parallel: %s != %s", refResult, result)); + refResult = (R) TerminalTestScenario.SINGLE_SEQUENTIAL.run(terminalF, source, shape); + tests.remove(TerminalTestScenario.SINGLE_SEQUENTIAL); } - if (testSet.contains(TerminalTestScenario.ALL_SEQUENTIAL)) { - // This may forEach or tryAdvance depending on the terminal op implementation - S_OUT source = streamF.apply(data.stream()); - R result = terminalF.apply(source); - LambdaTestHelpers.launderAssertion(() -> resultAsserter.assertResult(result, refResult, isOrdered, false), - () -> String.format("All sequential: %s != %s", refResult, result)); - } + for (BaseTerminalTestScenario test : tests) { + S_OUT source; + if (test.requiresSingleStageSource()) { + source = (S_OUT) createPipeline(shape, node.spliterator(), + StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SIZED, + test.requiresParallelSource()); + } + else { + source = streamF.apply(test.requiresParallelSource() + ? data.parallelStream() : data.stream()); + } - if (testSet.contains(TerminalTestScenario.ALL_SEQUENTIAL_SHORT_CIRCUIT)) { - S_OUT source = streamF.apply(data.stream()); - // Force short-circuit - source = (S_OUT) chain(source, new ShortCircuitOp<U>(shape)); - R result = terminalF.apply(source); - LambdaTestHelpers.launderAssertion(() -> resultAsserter.assertResult(result, refResult, isOrdered, false), - () -> String.format("All sequential pull: %s != %s", refResult, result)); - } + R result = (R) test.run(terminalF, source, shape); - if (testSet.contains(TerminalTestScenario.ALL_PARALLEL)) { - S_OUT source = streamF.apply(data.parallelStream()); - R result = terminalF.apply(source); - LambdaTestHelpers.launderAssertion(() -> resultAsserter.assertResult(result, refResult, isOrdered, true), - () -> String.format("All parallel: %s != %s", refResult, result)); - } - - if (testSet.contains(TerminalTestScenario.ALL_PARALLEL_SEQUENTIAL)) { - S_OUT source = streamF.apply(data.parallelStream()); - R result = terminalF.apply(source.sequential()); - LambdaTestHelpers.launderAssertion(() -> resultAsserter.assertResult(result, refResult, isOrdered, false), - () -> String.format("All parallel then sequential: %s != %s", refResult, result)); + LambdaTestHelpers.launderAssertion( + () -> resultAsserter.assertResult(result, refResult, isOrdered, test.requiresParallelSource()), + () -> String.format("%s: %s != %s", test, refResult, result)); } return refResult; @@ -596,10 +591,10 @@ // Test data - private class ShortCircuitOp<T> implements StatelessTestOp<T,T> { + static class ShortCircuitOp<T> implements StatelessTestOp<T,T> { private final StreamShape shape; - private ShortCircuitOp(StreamShape shape) { + ShortCircuitOp(StreamShape shape) { this.shape = shape; }
--- a/test/java/util/stream/bootlib/java/util/stream/StreamTestDataProvider.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/bootlib/java/util/stream/StreamTestDataProvider.java Wed Aug 14 15:53:13 2013 -0700 @@ -96,8 +96,14 @@ list.add(streamDataDescr("DelegatingStream(ArrayList):" + name, () -> new ArrayList<>(intsAsList).stream())); List<Integer> aList = new ArrayList<>(intsAsList); - list.add(collectionDataDescr("ArrayList.Sublist:" + name, - (ints.length) <= 1 ? aList.subList(0, 0) : aList.subList(1, ints.length / 2))); + if (LambdaTestMode.isNormalMode()) { + // Only include sub-lists for normal test execution mode + // This data is serialization-hostile since the state of the + // deserialized sub-list will be out of sync with the + // enclosing list. + list.add(collectionDataDescr("ArrayList.Sublist:" + name, + (ints.length) <= 1 ? aList.subList(0, 0) : aList.subList(1, ints.length / 2))); + } list.add(collectionDataDescr("LinkedList:" + name, new LinkedList<>(intsAsList))); list.add(collectionDataDescr("HashSet:" + name, new HashSet<>(intsAsList))); list.add(collectionDataDescr("LinkedHashSet:" + name, new LinkedHashSet<>(intsAsList)));
--- a/test/java/util/stream/bootlib/java/util/stream/StreamTestScenario.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/bootlib/java/util/stream/StreamTestScenario.java Wed Aug 14 15:53:13 2013 -0700 @@ -39,7 +39,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public enum StreamTestScenario implements OpTestCase.BaseStreamTestScenario { - STREAM_FOR_EACH(false) { + STREAM_FOR_EACH_WITH_CLOSE(false) { <T, U, S_IN extends BaseStream<T, S_IN>> void _run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, Stream<U>> m) { Stream<U> s = m.apply(data.stream()); @@ -47,6 +47,7 @@ s = s.sequential(); } s.forEach(b); + s.close(); } },
--- a/test/java/util/stream/boottest/java/util/stream/SliceSpliteratorTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/boottest/java/util/stream/SliceSpliteratorTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -131,7 +131,7 @@ SpliteratorTestHelper.testSpliterator(() -> { Spliterator<Integer> s = Arrays.spliterator(source.stream().toArray(Integer[]::new)); - + return new StreamSpliterators.UnorderedSliceSpliterator.OfRef<>(s, skip, limit); }, uca); };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/test/org/openjdk/tests/java/lang/invoke/GenBadMHTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2012, 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 org.openjdk.tests.java.lang.invoke; + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Handle; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandleInfo; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import static jdk.internal.org.objectweb.asm.Opcodes.*; +import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD; +import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +/** + * Regression test to ensure that the VM does not report invokevirtual method handles as invokespecial + */ +@Test(groups = { "serialization-hostile" }) +public class GenBadMHTest { + public void testMHForStringToUpper() throws IOException, ReflectiveOperationException { + MethodHandle mh = mhFor(Opcodes.H_INVOKEVIRTUAL, "java/lang/String", "toUpperCase", "()Ljava/lang/String;"); + MethodHandleInfo mhi = new MethodHandleInfo(mh); + assertTrue(mhi.getReferenceKind() == MethodHandleInfo.REF_invokeVirtual); + } + + public void testMHForAtomicLong() throws IOException, ReflectiveOperationException { + MethodHandle mh = mhFor(Opcodes.H_INVOKEVIRTUAL, "java/util/concurrent/atomic/AtomicLong", "addAndGet", "(J)J"); + MethodHandleInfo mhi = new MethodHandleInfo(mh); + assertTrue(mhi.getReferenceKind() == MethodHandleInfo.REF_invokeVirtual); + } + + private MethodHandle mhFor(int kind, String host, String method, String sig) throws IOException, ReflectiveOperationException { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cw.visit(51, ACC_SUPER + ACC_PUBLIC, "Foo", null, "java/lang/Object", new String[0]); + + // Constructor + MethodVisitor ctor = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + ctor.visitCode(); + ctor.visitVarInsn(ALOAD, 0); + ctor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + ctor.visitInsn(RETURN); + ctor.visitMaxs(-1, -1); + ctor.visitEnd(); + + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "foo", "()Ljava/lang/Object;", null, null); + mv.visitLdcInsn(new Handle(kind, host, method, sig)); + mv.visitInsn(ARETURN); + mv.visitMaxs(-1, -1); + mv.visitEnd(); + + cw.visitEnd(); + + byte[] bytes = cw.toByteArray(); + File f = new File("Foo.class"); + try (FileOutputStream fos = new FileOutputStream(f)) { + fos.write(bytes); + } + + List<URL> list = new ArrayList<>(); + list.add(new URL("file:" + f.getCanonicalFile().getParent().replace("\\", "/") + "/")); + Class<?> clazz = Class.forName("Foo", true, new URLClassLoader(list.toArray(new URL[list.size()]))); + Method m = clazz.getMethod("foo"); + return (MethodHandle) m.invoke(clazz.newInstance()); + } + + public void testPrivateInnerCtorRef() { + Supplier<Object> ctor = new Enclosing().foo(); + Enclosing.Inner inner = (Enclosing.Inner) ctor.get(); + assertNotNull(inner); + } + + static class Enclosing { + + public Supplier<Object> foo() { + return Inner::new; + } + + private class Inner { + private Inner() {} + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/SplittableRandomTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,367 @@ +/* + * 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. + */ +package org.openjdk.tests.java.util; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Spliterator; +import java.util.SplittableRandom; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.DoubleStream; +import java.util.stream.DoubleStreamTestScenario; +import java.util.stream.IntStream; +import java.util.stream.IntStreamTestScenario; +import java.util.stream.LongStream; +import java.util.stream.LongStreamTestScenario; +import java.util.stream.OpTestCase; +import java.util.stream.StreamSupport; +import java.util.stream.TestData; + +@Test +public class SplittableRandomTest extends OpTestCase { + + static class RandomBoxedSpliterator<T> implements Spliterator<T> { + final SplittableRandom rng; + long index; + final long fence; + final Function<SplittableRandom, T> rngF; + + RandomBoxedSpliterator(SplittableRandom rng, long index, long fence, Function<SplittableRandom, T> rngF) { + this.rng = rng; + this.index = index; + this.fence = fence; + this.rngF = rngF; + } + + public RandomBoxedSpliterator<T> trySplit() { + long i = index, m = (i + fence) >>> 1; + return (m <= i) ? null : + new RandomBoxedSpliterator<>(rng.split(), i, index = m, rngF); + } + + public long estimateSize() { + return fence - index; + } + + public int characteristics() { + return (Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.NONNULL | Spliterator.IMMUTABLE); + } + + @Override + public boolean tryAdvance(Consumer<? super T> consumer) { + if (consumer == null) throw new NullPointerException(); + long i = index, f = fence; + if (i < f) { + consumer.accept(rngF.apply(rng)); + index = i + 1; + return true; + } + return false; + } + } + + static final int SIZE = 1 << 16; + + // Ensure there is a range of a power of 2 + static final int[] BOUNDS = {256}; + static final int[] ORIGINS = {-16, 0, 16}; + + static <T extends Comparable<T>> ResultAsserter<Iterable<T>> randomAsserter(int size, T origin, T bound) { + return (act, exp, ord, par) -> { + int count = 0; + Set<Comparable<T>> values = new HashSet<>(); + for (Comparable<T> t : act) { + if (origin.compareTo(bound) < 0) { + assertTrue(t.compareTo(origin) >= 0); + assertTrue(t.compareTo(bound) < 0); + } + values.add(t); + count++; + } + assertEquals(count, size); + // Assert that at least one different result is produced + // For the size of the data it is highly improbable that this + // will cause a false negative (i.e. a false failure) + assertTrue(values.size() > 1); + }; + } + + @DataProvider(name = "ints") + public static Object[][] intsDataProvider() { + List<Object[]> data = new ArrayList<>(); + + // Function to create a stream using a RandomBoxedSpliterator + + Function<Function<SplittableRandom, Integer>, IntStream> rbsf = + sf -> StreamSupport.stream(new RandomBoxedSpliterator<>(new SplittableRandom(), 0, SIZE, sf), false). + mapToInt(i -> i); + + // Unbounded + + data.add(new Object[]{ + TestData.Factory.ofIntSupplier( + String.format("new SplittableRandom().ints().limit(%d)", SIZE), + () -> new SplittableRandom().ints().limit(SIZE)), + randomAsserter(SIZE, Integer.MAX_VALUE, 0) + }); + + data.add(new Object[]{ + TestData.Factory.ofIntSupplier( + String.format("new SplittableRandom().ints(%d)", SIZE), + () -> new SplittableRandom().ints(SIZE)), + randomAsserter(SIZE, Integer.MAX_VALUE, 0) + }); + + data.add(new Object[]{ + TestData.Factory.ofIntSupplier( + String.format("new RandomBoxedSpliterator(0, %d, sr -> sr.nextInt())", SIZE), + () -> rbsf.apply(sr -> sr.nextInt())), + randomAsserter(SIZE, Integer.MAX_VALUE, 0) + }); + + // Bounded + + for (int b : BOUNDS) { + for (int o : ORIGINS) { + final int origin = o; + final int bound = b; + + data.add(new Object[]{ + TestData.Factory.ofIntSupplier( + String.format("new SplittableRandom().ints(%d, %d).limit(%d)", origin, bound, SIZE), + () -> new SplittableRandom().ints(origin, bound).limit(SIZE)), + randomAsserter(SIZE, origin, bound) + }); + + data.add(new Object[]{ + TestData.Factory.ofIntSupplier( + String.format("new SplittableRandom().ints(%d, %d, %d)", SIZE, origin, bound), + () -> new SplittableRandom().ints(SIZE, origin, bound)), + randomAsserter(SIZE, origin, bound) + }); + + if (origin == 0) { + data.add(new Object[]{ + TestData.Factory.ofIntSupplier( + String.format("new RandomBoxedSpliterator(0, %d, sr -> sr.nextInt(%d))", SIZE, bound), + () -> rbsf.apply(sr -> sr.nextInt(bound))), + randomAsserter(SIZE, origin, bound) + }); + } + + data.add(new Object[]{ + TestData.Factory.ofIntSupplier( + String.format("new RandomBoxedSpliterator(0, %d, sr -> sr.nextInt(%d, %d))", SIZE, origin, bound), + () -> rbsf.apply(sr -> sr.nextInt(origin, bound))), + randomAsserter(SIZE, origin, bound) + }); + } + } + + return data.toArray(new Object[0][]); + } + + @Test(dataProvider = "ints") + public void testInts(TestData.OfInt data, ResultAsserter<Iterable<Integer>> ra) { + withData(data). + stream(s -> s). + without(IntStreamTestScenario.PAR_STREAM_TO_ARRAY_CLEAR_SIZED). + resultAsserter(ra). + exercise(); + } + + @DataProvider(name = "longs") + public static Object[][] longsDataProvider() { + List<Object[]> data = new ArrayList<>(); + + // Function to create a stream using a RandomBoxedSpliterator + + Function<Function<SplittableRandom, Long>, LongStream> rbsf = + sf -> StreamSupport.stream(new RandomBoxedSpliterator<>(new SplittableRandom(), 0, SIZE, sf), false). + mapToLong(i -> i); + + // Unbounded + + data.add(new Object[]{ + TestData.Factory.ofLongSupplier( + String.format("new SplittableRandom().longs().limit(%d)", SIZE), + () -> new SplittableRandom().longs().limit(SIZE)), + randomAsserter(SIZE, Long.MAX_VALUE, 0L) + }); + + data.add(new Object[]{ + TestData.Factory.ofLongSupplier( + String.format("new SplittableRandom().longs(%d)", SIZE), + () -> new SplittableRandom().longs(SIZE)), + randomAsserter(SIZE, Long.MAX_VALUE, 0L) + }); + + data.add(new Object[]{ + TestData.Factory.ofLongSupplier( + String.format("new RandomBoxedSpliterator(0, %d, sr -> sr.nextLong())", SIZE), + () -> rbsf.apply(sr -> sr.nextLong())), + randomAsserter(SIZE, Long.MAX_VALUE, 0L) + }); + + // Bounded + + for (int b : BOUNDS) { + for (int o : ORIGINS) { + final long origin = o; + final long bound = b; + + data.add(new Object[]{ + TestData.Factory.ofLongSupplier( + String.format("new SplittableRandom().longs(%d, %d).limit(%d)", origin, bound, SIZE), + () -> new SplittableRandom().longs(origin, bound).limit(SIZE)), + randomAsserter(SIZE, origin, bound) + }); + + data.add(new Object[]{ + TestData.Factory.ofLongSupplier( + String.format("new SplittableRandom().longs(%d, %d, %d)", SIZE, origin, bound), + () -> new SplittableRandom().longs(SIZE, origin, bound)), + randomAsserter(SIZE, origin, bound) + }); + + if (origin == 0) { + data.add(new Object[]{ + TestData.Factory.ofLongSupplier( + String.format("new RandomBoxedSpliterator(0, %d, sr -> sr.nextLong(%d))", SIZE, bound), + () -> rbsf.apply(sr -> sr.nextLong(bound))), + randomAsserter(SIZE, origin, bound) + }); + } + + data.add(new Object[]{ + TestData.Factory.ofLongSupplier( + String.format("new RandomBoxedSpliterator(0, %d, sr -> sr.nextLong(%d, %d))", SIZE, origin, bound), + () -> rbsf.apply(sr -> sr.nextLong(origin, bound))), + randomAsserter(SIZE, origin, bound) + }); + } + } + + return data.toArray(new Object[0][]); + } + + @Test(dataProvider = "longs") + public void testLongs(TestData.OfLong data, ResultAsserter<Iterable<Long>> ra) { + withData(data). + stream(s -> s). + without(LongStreamTestScenario.PAR_STREAM_TO_ARRAY_CLEAR_SIZED). + resultAsserter(ra). + exercise(); + } + + @DataProvider(name = "doubles") + public static Object[][] doublesDataProvider() { + List<Object[]> data = new ArrayList<>(); + + // Function to create a stream using a RandomBoxedSpliterator + + Function<Function<SplittableRandom, Double>, DoubleStream> rbsf = + sf -> StreamSupport.stream(new RandomBoxedSpliterator<>(new SplittableRandom(), 0, SIZE, sf), false). + mapToDouble(i -> i); + + // Unbounded + + data.add(new Object[]{ + TestData.Factory.ofDoubleSupplier( + String.format("new SplittableRandom().doubles().limit(%d)", SIZE), + () -> new SplittableRandom().doubles().limit(SIZE)), + randomAsserter(SIZE, Double.MAX_VALUE, 0d) + }); + + data.add(new Object[]{ + TestData.Factory.ofDoubleSupplier( + String.format("new SplittableRandom().doubles(%d)", SIZE), + () -> new SplittableRandom().doubles(SIZE)), + randomAsserter(SIZE, Double.MAX_VALUE, 0d) + }); + + data.add(new Object[]{ + TestData.Factory.ofDoubleSupplier( + String.format("new RandomBoxedSpliterator(0, %d, sr -> sr.nextDouble())", SIZE), + () -> rbsf.apply(sr -> sr.nextDouble())), + randomAsserter(SIZE, Double.MAX_VALUE, 0d) + }); + + // Bounded + + for (int b : BOUNDS) { + for (int o : ORIGINS) { + final double origin = o; + final double bound = b; + + data.add(new Object[]{ + TestData.Factory.ofDoubleSupplier( + String.format("new SplittableRandom().doubles(%f, %f).limit(%d)", origin, bound, SIZE), + () -> new SplittableRandom().doubles(origin, bound).limit(SIZE)), + randomAsserter(SIZE, origin, bound) + }); + + data.add(new Object[]{ + TestData.Factory.ofDoubleSupplier( + String.format("new SplittableRandom().doubles(%d, %f, %f)", SIZE, origin, bound), + () -> new SplittableRandom().doubles(SIZE, origin, bound)), + randomAsserter(SIZE, origin, bound) + }); + + if (origin == 0) { + data.add(new Object[]{ + TestData.Factory.ofDoubleSupplier( + String.format("new RandomBoxedSpliterator(0, %d, sr -> sr.nextDouble(%f))", SIZE, bound), + () -> rbsf.apply(sr -> sr.nextDouble(bound))), + randomAsserter(SIZE, origin, bound) + }); + } + + data.add(new Object[]{ + TestData.Factory.ofDoubleSupplier( + String.format("new RandomBoxedSpliterator(0, %d, sr -> sr.nextDouble(%f, %f))", SIZE, origin, bound), + () -> rbsf.apply(sr -> sr.nextDouble(origin, bound))), + randomAsserter(SIZE, origin, bound) + }); + } + } + + return data.toArray(new Object[0][]); + } + + @Test(dataProvider = "doubles") + public void testDoubles(TestData.OfDouble data, ResultAsserter<Iterable<Double>> ra) { + withData(data). + stream(s -> s). + without(DoubleStreamTestScenario.PAR_STREAM_TO_ARRAY_CLEAR_SIZED). + resultAsserter(ra). + exercise(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/regex/PatternTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,140 @@ +/* + * 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. + */ +package org.openjdk.tests.java.util.regex; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; +import java.util.regex.Pattern; +import java.util.stream.LambdaTestHelpers; +import java.util.stream.OpTestCase; +import java.util.stream.Stream; +import java.util.stream.TestData; + +@Test +public class PatternTest extends OpTestCase { + + @DataProvider(name = "Stream<String>") + public static Object[][] makeStreamTestData() { + List<Object[]> data = new ArrayList<>(); + + String description = ""; + String input = "awgqwefg1fefw4vssv1vvv1"; + Pattern pattern = Pattern.compile("4"); + List<String> expected = new ArrayList<>(); + expected.add("awgqwefg1fefw"); + expected.add("vssv1vvv1"); + + // Must match the type signature of the consumer of this data, testStrings + // String, String, Pattern, List<String> + data.add(new Object[]{description, input, pattern, expected}); + + input = "afbfq\u00a3abgwgb\u00a3awngnwggw\u00a3a\u00a3ahjrnhneerh"; + pattern = Pattern.compile("\u00a3a"); + expected = new ArrayList<>(); + expected.add("afbfq"); + expected.add("bgwgb"); + expected.add("wngnwggw"); + expected.add(""); + expected.add("hjrnhneerh"); + + data.add(new Object[]{description, input, pattern, expected}); + + + input = "awgqwefg1fefw4vssv1vvv1"; + pattern = Pattern.compile("1"); + expected = new ArrayList<>(); + expected.add("awgqwefg"); + expected.add("fefw4vssv"); + expected.add("vvv"); + + data.add(new Object[]{description, input, pattern, expected}); + + + input = "a\u4ebafg1fefw\u4eba4\u9f9cvssv\u9f9c1v\u672c\u672cvv"; + pattern = Pattern.compile("1"); + expected = new ArrayList<>(); + expected.add("a\u4ebafg"); + expected.add("fefw\u4eba4\u9f9cvssv\u9f9c"); + expected.add("v\u672c\u672cvv"); + + data.add(new Object[]{description, input, pattern, expected}); + + + input = "1\u56da23\u56da456\u56da7890"; + pattern = Pattern.compile("\u56da"); + expected = new ArrayList<>(); + expected.add("1"); + expected.add("23"); + expected.add("456"); + expected.add("7890"); + + data.add(new Object[]{description, input, pattern, expected}); + + + input = "1\u56da23\u9f9c\u672c\u672c\u56da456\u56da\u9f9c\u672c7890"; + pattern = Pattern.compile("\u56da"); + expected = new ArrayList<>(); + expected.add("1"); + expected.add("23\u9f9c\u672c\u672c"); + expected.add("456"); + expected.add("\u9f9c\u672c7890"); + + data.add(new Object[]{description, input, pattern, expected}); + + + input = ""; + pattern = Pattern.compile("\u56da"); + expected = new ArrayList<>(); + + data.add(new Object[]{description, input, pattern, expected}); + + + description = "Multiple separators"; + input = "This is,testing: with\tdifferent separators."; + pattern = Pattern.compile("[ \t,:.]"); + expected = new ArrayList<>(); + expected.add("This"); + expected.add("is"); + expected.add("testing"); + expected.add(""); + expected.add("with"); + expected.add("different"); + expected.add("separators"); + + data.add(new Object[] {description, input, pattern, expected}); + return data.toArray(new Object[0][]); + } + + @Test(dataProvider = "Stream<String>") + public void testStrings(String description, String input, Pattern pattern, List<String> expected) { + Supplier<Stream<String>> ss = () -> pattern.splitAsStream(input); + withData(TestData.Factory.ofSupplier(description, ss)) + .stream(LambdaTestHelpers.identity()) + .expectedResult(expected) + .exercise(); + } +}
--- a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ExplodeOpTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ExplodeOpTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -64,6 +64,9 @@ result = exerciseOps(data, s -> s.flatMap(mfNull)); assertEquals(0, result.size()); + result = exerciseOps(data, s-> s.flatMap(e -> Stream.empty())); + assertEquals(0, result.size()); + exerciseOps(data, s -> s.flatMap(mfLt)); exerciseOps(data, s -> s.flatMap(integerRangeMapper)); exerciseOps(data, s -> s.flatMap((Integer e) -> IntStream.range(0, e).boxed().limit(10)));
--- a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ForEachOpTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ForEachOpTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -69,7 +69,7 @@ }; } - @Test + @Test(groups = { "serialization-hostile" }) public void testForEachOrdered() { List<Integer> input = countTo(10000); TestData.OfRef<Integer> data = TestData.Factory.ofCollection("[1, 10000]", input); @@ -116,7 +116,7 @@ // - @Test + @Test(groups = { "serialization-hostile" }) public void testIntForEachOrdered() { List<Integer> input = countTo(10000); TestData.OfInt data = TestData.Factory.ofIntSupplier("[1, 10000]", @@ -164,7 +164,7 @@ // - @Test + @Test(groups = { "serialization-hostile" }) public void testLongForEachOrdered() { List<Integer> input = countTo(10000); TestData.OfLong data = TestData.Factory.ofLongSupplier("[1, 10000]", @@ -212,7 +212,7 @@ // - @Test + @Test(groups = { "serialization-hostile" }) public void testDoubleForEachOrdered() { List<Integer> input = countTo(10000); TestData.OfDouble data = TestData.Factory.ofDoubleSupplier("[1, 10000]",
--- a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/SliceOpTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/SliceOpTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -147,7 +147,8 @@ return Math.max(0, dataSize - skip); } - @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) + @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class, + groups = { "serialization-hostile" }) public void testSkipOps(String name, TestData.OfRef<Integer> data) { List<Integer> skips = sizes(data.size()); @@ -169,7 +170,8 @@ } } - @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) + @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class, + groups = { "serialization-hostile" }) public void testSkipLimitOps(String name, TestData.OfRef<Integer> data) { List<Integer> skips = sizes(data.size()); List<Integer> limits = skips; @@ -242,7 +244,8 @@ testSkipLimitOps("testSkipLimitOpsWithNonSplittingSpliterator", data); } - @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) + @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class, + groups = { "serialization-hostile" }) public void testLimitOps(String name, TestData.OfRef<Integer> data) { List<Integer> limits = sizes(data.size());
--- a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/StreamBuilderTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/StreamBuilderTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -97,7 +97,7 @@ checkISE(() -> sb.build()); } - @Test(dataProvider = "sizes") + @Test(dataProvider = "sizes", groups = { "serialization-hostile" }) public void testStreamBuilder(int size) { testStreamBuilder(size, (s) -> { Stream.Builder<Integer> sb = Stream.builder(); @@ -159,7 +159,7 @@ checkISE(() -> sb.build()); } - @Test(dataProvider = "sizes") + @Test(dataProvider = "sizes", groups = { "serialization-hostile" }) public void testIntStreamBuilder(int size) { testIntStreamBuilder(size, (s) -> { IntStream.Builder sb = IntStream.builder(); @@ -221,7 +221,7 @@ checkISE(() -> sb.build()); } - @Test(dataProvider = "sizes") + @Test(dataProvider = "sizes", groups = { "serialization-hostile" }) public void testLongStreamBuilder(int size) { testLongStreamBuilder(size, (s) -> { LongStream.Builder sb = LongStream.builder(); @@ -282,7 +282,7 @@ checkISE(() -> sb.build()); } - @Test(dataProvider = "sizes") + @Test(dataProvider = "sizes", groups = { "serialization-hostile" }) public void testDoubleStreamBuilder(int size) { testDoubleStreamBuilder(size, (s) -> { DoubleStream.Builder sb = DoubleStream.builder();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/StreamCloseTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2012, 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 org.openjdk.tests.java.util.stream; + +import java.util.Arrays; +import java.util.stream.OpTestCase; +import java.util.stream.Stream; + +import org.testng.annotations.Test; + +import static java.util.stream.LambdaTestHelpers.countTo; + +/** + * StreamCloseTest + * + * @author Brian Goetz + */ +@Test(groups = { "serialization-hostile" }) +public class StreamCloseTest extends OpTestCase { + public void testEmptyCloseHandler() { + try (Stream<Integer> ints = countTo(100).stream()) { + ints.forEach(i -> {}); + } + } + + public void testOneCloseHandler() { + final boolean[] holder = new boolean[1]; + Runnable closer = () -> { holder[0] = true; }; + + try (Stream<Integer> ints = countTo(100).stream()) { + ints.onClose(closer); + ints.forEach(i -> {}); + } + assertTrue(holder[0]); + + Arrays.fill(holder, false); + try (Stream<Integer> ints = countTo(100).stream().onClose(closer)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0]); + + Arrays.fill(holder, false); + try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(closer)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0]); + + Arrays.fill(holder, false); + try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(closer).filter(e -> true)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0]); + } + + public void testTwoCloseHandlers() { + final boolean[] holder = new boolean[2]; + Runnable close1 = () -> { holder[0] = true; }; + Runnable close2 = () -> { holder[1] = true; }; + + try (Stream<Integer> ints = countTo(100).stream()) { + ints.onClose(close1).onClose(close2); + ints.forEach(i -> {}); + } + assertTrue(holder[0] && holder[1]); + + Arrays.fill(holder, false); + try (Stream<Integer> ints = countTo(100).stream().onClose(close1).onClose(close2)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0] && holder[1]); + + Arrays.fill(holder, false); + try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0] && holder[1]); + + Arrays.fill(holder, false); + try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2).filter(e -> true)) { + ints.forEach(i -> {}); + } + assertTrue(holder[0] && holder[1]); + } + + public void testCascadedExceptions() { + final boolean[] holder = new boolean[3]; + boolean caught = false; + Runnable close1 = () -> { holder[0] = true; throw new RuntimeException("1"); }; + Runnable close2 = () -> { holder[1] = true; throw new RuntimeException("2"); }; + Runnable close3 = () -> { holder[2] = true; throw new RuntimeException("3"); }; + + try (Stream<Integer> ints = countTo(100).stream()) { + ints.onClose(close1).onClose(close2).onClose(close3); + ints.forEach(i -> {}); + } + catch (RuntimeException e) { + assertCascaded(e, 3); + assertTrue(holder[0] && holder[1] && holder[2]); + caught = true; + } + assertTrue(caught); + + Arrays.fill(holder, false); + caught = false; + try (Stream<Integer> ints = countTo(100).stream().onClose(close1).onClose(close2).onClose(close3)) { + ints.forEach(i -> {}); + } + catch (RuntimeException e) { + assertCascaded(e, 3); + assertTrue(holder[0] && holder[1] && holder[2]); + caught = true; + } + assertTrue(caught); + + caught = false; + Arrays.fill(holder, false); + try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2).onClose(close3)) { + ints.forEach(i -> {}); + } + catch (RuntimeException e) { + assertCascaded(e, 3); + assertTrue(holder[0] && holder[1] && holder[2]); + caught = true; + } + assertTrue(caught); + + caught = false; + Arrays.fill(holder, false); + try (Stream<Integer> ints = countTo(100).stream().filter(e -> true).onClose(close1).onClose(close2).filter(e -> true).onClose(close3)) { + ints.forEach(i -> {}); + } + catch (RuntimeException e) { + assertCascaded(e, 3); + assertTrue(holder[0] && holder[1] && holder[2]); + caught = true; + } + assertTrue(caught); + } + + private void assertCascaded(RuntimeException e, int n) { + assertTrue(e.getMessage().equals("1")); + assertTrue(e.getSuppressed().length == n - 1); + for (int i=0; i<n-1; i++) + assertTrue(e.getSuppressed()[i].getMessage().equals(String.valueOf(i + 2))); + } +}
--- a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/TabulatorsTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/TabulatorsTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -52,6 +53,7 @@ import org.testng.annotations.Test; +import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.groupingByConcurrent; import static java.util.stream.Collectors.partitioningBy; @@ -603,4 +605,17 @@ new PartitionAssertion<>(classifier, new ReduceAssertion<>(0, LambdaTestHelpers.identity(), Integer::sum))); } + + @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) + public void testComposeFinisher(String name, TestData.OfRef<Integer> data) throws ReflectiveOperationException { + List<Integer> asList = exerciseTerminalOps(data, s -> s.collect(toList())); + List<Integer> asImmutableList = exerciseTerminalOps(data, s -> s.collect(collectingAndThen(toList(), Collections::unmodifiableList))); + assertEquals(asList, asImmutableList); + try { + asImmutableList.add(0); + fail("Expecting immutable result"); + } + catch (UnsupportedOperationException ignored) { } + } + }
--- a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ToArrayOpTest.java Tue Aug 13 10:42:37 2013 -0700 +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/ToArrayOpTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -165,7 +165,8 @@ }; } - @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) + @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class, + groups = { "serialization-hostile" }) public void testStatefulOpPermutations(String name, TestData.OfRef<Integer> data) { for (Function<Stream<Integer>, Stream<Integer>> f : statefulOpPermutations) { withData(data).terminal(f, s -> s.toArray())
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/lambda/InvokespecialMethodKindTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2012, 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.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandleInfo; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.function.Function; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/** + * InvokespecialMethodKindTest + */ +@Test +public class InvokespecialMethodKindTest { + public void testStringToUpper() throws ReflectiveOperationException { + MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "toUpperCase", + MethodType.methodType(String.class)); + MethodHandleInfo mhi = new MethodHandleInfo(mh); + assertEquals(mhi.getReferenceKind(), MethodHandleInfo.REF_invokeVirtual); + } + + public void testSerializeStringToUpper() throws IOException, ClassNotFoundException { + Function<String, String> f = (Function<String, String> & Serializable) String::toUpperCase; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(f); + oos.flush(); + byte[] bs = bos.toByteArray(); + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bs)); + @SuppressWarnings("unchecked") + Function<String, String> ff = (Function<String, String>) ois.readObject(); + assertEquals("FOO", ff.apply("fOo")); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/lambda/TestInterfaceBridges.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,152 @@ +/* + * 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.List; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/** + * TestInterfaceBridges + * + * @author Brian Goetz + */ +@Test +public class TestInterfaceBridges { + public void testCovariantOverrideLambda() { + B b = () -> "Foo"; + assertEquals("Foo", b.make()); + assertEquals("Foo", ((A) b).make()); + } + + public void testCovariantOverrideIC() { + B b = new B() { + @Override + public String make() { + return "Foo"; + } + }; + assertEquals("Foo", b.make()); + assertEquals("Foo", ((A) b).make()); + } + + public static interface A { + Object make(); + } + + public static interface B extends A { + String make(); + } + + public void testCovariantMerge() { + D d = () -> "Foo"; + assertEquals("Foo", d.make()); + assertEquals("Foo", ((A) d).make()); + assertEquals("Foo", ((C) d).make()); + } + + public static interface C { + String make(); + } + + public static interface D extends A, C { } + + public void testGenericSpecialization() { + F<String, String> fss = x->x; + assertEquals("Foo", fss.m("Foo")); + } + + interface F<A,B> { + B m(A a); + } + + public void testGenericFboundSpecialization() { + G<String> gs = x->x; + assertEquals("Foo", gs.m("Foo")); + } + + interface G<X extends Comparable<X>> { + X m(X x); + } + + public void testGenericSpecializationInSubclass() { + F_S_S fss = (s)->s; + assertEquals("Foo", fss.m("Foo")); + assertEquals("Foo", ((F<String,String>)fss).m("Foo")); + } + + interface F_S_S extends F<String,String> {} + + public void testCovariantMultiMerge() { + M1234 m1234 = ()->1; + assertEquals(1, (int)m1234.m()); + assertEquals(1, (int)((M1)m1234).m()); + assertEquals(1, (int)((M2)m1234).m()); + assertEquals(1, (int)((M3)m1234).m()); + assertEquals(1, (int)((M4)m1234).m()); + } + + interface M1 {Number m();} + interface M2 {Integer m();} + interface M3 {java.io.Serializable m();} + interface M4 {Object m();} + + interface M1234 extends M1, M2, M3, M4 {} + + public void testDefaultMerge() { + DD12<String> dds = s->1; + assertEquals(1, ((DD1<String>)dds).m("")); + assertEquals(1, ((DD2)dds).m("")); + } + + interface DD1<T> { int m(T arg); } + interface DD2 { default int m(String arg) { return 42; } } + interface DD12<T> extends DD1<T>, DD2 {} + + public void testDefaultNoMerge() { + DD12<Integer> dds = s->1; + assertEquals(1, ((DD1<Integer>)dds).m(1)); + assertEquals(42, ((DD2)dds).m("")); + } + + public void testRawMerge() { + List<String> ls = null; + R12 r12 = l->"Foo"; + assertEquals("Foo", r12.m(ls)); + assertEquals("Foo", ((R1)r12).m(ls)); + assertEquals("Foo", ((R2)r12).m(ls)); + } + + interface R1 { + Object m(List<String> ls); + } + + @SuppressWarnings("rawtypes") + interface R2 { + String m(List l); + } + + interface R12 extends R1, R2 {} +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/lambda/vm/FDSeparateCompilationTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2012 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 vm; + +import java.util.ArrayList; +import java.util.Set; + +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.AfterSuite; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import separate.TestHarness; +import separate.Compiler; +import shapegen.ClassCase; +import shapegen.Hierarchy; +import shapegen.HierarchyGenerator; + +import static org.testng.Assert.fail; +import static separate.SourceModel.AbstractMethod; +import static separate.SourceModel.AccessFlag; +import static separate.SourceModel.Class; +import static separate.SourceModel.ConcreteMethod; +import static separate.SourceModel.DefaultMethod; +import static separate.SourceModel.Interface; +import static separate.SourceModel.Method; +import static separate.SourceModel.Type; + +public class FDSeparateCompilationTest extends TestHarness { + + private static String EMPTY = "\"\""; + + public FDSeparateCompilationTest() { + super(false, true); + } + + @DataProvider(name = "allShapes", parallel = true) + public Object[][] hierarchyGenerator() { + ArrayList<Object[]> allCases = new ArrayList<>(); + + HierarchyGenerator hg = new HierarchyGenerator(); + hg.getOK().stream().map(x->new Object[]{x}).forEach(allCases::add); + hg.getErr().stream().map(x -> new Object[] { x }).forEach(allCases::add); + return allCases.toArray(new Object[0][]); + } + + // The expected value obtained when invoking the method from the specified + // class. If returns null, then an AbstractMethodError is expected. + private static String getExpectedResult(ClassCase cc) { + Set<ClassCase> provs = cc.get_mprov(); + if (cc.get_mres() != null) { + return cc.get_mres().getName(); + } else if (provs != null && provs.size() == 1) { + ClassCase cand = provs.iterator().next(); + switch (cand.kind) { + case CCONCRETE: + case IDEFAULT: + return cand.getName(); + case CNONE: + case IVAC: + return getExpectedResult(cand); + } + } + return null; + } + + private static final ConcreteMethod canonicalMethod = new ConcreteMethod( + "String", "m", "returns " + EMPTY + ";", AccessFlag.PUBLIC); + + @Test(groups = "vm", dataProvider = "allShapes") + public void separateCompilationTest(Hierarchy hs) { + ClassCase cc = hs.root; + Type type = sourceTypeFrom(hs.root); + + Class specimen = null; + if (type instanceof Class) { + Class ctype = (Class)type; + if (ctype.isAbstract()) { + specimen = new Class("Test" + ctype.getName(), ctype); + } else { + specimen = ctype; + } + } else { + specimen = new Class("Test" + type.getName(), (Interface)type); + } + + String value = getExpectedResult(cc); + if (value != null) { + assertInvokeVirtualEquals(value, specimen, canonicalMethod, EMPTY); + } else { + assertThrows(AbstractMethodError.class, specimen, + canonicalMethod, EMPTY); + } + } + + @AfterMethod + public void printCaseError(ITestResult result) { + if (result.getStatus() == ITestResult.FAILURE) { + Hierarchy hs = (Hierarchy)result.getParameters()[0]; + System.out.println("Separate compilation case " + hs); + printCaseDetails(hs); + } + } + + @AfterSuite + public void cleanupCompilerCache() { + Compiler.purgeCache(); + } + + private void printCaseDetails(Hierarchy hs) { + String exp = getExpectedResult(hs.root); + hs.getDescription().forEach( + x -> System.out.println(" " + x)); + if (exp != null) { + System.out.println(" Expected \"" + exp + "\""); + } else { + System.out.println(" Expected AbstractMethodError"); + } + } + + private Type sourceTypeFrom(ClassCase cc) { + Type type = null; + + if (cc.isInterface()) { + Interface iface = new Interface(cc.getName()); + for (ClassCase scc : cc.getInterfaces()) { + Interface supertype = (Interface)sourceTypeFrom(scc); + iface.addSuperType(supertype); + } + type = iface; + } else { + Class cls = new Class(cc.getName()); + if (cc.hasSuperclass()) { + Class superc = (Class)sourceTypeFrom(cc.getSuperclass()); + cls.setSuperClass(superc); + } + for (ClassCase scc : cc.getInterfaces()) { + Interface supertype = (Interface)sourceTypeFrom(scc); + cls.addSuperType(supertype); + } + if (cc.isAbstract()) { + cls.getAccessFlags().add(AccessFlag.ABSTRACT); + } + type = cls; + } + Method method = methodFrom(cc); + if (method != null) { + type.addMethod(method); + } + return type; + } + + private Method methodFrom(ClassCase cc) { + switch (cc.kind) { + case IVAC: + case CNONE: return null; + case IPRESENT: + case CABSTRACT: + return new AbstractMethod("String", "m", AccessFlag.PUBLIC); + case IDEFAULT: + return new DefaultMethod( + "String", "m", "return \"" + cc.getName() + "\";"); + case CCONCRETE: + return new ConcreteMethod( + "String", "m", "return \"" + cc.getName() + "\";", + AccessFlag.PUBLIC); + default: + fail("Unknown method type in class"); + return null; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/lambda/vm/JVMBridgingTest.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2012 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 vm; + +import org.testng.annotations.Test; +import separate.AttributeInjector; +import separate.Compiler; +import separate.TestHarness; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; +import static separate.SourceModel.AbstractMethod; +import static separate.SourceModel.AccessFlag; +import static separate.SourceModel.Class; +import static separate.SourceModel.ConcreteMethod; +import static separate.SourceModel.Interface; +import static separate.SourceModel.MethodParameter; +import static separate.SourceModel.Type; +import static separate.SourceModel.TypeParameter; + +public class JVMBridgingTest extends TestHarness { + public JVMBridgingTest() { + super(false, false); + } + + private static final String STATIC_CLASS = "S"; + private static final String STATIC_METHOD_NAME = "foo"; + private static final int SUCCESS_VALUE = 99; + + enum BridgeMode { AUTO_BRIDGE, NO_AUTO_BRIDGE }; + + private boolean equalsSuccessValue( + BridgeMode mode, Type[] compUnits) { + Compiler compiler = new Compiler(); + + if (mode == BridgeMode.AUTO_BRIDGE) { + compiler.addPostprocessor( + new AttributeInjector("RequiresBridges", new byte[0])); + } + + ClassLoader cl = compiler.compile(compUnits); + + try { + java.lang.Class<?> S_class = + java.lang.Class.forName(STATIC_CLASS, true, cl); + assertNotNull(S_class); + java.lang.reflect.Method m = S_class.getMethod(STATIC_METHOD_NAME); + assertNotNull(m); + Integer res = (Integer)m.invoke(null); + return res.intValue() == SUCCESS_VALUE; + } catch (ClassNotFoundException e) { + fail("Could not find class"); + } catch (NoSuchMethodException e) { + fail("Could not find method"); + } catch (IllegalAccessException e) { + fail("Unexpected IllegalAccessException"); + } catch (java.lang.reflect.InvocationTargetException e) { + return false; /* A valid situation: the method may not exist */ + } finally { + compiler.cleanup(); + } + fail("Unexpected result"); + return false; // unreachable + } + + public Type[] setupTraditionalBridgeTest() { + Interface I = new Interface("I", new TypeParameter("T"), + new AbstractMethod("int", "m", new MethodParameter("T", "t"))); + Class C = new Class("C", I.with("String"), new ConcreteMethod( + "int", "m", "return " + SUCCESS_VALUE + ";", AccessFlag.PUBLIC, + new MethodParameter("String", "s"))); + ConcreteMethod mStub = new ConcreteMethod( + "int", "m", "return 0;", AccessFlag.PUBLIC, + new MethodParameter("Object", "o")); + Class Cstub = new Class("C", mStub); + Class S = new Class(STATIC_CLASS, new ConcreteMethod( + "int", STATIC_METHOD_NAME, "return (new C()).m(\"\");", + AccessFlag.PUBLIC, AccessFlag.STATIC)); + S.addCompilationDependency(Cstub); + S.addCompilationDependency(mStub); + + return new Type[] { S, C, I }; + } + + private void runTest(Type[] compUnits) { + assertFalse(equalsSuccessValue(BridgeMode.NO_AUTO_BRIDGE, compUnits)); + assertTrue(equalsSuccessValue(BridgeMode.AUTO_BRIDGE, compUnits)); + } + + @Test(groups = "vm_prototype") + public void testTraditionalBridge() { + runTest(setupTraditionalBridgeTest()); + } + + public Type[] setupCovarReturnTest() { + ConcreteMethod numberRet = new ConcreteMethod( + "Number", "m", "return 0;", AccessFlag.PUBLIC); + Class C = new Class("C", numberRet); + Class D = new Class("D", C, new ConcreteMethod("Integer", "m", + "return " + SUCCESS_VALUE + ";", AccessFlag.PUBLIC)); + + Class Dstub = new Class("D", numberRet); + Class S = new Class(STATIC_CLASS, + new ConcreteMethod("Integer", STATIC_METHOD_NAME, + "return (Integer)(new D()).m();", + AccessFlag.PUBLIC, AccessFlag.STATIC)); + S.addCompilationDependency(Dstub); + S.addCompilationDependency(numberRet); + + return new Type[] { S, D, C }; + } + + @Test(groups = "vm_prototype") + public void testCovarReturn() { + runTest(setupCovarReturnTest()); + } + + @Test(groups = "vm") + public void testNoBridgeNeeded() { + Interface I = new Interface("I", new TypeParameter("T"), + new AbstractMethod("int", "m", new MethodParameter("T", "t"))); + + ConcreteMethod implementing = new ConcreteMethod("int", "m", + "return 0;", AccessFlag.PUBLIC, new MethodParameter("String", "s")); + ConcreteMethod matching = new ConcreteMethod("int", "m", "return 99;", + AccessFlag.PUBLIC, new MethodParameter("Object", "o")); + + Class C = new Class("C", I.with("String"), implementing, matching); + + Class S = new Class(STATIC_CLASS, new ConcreteMethod( + "int", STATIC_METHOD_NAME, "return (new C()).m(new Object());", + AccessFlag.PUBLIC, AccessFlag.STATIC)); + S.addCompilationDependency(C); + S.addCompilationDependency(matching); + + Type[] compUnits = new Type[] { S, C, I }; + assertTrue(equalsSuccessValue(BridgeMode.AUTO_BRIDGE, compUnits)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/sqeutil/LambdaUtilities.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,674 @@ +/* + * Copyright (c) 2012, 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. + */ + +/** + * @summary This class provide basic support for lambda syntax + * @(#) LambdaUtilities.java + * @author Tristan Yan + */ + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigDecimal; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.*; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +public class LambdaUtilities { + + public static enum CharType { + DIGIT, LOWERCASE, UPPERCASE, SPECIAL + } + + public static enum StringPredicateType { + START_WTIH, NOT_START_WITH, MORE_THAN_LEN, LESS_THAN_LEN + } + + public static enum IntOp { + ADD, SUBTRACT, MULTIPLY, DIVIDE, MOD + } + + public static IntPredicate randomIntPredicate(boolean isUP, int limit) { + if (isUP) { + return i -> i >= limit; + } else { + return i -> i < limit; + } + } + + public static Predicate<Integer> randomIntegerPredicate(boolean isUP, int limit) { + if (isUP) { + return i -> i >= limit; + } else { + return i -> i < limit; + } + } + + public static LongPredicate randomLongPredicate(boolean isUP, long limit) { + if (isUP) { + return i -> i >= limit; + } else { + return i -> i < limit; + } + } + + public static Predicate<Path> startPathPredicate(Path start) { + return p -> p.startsWith(start); + } + + public static <T> Predicate<T> randomGenericPredicate(boolean isUp, T value, Comparator<T> c) { + if (isUp) { + return emp -> c.compare(emp, value) >= 0; + }else { + return emp -> c.compare(emp, value) < 0; + } + } + + public static Predicate<StringBuilder> randomSBPredicate(StringPredicateType type, String value) { + switch (type) { + case START_WTIH: + return sb -> Character.isDigit(sb.charAt(0)); + case NOT_START_WITH: + return sb -> Character.isLowerCase(sb.charAt(0)); + case MORE_THAN_LEN: + return sb -> Character.isUpperCase(sb.charAt(0)); + default: + return sb -> !Character.isLetterOrDigit(sb.charAt(0)); + } + } + + public static Predicate<? extends CharSequence> randomSBPredicate(CharType startType, + boolean first) { + switch (startType) { + case DIGIT: + return sb -> Character.isDigit(sb.charAt(first ? 0 : sb.toString().length() - 1)); + case LOWERCASE: + return sb -> Character.isLowerCase(sb.charAt(first ? 0 : sb.toString().length() - 1)); + case UPPERCASE: + return sb -> Character.isUpperCase(sb.charAt(first ? 0 : sb.toString().length() - 1)); + default: + return sb -> !Character.isLetterOrDigit(sb.charAt(first ? 0 : sb.toString().length() - 1)); + } + } + + public static Predicate<Character> isDigitCharacterPredicate() { + return c -> Character.isDigit(c); + } + + public static Function<Integer, Integer> posIntegerFunction(boolean isHighest) { + if (isHighest) { + return i -> Integer.valueOf(new StringBuilder().append(i < 0 ? -i : i).reverse().toString()) % 10; + } else { + return i -> i % 10 < 0 ? -i % 10 : i % 10; + } + } + + public static Function<StringBuilder, CharType> sbGenericFunction(boolean isFirst) { + if (isFirst) + return i -> Character.isAlphabetic(i.charAt(0)) ? (Character.isUpperCase(i.charAt(0)) ? CharType.UPPERCASE : CharType.LOWERCASE) : (Character.isDigit(i.charAt(0)) ? CharType.DIGIT : CharType.SPECIAL); + else + return i -> Character.isAlphabetic(i.charAt(i.length() - 1)) ? (Character.isUpperCase(i.charAt(i.length() - 1)) ? CharType.UPPERCASE : CharType.LOWERCASE) : (Character.isDigit(i.charAt(i.length() - 1)) ? CharType.DIGIT : CharType.SPECIAL); + } + + public static Function<String, Integer> mappingFunction(Map<String, Integer> m, IntOp op, int value) { + switch (op) { + case ADD: + return k -> (value != 0) ? m.get(k) + value : m.get(k); + case SUBTRACT: + return k -> (value != 0) ? m.get(k) - value : m.get(k); + case MULTIPLY: + return k -> (value != 0) ? m.get(k) * value : m.get(k); + case DIVIDE: + return k -> (value != 0) ? m.get(k) / value : m.get(k); + default: + return k -> (value != 0) ? m.get(k) % value : m.get(k); + } + } + + public static IntFunction<Integer> posIntFunction(boolean isHighest) { + if (isHighest) { + return i -> Integer.valueOf(new StringBuilder().append(i < 0 ? -i : i).reverse().toString()) % 10; + } else { + return i -> i % 10 < 0 ? -i % 10 : i % 10; + } + } + + public static BiFunction<Integer, Integer, Integer> randBetweenIntegerFunction() { + return (t1, t2) -> randBetween(t1, t2); + } + + public static int randBetween(int low, int up) { + assert (low < up && low >= 0); + Random rand = new Random(); + int i = rand.nextInt(up); + while (i < low) { + i = rand.nextInt(); + } + return i; + } + + public static ToIntFunction<Integer> highestPosValueIntFunction() { + return i -> Integer.valueOf(new StringBuilder().append(i < 0 ? -i : i).reverse().toString()) % 10; + } + + public static ToIntFunction<Integer> lowestPosValueIntFunction() { + return i -> i % 10 < 0 ? -i % 10 : i % 10; + } + + public static <T> Consumer<T> reverseConsumer(Set<T> set) { + return t -> { + set.add(t); + }; + } + + public static Consumer<Integer> addIntegerConsumer(AtomicInteger ai) { + return t -> { + ai.updateAndGet(t1 -> t1 + t); + }; + } + + public static Consumer<StringBuilder> appendSBConsumer(StringBuilder sb) { + return t -> { + sb.append(t); + }; + } + + public static IntConsumer addIntConsumer(AtomicInteger ai) { + return t -> { + ai.updateAndGet(t1 -> t1 + t); + }; + } + + public static IntConsumer addLongConsumer(AtomicLong ai) { + return t -> { + ai.updateAndGet(t1 -> t1 + t); + }; + } + + public static <T> Consumer<T> copyConsumer(List<T> list) { + return t -> { + list.add(t); + }; + } + + public static <T> Consumer<T> existsConsumer(Collection<T> in, Collection<T> out) { + return t -> { + if (in.contains(t)) { + out.add(t); + } + }; + } + + public static Supplier<StringBuilder> sbSupplier(StringBuilder value) { + return () -> value; + } + + public static Supplier<Integer> integerSupplier(int value) { + return () -> value; + } + + public static <T> Supplier<T> genericSuppiler(T value) { + return () -> value; + } + + public static IntSupplier intSupplier(int value) { + return () -> value; + } + + public static Supplier<Long> longSupplier(long value) { + return () -> value; + } + + public static Supplier<AtomicInteger> atomicIntegerSupplier(int value) { + return () -> new AtomicInteger(value); + } + + public static <T> Supplier<AtomicReference<T>> atomicGenericSupplier(T value) { + return () -> new AtomicReference<>(value); + } + + public static Supplier<AtomicReference<StringBuilder>> atomicSBSupplier(StringBuilder value) { + return () -> new AtomicReference<>(value); + } + + public static UnaryOperator<Integer> opIntegerUnaryOperator(IntOp op, int value) { + switch (op) { + case ADD: + return t -> t + value; + case SUBTRACT: + return t -> t - value; + case MULTIPLY: + return t -> t * value; + case DIVIDE: + return t -> t / value; + default: + return t -> t % value; + } + } + + public static IntUnaryOperator opIntUnaryOperator(IntOp op, int value) { + if(value == 0) + return t -> t; + switch (op) { + case ADD: + return t -> t + value; + case SUBTRACT: + return t -> t - value; + case MULTIPLY: + return t -> t * value; + case DIVIDE: + return t -> t / value; + default: + return t -> t % value; + } + } + + public static Function<Integer, Integer> opIntegerFunction(IntOp op, int value) { + if(value == 0) + return t -> t; + switch (op) { + case ADD: + return t -> Integer.valueOf(t + value); + case SUBTRACT: + return t -> Integer.valueOf(t - value); + case MULTIPLY: + return t -> Integer.valueOf(t * value); + case DIVIDE: + return t -> Integer.valueOf(t / value); + default: + return t -> Integer.valueOf(t % value); + } + } + + public static ToIntFunction<Integer> opToIntFunction(IntOp op, int value) { + if(value == 0) + return t -> t.intValue(); + switch (op) { + case ADD: + return t -> t.intValue() + value; + case SUBTRACT: + return t -> t.intValue() - value; + case MULTIPLY: + return t -> t.intValue() * value; + case DIVIDE: + return t -> t.intValue() / value; + default: + return t -> t.intValue() % value; + } + } + + public static IntFunction<Integer> opIntFunction(IntOp op, int value) { + if(value == 0) + return t -> t; + switch (op) { + case ADD: + return t -> t + value; + case SUBTRACT: + return t -> t - value; + case MULTIPLY: + return t -> t * value; + case DIVIDE: + return t -> t / value; + default: + return t -> t % value; + } + } + + public static IntUnaryOperator addIntUnaryOperator(int value) { + return t -> t + value; + } + + public static IntUnaryOperator subIntUnaryOperator(int value) { + return t -> t - value; + } + + public static IntUnaryOperator mulIntUnaryOperator(int value) { + return t -> t * value; + } + + public static IntUnaryOperator divIntUnaryOperator(int value) { + return t -> t / value; + } + + public static IntBinaryOperator minIntBinaryOperator() { + return (t1, t2) -> t1< t2 ? t1 : t2; + } + + public static BinaryOperator<Integer> minIntegerBinaryOperator(Comparator<Integer> c) { + return (t1, t2) -> c.compare(t1, t2) < 0 ? t1 : t2; + } + + public static <T>BinaryOperator<T> minGenericBinaryOperator(Comparator<T> c) { + return (t1, t2) -> c.compare(t1, t2) < 0 ? t1 : t2; + } + + public static BinaryOperator<StringBuilder> minSBBinaryOperator(Comparator<StringBuilder> c) { + return ( t1, t2 ) -> c.compare(t1, t2) < 0 ? t1 : t2; + } + + public static IntBinaryOperator maxIntBinaryOperator() { + return ( t1, t2 ) -> (t1< t2) ? t2: t1; + } + + public static BinaryOperator<Integer> maxIntegerBinaryOperator(Comparator<Integer> c) { + return (t1, t2) -> c.compare(t1, t2) < 0 ? t2 : t1; + } + + public static <T> BinaryOperator<T> maxGenericBinaryOperator(Comparator<T> c) { + return (t1, t2) -> c.compare(t1, t2) < 0 ? t2 : t1; + } + + public static IntBinaryOperator maxIntBinaryOperator(Comparator<Integer> c) { + return ( t1, t2 ) -> c.compare(t1, t2) < 0 ? t2 : t1; + } + + public static BinaryOperator<StringBuilder> maxSBBinaryOperator(Comparator<StringBuilder> c) { + return ( t1, t2 ) -> c.compare(t1, t2) < 0 ? t2 : t1; + } + + public static BinaryOperator<Integer> addIntegerBinaryOperator() { + return ( t1 , t2 ) -> t1 + t2; + } + + public static IntBinaryOperator addIntBinaryOperator() { + return ( t1 , t2 ) -> t1 + t2; + } + + public static BinaryOperator<BigDecimal> addBigDecimalBinaryOperator() { + return ( t1 , t2 ) -> t1.add(t2); + } + + public static DoubleBinaryOperator addDoubleBinaryOperator() { + return ( t1 , t2 ) -> t1 + t2; + } + + public static BinaryOperator<StringBuilder> appendSBBinaryOperator() { + return (t1 , t2) -> new StringBuilder().append(t1).append(t2); + } + + public static BinaryOperator<Integer> subIntegerBinaryOperator() { + return (t1, t2) -> t1 - t2; + } + + public static IntBinaryOperator subIntBinaryOperator() { + return (t1, t2) -> t1 - t2; + } + + public static BinaryOperator<StringBuilder> deleteSBBinaryOperator() { + return (t1, t2) -> {if (t1.length() >= t2.length()) { + int i1 = t1.indexOf(t2.toString()); + int i2 = i1 + t2.length(); + return new StringBuilder(t1).delete(i1, i2); + }else { + int i1 = t2.indexOf(t1.toString()); + int i2 = i1 + t1.length(); + return new StringBuilder(t2).delete(i1, i2); + } + }; + + } + + public static IntBinaryOperator mulIntBinaryOperator() { + return (t1, t2) -> t1 * t2; + } + + public static IntBinaryOperator divIntBinaryOperator() { + return (t1, t2) -> t1 / t2; + } + + public static LongUnaryOperator addLongUnaryOperator(long value) { + return t -> t + value; + } + + public static UnaryOperator<StringBuilder> appendSBUnaryOperator(StringBuilder value) { + return t -> t.append(value); + } + + public static LongUnaryOperator subLongUnaryOperator(long value) { + return t -> t - value; + } + + public static LongUnaryOperator mulLongUnaryOperator(long value) { + return t -> t * value; + } + + public static LongUnaryOperator divLongUnaryOperator(long value) { + return t -> t / value; + } + + public static LongBinaryOperator addLongBinaryOperator() { + return (t1, t2) -> t1 + t2; + } + + public static LongBinaryOperator subLongBinaryOperator() { + return (t1, t2) -> t1 - t2; + } + + public static LongBinaryOperator mulLongBinaryOperator() { + return (t1, t2) -> t1 * t2; + } + + public static LongBinaryOperator divLongBinaryOperator() { + return (t1, t2) -> t1 / t2; + } + + public static BiConsumer<AtomicInteger, Integer> addIntegerBiConsumer() { + return (t1 , t2 ) -> { t1.addAndGet(t2); }; + } + + public static BiConsumer<AtomicInteger, AtomicInteger> addAtomicIntegerBiConsumer() { + return (t1 , t2) -> { t1.addAndGet(t2.get()); }; + } + + public static BiConsumer<AtomicReference<StringBuilder>, StringBuilder> appendSBBiConsumer() { + return (t1, t2) -> {t1.updateAndGet(appendSBUnaryOperator(t2));}; + } + + public static BiConsumer<AtomicReference<StringBuilder>, AtomicReference<StringBuilder>> appendAtomicSBBiConsumer() { + return (t1, t2) -> {t1.updateAndGet(appendSBUnaryOperator(t2.get()));}; + } + + public static BiConsumer<AtomicInteger, Integer> maxIntegerBiConsumer(Comparator<Integer> c) { + return (t1 , t2 ) -> { t1.getAndUpdate(t -> max(t, t2, c)); }; + } + + public static <T> BiConsumer<AtomicReference<T>, T> maxGenericBiConsumer(Comparator<T> c) { + return (t1 , t2 ) -> { t1.getAndUpdate(t -> max(t, t2, c)); }; + } + + public static BiConsumer<AtomicInteger, AtomicInteger> maxAtomicIntegerBiConsumer(Comparator<Integer> c) { + return (t1 , t2) -> { t1.getAndUpdate(t -> max(t, t2.get(), c)); }; + } + + public static <T> BiConsumer<AtomicReference<T>, AtomicReference<T>> maxAtomicGenericBiConsumer(Comparator<T> c) { + return (t1 , t2) -> { t1.getAndUpdate(t -> max(t, t2.get(), c)); }; + } + + public static BiConsumer<AtomicInteger, Integer> minIntegerBiConsumer(Comparator<Integer> c) { + return (t1 , t2) -> { t1.getAndUpdate(t -> min(t, t2, c)); }; + } + + public static <T> BiConsumer<AtomicReference<T>, T> minGenericBiConsumer(Comparator<T> c) { + return (t1 , t2) -> { t1.getAndUpdate(t -> min(t, t2, c)); }; + } + + public static BiConsumer<AtomicInteger, AtomicInteger> minAtomicIntegerBiConsumer(Comparator<Integer> c) { + return (t1, t2) -> { t1.getAndUpdate(t -> min(t, t2.get(), c)); }; + } + + public static <T> BiConsumer<AtomicReference<T>, AtomicReference<T>> minAtomicGenericBiConsumer(Comparator<T> c) { + return (t1, t2) -> { t1.getAndUpdate(t -> min(t, t2.get(), c)); }; + } + + public static BiFunction<Integer, Integer, Integer> maxIntegerFunction(Comparator<Integer> c) { + return (t1, t2) -> max(t1, t2, c); + } + + public static BiFunction<BigDecimal, Integer, BigDecimal> deviationSequareFunction(double avg) { + return (bd, t) -> bd.add(new BigDecimal(avg - t).pow(2)); + } + + public static <T> BiFunction<T, T, T> maxGenericFunction(Comparator<T> c) { + return (t1, t2) -> max(t1, t2, c); + } + + public static BiFunction<StringBuilder, StringBuilder, StringBuilder> maxStringBuilderFunction(Comparator<StringBuilder> c) { + return (t1, t2) -> max(t1, t2, c); + } + + public static BiFunction<Integer, Integer, Integer> minIntegerFunction(Comparator<Integer> c) { + return (t1, t2) -> min(t1, t2, c); + } + + public static <T> BiFunction<T, T, T> minGenericFunction(Comparator<T> c) { + return (t1, t2) -> min(t1, t2, c); + } + + public static BiFunction<StringBuilder, StringBuilder, StringBuilder> minStringBuilderFunction(Comparator<StringBuilder> c) { + return (t1, t2) -> min(t1, t2, c); + } + + public static BiFunction<String, Integer, Integer> opBiFunction(IntOp op, int value) { + switch (op) { + case ADD: + return (k, v) -> (value != 0) ? v + value : v; + case SUBTRACT: + return (k, v) -> (value != 0) ? v - value : v; + case MULTIPLY: + return (k, v) -> (value != 0) ? v * value : v; + case DIVIDE: + return (k, v) -> (value != 0) ? v / value : v; + default: + return (k, v) -> (value != 0) ? v % value : v; + } + } + + + public static BiFunction<Integer, Integer, Integer> opBiFunction(IntOp op) { + switch (op) { + case ADD: + return (oldv, v) -> (v != 0) ? oldv + v : oldv; + case SUBTRACT: + return (oldv, v) -> (v != 0) ? oldv - v : oldv; + case MULTIPLY: + return (oldv, v) -> (v != 0) ? oldv * v : oldv; + case DIVIDE: + return (oldv, v) -> (v != 0) ? oldv / v : oldv; + default: + return (oldv, v) -> (v != 0) ? oldv % v : oldv; + } + } + + private static Integer min(Integer i1, Integer i2, Comparator<Integer> c) { + return c.compare(i1, i2) < 0 ? i1 : i2; + } + + private static <T> T min(T i1, T i2, Comparator<T> c) { + return c.compare(i1, i2) < 0 ? i1 : i2; + } + + private static StringBuilder min(StringBuilder sb1, StringBuilder sb2, Comparator<StringBuilder> c) { + return c.compare(sb1, sb2) < 0 ? sb1 : sb2; + } + + private static Integer max(Integer i1, Integer i2, Comparator<Integer> c) { + return c.compare(i1, i2) < 0 ? i2 : i1; + } + + private static <T> T max(T i1, T i2, Comparator<T> c) { + return c.compare(i1, i2) < 0 ? i2 : i1; + } + + private static StringBuilder max(StringBuilder sb1, StringBuilder sb2, Comparator<StringBuilder> c) { + return c.compare(sb1, sb2) < 0 ? sb2 : sb1; + } + /* + * Construct a Collection C object based on a C object, using generic type + * instead of Class type can help preventing type error in compilation + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static <E, C extends Collection<E>> C create(C c, int... initSize) + throws InstantiationException, IllegalAccessException, + NoSuchMethodException, IllegalArgumentException, + InvocationTargetException { + return create((Class<C>) c.getClass(), initSize); + } + + /* + * Construct a Collection C object based on a C's type, using generic type + * instead of Class type can help preventing type error in compilation + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static <E, T extends Collection<E>> T create( + Class<? extends Collection<E>> cls, int... initSize) + throws InstantiationException, IllegalAccessException, + NoSuchMethodException, IllegalArgumentException, + InvocationTargetException { + assert (initSize.length <= 1); + Collection<E> c; + if (initSize.length == 0) { + c = cls.newInstance(); + } else { + Constructor con = cls.getConstructor(int.class); + c = (Collection<E>) con.newInstance(initSize[0]); + } + return (T) c; + } + + /* + * Construct a T object based on T's type, using generic type instead of + * Class type can help preventing type error in compilation + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static <K, V, M extends Map<K, V>> M createMap(M m, int... initSize) + throws InstantiationException, IllegalAccessException, + NoSuchMethodException, IllegalArgumentException, + InvocationTargetException { + return createMap((Class<M>) m.getClass(), initSize); + } + + /* + * Construct a Map M object based on M's type, using generic type instead of + * Class type can help preventing type error in compilation + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static <K, V, M extends Map<K, V>> M createMap(Class<? extends Map<K, V>> cls, + int... initSize) throws InstantiationException, + IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException { + assert (initSize.length <= 1); + Map<K, V> map; + if (initSize.length == 0) { + map = cls.newInstance(); + } else { + Constructor con = cls.getConstructor(int.class); + map = (Map<K, V>) con.newInstance(initSize[0]); + } + return (M) map; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/sqeutil/StringUtilities.java Wed Aug 14 15:53:13 2013 -0700 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2012, 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. + */ + +/** + * + * @summary utilities class supporting common operation for tests. + * @(#)StringUtilities.java + * @author Tristan Yan + * @version 1.0 + */ + +import java.util.Random; + +public class StringUtilities { + private final static Random RANDOM = new Random(System.currentTimeMillis()); + + public static String randomString(int max_length, int min_length){ + return randomAscii(min_length + RANDOM.nextInt(max_length - min_length)); + } + + public static String random(int count, int start, int end, boolean letters, + boolean numbers, char[] chars, Random rnd) { + if (count == 0) { + return ""; + } else if (count < 0) { + throw new IllegalArgumentException("Requested random string length " + count + " is less than 0."); + } + if ((start == 0) && (end == 0)) { + end = 'z' + 1; + start = ' '; + if (!letters && !numbers) { + start = 0; + end = Integer.MAX_VALUE; + } + } + + char[] buffer = new char[count]; + int gap = end - start; + + while (count-- != 0) { + char ch; + if (chars == null) { + ch = (char) (rnd.nextInt(gap) + start); + } else { + ch = chars[rnd.nextInt(gap) + start]; + } + if ((letters && Character.isLetter(ch)) + || (numbers && Character.isDigit(ch)) + || (!letters && !numbers)) + { + if(ch >= 56320 && ch <= 57343) { + if(count == 0) { + count++; + } else { + // low surrogate, insert high surrogate after putting it in + buffer[count] = ch; + count--; + buffer[count] = (char) (55296 + rnd.nextInt(128)); + } + } else if(ch >= 55296 && ch <= 56191) { + if(count == 0) { + count++; + } else { + // high surrogate, insert low surrogate before putting it in + buffer[count] = (char) (56320 + rnd.nextInt(128)); + count--; + buffer[count] = ch; + } + } else if(ch >= 56192 && ch <= 56319) { + // private high surrogate, no effing clue, so skip it + count++; + } else { + buffer[count] = ch; + } + } else { + count++; + } + } + return new String(buffer); + } + public static String random(int count) { + return random(count, false, false); + } + + public static String randomAscii(int count) { + return random(count, 32, 127, false, false); + } + + public static String randomAlphabetic(int count) { + return random(count, true, false); + } + + public static String randomAlphanumeric(int count) { + return random(count, true, true); + } + + public static String randomNumeric(int count) { + return random(count, false, true); + } + + public static String random(int count, boolean letters, boolean numbers) { + return random(count, 0, 0, letters, numbers); + } + + public static String random(int count, int start, int end, boolean letters, boolean numbers) { + return random(count, start, end, letters, numbers, null, RANDOM); + } + + public static String random(int count, int start, int end, boolean letters, boolean numbers, char[] chars) { + return random(count, start, end, letters, numbers, chars, RANDOM); + } +}