OpenJDK / jdk8u / jdk8u / jdk
changeset 9119:bc7145ec33e4
Merge
author | coffeys |
---|---|
date | Fri, 31 Jan 2014 00:14:45 +0000 |
parents | a686b7df41c2 e385bd6f7338 |
children | 8ecfaacd2d8e |
files | |
diffstat | 13 files changed, 556 insertions(+), 9 deletions(-) [+] |
line wrap: on
line diff
--- a/THIRD_PARTY_README Fri Jan 24 14:49:06 2014 +0000 +++ b/THIRD_PARTY_README Fri Jan 31 00:14:45 2014 +0000 @@ -1400,7 +1400,7 @@ ------------------------------------------------------------------------------- %% This notice is provided with respect to Little CMS 2.4, which may be -included with OpenJDK 8. +included with JRE 8, JDK 8, and OpenJDK 8. --- begin of LICENSE ---
--- a/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Fri Jan 24 14:49:06 2014 +0000 +++ b/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java Fri Jan 31 00:14:45 2014 +0000 @@ -114,6 +114,11 @@ Class<?>[] markerInterfaces, MethodType[] additionalBridges) throws LambdaConversionException { + if ((caller.lookupModes() & MethodHandles.Lookup.PRIVATE) == 0) { + throw new LambdaConversionException(String.format( + "Invalid caller: %s", + caller.lookupClass().getName())); + } this.targetClass = caller.lookupClass(); this.invokedType = invokedType; @@ -221,6 +226,13 @@ String.format("Invalid receiver type %s; not a subtype of implementation type %s", receiverClass, implDefiningClass)); } + + Class<?> implReceiverClass = implMethod.type().parameterType(0); + if (implReceiverClass != implDefiningClass && !implReceiverClass.isAssignableFrom(receiverClass)) { + throw new LambdaConversionException( + String.format("Invalid receiver type %s; not a subtype of implementation receiver type %s", + receiverClass, implReceiverClass)); + } } else { // no receiver capturedStart = 0; @@ -256,11 +268,17 @@ (implKind == MethodHandleInfo.REF_newInvokeSpecial) ? implDefiningClass : implMethodType.returnType(); + Class<?> samReturnType = samMethodType.returnType(); if (!isAdaptableToAsReturn(actualReturnType, expectedType)) { throw new LambdaConversionException( String.format("Type mismatch for lambda return: %s is not convertible to %s", actualReturnType, expectedType)); } + if (!isAdaptableToAsReturn(expectedType, samReturnType)) { + throw new LambdaConversionException( + String.format("Type mismatch for lambda expected return: %s is not convertible to %s", + expectedType, samReturnType)); + } } /**
--- a/src/share/classes/java/util/jar/JarFile.java Fri Jan 24 14:49:06 2014 +0000 +++ b/src/share/classes/java/util/jar/JarFile.java Fri Jan 31 00:14:45 2014 +0000 @@ -40,6 +40,7 @@ import sun.security.action.GetPropertyAction; import sun.security.util.ManifestEntryVerifier; import sun.misc.SharedSecrets; +import sun.security.util.SignatureFileVerifier; /** * The <code>JarFile</code> class is used to read the contents of a jar file @@ -364,11 +365,13 @@ String[] names = getMetaInfEntryNames(); if (names != null) { for (int i = 0; i < names.length; i++) { - JarEntry e = getJarEntry(names[i]); - if (e == null) { - throw new JarException("corrupted jar file"); - } - if (!e.isDirectory()) { + String uname = names[i].toUpperCase(Locale.ENGLISH); + if (MANIFEST_NAME.equals(uname) + || SignatureFileVerifier.isBlockOrSF(uname)) { + JarEntry e = getJarEntry(names[i]); + if (e == null) { + throw new JarException("corrupted jar file"); + } if (mev == null) { mev = new ManifestEntryVerifier (getManifestFromReference());
--- a/src/share/classes/sun/invoke/util/VerifyAccess.java Fri Jan 24 14:49:06 2014 +0000 +++ b/src/share/classes/sun/invoke/util/VerifyAccess.java Fri Jan 31 00:14:45 2014 +0000 @@ -118,7 +118,7 @@ isSamePackage(defc, lookupClass)) return true; if ((allowedModes & PROTECTED) != 0 && - isPublicSuperClass(defc, lookupClass)) + isSuperClass(defc, lookupClass)) return true; return false; case PACKAGE_ONLY: // That is, zero. Unmarked member is package-only access. @@ -140,8 +140,8 @@ lookupClass.isAssignableFrom(refc)); } - static boolean isPublicSuperClass(Class<?> defc, Class<?> lookupClass) { - return isPublic(getClassModifiers(defc)) && defc.isAssignableFrom(lookupClass); + static boolean isSuperClass(Class<?> defc, Class<?> lookupClass) { + return defc.isAssignableFrom(lookupClass); } static int getClassModifiers(Class<?> c) {
--- a/src/solaris/native/common/jni_util_md.c Fri Jan 24 14:49:06 2014 +0000 +++ b/src/solaris/native/common/jni_util_md.c Fri Jan 31 00:14:45 2014 +0000 @@ -23,6 +23,8 @@ * questions. */ +#include <string.h> + #include "jni.h" #include "jni_util.h" #include "dlfcn.h" @@ -40,7 +42,11 @@ if (procHandle != NULL) { return procHandle; } +#ifdef __APPLE__ + procHandle = (void*)dlopen(NULL, RTLD_FIRST); +#else procHandle = (void*)dlopen(NULL, RTLD_LAZY); +#endif return procHandle; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/ProtectedMemberDifferentPackage/Test.java Fri Jan 31 00:14:45 2014 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +/** + * @test + * @bug 8032585 + * @summary JSR292: IllegalAccessError when attempting to invoke protected method from different package + * + * @compile p1/T2.java p2/T3.java + * @run main/othervm p2.T3 + */ +public class Test {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/ProtectedMemberDifferentPackage/p1/T2.java Fri Jan 31 00:14:45 2014 +0000 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package p1; + +class T1 { + protected void m() { System.out.println("T1.m");} +} + +public class T2 extends T1 {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/ProtectedMemberDifferentPackage/p2/T3.java Fri Jan 31 00:14:45 2014 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +package p2; + +import p1.T2; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class T3 extends T2 { + public static void main(String[] args) throws Throwable { + MethodHandles.lookup().findVirtual(T3.class, "m", MethodType.methodType(void.class)); + System.out.println("TEST PASSED"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/lambda/T8032697.java Fri Jan 31 00:14:45 2014 +0000 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8032697 + * @summary Issues with Lambda + */ + +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.LambdaConversionException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import T8032697_anotherpkg.T8032697_A; + +public class T8032697 extends T8032697_A { + + interface I { + int m(); + } + + interface IA { + int m(T8032697_A x); + } + + static MethodHandles.Lookup l; + static MethodHandle h; + private static MethodType mt(Class<?> k) { return MethodType.methodType(k); } + private static MethodType mt(Class<?> k, Class<?> k2) { return MethodType.methodType(k, k2); } + private static boolean mf(MethodType mti, MethodType mtf) { + try { + LambdaMetafactory.metafactory(l, "m", mti,mtf,h,mtf); + } catch(LambdaConversionException e) { + return true; + } + return false; + } + + public static void main(String[] args) throws Throwable { + l = MethodHandles.lookup(); + h = l.findVirtual(T8032697_A.class, "f", mt(int.class)); + if (mf(mt(I.class, T8032697.class), mt(int.class))) throw new AssertionError("Error: Should work"); + if (mf(mt(IA.class), mt(int.class, T8032697.class))) throw new AssertionError("Error: Should work"); + if (!mf(mt(I.class, T8032697_A.class), mt(int.class))) throw new AssertionError("Error: Should fail"); + if (!mf(mt(IA.class), mt(int.class, T8032697_A.class))) throw new AssertionError("Error: Should fail"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/lambda/T8032697_anotherpkg/T8032697_A.java Fri Jan 31 00:14:45 2014 +0000 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package T8032697_anotherpkg; + +public class T8032697_A { + protected final int f() { return 2; } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/lambda/T8032704.java Fri Jan 31 00:14:45 2014 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8032704 + * @summary Issues with lib perm in Lambda + */ + +import java.io.Closeable; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.LambdaConversionException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class T8032704 { + + public static void here() {} + static MethodHandle h; + private static MethodType mt(Class<?> k) { return MethodType.methodType(k); } + private static boolean mf(MethodHandles.Lookup l) { + try { + LambdaMetafactory.metafactory(l, "close", + mt(Closeable.class),mt(void.class),h,mt(void.class)); + } catch(LambdaConversionException e) { + return true; + } + return false; + } + + public static void main(String[] args) throws Throwable { + MethodHandles.Lookup ll = MethodHandles.lookup(); + h = ll.findStatic(T8032704.class, "here", mt(void.class)); + if (mf(ll)) throw new AssertionError("Error: Should work"); + if (!mf(MethodHandles.publicLookup())) throw new AssertionError("Error: Should fail - public"); + if (!mf(ll.in(T8032704other.class))) throw new AssertionError("Error: Should fail - other"); + if (!mf(ll.in(Thread.class))) throw new AssertionError("Error: Should fail - Thread"); + } +} + +class T8032704other {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/invoke/lambda/T8032711.java Fri Jan 31 00:14:45 2014 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8032711 + * @summary Issue with Lambda in handling + */ + +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.LambdaConversionException; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class T8032711 { + + interface I { + void m(); + } + + static void here() {} + static MethodHandles.Lookup l; + static MethodHandle h; + private static MethodType mt(Class<?> k) { return MethodType.methodType(k); } + private static boolean mf(Class<?> k) { + try { + LambdaMetafactory.metafactory(l, "m", + mt(I.class),mt(k),h,mt(void.class)); + } catch(LambdaConversionException e) { + return true; + } + return false; + } + + public static void main(String[] args) throws Throwable { + l = MethodHandles.lookup(); + h = l.findStatic(T8032711.class, "here", mt(void.class)); + if (mf(void.class)) throw new AssertionError("Error: Should work"); + if (!mf(String.class)) throw new AssertionError("Error: Should fail"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/sun/security/tools/jarsigner/EntriesOrder.java Fri Jan 31 00:14:45 2014 +0000 @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8031572 + * @summary jarsigner -verify exits with 0 when a jar file is not properly signed + */ + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.cert.Certificate; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class EntriesOrder { + + public static void main(String[] args) throws Exception { + + String[] entries = { + "META-INF/", + "META-INF/MANIFEST.MF", + "META-INF/A.RSA", + "META-INF/A.SF", + "META-INF/inf", + "a"}; + + Map<String,byte[]> content = new HashMap<>(); + + // We will create a jar containing entries above. Try all permutations + // and confirm 1) When opened as a JarFile, we can always get 3 signed + // ones (MANIFEST, inf, a), and 2) When opened as a JarInputStream, + // when the order is correct (MANIFEST at beginning, followed by RSA/SF, + // directory ignored), we can get 2 signed ones (inf, a). + + // Prepares raw files + Files.write(Paths.get("a"), "a".getBytes()); + Files.createDirectory(Paths.get("META-INF/")); + Files.write(Paths.get("META-INF/inf"), "inf".getBytes()); + + // Pack, sign, and extract to get all files + sun.tools.jar.Main m = + new sun.tools.jar.Main(System.out, System.err, "jar"); + if (!m.run("cvf a.jar a META-INF/inf".split(" "))) { + throw new Exception("jar creation failed"); + } + sun.security.tools.keytool.Main.main( + ("-keystore jks -storepass changeit -keypass changeit -dname" + + " CN=A -alias a -genkeypair -keyalg rsa").split(" ")); + sun.security.tools.jarsigner.Main.main( + "-keystore jks -storepass changeit a.jar a".split(" ")); + m = new sun.tools.jar.Main(System.out, System.err, "jar"); + if (!m.run("xvf a.jar".split(" "))) { + throw new Exception("jar extraction failed"); + } + + // Data + for (String s: entries) { + if (!s.endsWith("/")) { + content.put(s, Files.readAllBytes(Paths.get(s))); + } + } + + // Test + for (List<String> perm: Permute(entries)) { + + // Recreate a jar + try (ZipOutputStream zos + = new ZipOutputStream(new FileOutputStream("x.jar"))) { + for (String e: perm) { + zos.putNextEntry(new ZipEntry(e)); + if (Paths.get(e).toFile().isDirectory()) continue; + zos.write(content.get(e)); + } + } + + // Open with JarFile, number of signed entries should be 3. + int cc = 0; + try (JarFile jf = new JarFile("x.jar")) { + Enumeration<JarEntry> jes = jf.entries(); + while (jes.hasMoreElements()) { + JarEntry je = jes.nextElement(); + sun.misc.IOUtils.readFully(jf.getInputStream(je), -1, true); + Certificate[] certs = je.getCertificates(); + if (certs != null && certs.length > 0) { + cc++; + } + } + } + + if (cc != 3) { + System.out.println(perm + " - jf - " + cc); + throw new Exception(); + } + + // Open with JarInputStream + int signed; + + perm.remove("META-INF/"); + if (perm.get(0).equals("META-INF/MANIFEST.MF") && + perm.get(1).contains("/A.") && + perm.get(2).contains("/A.")) { + signed = 2; // Good order + } else { + signed = 0; // Bad order. In this case, the number of signed + // entries is not documented. Just test impl. + } + + cc = 0; + try (JarInputStream jis + = new JarInputStream(new FileInputStream("x.jar"))) { + while (true) { + JarEntry je = jis.getNextJarEntry(); + if (je == null) break; + sun.misc.IOUtils.readFully(jis, -1, true); + Certificate[] certs = je.getCertificates(); + if (certs != null && certs.length > 0) { + cc++; + } + } + } + + if (cc != signed) { + System.out.println(perm + " - jis - " + cc + " " + signed); + throw new Exception(); + } + } + } + + // Helper method to return all permutations of an array. Each output can + // be altered without damaging the iteration process. + static Iterable<List<String>> Permute(String[] entries) { + return new Iterable<List<String>>() { + + int s = entries.length; + long c = factorial(s) - 1; // number of permutations + + private long factorial(int n) { + return (n == 1) ? 1: (n * factorial(n-1)); + } + + @Override + public Iterator<List<String>> iterator() { + return new Iterator<List<String>>() { + @Override + public boolean hasNext() { + return c >= 0; + } + + @Override + public List<String> next() { + if (c < 0) return null; + List<String> result = new ArrayList<>(s); + LinkedList<String> source = new LinkedList<>( + Arrays.asList(entries)); + // Treat c as a integer with different radixes at + // different digits, i.e. at digit 0, radix is s; + // at digit 1, radix is s-1. Thus a s-digit number + // is able to represent s! different values. + long n = c; + for (int i=s; i>=1; i--) { + int x = (int)(n % i); + result.add(source.remove(x)); + n = n / i; + } + c--; + return result; + } + }; + } + }; + } +}