diff --git a/jdk/src/share/classes/java/net/Inet6Address.java b/jdk/src/share/classes/java/net/Inet6Address.java index a2e66f09b70..4a2d4e22473 100644 --- a/jdk/src/share/classes/java/net/Inet6Address.java +++ b/jdk/src/share/classes/java/net/Inet6Address.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -203,6 +203,12 @@ class Inet6Address extends InetAddress { */ private transient NetworkInterface scope_ifname; // null + /** + * set if the object is constructed with a scoped + * interface instead of a numeric scope id. + */ + private boolean scope_ifname_set; // false; + private static final long serialVersionUID = 6880410070516793377L; // Perform native initialization @@ -332,7 +338,7 @@ class Inet6Address extends InetAddress { } } - private void initif(String hostName, byte addr[],NetworkInterface nif) + private void initif(String hostName, byte addr[], NetworkInterface nif) throws UnknownHostException { holder().hostName = hostName; @@ -344,6 +350,7 @@ class Inet6Address extends InetAddress { scope_ifname = nif; scope_id = deriveNumericScope(nif); scope_id_set = true; + scope_ifname_set = true; // for consistency } } @@ -431,6 +438,7 @@ class Inet6Address extends InetAddress { try { scope_ifname = NetworkInterface.getByName(ifname); if (scope_ifname != null) { + scope_ifname_set = true; try { scope_id = deriveNumericScope(scope_ifname); } catch (UnknownHostException e) { @@ -438,6 +446,12 @@ class Inet6Address extends InetAddress { // the machine being used for deserialization has // the same interface name but without IPv6 configured. } + } else { + /* the interface does not exist on this system, so we clear + * the scope information completely */ + scope_id_set = false; + scope_ifname_set = false; + scope_id = 0; } } catch (SocketException e) {} @@ -784,8 +798,10 @@ class Inet6Address extends InetAddress { private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException { - if (scope_ifname != null) + if (scope_ifname != null) { ifname = scope_ifname.getName(); + scope_ifname_set = true; + } s.defaultWriteObject(); } } diff --git a/jdk/src/share/classes/java/sql/Driver.java b/jdk/src/share/classes/java/sql/Driver.java index 1682b75c910..27ad3e0407d 100644 --- a/jdk/src/share/classes/java/sql/Driver.java +++ b/jdk/src/share/classes/java/sql/Driver.java @@ -44,13 +44,16 @@ import java.util.logging.Logger; * *

When a Driver class is loaded, it should create an instance of * itself and register it with the DriverManager. This means that a - * user can load and register a driver by calling - *

- *   Class.forName("foo.bah.Driver")
- * 
- * + * user can load and register a driver by calling: + *

+ * {@code Class.forName("foo.bah.Driver")} + *

+ * A JDBC driver may create a {@linkplain DriverAction} implementation in order + * to receive notifications when {@linkplain DriverManager#deregisterDriver} has + * been called. * @see DriverManager * @see Connection + * @see DriverAction */ public interface Driver { diff --git a/jdk/src/share/classes/java/sql/DriverAction.java b/jdk/src/share/classes/java/sql/DriverAction.java new file mode 100644 index 00000000000..167168c9b85 --- /dev/null +++ b/jdk/src/share/classes/java/sql/DriverAction.java @@ -0,0 +1,66 @@ +/* + * 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.sql; + +/** + * An interface that must be implemented when a {@linkplain Driver} wants to be + * notified by {@code DriverManager}. + *

+ * A {@code DriverAction} implementation is not intended to be used + * directly by applications. A JDBC Driver may choose + * to create its {@code DriverAction} implementation in a private class + * to avoid it being called directly. + * + * The JDBC driver's static initialization block must call + * {@linkplain DriverManager#registerDriver(java.sql.Driver, java.sql.DriverAction) } in order + * to inform {@code DriverManager} which {@code DriverAction} implementation to + * call when the JDBC driver is de-registered. + * @since 1.8 + */ +public interface DriverAction { + /** + * Method called by + * {@linkplain DriverManager#deregisterDriver(Driver) } + * to notify the JDBC driver that it was de-registered. + *

+ * The {@code deregister} method is intended only to be used by JDBC Drivers + * and not by applications. JDBC drivers are recommended to not implement + * {@code DriverAction} in a public class. If there are active + * connections to the database at the time that the {@code deregister} + * method is called, it is implementation specific as to whether the + * connections are closed or allowed to continue. Once this method is + * called, it is implementation specific as to whether the driver may + * limit the ability to create new connections to the database, invoke + * other {@code Driver} methods or throw a {@code SQLException}. + * Consult your JDBC driver's documentation for additional information + * on its behavior. + * @see DriverManager#registerDriver(java.sql.Driver, java.sql.DriverAction) + * @see DriverManager#deregisterDriver(Driver) + * @since 1.8 + */ + void deregister(); + +} diff --git a/jdk/src/share/classes/java/sql/DriverManager.java b/jdk/src/share/classes/java/sql/DriverManager.java index b0d8decf40b..fbe16363529 100644 --- a/jdk/src/share/classes/java/sql/DriverManager.java +++ b/jdk/src/share/classes/java/sql/DriverManager.java @@ -110,6 +110,14 @@ public class DriverManager { final static SQLPermission SET_LOG_PERMISSION = new SQLPermission("setLog"); + /** + * The {@code SQLPermission} constant that allows the + * un-register a registered JDBC driver. + * @since 1.8 + */ + final static SQLPermission DEREGISTER_DRIVER_PERMISSION = + new SQLPermission("deregisterDriver"); + //--------------------------JDBC 2.0----------------------------- /** @@ -309,21 +317,42 @@ public class DriverManager { /** - * Registers the given driver with the DriverManager. + * Registers the given driver with the {@code DriverManager}. * A newly-loaded driver class should call - * the method registerDriver to make itself - * known to the DriverManager. + * the method {@code registerDriver} to make itself + * known to the {@code DriverManager}. If the driver had previously been + * registered, no action is taken. * * @param driver the new JDBC Driver that is to be registered with the - * DriverManager + * {@code DriverManager} * @exception SQLException if a database access error occurs */ public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { + registerDriver(driver, null); + } + + /** + * Registers the given driver with the {@code DriverManager}. + * A newly-loaded driver class should call + * the method {@code registerDriver} to make itself + * known to the {@code DriverManager}. If the driver had previously been + * registered, no action is taken. + * + * @param driver the new JDBC Driver that is to be registered with the + * {@code DriverManager} + * @param da the {@code DriverAction} implementation to be used when + * {@code DriverManager#deregisterDriver} is called + * @exception SQLException if a database access error occurs + */ + public static synchronized void registerDriver(java.sql.Driver driver, + DriverAction da) + throws SQLException { + /* Register the driver if it has not already been added to our list */ if(driver != null) { - registeredDrivers.addIfAbsent(new DriverInfo(driver)); + registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); } else { // This is for compatibility with the original DriverManager throw new NullPointerException(); @@ -334,11 +363,29 @@ public class DriverManager { } /** - * Drops a driver from the DriverManager's list. - * Applets can only deregister drivers from their own classloaders. + * Removes the specified driver from the {@code DriverManager}'s list of + * registered drivers. + *

+ * If a {@code null} value is specified for the driver to be removed, then no + * action is taken. + *

+ * If a security manager exists and its {@code checkPermission} denies + * permission, then a {@code SecurityException} will be thrown. + *

+ * If the specified driver is not found in the list of registered drivers, + * then no action is taken. If the driver was found, it will be removed + * from the list of registered drivers. + *

+ * If a {@code DriverAction} instance was specified when the JDBC driver was + * registered, its deregister method will be called + * prior to the driver being removed from the list of registered drivers. * - * @param driver the JDBC Driver to drop + * @param driver the JDBC Driver to remove * @exception SQLException if a database access error occurs + * @throws SecurityException if a security manager exists and its + * {@code checkPermission} method denies permission to deregister a driver. + * + * @see SecurityManager#checkPermission */ @CallerSensitive public static synchronized void deregisterDriver(Driver driver) @@ -347,11 +394,22 @@ public class DriverManager { return; } + SecurityManager sec = System.getSecurityManager(); + if (sec != null) { + sec.checkPermission(DEREGISTER_DRIVER_PERMISSION); + } + println("DriverManager.deregisterDriver: " + driver); - DriverInfo aDriver = new DriverInfo(driver); + DriverInfo aDriver = new DriverInfo(driver, null); if(registeredDrivers.contains(aDriver)) { if (isDriverAllowed(driver, Reflection.getCallerClass())) { + DriverInfo di = registeredDrivers.get(registeredDrivers.indexOf(aDriver)); + // If a DriverAction was specified, Call it to notify the + // driver that it has been deregistered + if(di.action() != null) { + di.action().deregister(); + } registeredDrivers.remove(aDriver); } else { // If the caller does not have permission to load the driver then @@ -639,8 +697,10 @@ public class DriverManager { class DriverInfo { final Driver driver; - DriverInfo(Driver driver) { + DriverAction da; + DriverInfo(Driver driver, DriverAction action) { this.driver = driver; + da = action; } @Override @@ -658,4 +718,8 @@ class DriverInfo { public String toString() { return ("driver[className=" + driver + "]"); } + + DriverAction action() { + return da; + } } diff --git a/jdk/src/share/classes/java/sql/SQLPermission.java b/jdk/src/share/classes/java/sql/SQLPermission.java index e2207db3262..3aa5a42860b 100644 --- a/jdk/src/share/classes/java/sql/SQLPermission.java +++ b/jdk/src/share/classes/java/sql/SQLPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 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 @@ -30,8 +30,9 @@ import java.security.*; /** * The permission for which the SecurityManager will check - * when code that is running in an applet, or an application with a + * when code that is running an application with a * SecurityManager enabled, calls the + * {@code DriverManager.deregisterDriver} method, * DriverManager.setLogWriter method, * DriverManager.setLogStream (deprecated) method, * {@code SyncFactory.setJNDIContext} method, @@ -95,14 +96,16 @@ import java.security.*; * Connection or * objects created from the Connection * will wait for the database to reply to any one request. + * + * deregisterDriver + * Allows the invocation of the {@code DriverManager} + * method {@code deregisterDriver} + * Permits an application to remove a JDBC driver from the list of + * registered Drivers and release its resources. + * * * *

- * The person running an applet decides what permissions to allow - * and will run the Policy Tool to create an - * SQLPermission in a policy file. A programmer does - * not use a constructor directly to create an instance of SQLPermission - * but rather uses a tool. * @since 1.3 * @see java.security.BasicPermission * @see java.security.Permission diff --git a/jdk/src/share/classes/java/util/Arrays.java b/jdk/src/share/classes/java/util/Arrays.java index e513398683b..af0663bff1a 100644 --- a/jdk/src/share/classes/java/util/Arrays.java +++ b/jdk/src/share/classes/java/util/Arrays.java @@ -25,7 +25,21 @@ package java.util; -import java.lang.reflect.*; +import java.lang.reflect.Array; +import java.util.concurrent.ForkJoinPool; +import java.util.function.BinaryOperator; +import java.util.function.DoubleBinaryOperator; +import java.util.function.IntBinaryOperator; +import java.util.function.IntFunction; +import java.util.function.IntToDoubleFunction; +import java.util.function.IntToLongFunction; +import java.util.function.IntUnaryOperator; +import java.util.function.LongBinaryOperator; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; +import java.util.stream.LongStream; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import static java.util.ArraysParallelSortHelpers.*; /** @@ -4306,17 +4320,171 @@ public class Arrays { dejaVu.remove(a); } + /** - * Creates a {@link Spliterator} covering all of the specified array. + * Set all elements of the specified array, using the provided + * generator function to compute each element. + * + *

If the generator function throws an exception, it is relayed to + * the caller and the array is left in an indeterminate state. + * + * @param type of elements of the array + * @param array array to be initialized + * @param generator a function accepting an index and producing the desired + * value for that position + * @throws NullPointerException if the generator is null + * @since 1.8 + */ + public static void setAll(T[] array, IntFunction generator) { + Objects.requireNonNull(generator); + for (int i = 0; i < array.length; i++) + array[i] = generator.apply(i); + } + + /** + * Set all elements of the specified array, in parallel, using the + * provided generator function to compute each element. + * + *

If the generator function throws an exception, an unchecked exception + * is thrown from {@code parallelSetAll} and the array is left in an + * indeterminate state. + * + * @param type of elements of the array + * @param array array to be initialized + * @param generator a function accepting an index and producing the desired + * value for that position + * @throws NullPointerException if the generator is null + * @since 1.8 + */ + public static void parallelSetAll(T[] array, IntFunction generator) { + Objects.requireNonNull(generator); + IntStream.range(0, array.length).parallel().forEach(i -> { array[i] = generator.apply(i); }); + } + + /** + * Set all elements of the specified array, using the provided + * generator function to compute each element. + * + *

If the generator function throws an exception, it is relayed to + * the caller and the array is left in an indeterminate state. + * + * @param array array to be initialized + * @param generator a function accepting an index and producing the desired + * value for that position + * @throws NullPointerException if the generator is null + * @since 1.8 + */ + public static void setAll(int[] array, IntUnaryOperator generator) { + Objects.requireNonNull(generator); + for (int i = 0; i < array.length; i++) + array[i] = generator.applyAsInt(i); + } + + /** + * Set all elements of the specified array, in parallel, using the + * provided generator function to compute each element. + * + *

If the generator function throws an exception, an unchecked exception + * is thrown from {@code parallelSetAll} and the array is left in an + * indeterminate state. + * + * @param array array to be initialized + * @param generator a function accepting an index and producing the desired + * value for that position + * @throws NullPointerException if the generator is null + * @since 1.8 + */ + public static void parallelSetAll(int[] array, IntUnaryOperator generator) { + Objects.requireNonNull(generator); + IntStream.range(0, array.length).parallel().forEach(i -> { array[i] = generator.applyAsInt(i); }); + } + + /** + * Set all elements of the specified array, using the provided + * generator function to compute each element. + * + *

If the generator function throws an exception, it is relayed to + * the caller and the array is left in an indeterminate state. + * + * @param array array to be initialized + * @param generator a function accepting an index and producing the desired + * value for that position + * @throws NullPointerException if the generator is null + * @since 1.8 + */ + public static void setAll(long[] array, IntToLongFunction generator) { + Objects.requireNonNull(generator); + for (int i = 0; i < array.length; i++) + array[i] = generator.applyAsLong(i); + } + + /** + * Set all elements of the specified array, in parallel, using the + * provided generator function to compute each element. + * + *

If the generator function throws an exception, an unchecked exception + * is thrown from {@code parallelSetAll} and the array is left in an + * indeterminate state. + * + * @param array array to be initialized + * @param generator a function accepting an index and producing the desired + * value for that position + * @throws NullPointerException if the generator is null + * @since 1.8 + */ + public static void parallelSetAll(long[] array, IntToLongFunction generator) { + Objects.requireNonNull(generator); + IntStream.range(0, array.length).parallel().forEach(i -> { array[i] = generator.applyAsLong(i); }); + } + + /** + * Set all elements of the specified array, using the provided + * generator function to compute each element. + * + *

If the generator function throws an exception, it is relayed to + * the caller and the array is left in an indeterminate state. + * + * @param array array to be initialized + * @param generator a function accepting an index and producing the desired + * value for that position + * @throws NullPointerException if the generator is null + * @since 1.8 + */ + public static void setAll(double[] array, IntToDoubleFunction generator) { + Objects.requireNonNull(generator); + for (int i = 0; i < array.length; i++) + array[i] = generator.applyAsDouble(i); + } + + /** + * Set all elements of the specified array, in parallel, using the + * provided generator function to compute each element. + * + *

If the generator function throws an exception, an unchecked exception + * is thrown from {@code parallelSetAll} and the array is left in an + * indeterminate state. + * + * @param array array to be initialized + * @param generator a function accepting an index and producing the desired + * value for that position + * @throws NullPointerException if the generator is null + * @since 1.8 + */ + public static void parallelSetAll(double[] array, IntToDoubleFunction generator) { + Objects.requireNonNull(generator); + IntStream.range(0, array.length).parallel().forEach(i -> { array[i] = generator.applyAsDouble(i); }); + } + + /** + * Returns a {@link Spliterator} covering all of the specified array. * *

The spliterator reports {@link Spliterator#SIZED}, * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and * {@link Spliterator#IMMUTABLE}. * - * @param Type of elements - * @param array The array, assumed to be unmodified during use - * @return A spliterator from the array - * @throws NullPointerException if the specified array is {@code null} + * @param type of elements + * @param array the array, assumed to be unmodified during use + * @return a spliterator for the array elements * @since 1.8 */ public static Spliterator spliterator(T[] array) { @@ -4325,39 +4493,38 @@ public class Arrays { } /** - * Creates a {@link Spliterator} covering the specified range of the + * Returns a {@link Spliterator} covering the specified range of the * specified array. * *

The spliterator reports {@link Spliterator#SIZED}, * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and * {@link Spliterator#IMMUTABLE}. * - * @param Type of elements - * @param array The array, assumed to be unmodified during use - * @param fromIndex The least index (inclusive) to cover - * @param toIndex One past the greatest index to cover - * @return A spliterator from the array - * @throws NullPointerException if the specified array is {@code null} - * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative, - * {@code toIndex} is less than {@code fromIndex}, or - * {@code toIndex} is greater than the array size + * @param type of elements + * @param array the array, assumed to be unmodified during use + * @param startInclusive the first index to cover, inclusive + * @param endExclusive index immediately past the last index to cover + * @return a spliterator for the array elements + * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is + * negative, {@code endExclusive} is less than + * {@code startInclusive}, or {@code endExclusive} is greater than + * the array size * @since 1.8 */ - public static Spliterator spliterator(T[] array, int fromIndex, int toIndex) { - return Spliterators.spliterator(array, fromIndex, toIndex, + public static Spliterator spliterator(T[] array, int startInclusive, int endExclusive) { + return Spliterators.spliterator(array, startInclusive, endExclusive, Spliterator.ORDERED | Spliterator.IMMUTABLE); } /** - * Creates a {@link Spliterator.OfInt} covering all of the specified array. + * Returns a {@link Spliterator.OfInt} covering all of the specified array. * *

The spliterator reports {@link Spliterator#SIZED}, * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and * {@link Spliterator#IMMUTABLE}. * - * @param array The array, assumed to be unmodified during use - * @return A spliterator from the array - * @throws NullPointerException if the specified array is {@code null} + * @param array the array, assumed to be unmodified during use + * @return a spliterator for the array elements * @since 1.8 */ public static Spliterator.OfInt spliterator(int[] array) { @@ -4366,38 +4533,37 @@ public class Arrays { } /** - * Creates a {@link Spliterator.OfInt} covering the specified range of the + * Returns a {@link Spliterator.OfInt} covering the specified range of the * specified array. * *

The spliterator reports {@link Spliterator#SIZED}, * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and * {@link Spliterator#IMMUTABLE}. * - * @param array The array, assumed to be unmodified during use - * @param fromIndex The least index (inclusive) to cover - * @param toIndex One past the greatest index to cover - * @return A spliterator from the array - * @throws NullPointerException if the specified array is {@code null} - * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative, - * {@code toIndex} is less than {@code fromIndex}, or - * {@code toIndex} is greater than the array size + * @param array the array, assumed to be unmodified during use + * @param startInclusive the first index to cover, inclusive + * @param endExclusive index immediately past the last index to cover + * @return a spliterator for the array elements + * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is + * negative, {@code endExclusive} is less than + * {@code startInclusive}, or {@code endExclusive} is greater than + * the array size * @since 1.8 */ - public static Spliterator.OfInt spliterator(int[] array, int fromIndex, int toIndex) { - return Spliterators.spliterator(array, fromIndex, toIndex, + public static Spliterator.OfInt spliterator(int[] array, int startInclusive, int endExclusive) { + return Spliterators.spliterator(array, startInclusive, endExclusive, Spliterator.ORDERED | Spliterator.IMMUTABLE); } /** - * Creates a {@link Spliterator.OfLong} covering all of the specified array. + * Returns a {@link Spliterator.OfLong} covering all of the specified array. * *

The spliterator reports {@link Spliterator#SIZED}, * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and * {@link Spliterator#IMMUTABLE}. * - * @param array The array, assumed to be unmodified during use - * @return A spliterator from the array - * @throws NullPointerException if the specified array is {@code null} + * @param array the array, assumed to be unmodified during use + * @return the spliterator for the array elements * @since 1.8 */ public static Spliterator.OfLong spliterator(long[] array) { @@ -4406,39 +4572,38 @@ public class Arrays { } /** - * Creates a {@link Spliterator.OfLong} covering the specified range of the + * Returns a {@link Spliterator.OfLong} covering the specified range of the * specified array. * *

The spliterator reports {@link Spliterator#SIZED}, * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and * {@link Spliterator#IMMUTABLE}. * - * @param array The array, assumed to be unmodified during use - * @param fromIndex The least index (inclusive) to cover - * @param toIndex One past the greatest index to cover - * @return A spliterator from the array - * @throws NullPointerException if the specified array is {@code null} - * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative, - * {@code toIndex} is less than {@code fromIndex}, or - * {@code toIndex} is greater than the array size + * @param array the array, assumed to be unmodified during use + * @param startInclusive the first index to cover, inclusive + * @param endExclusive index immediately past the last index to cover + * @return a spliterator for the array elements + * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is + * negative, {@code endExclusive} is less than + * {@code startInclusive}, or {@code endExclusive} is greater than + * the array size * @since 1.8 */ - public static Spliterator.OfLong spliterator(long[] array, int fromIndex, int toIndex) { - return Spliterators.spliterator(array, fromIndex, toIndex, + public static Spliterator.OfLong spliterator(long[] array, int startInclusive, int endExclusive) { + return Spliterators.spliterator(array, startInclusive, endExclusive, Spliterator.ORDERED | Spliterator.IMMUTABLE); } /** - * Creates a {@link Spliterator.OfDouble} covering all of the specified + * Returns a {@link Spliterator.OfDouble} covering all of the specified * array. * *

The spliterator reports {@link Spliterator#SIZED}, * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and * {@link Spliterator#IMMUTABLE}. * - * @param array The array, assumed to be unmodified during use - * @return A spliterator from the array - * @throws NullPointerException if the specified array is {@code null} + * @param array the array, assumed to be unmodified during use + * @return a spliterator for the array elements * @since 1.8 */ public static Spliterator.OfDouble spliterator(double[] array) { @@ -4447,25 +4612,147 @@ public class Arrays { } /** - * Creates a {@link Spliterator.OfDouble} covering the specified range of + * Returns a {@link Spliterator.OfDouble} covering the specified range of * the specified array. * *

The spliterator reports {@link Spliterator#SIZED}, * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and * {@link Spliterator#IMMUTABLE}. * - * @param array The array, assumed to be unmodified during use - * @param fromIndex The least index (inclusive) to cover - * @param toIndex One past the greatest index to cover - * @return A spliterator from the array - * @throws NullPointerException if the specified array is {@code null} - * @throws ArrayIndexOutOfBoundsException if {@code fromIndex} is negative, - * {@code toIndex} is less than {@code fromIndex}, or - * {@code toIndex} is greater than the array size + * @param array the array, assumed to be unmodified during use + * @param startInclusive the first index to cover, inclusive + * @param endExclusive index immediately past the last index to cover + * @return a spliterator for the array elements + * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is + * negative, {@code endExclusive} is less than + * {@code startInclusive}, or {@code endExclusive} is greater than + * the array size * @since 1.8 */ - public static Spliterator.OfDouble spliterator(double[] array, int fromIndex, int toIndex) { - return Spliterators.spliterator(array, fromIndex, toIndex, + public static Spliterator.OfDouble spliterator(double[] array, int startInclusive, int endExclusive) { + return Spliterators.spliterator(array, startInclusive, endExclusive, Spliterator.ORDERED | Spliterator.IMMUTABLE); } + + /** + * Returns a sequential {@link Stream} with the specified array as its + * source. + * + * @param The type of the array elements + * @param array The array, assumed to be unmodified during use + * @return a {@code Stream} for the array + * @since 1.8 + */ + public static Stream stream(T[] array) { + return stream(array, 0, array.length); + } + + /** + * Returns a sequential {@link Stream} with the specified range of the + * specified array as its source. + * + * @param the type of the array elements + * @param array the array, assumed to be unmodified during use + * @param startInclusive the first index to cover, inclusive + * @param endExclusive index immediately past the last index to cover + * @return a {@code Stream} for the array range + * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is + * negative, {@code endExclusive} is less than + * {@code startInclusive}, or {@code endExclusive} is greater than + * the array size + * @since 1.8 + */ + public static Stream stream(T[] array, int startInclusive, int endExclusive) { + return StreamSupport.stream(spliterator(array, startInclusive, endExclusive)); + } + + /** + * Returns a sequential {@link IntStream} with the specified array as its + * source. + * + * @param array the array, assumed to be unmodified during use + * @return an {@code IntStream} for the array + * @since 1.8 + */ + public static IntStream stream(int[] array) { + return stream(array, 0, array.length); + } + + /** + * Returns a sequential {@link IntStream} with the specified range of the + * specified array as its source. + * + * @param array the array, assumed to be unmodified during use + * @param startInclusive the first index to cover, inclusive + * @param endExclusive index immediately past the last index to cover + * @return an {@code IntStream} for the array range + * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is + * negative, {@code endExclusive} is less than + * {@code startInclusive}, or {@code endExclusive} is greater than + * the array size + * @since 1.8 + */ + public static IntStream stream(int[] array, int startInclusive, int endExclusive) { + return StreamSupport.intStream(spliterator(array, startInclusive, endExclusive)); + } + + /** + * Returns a sequential {@link LongStream} with the specified array as its + * source. + * + * @param array the array, assumed to be unmodified during use + * @return a {@code LongStream} for the array + * @since 1.8 + */ + public static LongStream stream(long[] array) { + return stream(array, 0, array.length); + } + + /** + * Returns a sequential {@link LongStream} with the specified range of the + * specified array as its source. + * + * @param array the array, assumed to be unmodified during use + * @param startInclusive the first index to cover, inclusive + * @param endExclusive index immediately past the last index to cover + * @return a {@code LongStream} for the array range + * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is + * negative, {@code endExclusive} is less than + * {@code startInclusive}, or {@code endExclusive} is greater than + * the array size + * @since 1.8 + */ + public static LongStream stream(long[] array, int startInclusive, int endExclusive) { + return StreamSupport.longStream(spliterator(array, startInclusive, endExclusive)); + } + + /** + * Returns a sequential {@link DoubleStream} with the specified array as its + * source. + * + * @param array the array, assumed to be unmodified during use + * @return a {@code DoubleStream} for the array + * @since 1.8 + */ + public static DoubleStream stream(double[] array) { + return stream(array, 0, array.length); + } + + /** + * Returns a sequential {@link DoubleStream} with the specified range of the + * specified array as its source. + * + * @param array the array, assumed to be unmodified during use + * @param startInclusive the first index to cover, inclusive + * @param endExclusive index immediately past the last index to cover + * @return a {@code DoubleStream} for the array range + * @throws ArrayIndexOutOfBoundsException if {@code startInclusive} is + * negative, {@code endExclusive} is less than + * {@code startInclusive}, or {@code endExclusive} is greater than + * the array size + * @since 1.8 + */ + public static DoubleStream stream(double[] array, int startInclusive, int endExclusive) { + return StreamSupport.doubleStream(spliterator(array, startInclusive, endExclusive)); + } } diff --git a/jdk/src/share/classes/java/util/stream/Collectors.java b/jdk/src/share/classes/java/util/stream/Collectors.java new file mode 100644 index 00000000000..0d14c888e9e --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/Collectors.java @@ -0,0 +1,1320 @@ +/* + * 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. 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.AbstractMap; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Comparators; +import java.util.DoubleSummaryStatistics; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IntSummaryStatistics; +import java.util.Iterator; +import java.util.List; +import java.util.LongSummaryStatistics; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Set; +import java.util.StringJoiner; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Function; +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; + +/** + * Implementations of {@link Collector} that implement various useful reduction + * operations, such as accumulating elements into collections, summarizing + * elements according to various criteria, etc. + * + *

The following are examples of using the predefined {@code Collector} + * implementations in {@link Collectors} with the {@code Stream} API to perform + * mutable reduction tasks: + * + *

{@code
+ *     // Accumulate elements into a List
+ *     List list = people.collect(Collectors.toList());
+ *
+ *     // Accumulate elements into a TreeSet
+ *     List list = people.collect(Collectors.toCollection(TreeSet::new));
+ *
+ *     // Convert elements to strings and concatenate them, separated by commas
+ *     String joined = stream.map(Object::toString)
+ *                           .collect(Collectors.toStringJoiner(", "))
+ *                           .toString();
+ *
+ *     // Find highest-paid employee
+ *     Employee highestPaid = employees.stream()
+ *                                     .collect(Collectors.maxBy(Comparators.comparing(Employee::getSalary)));
+ *
+ *     // Group employees by department
+ *     Map> byDept
+ *         = employees.stream()
+ *                    .collect(Collectors.groupingBy(Employee::getDepartment));
+ *
+ *     // Find highest-paid employee by department
+ *     Map highestPaidByDept
+ *         = employees.stream()
+ *                    .collect(Collectors.groupingBy(Employee::getDepartment,
+ *                                                   Collectors.maxBy(Comparators.comparing(Employee::getSalary))));
+ *
+ *     // Partition students into passing and failing
+ *     Map> passingFailing =
+ *         students.stream()
+ *                 .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD);
+ *
+ * }
+ * + * TODO explanation of parallel collection + * + * @since 1.8 + */ +public final class Collectors { + + private static final Set CH_CONCURRENT + = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.CONCURRENT, + Collector.Characteristics.STRICTLY_MUTATIVE, + Collector.Characteristics.UNORDERED)); + private static final Set CH_STRICT + = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.STRICTLY_MUTATIVE)); + private static final Set CH_STRICT_UNORDERED + = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.STRICTLY_MUTATIVE, + Collector.Characteristics.UNORDERED)); + + private Collectors() { } + + /** + * Returns a merge function, suitable for use in + * {@link Map#merge(Object, Object, BiFunction) Map.merge()} or + * {@link #toMap(Function, Function, BinaryOperator) toMap()}, which always + * throws {@code IllegalStateException}. This can be used to enforce the + * assumption that the elements being collected are distinct. + * + * @param the type of input arguments to the merge function + * @return a merge function which always throw {@code IllegalStateException} + * + * @see #firstWinsMerger() + * @see #lastWinsMerger() + */ + public static BinaryOperator throwingMerger() { + return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }; + } + + /** + * Returns a merge function, suitable for use in + * {@link Map#merge(Object, Object, BiFunction) Map.merge()} or + * {@link #toMap(Function, Function, BinaryOperator) toMap()}, + * which implements a "first wins" policy. + * + * @param the type of input arguments to the merge function + * @return a merge function which always returns its first argument + * @see #lastWinsMerger() + * @see #throwingMerger() + */ + public static BinaryOperator firstWinsMerger() { + return (u,v) -> u; + } + + /** + * Returns a merge function, suitable for use in + * {@link Map#merge(Object, Object, BiFunction) Map.merge()} or + * {@link #toMap(Function, Function, BinaryOperator) toMap()}, + * which implements a "last wins" policy. + * + * @param the type of input arguments to the merge function + * @return a merge function which always returns its second argument + * @see #firstWinsMerger() + * @see #throwingMerger() + */ + public static BinaryOperator lastWinsMerger() { + return (u,v) -> v; + } + + /** + * Simple implementation class for {@code Collector}. + * + * @param the type of elements to be collected + * @param the type of the result + */ + private static final class CollectorImpl implements Collector { + private final Supplier resultSupplier; + private final BiFunction accumulator; + private final BinaryOperator combiner; + private final Set characteristics; + + CollectorImpl(Supplier resultSupplier, + BiFunction accumulator, + BinaryOperator combiner, + Set characteristics) { + this.resultSupplier = resultSupplier; + this.accumulator = accumulator; + this.combiner = combiner; + this.characteristics = characteristics; + } + + CollectorImpl(Supplier resultSupplier, + BiFunction accumulator, + BinaryOperator combiner) { + this(resultSupplier, accumulator, combiner, Collections.emptySet()); + } + + @Override + public BiFunction accumulator() { + return accumulator; + } + + @Override + public Supplier resultSupplier() { + return resultSupplier; + } + + @Override + public BinaryOperator combiner() { + return combiner; + } + + @Override + public Set characteristics() { + return characteristics; + } + } + + /** + * Returns a {@code Collector} that accumulates the input elements into a + * new {@code Collection}, in encounter order. The {@code Collection} is + * created by the provided factory. + * + * @param the type of the input elements + * @param the type of the resulting {@code Collection} + * @param collectionFactory a {@code Supplier} which returns a new, empty + * {@code Collection} of the appropriate type + * @return a {@code Collector} which collects all the input elements into a + * {@code Collection}, in encounter order + */ + public static > + Collector toCollection(Supplier collectionFactory) { + return new CollectorImpl<>(collectionFactory, + (r, t) -> { r.add(t); return r; }, + (r1, r2) -> { r1.addAll(r2); return r1; }, + CH_STRICT); + } + + /** + * 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. + * + * @param the type of the input elements + * @return a {@code Collector} which collects all the input elements into a + * {@code List}, in encounter order + */ + public static + Collector> toList() { + BiFunction, T, List> accumulator = (list, t) -> { + switch (list.size()) { + case 0: + return Collections.singletonList(t); + case 1: + List newList = new ArrayList<>(); + newList.add(list.get(0)); + newList.add(t); + return newList; + default: + list.add(t); + return list; + } + }; + BinaryOperator> combiner = (left, right) -> { + switch (left.size()) { + case 0: + return right; + case 1: + List newList = new ArrayList<>(left.size() + right.size()); + newList.addAll(left); + newList.addAll(right); + return newList; + default: + left.addAll(right); + return left; + } + }; + return new CollectorImpl<>(Collections::emptyList, accumulator, combiner); + } + + /** + * 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. + * + *

This is an {@link Collector.Characteristics#UNORDERED unordered} + * Collector. + * + * @param the type of the input elements + * @return a {@code Collector} which collects all the input elements into a + * {@code Set} + */ + public static + Collector> toSet() { + return new CollectorImpl<>((Supplier>) HashSet::new, + (r, t) -> { r.add(t); return r; }, + (r1, r2) -> { r1.addAll(r2); return r1; }, + CH_STRICT_UNORDERED); + } + + /** + * Returns a {@code Collector} that concatenates the input elements into a + * new {@link StringBuilder}. + * + * @return a {@code Collector} which collects String elements into a + * {@code StringBuilder}, in encounter order + */ + public static Collector toStringBuilder() { + return new CollectorImpl<>(StringBuilder::new, + (r, t) -> { r.append(t); return r; }, + (r1, r2) -> { r1.append(r2); return r1; }, + CH_STRICT); + } + + /** + * Returns a {@code Collector} that concatenates the input elements into a + * new {@link StringJoiner}, using the specified delimiter. + * + * @param delimiter the delimiter to be used between each element + * @return A {@code Collector} which collects String elements into a + * {@code StringJoiner}, in encounter order + */ + public static Collector toStringJoiner(CharSequence delimiter) { + BinaryOperator merger = (sj, other) -> { + if (other.length() > 0) + sj.add(other.toString()); + return sj; + }; + return new CollectorImpl<>(() -> new StringJoiner(delimiter), + (r, t) -> { r.add(t); return r; }, + merger, CH_STRICT); + } + + /** + * {@code BinaryOperator} that merges the contents of its right + * argument into its left argument, using the provided merge function to + * handle duplicate keys. + * + * @param type of the map keys + * @param type of the map values + * @param type of the map + * @param mergeFunction A merge function suitable for + * {@link Map#merge(Object, Object, BiFunction) Map.merge()} + * @return a merge function for two maps + */ + private static > + BinaryOperator mapMerger(BinaryOperator mergeFunction) { + return (m1, m2) -> { + for (Map.Entry e : m2.entrySet()) + m1.merge(e.getKey(), e.getValue(), mergeFunction); + return m1; + }; + } + + /** + * Adapts a {@code Collector} to a {@code Collector} by applying + * a mapping function to each input element before accumulation. + * + * @apiNote + * The {@code mapping()} collectors are most useful when used in a + * multi-level reduction, downstream of {@code groupingBy} or + * {@code partitioningBy}. For example, given a stream of + * {@code Person}, to accumulate the set of last names in each city: + *

{@code
+     *     Map> lastNamesByCity
+     *         = people.stream().collect(groupingBy(Person::getCity,
+     *                                              mapping(Person::getLastName, toSet())));
+     * }
+ * + * @param the type of the input elements + * @param type of elements accepted by downstream collector + * @param result type of collector + * @param mapper a function to be applied to the input elements + * @param downstream a collector which will accept mapped values + * @return a collector which applies the mapping function to the input + * elements and provides the mapped results to the downstream collector + */ + public static Collector + mapping(Function mapper, Collector downstream) { + BiFunction downstreamAccumulator = downstream.accumulator(); + return new CollectorImpl<>(downstream.resultSupplier(), + (r, t) -> downstreamAccumulator.apply(r, mapper.apply(t)), + downstream.combiner(), downstream.characteristics()); + } + + /** + * Returns a {@code Collector} that counts the number of input + * elements. + * + * @implSpec + * This produces a result equivalent to: + *
{@code
+     *     reducing(0L, e -> 1L, Long::sum)
+     * }
+ * + * @param the type of the input elements + * @return a {@code Collector} that counts the input elements + */ + public static Collector + counting() { + return reducing(0L, e -> 1L, Long::sum); + } + + /** + * Returns a {@code Collector} that produces the minimal element + * according to a given {@code Comparator}. + * + * @implSpec + * This produces a result equivalent to: + *
{@code
+     *     reducing(Comparators.lesserOf(comparator))
+     * }
+ * + * @param the type of the input elements + * @param comparator a {@code Comparator} for comparing elements + * @return a {@code Collector} that produces the minimal value + */ + public static Collector + minBy(Comparator comparator) { + return reducing(Comparators.lesserOf(comparator)); + } + + /** + * Returns a {@code Collector} that produces the maximal element + * according to a given {@code Comparator}. + * + * @implSpec + * This produces a result equivalent to: + *
{@code
+     *     reducing(Comparators.greaterOf(comparator))
+     * }
+ * + * @param the type of the input elements + * @param comparator a {@code Comparator} for comparing elements + * @return a {@code Collector} that produces the maximal value + */ + public static Collector + maxBy(Comparator comparator) { + return reducing(Comparators.greaterOf(comparator)); + } + + /** + * Returns a {@code Collector} that produces the sum of a + * long-valued function applied to the input element. + * + * @implSpec + * This produces a result equivalent to: + *
{@code
+     *     reducing(0L, mapper, Long::sum)
+     * }
+ * + * @param the type of the input elements + * @param mapper a function extracting the property to be summed + * @return a {@code Collector} that produces the sum of a derived property + */ + public static Collector + sumBy(Function mapper) { + return reducing(0L, mapper, Long::sum); + } + + /** + * Returns a {@code Collector} which performs a reduction of its + * input elements under a specified {@code BinaryOperator}. + * + * @apiNote + * The {@code reducing()} collectors are most useful when used in a + * multi-level reduction, downstream of {@code groupingBy} or + * {@code partitioningBy}. To perform a simple reduction on a stream, + * use {@link Stream#reduce(BinaryOperator)} instead. + * + * @param element type for the input and output of the reduction + * @param identity the identity value for the reduction (also, the value + * that is returned when there are no input elements) + * @param op a {@code BinaryOperator} used to reduce the input elements + * @return a {@code Collector} which implements the reduction operation + * + * @see #reducing(BinaryOperator) + * @see #reducing(Object, Function, BinaryOperator) + */ + public static Collector + reducing(T identity, BinaryOperator op) { + return new CollectorImpl<>(() -> identity, (r, t) -> (r == null ? t : op.apply(r, t)), op); + } + + /** + * Returns a {@code Collector} which performs a reduction of its + * input elements under a specified {@code BinaryOperator}. + * + * @apiNote + * The {@code reducing()} collectors are most useful when used in a + * multi-level reduction, downstream of {@code groupingBy} or + * {@code partitioningBy}. To perform a simple reduction on a stream, + * use {@link Stream#reduce(BinaryOperator)} instead. + * + *

For example, given a stream of {@code Person}, to calculate tallest + * person in each city: + *

{@code
+     *     Comparator byHeight = Comparators.comparing(Person::getHeight);
+     *     BinaryOperator tallerOf = Comparators.greaterOf(byHeight);
+     *     Map tallestByCity
+     *         = people.stream().collect(groupingBy(Person::getCity, reducing(tallerOf)));
+     * }
+ * + * @implSpec + * The default implementation is equivalent to: + *
{@code
+     *     reducing(null, op);
+     * }
+ * + * @param element type for the input and output of the reduction + * @param op a {@code BinaryOperator} used to reduce the input elements + * @return a {@code Collector} which implements the reduction operation + * + * @see #reducing(Object, BinaryOperator) + * @see #reducing(Object, Function, BinaryOperator) + */ + public static Collector + reducing(BinaryOperator op) { + return reducing(null, op); + } + + /** + * Returns a {@code Collector} which performs a reduction of its + * input elements under a specified mapping function and + * {@code BinaryOperator}. This is a generalization of + * {@link #reducing(Object, BinaryOperator)} which allows a transformation + * of the elements before reduction. + * + * @apiNote + * The {@code reducing()} collectors are most useful when used in a + * multi-level reduction, downstream of {@code groupingBy} or + * {@code partitioningBy}. To perform a simple reduction on a stream, + * use {@link Stream#reduce(BinaryOperator)} instead. + * + *

For example, given a stream of {@code Person}, to calculate the longest + * last name of residents in each city: + *

{@code
+     *     Comparator byLength = Comparators.comparing(String::length);
+     *     BinaryOperator longerOf = Comparators.greaterOf(byLength);
+     *     Map longestLastNameByCity
+     *         = people.stream().collect(groupingBy(Person::getCity,
+     *                                              reducing(Person::getLastName, longerOf)));
+     * }
+ * + * @param the type of the input elements + * @param the type of the mapped values + * @param identity the identity value for the reduction (also, the value + * that is returned when there are no input elements) + * @param mapper a mapping function to apply to each input value + * @param op a {@code BinaryOperator} used to reduce the mapped values + * @return a {@code Collector} implementing the map-reduce operation + * + * @see #reducing(Object, BinaryOperator) + * @see #reducing(BinaryOperator) + */ + public static + Collector reducing(U identity, + Function mapper, + BinaryOperator op) { + return new CollectorImpl<>(() -> identity, + (r, t) -> (r == null ? mapper.apply(t) : op.apply(r, mapper.apply(t))), + op); + } + + /** + * Returns a {@code Collector} implementing a "group by" operation on + * input elements of type {@code T}, grouping elements according to a + * classification function. + * + *

The classification function maps elements to some key type {@code K}. + * The collector produces a {@code Map>} whose keys are the + * values resulting from applying the classification function to the input + * elements, and whose corresponding values are {@code List}s containing the + * input elements which map to the associated key under the classification + * function. + * + *

There are no guarantees on the type, mutability, serializability, or + * thread-safety of the {@code Map} or {@code List} objects returned. + * @implSpec + * This produces a result similar to: + *

{@code
+     *     groupingBy(classifier, toList());
+     * }
+ * + * @param the type of the input elements + * @param the type of the keys + * @param classifier the classifier function mapping input elements to keys + * @return a {@code Collector} implementing the group-by operation + * + * @see #groupingBy(Function, Collector) + * @see #groupingBy(Function, Supplier, Collector) + * @see #groupingByConcurrent(Function) + */ + public static + Collector>> groupingBy(Function classifier) { + return groupingBy(classifier, HashMap::new, toList()); + } + + /** + * Returns a {@code Collector} implementing a cascaded "group by" operation + * on input elements of type {@code T}, grouping elements according to a + * classification function, and then performing a reduction operation on + * the values associated with a given key using the specified downstream + * {@code Collector}. + * + *

The classification function maps elements to some key type {@code K}. + * The downstream collector operates on elements of type {@code T} and + * produces a result of type {@code D}. The resulting collector produces a + * {@code Map}. + * + *

There are no guarantees on the type, mutability, + * serializability, or thread-safety of the {@code Map} returned. + * + *

For example, to compute the set of last names of people in each city: + *

{@code
+     *     Map> namesByCity
+     *         = people.stream().collect(groupingBy(Person::getCity,
+     *                                              mapping(Person::getLastName, toSet())));
+     * }
+ * + * @param the type of the input elements + * @param the type of the keys + * @param the result type of the downstream reduction + * @param classifier a classifier function mapping input elements to keys + * @param downstream a {@code Collector} implementing the downstream reduction + * @return a {@code Collector} implementing the cascaded group-by operation + * @see #groupingBy(Function) + * + * @see #groupingBy(Function, Supplier, Collector) + * @see #groupingByConcurrent(Function, Collector) + */ + public static + Collector> groupingBy(Function classifier, + Collector downstream) { + return groupingBy(classifier, HashMap::new, downstream); + } + + /** + * Returns a {@code Collector} implementing a cascaded "group by" operation + * on input elements of type {@code T}, grouping elements according to a + * classification function, and then performing a reduction operation on + * the values associated with a given key using the specified downstream + * {@code Collector}. The {@code Map} produced by the Collector is created + * with the supplied factory function. + * + *

The classification function maps elements to some key type {@code K}. + * The downstream collector operates on elements of type {@code T} and + * produces a result of type {@code D}. The resulting collector produces a + * {@code Map}. + * + *

For example, to compute the set of last names of people in each city, + * where the city names are sorted: + *

{@code
+     *     Map> namesByCity
+     *         = people.stream().collect(groupingBy(Person::getCity, TreeMap::new,
+     *                                              mapping(Person::getLastName, toSet())));
+     * }
+ * + * @param the type of the input elements + * @param the type of the keys + * @param the result type of the downstream reduction + * @param the type of the resulting {@code Map} + * @param classifier a classifier function mapping input elements to keys + * @param downstream a {@code Collector} implementing the downstream reduction + * @param mapFactory a function which, when called, produces a new empty + * {@code Map} of the desired type + * @return a {@code Collector} implementing the cascaded group-by operation + * + * @see #groupingBy(Function, Collector) + * @see #groupingBy(Function) + * @see #groupingByConcurrent(Function, Supplier, Collector) + */ + public static > + Collector groupingBy(Function classifier, + Supplier mapFactory, + Collector downstream) { + Supplier downstreamSupplier = downstream.resultSupplier(); + BiFunction downstreamAccumulator = downstream.accumulator(); + BiFunction accumulator = (m, t) -> { + K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key"); + D oldContainer = m.computeIfAbsent(key, k -> downstreamSupplier.get()); + D newContainer = downstreamAccumulator.apply(oldContainer, t); + if (newContainer != oldContainer) + m.put(key, newContainer); + return m; + }; + return new CollectorImpl<>(mapFactory, accumulator, mapMerger(downstream.combiner()), CH_STRICT); + } + + /** + * Returns a {@code Collector} implementing a concurrent "group by" + * operation on input elements of type {@code T}, grouping elements + * according to a classification function. + * + *

This is a {@link Collector.Characteristics#CONCURRENT concurrent} and + * {@link Collector.Characteristics#UNORDERED unordered} Collector. + * + *

The classification function maps elements to some key type {@code K}. + * The collector produces a {@code ConcurrentMap>} whose keys are the + * values resulting from applying the classification function to the input + * elements, and whose corresponding values are {@code List}s containing the + * input elements which map to the associated key under the classification + * function. + * + *

There are no guarantees on the type, mutability, or serializability + * of the {@code Map} or {@code List} objects returned, or of the + * thread-safety of the {@code List} objects returned. + * @implSpec + * This produces a result similar to: + *

{@code
+     *     groupingByConcurrent(classifier, toList());
+     * }
+ * + * @param the type of the input elements + * @param the type of the keys + * @param classifier a classifier function mapping input elements to keys + * @return a {@code Collector} implementing the group-by operation + * + * @see #groupingBy(Function) + * @see #groupingByConcurrent(Function, Collector) + * @see #groupingByConcurrent(Function, Supplier, Collector) + */ + public static + Collector>> groupingByConcurrent(Function classifier) { + return groupingByConcurrent(classifier, ConcurrentHashMap::new, toList()); + } + + /** + * Returns a {@code Collector} implementing a concurrent cascaded "group by" + * operation on input elements of type {@code T}, grouping elements + * according to a classification function, and then performing a reduction + * operation on the values associated with a given key using the specified + * downstream {@code Collector}. + * + *

This is a {@link Collector.Characteristics#CONCURRENT concurrent} and + * {@link Collector.Characteristics#UNORDERED unordered} Collector. + * + *

The classification function maps elements to some key type {@code K}. + * The downstream collector operates on elements of type {@code T} and + * produces a result of type {@code D}. The resulting collector produces a + * {@code Map}. + * + *

For example, to compute the set of last names of people in each city, + * where the city names are sorted: + *

{@code
+     *     ConcurrentMap> namesByCity
+     *         = people.stream().collect(groupingByConcurrent(Person::getCity, TreeMap::new,
+     *                                                        mapping(Person::getLastName, toSet())));
+     * }
+ * + * @param the type of the input elements + * @param the type of the keys + * @param the result type of the downstream reduction + * @param classifier a classifier function mapping input elements to keys + * @param downstream a {@code Collector} implementing the downstream reduction + * @return a {@code Collector} implementing the cascaded group-by operation + * + * @see #groupingBy(Function, Collector) + * @see #groupingByConcurrent(Function) + * @see #groupingByConcurrent(Function, Supplier, Collector) + */ + public static + Collector> groupingByConcurrent(Function classifier, + Collector downstream) { + return groupingByConcurrent(classifier, ConcurrentHashMap::new, downstream); + } + + /** + * Returns a concurrent {@code Collector} implementing a cascaded "group by" + * operation on input elements of type {@code T}, grouping elements + * according to a classification function, and then performing a reduction + * operation on the values associated with a given key using the specified + * downstream {@code Collector}. The {@code ConcurrentMap} produced by the + * Collector is created with the supplied factory function. + * + *

This is a {@link Collector.Characteristics#CONCURRENT concurrent} and + * {@link Collector.Characteristics#UNORDERED unordered} Collector. + * + *

The classification function maps elements to some key type {@code K}. + * The downstream collector operates on elements of type {@code T} and + * produces a result of type {@code D}. The resulting collector produces a + * {@code Map}. + * + *

For example, to compute the set of last names of people in each city, + * where the city names are sorted: + *

{@code
+     *     ConcurrentMap> namesByCity
+     *         = people.stream().collect(groupingBy(Person::getCity, ConcurrentSkipListMap::new,
+     *                                              mapping(Person::getLastName, toSet())));
+     * }
+ * + * + * @param the type of the input elements + * @param the type of the keys + * @param the result type of the downstream reduction + * @param the type of the resulting {@code ConcurrentMap} + * @param classifier a classifier function mapping input elements to keys + * @param downstream a {@code Collector} implementing the downstream reduction + * @param mapFactory a function which, when called, produces a new empty + * {@code ConcurrentMap} of the desired type + * @return a {@code Collector} implementing the cascaded group-by operation + * + * @see #groupingByConcurrent(Function) + * @see #groupingByConcurrent(Function, Collector) + * @see #groupingBy(Function, Supplier, Collector) + */ + public static > + Collector groupingByConcurrent(Function classifier, + Supplier mapFactory, + Collector downstream) { + Supplier downstreamSupplier = downstream.resultSupplier(); + BiFunction downstreamAccumulator = downstream.accumulator(); + BinaryOperator combiner = mapMerger(downstream.combiner()); + if (downstream.characteristics().contains(Collector.Characteristics.CONCURRENT)) { + BiFunction accumulator = (m, t) -> { + K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key"); + downstreamAccumulator.apply(m.computeIfAbsent(key, k -> downstreamSupplier.get()), t); + return m; + }; + return new CollectorImpl<>(mapFactory, accumulator, combiner, CH_CONCURRENT); + } else if (downstream.characteristics().contains(Collector.Characteristics.STRICTLY_MUTATIVE)) { + BiFunction accumulator = (m, t) -> { + K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key"); + D resultContainer = m.computeIfAbsent(key, k -> downstreamSupplier.get()); + synchronized (resultContainer) { + downstreamAccumulator.apply(resultContainer, t); + } + return m; + }; + return new CollectorImpl<>(mapFactory, accumulator, combiner, CH_CONCURRENT); + } else { + BiFunction accumulator = (m, t) -> { + K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key"); + do { + D oldResult = m.computeIfAbsent(key, k -> downstreamSupplier.get()); + if (oldResult == null) { + if (m.putIfAbsent(key, downstreamAccumulator.apply(null, t)) == null) + return m; + } else { + synchronized (oldResult) { + if (m.get(key) != oldResult) + continue; + D newResult = downstreamAccumulator.apply(oldResult, t); + if (oldResult != newResult) + m.put(key, newResult); + return m; + } + } + } while (true); + }; + return new CollectorImpl<>(mapFactory, accumulator, combiner, CH_CONCURRENT); + } + } + + /** + * Returns a {@code Collector} which partitions the input elements according + * to a {@code Predicate}, and organizes them into a + * {@code Map>}. + * + * There are no guarantees on the type, mutability, + * serializability, or thread-safety of the {@code Map} returned. + * + * @param the type of the input elements + * @param predicate a predicate used for classifying input elements + * @return a {@code Collector} implementing the partitioning operation + * + * @see #partitioningBy(Predicate, Collector) + */ + public static + Collector>> partitioningBy(Predicate predicate) { + return partitioningBy(predicate, toList()); + } + + /** + * Returns a {@code Collector} which partitions the input elements according + * to a {@code Predicate}, reduces the values in each partition according to + * another {@code Collector}, and organizes them into a + * {@code Map} whose values are the result of the downstream + * reduction. + * + *

There are no guarantees on the type, mutability, + * serializability, or thread-safety of the {@code Map} returned. + * + * @param the type of the input elements + * @param the result type of the downstream reduction + * @param predicate a predicate used for classifying input elements + * @param downstream a {@code Collector} implementing the downstream + * reduction + * @return a {@code Collector} implementing the cascaded partitioning + * operation + * + * @see #partitioningBy(Predicate) + */ + public static + Collector> partitioningBy(Predicate predicate, + Collector downstream) { + BiFunction downstreamAccumulator = downstream.accumulator(); + BiFunction, T, Map> accumulator = (result, t) -> { + Partition asPartition = ((Partition) result); + if (predicate.test(t)) { + D newResult = downstreamAccumulator.apply(asPartition.forTrue, t); + if (newResult != asPartition.forTrue) + asPartition.forTrue = newResult; + } else { + D newResult = downstreamAccumulator.apply(asPartition.forFalse, t); + if (newResult != asPartition.forFalse) + asPartition.forFalse = newResult; + } + return result; + }; + return new CollectorImpl<>(() -> new Partition<>(downstream.resultSupplier().get(), + downstream.resultSupplier().get()), + accumulator, partitionMerger(downstream.combiner()), CH_STRICT); + } + + /** + * Merge function for two partitions, given a merge function for the + * elements. + */ + private static BinaryOperator> partitionMerger(BinaryOperator op) { + return (m1, m2) -> { + Partition left = (Partition) m1; + Partition right = (Partition) m2; + if (left.forFalse == null) + left.forFalse = right.forFalse; + else if (right.forFalse != null) + left.forFalse = op.apply(left.forFalse, right.forFalse); + if (left.forTrue == null) + left.forTrue = right.forTrue; + else if (right.forTrue != null) + left.forTrue = op.apply(left.forTrue, right.forTrue); + return left; + }; + } + + /** + * Accumulate elements into a {@code Map} whose keys and values are the + * result of applying mapping functions to the input elements. + * If the mapped keys contains duplicates (according to + * {@link Object#equals(Object)}), an {@code IllegalStateException} is + * thrown when the collection operation is performed. If the mapped keys + * may have duplicates, use {@link #toMap(Function, Function, BinaryOperator)} + * instead. + * + * @apiNote + * It is common for either the key or the value to be the input elements. + * In this case, the utility method + * {@link java.util.function.Function#identity()} may be helpful. + * For example, the following produces a {@code Map} mapping + * students to their grade point average: + *

{@code
+     *     Map studentToGPA
+     *         students.stream().collect(toMap(Functions.identity(),
+     *                                         student -> computeGPA(student)));
+     * }
+ * And the following produces a {@code Map} mapping a unique identifier to + * students: + *
{@code
+     *     Map studentIdToStudent
+     *         students.stream().collect(toMap(Student::getId,
+     *                                         Functions.identity());
+     * }
+ * + * @param the type of the input elements + * @param the output type of the key mapping function + * @param the output type of the value mapping function + * @param keyMapper a mapping function to produce keys + * @param valueMapper a mapping function to produce values + * @return a {@code Collector} which collects elements into a {@code Map} + * whose keys and values are the result of applying mapping functions to + * the input elements + * + * @see #toMap(Function, Function, BinaryOperator) + * @see #toMap(Function, Function, BinaryOperator, Supplier) + * @see #toConcurrentMap(Function, Function) + */ + public static + Collector> toMap(Function keyMapper, + Function valueMapper) { + return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new); + } + + /** + * Accumulate elements into a {@code Map} whose keys and values are the + * result of applying mapping functions to the input elements. If the mapped + * keys contains duplicates (according to {@link Object#equals(Object)}), + * the value mapping function is applied to each equal element, and the + * results are merged using the provided merging function. + * + * @apiNote + * There are multiple ways to deal with collisions between multiple elements + * mapping to the same key. There are some predefined merging functions, + * such as {@link #throwingMerger()}, {@link #firstWinsMerger()}, and + * {@link #lastWinsMerger()}, that implement common policies, or you can + * implement custom policies easily. For example, if you have a stream + * of {@code Person}, and you want to produce a "phone book" mapping name to + * address, but it is possible that two persons have the same name, you can + * do as follows to gracefully deals with these collisions, and produce a + * {@code Map} mapping names to a concatenated list of addresses: + *
{@code
+     *     Map phoneBook
+     *         people.stream().collect(toMap(Person::getName,
+     *                                       Person::getAddress,
+     *                                       (s, a) -> s + ", " + a));
+     * }
+ * + * @param the type of the input elements + * @param the output type of the key mapping function + * @param the output type of the value mapping function + * @param keyMapper a mapping function to produce keys + * @param valueMapper a mapping function to produce values + * @param mergeFunction a merge function, used to resolve collisions between + * values associated with the same key, as supplied + * to {@link Map#merge(Object, Object, BiFunction)} + * @return a {@code Collector} which collects elements into a {@code Map} + * whose keys are the result of applying a key mapping function to the input + * elements, and whose values are the result of applying a value mapping + * function to all input elements equal to the key and combining them + * using the merge function + * + * @see #toMap(Function, Function) + * @see #toMap(Function, Function, BinaryOperator, Supplier) + * @see #toConcurrentMap(Function, Function, BinaryOperator) + */ + public static + Collector> toMap(Function keyMapper, + Function valueMapper, + BinaryOperator mergeFunction) { + return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new); + } + + /** + * Accumulate elements into a {@code Map} whose keys and values are the + * result of applying mapping functions to the input elements. If the mapped + * keys contains duplicates (according to {@link Object#equals(Object)}), + * the value mapping function is applied to each equal element, and the + * results are merged using the provided merging function. The {@code Map} + * is created by a provided supplier function. + * + * @param the type of the input elements + * @param the output type of the key mapping function + * @param the output type of the value mapping function + * @param the type of the resulting {@code Map} + * @param keyMapper a mapping function to produce keys + * @param valueMapper a mapping function to produce values + * @param mergeFunction a merge function, used to resolve collisions between + * values associated with the same key, as supplied + * to {@link Map#merge(Object, Object, BiFunction)} + * @param mapSupplier a function which returns a new, empty {@code Map} into + * which the results will be inserted + * @return a {@code Collector} which collects elements into a {@code Map} + * whose keys are the result of applying a key mapping function to the input + * elements, and whose values are the result of applying a value mapping + * function to all input elements equal to the key and combining them + * using the merge function + * + * @see #toMap(Function, Function) + * @see #toMap(Function, Function, BinaryOperator) + * @see #toConcurrentMap(Function, Function, BinaryOperator, Supplier) + */ + public static > + Collector toMap(Function keyMapper, + Function valueMapper, + BinaryOperator mergeFunction, + Supplier mapSupplier) { + BiFunction accumulator + = (map, element) -> { + map.merge(keyMapper.apply(element), valueMapper.apply(element), mergeFunction); + return map; + }; + return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_STRICT); + } + + /** + * Accumulate elements into a {@code ConcurrentMap} whose keys and values + * are the result of applying mapping functions to the input elements. + * If the mapped keys contains duplicates (according to + * {@link Object#equals(Object)}), an {@code IllegalStateException} is + * thrown when the collection operation is performed. If the mapped keys + * may have duplicates, use + * {@link #toConcurrentMap(Function, Function, BinaryOperator)} instead. + * + * @apiNote + * It is common for either the key or the value to be the input elements. + * In this case, the utility method + * {@link java.util.function.Function#identity()} may be helpful. + * For example, the following produces a {@code Map} mapping + * students to their grade point average: + *
{@code
+     *     Map studentToGPA
+     *         students.stream().collect(toMap(Functions.identity(),
+     *                                         student -> computeGPA(student)));
+     * }
+ * And the following produces a {@code Map} mapping a unique identifier to + * students: + *
{@code
+     *     Map studentIdToStudent
+     *         students.stream().collect(toConcurrentMap(Student::getId,
+     *                                                   Functions.identity());
+     * }
+ * + *

This is a {@link Collector.Characteristics#CONCURRENT concurrent} and + * {@link Collector.Characteristics#UNORDERED unordered} Collector. + * + * @param the type of the input elements + * @param the output type of the key mapping function + * @param the output type of the value mapping function + * @param keyMapper the mapping function to produce keys + * @param valueMapper the mapping function to produce values + * @return a concurrent {@code Collector} which collects elements into a + * {@code ConcurrentMap} whose keys are the result of applying a key mapping + * function to the input elements, and whose values are the result of + * applying a value mapping function to the input elements + * + * @see #toMap(Function, Function) + * @see #toConcurrentMap(Function, Function, BinaryOperator) + * @see #toConcurrentMap(Function, Function, BinaryOperator, Supplier) + */ + public static + Collector> toConcurrentMap(Function keyMapper, + Function valueMapper) { + return toConcurrentMap(keyMapper, valueMapper, throwingMerger(), ConcurrentHashMap::new); + } + + /** + * Accumulate elements into a {@code ConcurrentMap} whose keys and values + * are the result of applying mapping functions to the input elements. If + * the mapped keys contains duplicates (according to {@link Object#equals(Object)}), + * the value mapping function is applied to each equal element, and the + * results are merged using the provided merging function. + * + * @apiNote + * There are multiple ways to deal with collisions between multiple elements + * mapping to the same key. There are some predefined merging functions, + * such as {@link #throwingMerger()}, {@link #firstWinsMerger()}, and + * {@link #lastWinsMerger()}, that implement common policies, or you can + * implement custom policies easily. For example, if you have a stream + * of {@code Person}, and you want to produce a "phone book" mapping name to + * address, but it is possible that two persons have the same name, you can + * do as follows to gracefully deals with these collisions, and produce a + * {@code Map} mapping names to a concatenated list of addresses: + *

{@code
+     *     Map phoneBook
+     *         people.stream().collect(toConcurrentMap(Person::getName,
+     *                                                 Person::getAddress,
+     *                                                 (s, a) -> s + ", " + a));
+     * }
+ * + *

This is a {@link Collector.Characteristics#CONCURRENT concurrent} and + * {@link Collector.Characteristics#UNORDERED unordered} Collector. + * + * @param the type of the input elements + * @param the output type of the key mapping function + * @param the output type of the value mapping function + * @param keyMapper a mapping function to produce keys + * @param valueMapper a mapping function to produce values + * @param mergeFunction a merge function, used to resolve collisions between + * values associated with the same key, as supplied + * to {@link Map#merge(Object, Object, BiFunction)} + * @return a concurrent {@code Collector} which collects elements into a + * {@code ConcurrentMap} whose keys are the result of applying a key mapping + * function to the input elements, and whose values are the result of + * applying a value mapping function to all input elements equal to the key + * and combining them using the merge function + * + * @see #toConcurrentMap(Function, Function) + * @see #toConcurrentMap(Function, Function, BinaryOperator, Supplier) + * @see #toMap(Function, Function, BinaryOperator) + */ + public static + Collector> toConcurrentMap(Function keyMapper, + Function valueMapper, + BinaryOperator mergeFunction) { + return toConcurrentMap(keyMapper, valueMapper, mergeFunction, ConcurrentHashMap::new); + } + + /** + * Accumulate elements into a {@code ConcurrentMap} whose keys and values + * are the result of applying mapping functions to the input elements. If + * the mapped keys contains duplicates (according to {@link Object#equals(Object)}), + * the value mapping function is applied to each equal element, and the + * results are merged using the provided merging function. The + * {@code ConcurrentMap} is created by a provided supplier function. + * + *

This is a {@link Collector.Characteristics#CONCURRENT concurrent} and + * {@link Collector.Characteristics#UNORDERED unordered} Collector. + * + * @param the type of the input elements + * @param the output type of the key mapping function + * @param the output type of the value mapping function + * @param the type of the resulting {@code ConcurrentMap} + * @param keyMapper a mapping function to produce keys + * @param valueMapper a mapping function to produce values + * @param mergeFunction a merge function, used to resolve collisions between + * values associated with the same key, as supplied + * to {@link Map#merge(Object, Object, BiFunction)} + * @param mapSupplier a function which returns a new, empty {@code Map} into + * which the results will be inserted + * @return a concurrent {@code Collector} which collects elements into a + * {@code ConcurrentMap} whose keys are the result of applying a key mapping + * function to the input elements, and whose values are the result of + * applying a value mapping function to all input elements equal to the key + * and combining them using the merge function + * + * @see #toConcurrentMap(Function, Function) + * @see #toConcurrentMap(Function, Function, BinaryOperator) + * @see #toMap(Function, Function, BinaryOperator, Supplier) + */ + public static > + Collector toConcurrentMap(Function keyMapper, + Function valueMapper, + BinaryOperator mergeFunction, + Supplier mapSupplier) { + BiFunction accumulator = (map, element) -> { + map.merge(keyMapper.apply(element), valueMapper.apply(element), mergeFunction); + return map; + }; + return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_CONCURRENT); + } + + /** + * Returns a {@code Collector} which applies an {@code int}-producing + * mapping function to each input element, and returns summary statistics + * for the resulting values. + * + * @param the type of the input elements + * @param mapper a mapping function to apply to each element + * @return a {@code Collector} implementing the summary-statistics reduction + * + * @see #toDoubleSummaryStatistics(ToDoubleFunction) + * @see #toLongSummaryStatistics(ToLongFunction) + */ + public static + Collector toIntSummaryStatistics(ToIntFunction mapper) { + return new CollectorImpl<>(IntSummaryStatistics::new, + (r, t) -> { r.accept(mapper.applyAsInt(t)); return r; }, + (l, r) -> { l.combine(r); return l; }, CH_STRICT); + } + + /** + * Returns a {@code Collector} which applies an {@code long}-producing + * mapping function to each input element, and returns summary statistics + * for the resulting values. + * + * @param the type of the input elements + * @param mapper the mapping function to apply to each element + * @return a {@code Collector} implementing the summary-statistics reduction + * + * @see #toDoubleSummaryStatistics(ToDoubleFunction) + * @see #toIntSummaryStatistics(ToIntFunction) + */ + public static + Collector toLongSummaryStatistics(ToLongFunction mapper) { + return new CollectorImpl<>(LongSummaryStatistics::new, + (r, t) -> { r.accept(mapper.applyAsLong(t)); return r; }, + (l, r) -> { l.combine(r); return l; }, CH_STRICT); + } + + /** + * Returns a {@code Collector} which applies an {@code double}-producing + * mapping function to each input element, and returns summary statistics + * for the resulting values. + * + * @param the type of the input elements + * @param mapper a mapping function to apply to each element + * @return a {@code Collector} implementing the summary-statistics reduction + * + * @see #toLongSummaryStatistics(ToLongFunction) + * @see #toIntSummaryStatistics(ToIntFunction) + */ + public static + Collector toDoubleSummaryStatistics(ToDoubleFunction mapper) { + return new CollectorImpl<>(DoubleSummaryStatistics::new, + (r, t) -> { r.accept(mapper.applyAsDouble(t)); return r; }, + (l, r) -> { l.combine(r); return l; }, CH_STRICT); + } + + /** + * Implementation class used by partitioningBy. + */ + private static final class Partition + extends AbstractMap + implements Map { + T forTrue; + T forFalse; + + Partition(T forTrue, T forFalse) { + this.forTrue = forTrue; + this.forFalse = forFalse; + } + + @Override + public Set> entrySet() { + return new AbstractSet>() { + @Override + public Iterator> iterator() { + + return new Iterator>() { + int state = 0; + + @Override + public boolean hasNext() { + return state < 2; + } + + @Override + public Map.Entry next() { + if (state >= 2) + throw new NoSuchElementException(); + return (state++ == 0) + ? new SimpleImmutableEntry<>(false, forFalse) + : new SimpleImmutableEntry<>(true, forTrue); + } + }; + } + + @Override + public int size() { + return 2; + } + }; + } + } +} diff --git a/jdk/src/share/classes/java/util/stream/DoubleStream.java b/jdk/src/share/classes/java/util/stream/DoubleStream.java index e9d3fd15b0d..f105453603a 100644 --- a/jdk/src/share/classes/java/util/stream/DoubleStream.java +++ b/jdk/src/share/classes/java/util/stream/DoubleStream.java @@ -24,15 +24,19 @@ */ package java.util.stream; +import java.util.Arrays; 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.function.BiConsumer; import java.util.function.DoubleBinaryOperator; import java.util.function.DoubleConsumer; import java.util.function.DoubleFunction; import java.util.function.DoublePredicate; +import java.util.function.DoubleSupplier; import java.util.function.DoubleToIntFunction; import java.util.function.DoubleToLongFunction; import java.util.function.DoubleUnaryOperator; @@ -649,4 +653,175 @@ public interface DoubleStream extends BaseStream { @Override Spliterator.OfDouble spliterator(); + + + // Static factories + + /** + * Returns a builder for a {@code DoubleStream}. + * + * @return a stream builder + */ + public static StreamBuilder.OfDouble builder() { + return new Streams.DoubleStreamBuilderImpl(); + } + + /** + * Returns an empty sequential {@code DoubleStream}. + * + * @return an empty sequential stream + */ + public static DoubleStream empty() { + return StreamSupport.doubleStream(Spliterators.emptyDoubleSpliterator()); + } + + /** + * Returns a sequential {@code DoubleStream} containing a single element. + * + * @param t the single element + * @return a singleton sequential stream + */ + public static DoubleStream of(double t) { + return StreamSupport.doubleStream(new Streams.DoubleStreamBuilderImpl(t)); + } + + /** + * Returns a sequential stream whose elements are the specified values. + * + * @param values the elements of the new stream + * @return the new stream + */ + public static DoubleStream of(double... values) { + return Arrays.stream(values); + } + + /** + * Returns an infinite sequential {@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. + * + *

The first element (position {@code 0}) in the {@code DoubleStream} + * will be the provided {@code seed}. For {@code n > 0}, the element at + * position {@code n}, will be the result of applying the function {@code f} + * to the element at position {@code n - 1}. + * + * @param seed the initial element + * @param f a function to be applied to to the previous element to produce + * a new element + * @return a new sequential {@code DoubleStream} + */ + public static DoubleStream iterate(final double seed, final DoubleUnaryOperator f) { + Objects.requireNonNull(f); + final PrimitiveIterator.OfDouble iterator = new PrimitiveIterator.OfDouble() { + double t = seed; + + @Override + public boolean hasNext() { + return true; + } + + @Override + public double nextDouble() { + double v = t; + t = f.applyAsDouble(t); + return v; + } + }; + return StreamSupport.doubleStream(Spliterators.spliteratorUnknownSize( + iterator, + Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL)); + } + + /** + * Returns a sequential {@code DoubleStream} where each element is + * generated by an {@code DoubleSupplier}. This is suitable for generating + * constant streams, streams of random elements, etc. + * + * @param s the {@code DoubleSupplier} for generated elements + * @return a new sequential {@code DoubleStream} + */ + public static DoubleStream generate(DoubleSupplier s) { + Objects.requireNonNull(s); + return StreamSupport.doubleStream(Spliterators.spliteratorUnknownSize( + new PrimitiveIterator.OfDouble() { + @Override + public boolean hasNext() { return true; } + + @Override + public double nextDouble() { return s.getAsDouble(); } + }, + Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL)); + } + + /** + * Returns a sequential {@code DoubleStream} from {@code startInclusive} (inclusive) + * to {@code endExclusive} (exclusive) by an incremental step of 1.0. + * + * @implSpec + * The implementation behaves as if: + *

{@code
+     *     doubleRange(startInclusive, endExclusive, 1.0);
+     * }
+ * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @return a sequential {@code DoubleStream} for the range of {@code double} + * elements + */ + public static DoubleStream range(double startInclusive, double endExclusive) { + return range(startInclusive, endExclusive, 1.0); + } + + /** + * Returns a sequential {@code DoubleStream} from {@code startInclusive} + * (inclusive) to {@code endExclusive} (exclusive) by {@code step}. If + * {@code startInclusive} is greater than or equal to {@code + * endExclusive}, an empty stream is returned. + * + * An equivalent sequence of increasing values can be produced + * sequentially using a {@code for} loop as follows: + *
{@code
+     *     long size = (long) Math.ceil((startInclusive - endExclusive) / step);
+     *     long i = 0
+     *     for (double v = startInclusive; i < size; i++, v = startInclusive + step * i) {
+     *         ...
+     *     }
+     * }
+ * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @param step the difference between consecutive values + * @return a sequential {@code DoubleStream} for tne range of {@code double} + * elements + * @throws IllegalArgumentException if {@code step} is less than or equal to + * 0. is {@code NaN}, or the count of elements in the range would be + * greater than {@code Long.MAX_VALUE} + */ + public static DoubleStream range(double startInclusive, double endExclusive, double step) { + // @@@ Need to check for ranges that may not produce distinct values + // such as when the step is very small + // Also clarify the size of the range which may produce more or less + // than expected + if (step <= 0 || Double.isNaN(step)) { + throw new IllegalArgumentException(String.format("Illegal step: %f", step)); + } else { + double range = endExclusive - startInclusive; + if (range <= 0) { + return empty(); + } + double size = Math.ceil((endExclusive - startInclusive) / step); + if (Double.isNaN(size)) { + throw new IllegalArgumentException( + String.format("Illegal range: %f size is NaN", size)); + } else if (size > Long.MAX_VALUE) { + throw new IllegalArgumentException( + String.format("Illegal range: size %f > Long.MAX_VALUE", size)); + } else { + return StreamSupport.doubleStream( + new Streams.RangeDoubleSpliterator( + startInclusive, endExclusive, step, 0, (long) size)); + } + } + } } diff --git a/jdk/src/share/classes/java/util/stream/IntStream.java b/jdk/src/share/classes/java/util/stream/IntStream.java index 65fd16df4cc..9b343292752 100644 --- a/jdk/src/share/classes/java/util/stream/IntStream.java +++ b/jdk/src/share/classes/java/util/stream/IntStream.java @@ -24,17 +24,21 @@ */ package java.util.stream; +import java.util.Arrays; import java.util.IntSummaryStatistics; +import java.util.Objects; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.PrimitiveIterator; import java.util.Spliterator; +import java.util.Spliterators; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.IntBinaryOperator; import java.util.function.IntConsumer; import java.util.function.IntFunction; import java.util.function.IntPredicate; +import java.util.function.IntSupplier; import java.util.function.IntToDoubleFunction; import java.util.function.IntToLongFunction; import java.util.function.IntUnaryOperator; @@ -652,4 +656,153 @@ public interface IntStream extends BaseStream { @Override Spliterator.OfInt spliterator(); + + // Static factories + + /** + * Returns a builder for an {@code IntStream}. + * + * @return a stream builder + */ + public static StreamBuilder.OfInt builder() { + return new Streams.IntStreamBuilderImpl(); + } + + /** + * Returns an empty sequential {@code IntStream}. + * + * @return an empty sequential stream + */ + public static IntStream empty() { + return StreamSupport.intStream(Spliterators.emptyIntSpliterator()); + } + + /** + * Returns a sequential {@code IntStream} containing a single element. + * + * @param t the single element + * @return a singleton sequential stream + */ + public static IntStream of(int t) { + return StreamSupport.intStream(new Streams.IntStreamBuilderImpl(t)); + } + + /** + * Returns a sequential stream whose elements are the specified values. + * + * @param values the elements of the new stream + * @return the new stream + */ + public static IntStream of(int... values) { + return Arrays.stream(values); + } + + /** + * Returns an infinite sequential {@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. + * + *

The first element (position {@code 0}) in the {@code IntStream} will be + * the provided {@code seed}. For {@code n > 0}, the element at position + * {@code n}, will be the result of applying the function {@code f} to the + * element at position {@code n - 1}. + * + * @param seed the initial element + * @param f a function to be applied to to the previous element to produce + * a new element + * @return A new sequential {@code IntStream} + */ + public static IntStream iterate(final int seed, final IntUnaryOperator f) { + Objects.requireNonNull(f); + final PrimitiveIterator.OfInt iterator = new PrimitiveIterator.OfInt() { + int t = seed; + + @Override + public boolean hasNext() { + return true; + } + + @Override + public int nextInt() { + int v = t; + t = f.applyAsInt(t); + return v; + } + }; + return StreamSupport.intStream(Spliterators.spliteratorUnknownSize( + iterator, + Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL)); + } + + /** + * Returns a sequential {@code IntStream} where each element is + * generated by an {@code IntSupplier}. This is suitable for generating + * constant streams, streams of random elements, etc. + * + * @param s the {@code IntSupplier} for generated elements + * @return a new sequential {@code IntStream} + */ + public static IntStream generate(IntSupplier s) { + Objects.requireNonNull(s); + return StreamSupport.intStream(Spliterators.spliteratorUnknownSize( + new PrimitiveIterator.OfInt() { + @Override + public boolean hasNext() { return true; } + + @Override + public int nextInt() { return s.getAsInt(); } + }, + Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL)); + } + + /** + * Returns a sequential {@code IntStream} from {@code startInclusive} + * (inclusive) to {@code endExclusive} (exclusive) by an incremental step of + * 1. + * + * @implSpec + * The implementation behaves as if: + *

{@code
+     *     intRange(startInclusive, endExclusive, 1);
+     * }
+ * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @return a sequential {@code IntStream} for the range of {@code int} + * elements + */ + public static IntStream range(int startInclusive, int endExclusive) { + return range(startInclusive, endExclusive, 1); + } + + /** + * Returns a sequential {@code IntStream} from {@code startInclusive} + * (inclusive) to {@code endExclusive} (exclusive) by a positive {@code + * step}. If {@code startInclusive} is greater than or equal to {@code + * endExclusive}, an empty stream is returned. + * + *

An equivalent sequence of increasing values can be produced + * sequentially using a {@code for} loop as follows: + *

{@code
+     *     for (int i = startInclusive; i < endExclusive ; i += step) { ... }
+     * }
+ * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @param step the positive difference between consecutive values + * @return a sequential {@code IntStream} for the range of {@code int} + * elements + * @throws IllegalArgumentException if {@code step} is less than or equal to + * 0 + */ + public static IntStream range(int startInclusive, int endExclusive, int step) { + if (step <= 0) { + throw new IllegalArgumentException(String.format("Illegal step: %d", step)); + } else if (startInclusive >= endExclusive) { + return empty(); + } else { + return StreamSupport.intStream(new Streams.RangeIntSpliterator(startInclusive, endExclusive, step)); + } + } } diff --git a/jdk/src/share/classes/java/util/stream/LongStream.java b/jdk/src/share/classes/java/util/stream/LongStream.java index 94425dcbb44..cde4d025e5a 100644 --- a/jdk/src/share/classes/java/util/stream/LongStream.java +++ b/jdk/src/share/classes/java/util/stream/LongStream.java @@ -24,17 +24,21 @@ */ package java.util.stream; +import java.util.Arrays; import java.util.LongSummaryStatistics; +import java.util.Objects; import java.util.OptionalDouble; import java.util.OptionalLong; import java.util.PrimitiveIterator; import java.util.Spliterator; +import java.util.Spliterators; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.LongBinaryOperator; import java.util.function.LongConsumer; import java.util.function.LongFunction; import java.util.function.LongPredicate; +import java.util.function.LongSupplier; import java.util.function.LongToDoubleFunction; import java.util.function.LongToIntFunction; import java.util.function.LongUnaryOperator; @@ -643,4 +647,153 @@ public interface LongStream extends BaseStream { @Override Spliterator.OfLong spliterator(); + + // Static factories + + /** + * Returns a builder for a {@code LongStream}. + * + * @return a stream builder + */ + public static StreamBuilder.OfLong builder() { + return new Streams.LongStreamBuilderImpl(); + } + + /** + * Returns an empty sequential {@code LongStream}. + * + * @return an empty sequential stream + */ + public static LongStream empty() { + return StreamSupport.longStream(Spliterators.emptyLongSpliterator()); + } + + /** + * Returns a sequential {@code LongStream} containing a single element. + * + * @param t the single element + * @return a singleton sequential stream + */ + public static LongStream of(long t) { + return StreamSupport.longStream(new Streams.LongStreamBuilderImpl(t)); + } + + /** + * Returns a sequential stream whose elements are the specified values. + * + * @param values the elements of the new stream + * @return the new stream + */ + public static LongStream of(long... values) { + return Arrays.stream(values); + } + + /** + * Returns an infinite sequential {@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. + * + *

The first element (position {@code 0}) in the {@code LongStream} will + * be the provided {@code seed}. For {@code n > 0}, the element at position + * {@code n}, will be the result of applying the function {@code f} to the + * element at position {@code n - 1}. + * + * @param seed the initial element + * @param f a function to be applied to to the previous element to produce + * a new element + * @return a new sequential {@code LongStream} + */ + public static LongStream iterate(final long seed, final LongUnaryOperator f) { + Objects.requireNonNull(f); + final PrimitiveIterator.OfLong iterator = new PrimitiveIterator.OfLong() { + long t = seed; + + @Override + public boolean hasNext() { + return true; + } + + @Override + public long nextLong() { + long v = t; + t = f.applyAsLong(t); + return v; + } + }; + return StreamSupport.longStream(Spliterators.spliteratorUnknownSize( + iterator, + Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL)); + } + + /** + * 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. + * + * @param s the {@code LongSupplier} for generated elements + * @return a new sequential {@code LongStream} + */ + public static LongStream generate(LongSupplier s) { + Objects.requireNonNull(s); + return StreamSupport.longStream(Spliterators.spliteratorUnknownSize( + new PrimitiveIterator.OfLong() { + @Override + public boolean hasNext() { return true; } + + @Override + public long nextLong() { return s.getAsLong(); } + }, + Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL)); + } + + /** + * Returns a sequential {@code LongStream} from {@code startInclusive} + * (inclusive) to {@code endExclusive} (exclusive) by an incremental step of + * 1. + * + * @implSpec + * The implementation behaves as if: + *

{@code
+     *     longRange(startInclusive, endExclusive, 1);
+     * }
+ * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @return a sequential {@code LongStream} for the range of {@code long} + * elements + */ + public static LongStream range(long startInclusive, final long endExclusive) { + return range(startInclusive, endExclusive, 1); + } + + /** + * Returns a sequential {@code LongStream} from {@code startInclusive} + * (inclusive) to {@code endExclusive} (exclusive) by {@code step}. If + * {@code startInclusive} is greater than or equal to {@code + * endExclusive}, an empty stream is returned. + * + *

An equivalent sequence of increasing values can be produced + * sequentially using a {@code for} loop as follows: + *

{@code
+     *     for (long i = startInclusive; i < endExclusive ; i += step) { ... }
+     * }
+ * + * @param startInclusive the (inclusive) initial value + * @param endExclusive the exclusive upper bound + * @param step the difference between consecutive values + * @return a sequential {@code LongStream} for the range of {@code long} + * elements + * @throws IllegalArgumentException if {@code step} is less than or equal to + * 0 + */ + public static LongStream range(long startInclusive, final long endExclusive, final long step) { + if (step <= 0) { + throw new IllegalArgumentException(String.format("Illegal step: %d", step)); + } else if (startInclusive >= endExclusive) { + return empty(); + } else { + return StreamSupport.longStream(new Streams.RangeLongSpliterator(startInclusive, endExclusive, step)); + } + } } diff --git a/jdk/src/share/classes/java/util/stream/Stream.java b/jdk/src/share/classes/java/util/stream/Stream.java index 0624f50b29d..516976280fe 100644 --- a/jdk/src/share/classes/java/util/stream/Stream.java +++ b/jdk/src/share/classes/java/util/stream/Stream.java @@ -24,8 +24,13 @@ */ package java.util.stream; +import java.util.Arrays; 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.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.BinaryOperator; @@ -37,6 +42,7 @@ import java.util.function.Supplier; import java.util.function.ToDoubleFunction; import java.util.function.ToIntFunction; import java.util.function.ToLongFunction; +import java.util.function.UnaryOperator; // @@@ Specification to-do list @@@ // - Describe the difference between sequential and parallel streams @@ -779,4 +785,109 @@ public interface Stream extends BaseStream> { * @see #findFirst() */ Optional findAny(); + + // Static factories + + /** + * Returns a builder for a {@code Stream}. + * + * @param type of elements + * @return a stream builder + */ + public static StreamBuilder builder() { + return new Streams.StreamBuilderImpl<>(); + } + + /** + * Returns an empty sequential {@code Stream}. + * + * @param the type of stream elements + * @return an empty sequential stream + */ + public static Stream empty() { + return StreamSupport.stream(Spliterators.emptySpliterator()); + } + + /** + * Returns a sequential {@code Stream} containing a single element. + * + * @param t the single element + * @param the type of stream elements + * @return a singleton sequential stream + */ + public static Stream of(T t) { + return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t)); + } + + /** + * Returns a sequential stream whose elements are the specified values. + * + * @param the type of stream elements + * @param values the elements of the new stream + * @return the new stream + */ + @SafeVarargs + public static Stream of(T... values) { + return Arrays.stream(values); + } + + /** + * Returns an infinite sequential {@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. + * + *

The first element (position {@code 0}) in the {@code Stream} will be + * the provided {@code seed}. For {@code n > 0}, the element at position + * {@code n}, will be the result of applying the function {@code f} to the + * element at position {@code n - 1}. + * + * @param the type of stream elements + * @param seed the initial element + * @param f a function to be applied to to the previous element to produce + * a new element + * @return a new sequential {@code Stream} + */ + public static Stream iterate(final T seed, final UnaryOperator f) { + Objects.requireNonNull(f); + final Iterator iterator = new Iterator() { + @SuppressWarnings("unchecked") + T t = (T) Streams.NONE; + + @Override + public boolean hasNext() { + return true; + } + + @Override + public T next() { + return t = (t == Streams.NONE) ? seed : f.apply(t); + } + }; + return StreamSupport.stream(Spliterators.spliteratorUnknownSize( + iterator, + Spliterator.ORDERED | Spliterator.IMMUTABLE)); + } + + /** + * Returns a sequential {@code Stream} where each element is + * generated by a {@code Supplier}. This is suitable for generating + * constant streams, streams of random elements, etc. + * + * @param the type of stream elements + * @param s the {@code Supplier} of generated elements + * @return a new sequential {@code Stream} + */ + public static Stream generate(Supplier s) { + Objects.requireNonNull(s); + return StreamSupport.stream(Spliterators.spliteratorUnknownSize( + new Iterator() { + @Override + public boolean hasNext() { return true; } + + @Override + public T next() { return s.get(); } + }, + Spliterator.ORDERED | Spliterator.IMMUTABLE)); + } } diff --git a/jdk/src/share/classes/java/util/stream/StreamBuilder.java b/jdk/src/share/classes/java/util/stream/StreamBuilder.java new file mode 100644 index 00000000000..cc3bc9a1842 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/StreamBuilder.java @@ -0,0 +1,265 @@ +/* + * 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.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; + +/** + * A mutable builder for a {@code Stream}. This allows the creation of a + * {@code Stream} by generating elements individually and adding them to the + * {@code StreamBuilder} (without the copying overhead that comes from using + * an {@code ArrayList} as a temporary buffer.) + * + *

A {@code StreamBuilder} 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 begins + * when the {@link #build()}} method is called, which creates an ordered + * {@code Stream} whose elements are the elements that were added to the stream + * builder, in the order they were added. + * + *

Primitive specializations of {@code StreamBuilder} are provided + * for {@link OfInt int}, {@link OfLong long}, and {@link OfDouble double} + * values. + * + * @param the type of stream elements + * @see Stream#builder() + * @since 1.8 + */ +public interface StreamBuilder extends Consumer { + + /** + * Adds an element to the stream being built. + * + * @throws IllegalStateException if the builder has already transitioned to + * the built state + */ + @Override + void accept(T t); + + /** + * Adds an element to the stream being built. + * + * @implSpec + * The default implementation behaves as if: + *

{@code
+     *     accept(t)
+     *     return this;
+     * }
+ * + * @param t the element to add + * @return {@code this} builder + * @throws IllegalStateException if the builder has already transitioned to + * the built state + */ + default StreamBuilder add(T t) { + accept(t); + return this; + } + + /** + * Builds the stream, transitioning this builder to the built state. + * An {@code IllegalStateException} is thrown if there are further attempts + * to operate on the builder after it has entered the built state. + * + * @return the built stream + * @throws IllegalStateException if the builder has already transitioned to + * the built state + */ + Stream build(); + + /** + * A mutable builder for an {@code IntStream}. + * + *

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 + * 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. + * + * @see IntStream#builder() + * @since 1.8 + */ + interface OfInt extends IntConsumer { + + /** + * Adds an element to the stream being built. + * + * @throws IllegalStateException if the builder has already transitioned + * to the built state + */ + @Override + void accept(int t); + + /** + * Adds an element to the stream being built. + * + * @implSpec + * The default implementation behaves as if: + *

{@code
+         *     accept(t)
+         *     return this;
+         * }
+ * + * @param t the element to add + * @return {@code this} builder + * @throws IllegalStateException if the builder has already transitioned + * to the built state + */ + default StreamBuilder.OfInt add(int t) { + accept(t); + return this; + } + + /** + * Builds the stream, transitioning this builder to the built state. + * An {@code IllegalStateException} is thrown if there are further + * attempts to operate on the builder after it has entered the built + * state. + * + * @return the built stream + * @throws IllegalStateException if the builder has already transitioned to + * the built state + */ + IntStream build(); + } + + /** + * A mutable builder for a {@code LongStream}. + * + *

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 + * 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. + * + * @see LongStream#builder() + * @since 1.8 + */ + interface OfLong extends LongConsumer { + + /** + * Adds an element to the stream being built. + * + * @throws IllegalStateException if the builder has already transitioned + * to the built state + */ + @Override + void accept(long t); + + /** + * Adds an element to the stream being built. + * + * @implSpec + * The default implementation behaves as if: + *

{@code
+         *     accept(t)
+         *     return this;
+         * }
+ * + * @param t the element to add + * @return {@code this} builder + * @throws IllegalStateException if the builder has already transitioned + * to the built state + */ + default StreamBuilder.OfLong add(long t) { + accept(t); + return this; + } + + /** + * Builds the stream, transitioning this builder to the built state. + * An {@code IllegalStateException} is thrown if there are further + * attempts to operate on the builder after it has entered the built + * state. + * + * @return the built stream + * @throws IllegalStateException if the builder has already transitioned + * to the built state + */ + LongStream build(); + } + + /** + * A mutable builder for a {@code DoubleStream}. + * + * @see LongStream#builder() + * @since 1.8 + */ + interface OfDouble extends DoubleConsumer { + + /** + * Adds an element to the stream being built. + * + *

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 + * 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. + * + * @throws IllegalStateException if the builder has already transitioned + * to the built state + */ + @Override + void accept(double t); + + /** + * Adds an element to the stream being built. + * + * @implSpec + * The default implementation behaves as if: + *

{@code
+         *     accept(t)
+         *     return this;
+         * }
+ * + * @param t the element to add + * @return {@code this} builder + * @throws IllegalStateException if the builder has already transitioned + * to the built state + */ + default StreamBuilder.OfDouble add(double t) { + accept(t); + return this; + } + + /** + * Builds the stream, transitioning this builder to the built state. + * An {@code IllegalStateException} is thrown if there are further + * attempts to operate on the builder after it has entered the built + * state. + * + * @return the built stream + * @throws IllegalStateException if the builder has already transitioned + * to the built state + */ + DoubleStream build(); + } +} diff --git a/jdk/src/share/classes/java/util/stream/Streams.java b/jdk/src/share/classes/java/util/stream/Streams.java new file mode 100644 index 00000000000..9693839de62 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/Streams.java @@ -0,0 +1,656 @@ +/* + * 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. 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.Spliterator; +import java.util.Spliterators; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; + +/** + * Utility methods for operating on and creating streams. + * + *

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. + * + * @since 1.8 + */ +class Streams { + + private Streams() { + throw new Error("no instances"); + } + + /** + * An object instance representing no value, that cannot be an actual + * data element of a stream. Used when processing streams that can contain + * {@code null} elements to distinguish between a {@code null} value and no + * value. + */ + static final Object NONE = new Object(); + + /** + * An {@code int} range spliterator. + */ + static final class RangeIntSpliterator implements Spliterator.OfInt { + private int from; + private final int upTo; + private final int step; + + RangeIntSpliterator(int from, int upTo, int step) { + this.from = from; + this.upTo = upTo; + this.step = step; + } + + @Override + public boolean tryAdvance(IntConsumer consumer) { + boolean hasNext = from < upTo; + if (hasNext) { + consumer.accept(from); + from += step; + } + return hasNext; + } + + @Override + public void forEachRemaining(IntConsumer consumer) { + int hUpTo = upTo; + int hStep = step; // hoist accesses and checks from loop + for (int i = from; i < hUpTo; i += hStep) + consumer.accept(i); + from = upTo; + } + + @Override + public long estimateSize() { + int d = upTo - from; + return (d / step) + ((d % step == 0) ? 0 : 1); + } + + @Override + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.IMMUTABLE | Spliterator.NONNULL | + Spliterator.DISTINCT | Spliterator.SORTED; + } + + @Override + public Comparator getComparator() { + return null; + } + + @Override + public Spliterator.OfInt trySplit() { + return estimateSize() <= 1 + ? null + : new RangeIntSpliterator(from, from = from + midPoint(), step); + } + + private int midPoint() { + // Size is known to be >= 2 + int bisection = (upTo - from) / 2; + // If bisection > step then round down to nearest multiple of step + // otherwise round up to step + return bisection > step ? bisection - bisection % step : step; + } + } + + /** + * A {@code long} range spliterator. + */ + static final class RangeLongSpliterator implements Spliterator.OfLong { + private long from; + private final long upTo; + private final long step; + + RangeLongSpliterator(long from, long upTo, long step) { + this.from = from; + this.upTo = upTo; + this.step = step; + } + + @Override + public boolean tryAdvance(LongConsumer consumer) { + boolean hasNext = from < upTo; + if (hasNext) { + consumer.accept(from); + from += step; + } + return hasNext; + } + + @Override + public void forEachRemaining(LongConsumer consumer) { + long hUpTo = upTo; + long hStep = step; // hoist accesses and checks from loop + for (long i = from; i < hUpTo; i += hStep) + consumer.accept(i); + from = upTo; + } + + @Override + public long estimateSize() { + long d = upTo - from; + return (d / step) + ((d % step == 0) ? 0 : 1); + } + + @Override + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.IMMUTABLE | Spliterator.NONNULL | + Spliterator.DISTINCT | Spliterator.SORTED; + } + + @Override + public Comparator getComparator() { + return null; + } + + @Override + public Spliterator.OfLong trySplit() { + return estimateSize() <= 1 + ? null + : new RangeLongSpliterator(from, from = from + midPoint(), step); + } + + private long midPoint() { + // Size is known to be >= 2 + long bisection = (upTo - from) / 2; + // If bisection > step then round down to nearest multiple of step + // otherwise round up to step + return bisection > step ? bisection - bisection % step : step; + } + } + + /** + * A {@code double} range spliterator. + * + *

The traversing and splitting logic is equivalent to that of + * {@code RangeLongSpliterator} for increasing values with a {@code step} of + * {@code 1}. + * + *

A {@code double} value is calculated from the function + * {@code start + i * step} where {@code i} is the absolute position of the + * value when traversing an instance of this class that has not been split. + * This ensures the same values are produced at the same absolute positions + * regardless of how an instance of this class is split or traversed. + */ + static final class RangeDoubleSpliterator implements Spliterator.OfDouble { + private final double from; + private final double upTo; + private final double step; + + private long lFrom; + private final long lUpTo; + + RangeDoubleSpliterator(double from, double upTo, double step, long lFrom, long lUpTo) { + this.from = from; + this.upTo = upTo; + this.step = step; + this.lFrom = lFrom; + this.lUpTo = lUpTo; + } + + @Override + public boolean tryAdvance(DoubleConsumer consumer) { + boolean hasNext = lFrom < lUpTo; + if (hasNext) { + consumer.accept(from + lFrom * step); + lFrom++; + } + return hasNext; + } + + @Override + public void forEachRemaining(DoubleConsumer consumer) { + double hOrigin = from; + double hStep = step; + long hLUpTo = lUpTo; + long i = lFrom; + for (; i < hLUpTo; i++) { + consumer.accept(hOrigin + i * hStep); + } + lFrom = i; + } + + @Override + public long estimateSize() { + return lUpTo - lFrom; + } + + @Override + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.IMMUTABLE | Spliterator.NONNULL | + Spliterator.DISTINCT | Spliterator.SORTED; + } + + @Override + public Comparator getComparator() { + return null; + } + + @Override + public Spliterator.OfDouble trySplit() { + return estimateSize() <= 1 + ? null + : new RangeDoubleSpliterator(from, upTo, step, lFrom, lFrom = lFrom + midPoint()); + } + + private long midPoint() { + // Size is known to be >= 2 + return (lUpTo - lFrom) / 2; + } + } + + private static abstract class AbstractStreamBuilderImpl> implements Spliterator { + // >= 0 when building, < 0 when built + // -1 == no elements + // -2 == one element, held by first + // -3 == two or more elements, held by buffer + int count; + + // Spliterator implementation for 0 or 1 element + // count == -1 for no elements + // count == -2 for one element held by first + + @Override + public S trySplit() { + return null; + } + + @Override + public long estimateSize() { + return -count - 1; + } + + @Override + public int characteristics() { + return Spliterator.SIZED | Spliterator.SUBSIZED | + Spliterator.ORDERED | Spliterator.IMMUTABLE; + } + } + + static final class StreamBuilderImpl + extends AbstractStreamBuilderImpl> + implements StreamBuilder { + // The first element in the stream + // valid if count == 1 + T first; + + // The first and subsequent elements in the stream + // non-null if count == 2 + SpinedBuffer buffer; + + /** + * Constructor for building a stream of 0 or more elements. + */ + StreamBuilderImpl() { } + + /** + * Constructor for a singleton stream. + * + * @param t the single element + */ + StreamBuilderImpl(T t) { + first = t; + count = -2; + } + + // StreamBuilder implementation + + @Override + public void accept(T t) { + if (count == 0) { + first = t; + count++; + } + else if (count > 0) { + if (buffer == null) { + buffer = new SpinedBuffer<>(); + buffer.accept(first); + count++; + } + + buffer.accept(t); + } + else { + throw new IllegalStateException(); + } + } + + public StreamBuilder add(T t) { + accept(t); + return this; + } + + @Override + public Stream build() { + int c = count; + if (c >= 0) { + // Switch count to negative value signalling the builder is built + count = -count - 1; + // Use this spliterator if 0 or 1 elements, otherwise use + // the spliterator of the spined buffer + return (c < 2) ? StreamSupport.stream(this) : StreamSupport.stream(buffer.spliterator()); + } + + throw new IllegalStateException(); + } + + // Spliterator implementation for 0 or 1 element + // count == -1 for no elements + // count == -2 for one element held by first + + @Override + public boolean tryAdvance(Consumer action) { + if (count == -2) { + action.accept(first); + count = -1; + return true; + } + else { + return false; + } + } + + @Override + public void forEachRemaining(Consumer action) { + if (count == -2) { + action.accept(first); + count = -1; + } + } + } + + static final class IntStreamBuilderImpl + extends AbstractStreamBuilderImpl + implements StreamBuilder.OfInt, Spliterator.OfInt { + // The first element in the stream + // valid if count == 1 + int first; + + // The first and subsequent elements in the stream + // non-null if count == 2 + SpinedBuffer.OfInt buffer; + + /** + * Constructor for building a stream of 0 or more elements. + */ + IntStreamBuilderImpl() { } + + /** + * Constructor for a singleton stream. + * + * @param t the single element + */ + IntStreamBuilderImpl(int t) { + first = t; + count = -2; + } + + // StreamBuilder implementation + + @Override + public void accept(int t) { + if (count == 0) { + first = t; + count++; + } + else if (count > 0) { + if (buffer == null) { + buffer = new SpinedBuffer.OfInt(); + buffer.accept(first); + count++; + } + + buffer.accept(t); + } + else { + throw new IllegalStateException(); + } + } + + @Override + public IntStream build() { + int c = count; + if (c >= 0) { + // Switch count to negative value signalling the builder is built + count = -count - 1; + // Use this spliterator if 0 or 1 elements, otherwise use + // the spliterator of the spined buffer + return (c < 2) ? StreamSupport.intStream(this) : StreamSupport.intStream(buffer.spliterator()); + } + + throw new IllegalStateException(); + } + + // Spliterator implementation for 0 or 1 element + // count == -1 for no elements + // count == -2 for one element held by first + + @Override + public boolean tryAdvance(IntConsumer action) { + if (count == -2) { + action.accept(first); + count = -1; + return true; + } + else { + return false; + } + } + + @Override + public void forEachRemaining(IntConsumer action) { + if (count == -2) { + action.accept(first); + count = -1; + } + } + } + + static final class LongStreamBuilderImpl + extends AbstractStreamBuilderImpl + implements StreamBuilder.OfLong, Spliterator.OfLong { + // The first element in the stream + // valid if count == 1 + long first; + + // The first and subsequent elements in the stream + // non-null if count == 2 + SpinedBuffer.OfLong buffer; + + /** + * Constructor for building a stream of 0 or more elements. + */ + LongStreamBuilderImpl() { } + + /** + * Constructor for a singleton stream. + * + * @param t the single element + */ + LongStreamBuilderImpl(long t) { + first = t; + count = -2; + } + + // StreamBuilder implementation + + @Override + public void accept(long t) { + if (count == 0) { + first = t; + count++; + } + else if (count > 0) { + if (buffer == null) { + buffer = new SpinedBuffer.OfLong(); + buffer.accept(first); + count++; + } + + buffer.accept(t); + } + else { + throw new IllegalStateException(); + } + } + + @Override + public LongStream build() { + int c = count; + if (c >= 0) { + // Switch count to negative value signalling the builder is built + count = -count - 1; + // Use this spliterator if 0 or 1 elements, otherwise use + // the spliterator of the spined buffer + return (c < 2) ? StreamSupport.longStream(this) : StreamSupport.longStream(buffer.spliterator()); + } + + throw new IllegalStateException(); + } + + // Spliterator implementation for 0 or 1 element + // count == -1 for no elements + // count == -2 for one element held by first + + @Override + public boolean tryAdvance(LongConsumer action) { + if (count == -2) { + action.accept(first); + count = -1; + return true; + } + else { + return false; + } + } + + @Override + public void forEachRemaining(LongConsumer action) { + if (count == -2) { + action.accept(first); + count = -1; + } + } + } + + static final class DoubleStreamBuilderImpl + extends AbstractStreamBuilderImpl + implements StreamBuilder.OfDouble, Spliterator.OfDouble { + // The first element in the stream + // valid if count == 1 + double first; + + // The first and subsequent elements in the stream + // non-null if count == 2 + SpinedBuffer.OfDouble buffer; + + /** + * Constructor for building a stream of 0 or more elements. + */ + DoubleStreamBuilderImpl() { } + + /** + * Constructor for a singleton stream. + * + * @param t the single element + */ + DoubleStreamBuilderImpl(double t) { + first = t; + count = -2; + } + + // StreamBuilder implementation + + @Override + public void accept(double t) { + if (count == 0) { + first = t; + count++; + } + else if (count > 0) { + if (buffer == null) { + buffer = new SpinedBuffer.OfDouble(); + buffer.accept(first); + count++; + } + + buffer.accept(t); + } + else { + throw new IllegalStateException(); + } + } + + @Override + public DoubleStream build() { + int c = count; + if (c >= 0) { + // Switch count to negative value signalling the builder is built + count = -count - 1; + // Use this spliterator if 0 or 1 elements, otherwise use + // the spliterator of the spined buffer + return (c < 2) ? StreamSupport.doubleStream(this) : StreamSupport.doubleStream(buffer.spliterator()); + } + + throw new IllegalStateException(); + } + + // Spliterator implementation for 0 or 1 element + // count == -1 for no elements + // count == -2 for one element held by first + + @Override + public boolean tryAdvance(DoubleConsumer action) { + if (count == -2) { + action.accept(first); + count = -1; + return true; + } + else { + return false; + } + } + + @Override + public void forEachRemaining(DoubleConsumer action) { + if (count == -2) { + action.accept(first); + count = -1; + } + } + } +} diff --git a/jdk/src/share/classes/sun/management/AgentConfigurationError.java b/jdk/src/share/classes/sun/management/AgentConfigurationError.java index 601117e1bcf..bda11b660a5 100644 --- a/jdk/src/share/classes/sun/management/AgentConfigurationError.java +++ b/jdk/src/share/classes/sun/management/AgentConfigurationError.java @@ -128,19 +128,13 @@ public class AgentConfigurationError extends Error { public AgentConfigurationError(String error, String... params) { super(); this.error = error; - this.params = new String[params.length]; - for (int i = 0; i < params.length; i++) { - this.params[i] = params[i]; - } + this.params = params.clone(); } public AgentConfigurationError(String error, Throwable cause, String... params) { super(cause); this.error = error; - this.params = new String[params.length]; - for (int i = 0; i < params.length; i++) { - this.params[i] = params[i]; - } + this.params = params.clone(); } public String getError() { @@ -148,7 +142,7 @@ public class AgentConfigurationError extends Error { } public String[] getParams() { - return params; + return params.clone(); } private static final long serialVersionUID = 1211605593516195475L; diff --git a/jdk/test/java/net/Inet6Address/serialize/Inet6AddressSerializationTest.java b/jdk/test/java/net/Inet6Address/serialize/Inet6AddressSerializationTest.java new file mode 100644 index 00000000000..b6431acc84b --- /dev/null +++ b/jdk/test/java/net/Inet6Address/serialize/Inet6AddressSerializationTest.java @@ -0,0 +1,1122 @@ +/* + * 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.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.PrintStream; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; + +/** + * @test + * @bug 8007373 + * @summary jdk7 backward compatibility serialization problem + */ + +public class Inet6AddressSerializationTest { + + static boolean failed; + + public static final int LOOPBACK_SCOPE_ID = 0; + + public static final byte[] IN6ADDR_ANY_INIT = { (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; + + public static final byte[] LOOPBACKIPV6ADDRESS = { (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 }; + + // fe80::21b:24ff:febd:f29c + public static final byte[] E1000G0IPV6ADDRESS = { (byte) 0xfe, (byte) 0x80, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x02, (byte) 0x1b, (byte) 0x24, (byte) 0xff, + (byte) 0xfe, (byte) 0xbd, (byte) 0xf2, (byte) 0x9c }; + + public static final String E1000G0HOSTNAME = "fe80:0:0:0:21b:24ff:febd:f29c%e1000g0"; + + public static final String LOCALHOSTNAME = "localhost"; + + public static final String NETWORK_IF_E1000G0 = "e1000g0"; + + public static final String NETWORK_IF_LO0 = "lo0"; + + public static final int SCOPE_ID_E1000G0 = 2; + + public static final int SCOPE_ID_LO0 = 1; + + public static final int SCOPE_ID_ZERO = 0; + + public static void main(String[] args) throws Exception { + // args[0] == generate-loopback generates serial data for loopback if + // args[0] == generateAll generates serial data for interfaces with an + // IPV6 address binding + + if (args.length != 0) { + + if (args[0].equals("generate-loopback")) { + + generateSerializedInet6AddressData(Inet6Address.getByAddress( + InetAddress.getLoopbackAddress().getHostName(), + LOOPBACKIPV6ADDRESS, LOOPBACK_SCOPE_ID), System.out, + true); + + } else { + generateAllInet6AddressSerializedData(); + } + } else { + runTests(); + } + } + + private static void runTests() throws UnknownHostException, Exception, + IOException { + byte[] thisHostIPV6Address = null; + int scope_id = LOOPBACK_SCOPE_ID; + + System.out.println("Hostname: " + + InetAddress.getLocalHost().getHostName()); + System.out.println("LocalHost isLoopback : " + + InetAddress.getLocalHost().isLoopbackAddress()); + thisHostIPV6Address = getThisHostIPV6Address(InetAddress.getLocalHost() + .getHostName()); + + if (thisHostIPV6Address == null) { + thisHostIPV6Address = IN6ADDR_ANY_INIT; + } + + // testing JDK7 generated serialized loopback against locally generated + // loopback address + testInet6AddressSerialization(Inet6Address.getByAddress(InetAddress + .getLoopbackAddress().getHostName(), LOOPBACKIPV6ADDRESS, + scope_id), JDK7Inet6AddressSerialData); + // testing JDK8 generated serialized loopback against locally generated + // loopback address + testInet6AddressSerialization(Inet6Address.getByAddress(InetAddress + .getLoopbackAddress().getHostName(), LOOPBACKIPV6ADDRESS, + scope_id), JDK8Inet6AddressSerialData); + testInet6AddressSerialization(Inet6Address.getByAddress(InetAddress + .getLocalHost().getHostName(), IN6ADDR_ANY_INIT, scope_id), + null); + testInet6AddressSerialization(Inet6Address.getByAddress(InetAddress + .getLocalHost().getHostName(), thisHostIPV6Address, scope_id), + null); + testAllNetworkInterfaces(); + + // test against lo0 + testSerializedLo0Inet6Address(); + + testSerializedE1000gInet6Address(); + + if (failed) + throw new RuntimeException("Some tests failed, check output"); + } + + private static byte[] getThisHostIPV6Address(String hostName) + throws Exception { + InetAddress[] thisHostIPAddresses = null; + try { + thisHostIPAddresses = InetAddress.getAllByName(InetAddress + .getLocalHost().getHostName()); + } catch (UnknownHostException uhEx) { + uhEx.printStackTrace(); + throw uhEx; + } + byte[] thisHostIPV6Address = null; + for (InetAddress inetAddress : thisHostIPAddresses) { + if (inetAddress instanceof Inet6Address) { + if (inetAddress.getHostName().equals(hostName)) { + thisHostIPV6Address = inetAddress.getAddress(); + break; + } + } + } + // System.err.println("getThisHostIPV6Address: address is " + // + Arrays.toString(thisHostIPV6Address)); + return thisHostIPV6Address; + } + + static void testAllNetworkInterfaces() throws Exception { + System.err.println("\n testAllNetworkInterfaces: \n "); + for (Enumeration e = NetworkInterface + .getNetworkInterfaces(); e.hasMoreElements();) { + NetworkInterface netIF = e.nextElement(); + for (Enumeration iadrs = netIF.getInetAddresses(); iadrs + .hasMoreElements();) { + InetAddress iadr = iadrs.nextElement(); + if (iadr instanceof Inet6Address) { + System.err.println("Test NetworkInterface: " + netIF); + Inet6Address i6adr = (Inet6Address) iadr; + System.err.println("Testing with " + iadr); + System.err.println(" scoped iface: " + + i6adr.getScopedInterface()); + testInet6AddressSerialization(i6adr, null); + } + } + } + } + + static void displayExpectedInet6Address(Inet6Address expectedInet6Address) { + + String expectedHostName = expectedInet6Address.getHostName(); + byte[] expectedAddress = expectedInet6Address.getAddress(); + String expectedHostAddress = expectedInet6Address.getHostAddress(); + int expectedScopeId = expectedInet6Address.getScopeId(); + NetworkInterface expectedNetIf = expectedInet6Address + .getScopedInterface(); + + System.err.println("Excpected HostName: " + expectedHostName); + System.err.println("Expected Address: " + + Arrays.toString(expectedAddress)); + System.err.println("Expected HostAddress: " + expectedHostAddress); + System.err.println("Expected Scope Id " + expectedScopeId); + System.err.println("Expected NetworkInterface " + expectedNetIf); + System.err.println("Expected Inet6Address " + expectedInet6Address); + } + + // test serialization deserialization of Inet6Address + static void testInet6AddressSerialization( + Inet6Address expectedInet6Address, byte[] serializedAddress) + throws IOException { + System.err.println("\n testInet6AddressSerialization: enter \n"); + + // displayExpectedInet6Address(expectedInet6Address); + + byte[] serialData = serializedAddress != null ? serializedAddress + : generateSerializedInet6AddressData(expectedInet6Address, + null, false); + try (ByteArrayInputStream bis = new ByteArrayInputStream(serialData); + ObjectInputStream oin = new ObjectInputStream(bis)) { + Inet6Address deserializedIPV6Addr = (Inet6Address) oin.readObject(); + System.err.println("Deserialized Inet6Address " + + deserializedIPV6Addr); + assertHostNameEqual(expectedInet6Address.getHostName(), + deserializedIPV6Addr.getHostName()); + assertHostAddressEqual(expectedInet6Address.getHostAddress(), + deserializedIPV6Addr.getHostAddress()); + assertAddressEqual(expectedInet6Address.getAddress(), + deserializedIPV6Addr.getAddress()); + assertScopeIdEqual(expectedInet6Address.getScopeId(), + deserializedIPV6Addr.getScopeId()); + assertNetworkInterfaceEqual( + expectedInet6Address.getScopedInterface(), + deserializedIPV6Addr.getScopedInterface()); + } catch (Exception e) { + System.err.println("Exception caught during deserialization"); + failed = true; + e.printStackTrace(); + } + } + + static void testSerializedE1000gInet6Address() throws IOException { + System.err.println("\n testSerializedE1000gInet6Address: enter \n"); + boolean testWithNetIf = true; + boolean useMockInet6Address = false; + + NetworkInterface testNetIf = NetworkInterface + .getByName(NETWORK_IF_E1000G0); + Inet6Address expectedInet6Address = null; + if (testNetIf != null) { + System.err + .println("\n testSerializedE1000gInet6Address: using netif \n"); + try { + expectedInet6Address = Inet6Address.getByAddress( + E1000G0HOSTNAME, E1000G0IPV6ADDRESS, testNetIf); + } catch (UnknownHostException ukhEx) { + ukhEx.printStackTrace(); + testWithNetIf = true; + useMockInet6Address = true; + } + } else { + System.err + .println("\n testSerializedE1000gInet6Address: using index \n"); + try { + expectedInet6Address = Inet6Address.getByAddress( + E1000G0HOSTNAME, E1000G0IPV6ADDRESS, SCOPE_ID_ZERO); + } catch (UnknownHostException ukhEx1) { + ukhEx1.printStackTrace(); + useMockInet6Address = true; + } + testWithNetIf = false; + } + + byte[] serializedAddress = SerialData_ifname_e1000g0; + + // displayExpectedInet6Address(expectedInet6Address); + + try (ByteArrayInputStream bis = new ByteArrayInputStream( + serializedAddress); + ObjectInputStream oin = new ObjectInputStream(bis)) { + Inet6Address deserializedIPV6Addr = (Inet6Address) oin.readObject(); + System.err.println("Deserialized Inet6Address " + + deserializedIPV6Addr); + + if (!useMockInet6Address) { + assertHostNameEqual(expectedInet6Address.getHostName(), + deserializedIPV6Addr.getHostName()); + if (testWithNetIf) { + assertHostAddressEqual( + expectedInet6Address.getHostAddress(), + deserializedIPV6Addr.getHostAddress()); + } else { + assertHostAddressEqual( + MockE1000g0Inet6Address.getBareHostAddress(), + deserializedIPV6Addr.getHostAddress()); + } + assertAddressEqual(expectedInet6Address.getAddress(), + deserializedIPV6Addr.getAddress()); + assertScopeIdEqual(expectedInet6Address.getScopeId(), + deserializedIPV6Addr.getScopeId()); + if (testWithNetIf) { + assertNetworkInterfaceEqual( + expectedInet6Address.getScopedInterface(), + deserializedIPV6Addr.getScopedInterface()); + } else { + assertNetworkInterfaceEqual(null, + deserializedIPV6Addr.getScopedInterface()); + } + } else { // use MockLo0Inet6Address + assertHostNameEqual(MockE1000g0Inet6Address.getHostName(), + deserializedIPV6Addr.getHostName()); + if (testWithNetIf) { + assertHostAddressEqual( + MockE1000g0Inet6Address.getHostAddress(), + deserializedIPV6Addr.getHostAddress()); + } else { + assertHostAddressEqual( + MockE1000g0Inet6Address.getHostAddressWithIndex(), + deserializedIPV6Addr.getHostAddress()); + } + assertAddressEqual(MockE1000g0Inet6Address.getAddress(), + deserializedIPV6Addr.getAddress()); + if (testWithNetIf) { + assertScopeIdEqual(MockE1000g0Inet6Address.getScopeId(), + deserializedIPV6Addr.getScopeId()); + } else { + assertScopeIdEqual(MockE1000g0Inet6Address.getScopeZero(), + deserializedIPV6Addr.getScopeId()); + } + assertNetworkInterfaceNameEqual( + MockE1000g0Inet6Address.getScopeIfName(), + deserializedIPV6Addr.getScopedInterface()); + } + } catch (Exception e) { + System.err.println("Exception caught during deserialization"); + failed = true; + e.printStackTrace(); + } + } + + static void testSerializedLo0Inet6Address() throws IOException { + System.err.println("\n testSerializedLo0Inet6Address: enter \n"); + boolean testWithNetIf = true; + boolean useMockInet6Address = false; + + NetworkInterface testNetIf = NetworkInterface.getByName(NETWORK_IF_LO0); + Inet6Address expectedInet6Address = null; + if (testNetIf != null) { + System.err + .println("\n testSerializedLo0Inet6Address: using netif \n"); + try { + expectedInet6Address = Inet6Address.getByAddress(LOCALHOSTNAME, + LOOPBACKIPV6ADDRESS, testNetIf); + } catch (UnknownHostException ukhEx) { + ukhEx.printStackTrace(); + testWithNetIf = true; + useMockInet6Address = true; + } + } else { + System.err + .println("\n testSerializedLo0Inet6Address: using index \n"); + try { + expectedInet6Address = Inet6Address.getByAddress(LOCALHOSTNAME, + LOOPBACKIPV6ADDRESS, SCOPE_ID_ZERO); + } catch (UnknownHostException ukhEx1) { + ukhEx1.printStackTrace(); + useMockInet6Address = true; + } + testWithNetIf = false; + } + + // displayExpectedInet6Address(expectedInet6Address); + + byte[] serializedAddress = SerialData_ifname_lo0; + + try (ByteArrayInputStream bis = new ByteArrayInputStream( + serializedAddress); + ObjectInputStream oin = new ObjectInputStream(bis)) { + Inet6Address deserializedIPV6Addr = (Inet6Address) oin.readObject(); + System.err.println("Deserialized Inet6Address " + + deserializedIPV6Addr); + if (!useMockInet6Address) { + assertHostNameEqual(expectedInet6Address.getHostName(), + deserializedIPV6Addr.getHostName()); + if (testWithNetIf) { + assertHostAddressEqual( + expectedInet6Address.getHostAddress(), + deserializedIPV6Addr.getHostAddress()); + } else { + assertHostAddressEqual( + MockLo0Inet6Address.getBareHostAddress(), + deserializedIPV6Addr.getHostAddress()); + } + assertAddressEqual(expectedInet6Address.getAddress(), + deserializedIPV6Addr.getAddress()); + assertScopeIdEqual(expectedInet6Address.getScopeId(), + deserializedIPV6Addr.getScopeId()); + if (testWithNetIf) { + assertNetworkInterfaceEqual( + expectedInet6Address.getScopedInterface(), + deserializedIPV6Addr.getScopedInterface()); + } else { + assertNetworkInterfaceEqual(null, + deserializedIPV6Addr.getScopedInterface()); + } + } else { // use MockLo0Inet6Address + assertHostNameEqual(MockLo0Inet6Address.getHostName(), + deserializedIPV6Addr.getHostName()); + if (testWithNetIf) { + assertHostAddressEqual( + MockLo0Inet6Address.getHostAddress(), + deserializedIPV6Addr.getHostAddress()); + } else { + assertHostAddressEqual( + MockLo0Inet6Address.getHostAddressWithIndex(), + deserializedIPV6Addr.getHostAddress()); + } + assertAddressEqual(MockLo0Inet6Address.getAddress(), + deserializedIPV6Addr.getAddress()); + if (testWithNetIf) { + assertScopeIdEqual(MockLo0Inet6Address.getScopeId(), + deserializedIPV6Addr.getScopeId()); + } else { + assertScopeIdEqual(MockLo0Inet6Address.getScopeZero(), + deserializedIPV6Addr.getScopeId()); + } + assertNetworkInterfaceNameEqual( + MockLo0Inet6Address.getScopeIfName(), + deserializedIPV6Addr.getScopedInterface()); + } + } catch (Exception e) { + System.err.println("Exception caught during deserialization"); + failed = true; + e.printStackTrace(); + } + } + + static List getAllInet6Addresses() throws Exception { + // System.err.println("\n getAllInet6Addresses: \n "); + ArrayList inet6Addresses = new ArrayList(); + for (Enumeration e = NetworkInterface + .getNetworkInterfaces(); e.hasMoreElements();) { + NetworkInterface netIF = e.nextElement(); + for (Enumeration iadrs = netIF.getInetAddresses(); iadrs + .hasMoreElements();) { + InetAddress iadr = iadrs.nextElement(); + if (iadr instanceof Inet6Address) { + System.err.println("Test NetworkInterface: " + netIF); + Inet6Address i6adr = (Inet6Address) iadr; + System.err.println(" address " + iadr); + System.err.println(" scoped iface: " + + i6adr.getScopedInterface()); + // using this to actually set the hostName for an + // InetAddress + // created through the NetworkInterface + // have found that the fabricated instances has a null + // hostName + System.err.println(" hostName: " + i6adr.getHostName()); + inet6Addresses.add(i6adr); + } + } + } + return inet6Addresses; + } + + static void assertHostNameEqual(String expectedHostName, + String deserializedHostName) { + System.err + .println("Inet6AddressSerializationTest.assertHostNameEqual:"); + if (expectedHostName == null) { + if (deserializedHostName == null) { + // ok, do nothing. + } else { + System.err.println("Error checking " + " HostName, expected:" + + expectedHostName + ", got :" + deserializedHostName); + failed = true; + } + } else if (!expectedHostName.equals(deserializedHostName)) { + System.err.println("Error checking " + + // versionStr + + " HostName, expected:" + expectedHostName + ", got :" + + deserializedHostName); + failed = true; + } else { + System.err.println("HostName equality " + + // versionStr + + " HostName, expected:" + expectedHostName + ", got :" + + deserializedHostName); + } + } + + static void assertHostAddressEqual(String expectedHostAddress, + String deserializedHostAddress) { + System.err + .println("Inet6AddressSerializationTest.assertHostAddressEqual:"); + if (expectedHostAddress == null) { + if (deserializedHostAddress == null) { + // ok, do nothing. + } else { + System.err.println("Error checking " + + " HostAddress, expected: " + expectedHostAddress + + ", got: " + deserializedHostAddress); + failed = true; + } + } else if (!expectedHostAddress.equals(deserializedHostAddress)) { + System.err.println("Error checking " + + // versionStr + + " HostAddress, expected: " + expectedHostAddress + + ", got: " + deserializedHostAddress); + failed = true; + } else { + System.err.println("HostAddress equality " + + // versionStr + + " HostAddress, expected: " + expectedHostAddress + + ", got: " + deserializedHostAddress); + } + } + + static void assertAddressEqual(byte[] expectedAddress, + byte[] deserializedAddress) { + System.err.println("Inet6AddressSerializationTest.assertAddressEqual:"); + if (expectedAddress == null) { + if (deserializedAddress == null) { + // ok, do nothing. + } else { + System.err.println("Error checking " + " Address, expected:" + + Arrays.toString(expectedAddress) + ", got: " + + Arrays.toString(deserializedAddress)); + failed = true; + } + } else if (!Arrays.equals(expectedAddress, deserializedAddress)) { + System.err.println("Error checking " + + // versionStr + + " Address, expected: " + Arrays.toString(expectedAddress) + + ", got: " + Arrays.toString(deserializedAddress)); + failed = true; + } else { + System.err.println("Address equality " + + // versionStr + + " Address, expected: " + Arrays.toString(expectedAddress) + + ", got: " + Arrays.toString(deserializedAddress)); + } + } + + static void assertScopeIdEqual(int expectedScopeId, int deserializedScopeId) { + System.err.println("Inet6AddressSerializationTest.assertScopeIdEqual:"); + if (expectedScopeId != deserializedScopeId) { + System.err.println("Error checking " + " ScopeId, expected:" + + expectedScopeId + ", got: " + deserializedScopeId); + failed = true; + } else { + System.err.println("ScopeId equality " + + // versionStr + + " ScopeId, expected: " + expectedScopeId + ", got: " + + deserializedScopeId); + } + } + + static void assertNetworkInterfaceNameEqual(String expectedNetworkIfName, + NetworkInterface deserializedNetworkInterface) { + + if (deserializedNetworkInterface != null) { + String deserializedNetworkIfName = deserializedNetworkInterface + .getName(); + System.err + .println("Inet6AddressSerializationTest.assertHostNameEqual:"); + if (expectedNetworkIfName == null) { + if (deserializedNetworkIfName == null) { + // ok, do nothing. + } else { + System.err.println("Error checking " + + " NetworkIfName, expected: " + + expectedNetworkIfName + ", got: " + + deserializedNetworkIfName); + failed = true; + } + } else if (!expectedNetworkIfName.equals(deserializedNetworkIfName)) { + System.err.println("Error checking " + + " NetworkIfName, expected: " + expectedNetworkIfName + + ", got: " + deserializedNetworkIfName); + failed = true; + } else { + System.err.println("NetworkIfName equality " + + " NetworkIfName, expected: " + expectedNetworkIfName + + ", got: " + deserializedNetworkIfName); + } + } else { + System.err + .println("Warning " + + " NetworkInterface expected, but is null - ifname not relevant on deserializing host"); + } + } + + static void assertNetworkInterfaceEqual( + NetworkInterface expectedNetworkInterface, + NetworkInterface deserializedNetworkInterface) { + System.err + .println("Inet6AddressSerializationTest.assertNetworkInterfaceEqual:"); + if (expectedNetworkInterface == null) { + if (deserializedNetworkInterface == null) { + // ok, do nothing. + System.err.println("Network Interface equality " + + " NetworkInterface, expected:" + + expectedNetworkInterface + ", got :" + + deserializedNetworkInterface); + } else { + System.err.println("Error checking " + + " NetworkInterface, expected:" + + expectedNetworkInterface + ", got :" + + deserializedNetworkInterface); + failed = true; + } + } else if (!expectedNetworkInterface + .equals(deserializedNetworkInterface)) { + System.err.println("Error checking " + + // versionStr + + " NetworkInterface, expected:" + expectedNetworkInterface + + ", got :" + deserializedNetworkInterface); + failed = true; + } else { + System.err.println("Network Interface equality " + + " NetworkInterface, expected:" + expectedNetworkInterface + + ", got :" + deserializedNetworkInterface); + } + } + + static void equal(Object expected, Object got) { + if (expected == null) { + if (got == null) { + // ok, do nothing. + } else { + System.err.println("Error checking " + + " serial data, expected:" + expected + ", got :" + + got); + failed = true; + } + } else if (!expected.equals(got)) { + System.err.println("Error checking " + // versionStr + + " serial data, expected:" + expected + ", got :" + got); + failed = true; + } + } + + // Used to generate serialData. + static byte[] generateSerializedInet6AddressData(Inet6Address addr, + PrintStream out, boolean outputToFile) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(addr); + } + + String ifname = getIfName(addr); + byte[] ba = bos.toByteArray(); + if (out != null) { + out.format("static final byte[] SerialData" + ifname + " = {\n"); + for (int i = 0; i < ba.length; i++) { + out.format(" (byte)0x%02X", ba[i]); + if (i != (ba.length - 1)) + out.format(","); + if (((i + 1) % 6) == 0) + out.format("\n"); + } + out.format(" };\n \n"); + } + if (outputToFile) { + serializeInet6AddressToFile(addr); + } + return ba; + } + + private static String getIfName(Inet6Address inet6Addr) { + String ifname; + if (inet6Addr.getScopedInterface() != null) { + ifname = "_ifname_" + inet6Addr.getScopedInterface().getName(); + } else { + ifname = "_ifname_" + + Integer.valueOf(inet6Addr.getScopeId()).toString(); + } + return ifname; + } + + static void generateAllInet6AddressSerializedData() throws IOException { + // System.err.println("generateAllInet6AddressSerializedData: enter ...."); + + List inet6Addresses; + + try { + inet6Addresses = getAllInet6Addresses(); + } catch (Exception e) { + e.printStackTrace(); + throw new IOException(e); + } + + for (Inet6Address inet6Address : inet6Addresses) { + generateSerializedInet6AddressData(inet6Address, System.out, true); + } + } + + static void serializeInet6AddressToFile(Inet6Address inet6Addr) { + + // System.err + // .println("serializeInet6AddressToIPV6AddressFile: enter ...."); + + FileOutputStream fOut = null; + String inet6AddressOutputFilename = null; + inet6AddressOutputFilename = createOutputFileName(inet6Addr); + try { + fOut = new FileOutputStream(inet6AddressOutputFilename); + } catch (FileNotFoundException fnfEx) { + + fnfEx.printStackTrace(); + } + ObjectOutputStream ooStream = null; + try { + if (fOut != null) { + ooStream = new ObjectOutputStream(fOut); + } else { + System.err.println("Problem initilising Object output stream "); + System.exit(-1); + } + + } catch (IOException e) { + e.printStackTrace(); + System.exit(-1); + } + + // serialise the last Inet6Address + /* + * System.err + * .println("serializeInet6AddressToIPV6AddressFile scoped iface: \n" + + * inet6Addr.getScopedInterface()); + */ + try { + ooStream.writeObject(inet6Addr); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(-1); + } + + try { + ooStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static String createOutputFileName(Inet6Address inet6Addr) { + String inet6AddressOutputFilename; + if (inet6Addr.getScopedInterface() != null) { + inet6AddressOutputFilename = "IPV6Address_" + + inet6Addr.getScopedInterface().getName() + ".out"; + } else { + inet6AddressOutputFilename = "IPV6Address_" + + Integer.valueOf(inet6Addr.getScopeId()).toString() + + ".out"; + } + return inet6AddressOutputFilename; + } + + // --- Generated data --- + // JDK7 output java Inet6AddressSerializationTest generate. + + // loopback lo0 interface on Solaris 10 + + static final byte[] JDK7Inet6AddressSerialData = { (byte) 0xAC, + (byte) 0xED, (byte) 0x00, (byte) 0x05, (byte) 0x73, (byte) 0x72, + (byte) 0x00, (byte) 0x15, (byte) 0x6A, (byte) 0x61, (byte) 0x76, + (byte) 0x61, (byte) 0x2E, (byte) 0x6E, (byte) 0x65, (byte) 0x74, + (byte) 0x2E, (byte) 0x49, (byte) 0x6E, (byte) 0x65, (byte) 0x74, + (byte) 0x36, (byte) 0x41, (byte) 0x64, (byte) 0x64, (byte) 0x72, + (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x5F, (byte) 0x7C, + (byte) 0x20, (byte) 0x81, (byte) 0x52, (byte) 0x2C, (byte) 0x80, + (byte) 0x21, (byte) 0x03, (byte) 0x00, (byte) 0x05, (byte) 0x49, + (byte) 0x00, (byte) 0x08, (byte) 0x73, (byte) 0x63, (byte) 0x6F, + (byte) 0x70, (byte) 0x65, (byte) 0x5F, (byte) 0x69, (byte) 0x64, + (byte) 0x5A, (byte) 0x00, (byte) 0x0C, (byte) 0x73, (byte) 0x63, + (byte) 0x6F, (byte) 0x70, (byte) 0x65, (byte) 0x5F, (byte) 0x69, + (byte) 0x64, (byte) 0x5F, (byte) 0x73, (byte) 0x65, (byte) 0x74, + (byte) 0x5A, (byte) 0x00, (byte) 0x10, (byte) 0x73, (byte) 0x63, + (byte) 0x6F, (byte) 0x70, (byte) 0x65, (byte) 0x5F, (byte) 0x69, + (byte) 0x66, (byte) 0x6E, (byte) 0x61, (byte) 0x6D, (byte) 0x65, + (byte) 0x5F, (byte) 0x73, (byte) 0x65, (byte) 0x74, (byte) 0x4C, + (byte) 0x00, (byte) 0x06, (byte) 0x69, (byte) 0x66, (byte) 0x6E, + (byte) 0x61, (byte) 0x6D, (byte) 0x65, (byte) 0x74, (byte) 0x00, + (byte) 0x12, (byte) 0x4C, (byte) 0x6A, (byte) 0x61, (byte) 0x76, + (byte) 0x61, (byte) 0x2F, (byte) 0x6C, (byte) 0x61, (byte) 0x6E, + (byte) 0x67, (byte) 0x2F, (byte) 0x53, (byte) 0x74, (byte) 0x72, + (byte) 0x69, (byte) 0x6E, (byte) 0x67, (byte) 0x3B, (byte) 0x5B, + (byte) 0x00, (byte) 0x09, (byte) 0x69, (byte) 0x70, (byte) 0x61, + (byte) 0x64, (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, + (byte) 0x73, (byte) 0x74, (byte) 0x00, (byte) 0x02, (byte) 0x5B, + (byte) 0x42, (byte) 0x78, (byte) 0x72, (byte) 0x00, (byte) 0x14, + (byte) 0x6A, (byte) 0x61, (byte) 0x76, (byte) 0x61, (byte) 0x2E, + (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x2E, (byte) 0x49, + (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x41, (byte) 0x64, + (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, + (byte) 0x2D, (byte) 0x9B, (byte) 0x57, (byte) 0xAF, (byte) 0x9F, + (byte) 0xE3, (byte) 0xEB, (byte) 0xDB, (byte) 0x02, (byte) 0x00, + (byte) 0x03, (byte) 0x49, (byte) 0x00, (byte) 0x07, (byte) 0x61, + (byte) 0x64, (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, + (byte) 0x73, (byte) 0x49, (byte) 0x00, (byte) 0x06, (byte) 0x66, + (byte) 0x61, (byte) 0x6D, (byte) 0x69, (byte) 0x6C, (byte) 0x79, + (byte) 0x4C, (byte) 0x00, (byte) 0x08, (byte) 0x68, (byte) 0x6F, + (byte) 0x73, (byte) 0x74, (byte) 0x4E, (byte) 0x61, (byte) 0x6D, + (byte) 0x65, (byte) 0x71, (byte) 0x00, (byte) 0x7E, (byte) 0x00, + (byte) 0x01, (byte) 0x78, (byte) 0x70, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x74, (byte) 0x00, (byte) 0x09, (byte) 0x6C, + (byte) 0x6F, (byte) 0x63, (byte) 0x61, (byte) 0x6C, (byte) 0x68, + (byte) 0x6F, (byte) 0x73, (byte) 0x74, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x70, + (byte) 0x75, (byte) 0x72, (byte) 0x00, (byte) 0x02, (byte) 0x5B, + (byte) 0x42, (byte) 0xAC, (byte) 0xF3, (byte) 0x17, (byte) 0xF8, + (byte) 0x06, (byte) 0x08, (byte) 0x54, (byte) 0xE0, (byte) 0x02, + (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0x70, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x78 }; + + // JDK8 output java Inet6AddressSerializationTest generate. + // loopback lo0 interface on Solaris 10 + + static final byte[] JDK8Inet6AddressSerialData = { (byte) 0xAC, + (byte) 0xED, (byte) 0x00, (byte) 0x05, (byte) 0x73, (byte) 0x72, + (byte) 0x00, (byte) 0x15, (byte) 0x6A, (byte) 0x61, (byte) 0x76, + (byte) 0x61, (byte) 0x2E, (byte) 0x6E, (byte) 0x65, (byte) 0x74, + (byte) 0x2E, (byte) 0x49, (byte) 0x6E, (byte) 0x65, (byte) 0x74, + (byte) 0x36, (byte) 0x41, (byte) 0x64, (byte) 0x64, (byte) 0x72, + (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x5F, (byte) 0x7C, + (byte) 0x20, (byte) 0x81, (byte) 0x52, (byte) 0x2C, (byte) 0x80, + (byte) 0x21, (byte) 0x03, (byte) 0x00, (byte) 0x05, (byte) 0x49, + (byte) 0x00, (byte) 0x08, (byte) 0x73, (byte) 0x63, (byte) 0x6F, + (byte) 0x70, (byte) 0x65, (byte) 0x5F, (byte) 0x69, (byte) 0x64, + (byte) 0x5A, (byte) 0x00, (byte) 0x0C, (byte) 0x73, (byte) 0x63, + (byte) 0x6F, (byte) 0x70, (byte) 0x65, (byte) 0x5F, (byte) 0x69, + (byte) 0x64, (byte) 0x5F, (byte) 0x73, (byte) 0x65, (byte) 0x74, + (byte) 0x5A, (byte) 0x00, (byte) 0x10, (byte) 0x73, (byte) 0x63, + (byte) 0x6F, (byte) 0x70, (byte) 0x65, (byte) 0x5F, (byte) 0x69, + (byte) 0x66, (byte) 0x6E, (byte) 0x61, (byte) 0x6D, (byte) 0x65, + (byte) 0x5F, (byte) 0x73, (byte) 0x65, (byte) 0x74, (byte) 0x4C, + (byte) 0x00, (byte) 0x06, (byte) 0x69, (byte) 0x66, (byte) 0x6E, + (byte) 0x61, (byte) 0x6D, (byte) 0x65, (byte) 0x74, (byte) 0x00, + (byte) 0x12, (byte) 0x4C, (byte) 0x6A, (byte) 0x61, (byte) 0x76, + (byte) 0x61, (byte) 0x2F, (byte) 0x6C, (byte) 0x61, (byte) 0x6E, + (byte) 0x67, (byte) 0x2F, (byte) 0x53, (byte) 0x74, (byte) 0x72, + (byte) 0x69, (byte) 0x6E, (byte) 0x67, (byte) 0x3B, (byte) 0x5B, + (byte) 0x00, (byte) 0x09, (byte) 0x69, (byte) 0x70, (byte) 0x61, + (byte) 0x64, (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, + (byte) 0x73, (byte) 0x74, (byte) 0x00, (byte) 0x02, (byte) 0x5B, + (byte) 0x42, (byte) 0x78, (byte) 0x72, (byte) 0x00, (byte) 0x14, + (byte) 0x6A, (byte) 0x61, (byte) 0x76, (byte) 0x61, (byte) 0x2E, + (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x2E, (byte) 0x49, + (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x41, (byte) 0x64, + (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, + (byte) 0x2D, (byte) 0x9B, (byte) 0x57, (byte) 0xAF, (byte) 0x9F, + (byte) 0xE3, (byte) 0xEB, (byte) 0xDB, (byte) 0x02, (byte) 0x00, + (byte) 0x03, (byte) 0x49, (byte) 0x00, (byte) 0x07, (byte) 0x61, + (byte) 0x64, (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, + (byte) 0x73, (byte) 0x49, (byte) 0x00, (byte) 0x06, (byte) 0x66, + (byte) 0x61, (byte) 0x6D, (byte) 0x69, (byte) 0x6C, (byte) 0x79, + (byte) 0x4C, (byte) 0x00, (byte) 0x08, (byte) 0x68, (byte) 0x6F, + (byte) 0x73, (byte) 0x74, (byte) 0x4E, (byte) 0x61, (byte) 0x6D, + (byte) 0x65, (byte) 0x71, (byte) 0x00, (byte) 0x7E, (byte) 0x00, + (byte) 0x01, (byte) 0x78, (byte) 0x70, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x74, (byte) 0x00, (byte) 0x09, (byte) 0x6C, + (byte) 0x6F, (byte) 0x63, (byte) 0x61, (byte) 0x6C, (byte) 0x68, + (byte) 0x6F, (byte) 0x73, (byte) 0x74, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x70, + (byte) 0x75, (byte) 0x72, (byte) 0x00, (byte) 0x02, (byte) 0x5B, + (byte) 0x42, (byte) 0xAC, (byte) 0xF3, (byte) 0x17, (byte) 0xF8, + (byte) 0x06, (byte) 0x08, (byte) 0x54, (byte) 0xE0, (byte) 0x02, + (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0x70, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x78 }; + + // java Inet6AddressSerializationTest generateAll produces this inet6address + // serial data + // jdk8 generated serialization of on address fe80:0:0:0:21b:24ff:febd:f29c + // net if e1000g0 + + static final byte[] SerialData_ifname_e1000g0 = { (byte) 0xAC, (byte) 0xED, + (byte) 0x00, (byte) 0x05, (byte) 0x73, (byte) 0x72, (byte) 0x00, + (byte) 0x15, (byte) 0x6A, (byte) 0x61, (byte) 0x76, (byte) 0x61, + (byte) 0x2E, (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x2E, + (byte) 0x49, (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x36, + (byte) 0x41, (byte) 0x64, (byte) 0x64, (byte) 0x72, (byte) 0x65, + (byte) 0x73, (byte) 0x73, (byte) 0x5F, (byte) 0x7C, (byte) 0x20, + (byte) 0x81, (byte) 0x52, (byte) 0x2C, (byte) 0x80, (byte) 0x21, + (byte) 0x03, (byte) 0x00, (byte) 0x05, (byte) 0x49, (byte) 0x00, + (byte) 0x08, (byte) 0x73, (byte) 0x63, (byte) 0x6F, (byte) 0x70, + (byte) 0x65, (byte) 0x5F, (byte) 0x69, (byte) 0x64, (byte) 0x5A, + (byte) 0x00, (byte) 0x0C, (byte) 0x73, (byte) 0x63, (byte) 0x6F, + (byte) 0x70, (byte) 0x65, (byte) 0x5F, (byte) 0x69, (byte) 0x64, + (byte) 0x5F, (byte) 0x73, (byte) 0x65, (byte) 0x74, (byte) 0x5A, + (byte) 0x00, (byte) 0x10, (byte) 0x73, (byte) 0x63, (byte) 0x6F, + (byte) 0x70, (byte) 0x65, (byte) 0x5F, (byte) 0x69, (byte) 0x66, + (byte) 0x6E, (byte) 0x61, (byte) 0x6D, (byte) 0x65, (byte) 0x5F, + (byte) 0x73, (byte) 0x65, (byte) 0x74, (byte) 0x4C, (byte) 0x00, + (byte) 0x06, (byte) 0x69, (byte) 0x66, (byte) 0x6E, (byte) 0x61, + (byte) 0x6D, (byte) 0x65, (byte) 0x74, (byte) 0x00, (byte) 0x12, + (byte) 0x4C, (byte) 0x6A, (byte) 0x61, (byte) 0x76, (byte) 0x61, + (byte) 0x2F, (byte) 0x6C, (byte) 0x61, (byte) 0x6E, (byte) 0x67, + (byte) 0x2F, (byte) 0x53, (byte) 0x74, (byte) 0x72, (byte) 0x69, + (byte) 0x6E, (byte) 0x67, (byte) 0x3B, (byte) 0x5B, (byte) 0x00, + (byte) 0x09, (byte) 0x69, (byte) 0x70, (byte) 0x61, (byte) 0x64, + (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, + (byte) 0x74, (byte) 0x00, (byte) 0x02, (byte) 0x5B, (byte) 0x42, + (byte) 0x78, (byte) 0x72, (byte) 0x00, (byte) 0x14, (byte) 0x6A, + (byte) 0x61, (byte) 0x76, (byte) 0x61, (byte) 0x2E, (byte) 0x6E, + (byte) 0x65, (byte) 0x74, (byte) 0x2E, (byte) 0x49, (byte) 0x6E, + (byte) 0x65, (byte) 0x74, (byte) 0x41, (byte) 0x64, (byte) 0x64, + (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x2D, + (byte) 0x9B, (byte) 0x57, (byte) 0xAF, (byte) 0x9F, (byte) 0xE3, + (byte) 0xEB, (byte) 0xDB, (byte) 0x02, (byte) 0x00, (byte) 0x03, + (byte) 0x49, (byte) 0x00, (byte) 0x07, (byte) 0x61, (byte) 0x64, + (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, + (byte) 0x49, (byte) 0x00, (byte) 0x06, (byte) 0x66, (byte) 0x61, + (byte) 0x6D, (byte) 0x69, (byte) 0x6C, (byte) 0x79, (byte) 0x4C, + (byte) 0x00, (byte) 0x08, (byte) 0x68, (byte) 0x6F, (byte) 0x73, + (byte) 0x74, (byte) 0x4E, (byte) 0x61, (byte) 0x6D, (byte) 0x65, + (byte) 0x71, (byte) 0x00, (byte) 0x7E, (byte) 0x00, (byte) 0x01, + (byte) 0x78, (byte) 0x70, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, + (byte) 0x74, (byte) 0x00, (byte) 0x25, (byte) 0x66, (byte) 0x65, + (byte) 0x38, (byte) 0x30, (byte) 0x3A, (byte) 0x30, (byte) 0x3A, + (byte) 0x30, (byte) 0x3A, (byte) 0x30, (byte) 0x3A, (byte) 0x32, + (byte) 0x31, (byte) 0x62, (byte) 0x3A, (byte) 0x32, (byte) 0x34, + (byte) 0x66, (byte) 0x66, (byte) 0x3A, (byte) 0x66, (byte) 0x65, + (byte) 0x62, (byte) 0x64, (byte) 0x3A, (byte) 0x66, (byte) 0x32, + (byte) 0x39, (byte) 0x63, (byte) 0x25, (byte) 0x65, (byte) 0x31, + (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x67, (byte) 0x30, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x01, + (byte) 0x01, (byte) 0x74, (byte) 0x00, (byte) 0x07, (byte) 0x65, + (byte) 0x31, (byte) 0x30, (byte) 0x30, (byte) 0x30, (byte) 0x67, + (byte) 0x30, (byte) 0x75, (byte) 0x72, (byte) 0x00, (byte) 0x02, + (byte) 0x5B, (byte) 0x42, (byte) 0xAC, (byte) 0xF3, (byte) 0x17, + (byte) 0xF8, (byte) 0x06, (byte) 0x08, (byte) 0x54, (byte) 0xE0, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x78, (byte) 0x70, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10, (byte) 0xFE, + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x1B, (byte) 0x24, + (byte) 0xFF, (byte) 0xFE, (byte) 0xBD, (byte) 0xF2, (byte) 0x9C, + (byte) 0x78 }; + + // jdk8 generated serialization of address 0::1 on net if lo0 hostname + // localhost scope_id 1 + + static final byte[] SerialData_ifname_lo0 = { (byte) 0xAC, (byte) 0xED, + (byte) 0x00, (byte) 0x05, (byte) 0x73, (byte) 0x72, (byte) 0x00, + (byte) 0x15, (byte) 0x6A, (byte) 0x61, (byte) 0x76, (byte) 0x61, + (byte) 0x2E, (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x2E, + (byte) 0x49, (byte) 0x6E, (byte) 0x65, (byte) 0x74, (byte) 0x36, + (byte) 0x41, (byte) 0x64, (byte) 0x64, (byte) 0x72, (byte) 0x65, + (byte) 0x73, (byte) 0x73, (byte) 0x5F, (byte) 0x7C, (byte) 0x20, + (byte) 0x81, (byte) 0x52, (byte) 0x2C, (byte) 0x80, (byte) 0x21, + (byte) 0x03, (byte) 0x00, (byte) 0x05, (byte) 0x49, (byte) 0x00, + (byte) 0x08, (byte) 0x73, (byte) 0x63, (byte) 0x6F, (byte) 0x70, + (byte) 0x65, (byte) 0x5F, (byte) 0x69, (byte) 0x64, (byte) 0x5A, + (byte) 0x00, (byte) 0x0C, (byte) 0x73, (byte) 0x63, (byte) 0x6F, + (byte) 0x70, (byte) 0x65, (byte) 0x5F, (byte) 0x69, (byte) 0x64, + (byte) 0x5F, (byte) 0x73, (byte) 0x65, (byte) 0x74, (byte) 0x5A, + (byte) 0x00, (byte) 0x10, (byte) 0x73, (byte) 0x63, (byte) 0x6F, + (byte) 0x70, (byte) 0x65, (byte) 0x5F, (byte) 0x69, (byte) 0x66, + (byte) 0x6E, (byte) 0x61, (byte) 0x6D, (byte) 0x65, (byte) 0x5F, + (byte) 0x73, (byte) 0x65, (byte) 0x74, (byte) 0x4C, (byte) 0x00, + (byte) 0x06, (byte) 0x69, (byte) 0x66, (byte) 0x6E, (byte) 0x61, + (byte) 0x6D, (byte) 0x65, (byte) 0x74, (byte) 0x00, (byte) 0x12, + (byte) 0x4C, (byte) 0x6A, (byte) 0x61, (byte) 0x76, (byte) 0x61, + (byte) 0x2F, (byte) 0x6C, (byte) 0x61, (byte) 0x6E, (byte) 0x67, + (byte) 0x2F, (byte) 0x53, (byte) 0x74, (byte) 0x72, (byte) 0x69, + (byte) 0x6E, (byte) 0x67, (byte) 0x3B, (byte) 0x5B, (byte) 0x00, + (byte) 0x09, (byte) 0x69, (byte) 0x70, (byte) 0x61, (byte) 0x64, + (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, + (byte) 0x74, (byte) 0x00, (byte) 0x02, (byte) 0x5B, (byte) 0x42, + (byte) 0x78, (byte) 0x72, (byte) 0x00, (byte) 0x14, (byte) 0x6A, + (byte) 0x61, (byte) 0x76, (byte) 0x61, (byte) 0x2E, (byte) 0x6E, + (byte) 0x65, (byte) 0x74, (byte) 0x2E, (byte) 0x49, (byte) 0x6E, + (byte) 0x65, (byte) 0x74, (byte) 0x41, (byte) 0x64, (byte) 0x64, + (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x2D, + (byte) 0x9B, (byte) 0x57, (byte) 0xAF, (byte) 0x9F, (byte) 0xE3, + (byte) 0xEB, (byte) 0xDB, (byte) 0x02, (byte) 0x00, (byte) 0x03, + (byte) 0x49, (byte) 0x00, (byte) 0x07, (byte) 0x61, (byte) 0x64, + (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, + (byte) 0x49, (byte) 0x00, (byte) 0x06, (byte) 0x66, (byte) 0x61, + (byte) 0x6D, (byte) 0x69, (byte) 0x6C, (byte) 0x79, (byte) 0x4C, + (byte) 0x00, (byte) 0x08, (byte) 0x68, (byte) 0x6F, (byte) 0x73, + (byte) 0x74, (byte) 0x4E, (byte) 0x61, (byte) 0x6D, (byte) 0x65, + (byte) 0x71, (byte) 0x00, (byte) 0x7E, (byte) 0x00, (byte) 0x01, + (byte) 0x78, (byte) 0x70, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, + (byte) 0x74, (byte) 0x00, (byte) 0x09, (byte) 0x6C, (byte) 0x6F, + (byte) 0x63, (byte) 0x61, (byte) 0x6C, (byte) 0x68, (byte) 0x6F, + (byte) 0x73, (byte) 0x74, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x74, (byte) 0x00, + (byte) 0x03, (byte) 0x6C, (byte) 0x6F, (byte) 0x30, (byte) 0x75, + (byte) 0x72, (byte) 0x00, (byte) 0x02, (byte) 0x5B, (byte) 0x42, + (byte) 0xAC, (byte) 0xF3, (byte) 0x17, (byte) 0xF8, (byte) 0x06, + (byte) 0x08, (byte) 0x54, (byte) 0xE0, (byte) 0x02, (byte) 0x00, + (byte) 0x00, (byte) 0x78, (byte) 0x70, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x78 }; + +} + +class MockLo0Inet6Address { + + private static final byte[] LOOPBACKIPV6ADDRESS = { (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 }; + + private static final String LOCALHOSTNAME = "localhost"; + + private static final String LO0HOSTADDRESS = "0:0:0:0:0:0:0:1%lo0"; + + private static final String BARE_LO0HOSTADDRESS = "0:0:0:0:0:0:0:1"; + + private static final String LO0HOSTADDRESS_WITHINDEX = "0:0:0:0:0:0:0:1%1"; + + private static final int SCOPE_ID_LO0 = 1; + + private static final int SCOPE_ID_ZERO = 0; + + public static final String NETWORK_IF_LO0 = "lo0"; + + static String getHostName() { + return LOCALHOSTNAME; + } + + static String getHostAddress() { + return LO0HOSTADDRESS; + } + + static String getBareHostAddress() { + return BARE_LO0HOSTADDRESS; + } + + static String getHostAddressWithIndex() { + return LO0HOSTADDRESS_WITHINDEX; + } + + static byte[] getAddress() { + return LOOPBACKIPV6ADDRESS; + } + + static int getScopeId() { + return SCOPE_ID_LO0; + } + + static int getScopeZero() { + return SCOPE_ID_ZERO; + } + + static String getScopeIfName() { + return NETWORK_IF_LO0; + } + +} + +class MockE1000g0Inet6Address { + + // fe80::21b:24ff:febd:f29c + private static final byte[] E1000G0IPV6ADDRESS = { (byte) 0xfe, + (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x1b, (byte) 0x24, + (byte) 0xff, (byte) 0xfe, (byte) 0xbd, (byte) 0xf2, (byte) 0x9c }; + + private static final String E1000G0HOSTNAME = "fe80:0:0:0:21b:24ff:febd:f29c%e1000g0"; + + private static final String BARE_E1000G0HOSTADDRESS = "fe80:0:0:0:21b:24ff:febd:f29c"; + + private static final String E1000G0HOSTADDRESS_WITHINDEX = "fe80:0:0:0:21b:24ff:febd:f29c%2"; + + private static final String E1000G0HOSTADDRESS = "fe80:0:0:0:21b:24ff:febd:f29c%e1000g0"; + + private static final String NETWORK_IF_E1000G0 = "e1000g0"; + + private static final int SCOPE_ID_E1000G0 = 2; + + private static final int SCOPE_ID_ZERO = 0; + + static String getHostName() { + return E1000G0HOSTNAME; + } + + static String getHostAddress() { + return E1000G0HOSTADDRESS; + } + + static String getHostAddressWithIndex() { + return E1000G0HOSTADDRESS_WITHINDEX; + } + + static String getBareHostAddress() { + return BARE_E1000G0HOSTADDRESS; + } + + static byte[] getAddress() { + return E1000G0IPV6ADDRESS; + } + + static int getScopeId() { + return SCOPE_ID_E1000G0; + } + + static int getScopeZero() { + return SCOPE_ID_ZERO; + } + + static String getScopeIfName() { + return NETWORK_IF_E1000G0; + } + +} diff --git a/jdk/test/java/util/Arrays/SetAllTest.java b/jdk/test/java/util/Arrays/SetAllTest.java new file mode 100644 index 00000000000..2388a7bfd55 --- /dev/null +++ b/jdk/test/java/util/Arrays/SetAllTest.java @@ -0,0 +1,281 @@ +/* + * 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 8012650 + * @summary Unit test for setAll, parallelSetAll variants + * @run testng SetAllTest + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +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.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.fail; + +@Test +public class SetAllTest { + private static final IntFunction toString = i -> "N" + Integer.valueOf(i); + private static final IntFunction fillString = i -> "X"; + private static final String[] r0 = {}; + private static final String[] r1 = { "N0" }; + private static final String[] r10 = { "N0", "N1", "N2", "N3", "N4", "N5", "N6", "N7", "N8", "N9" }; + + private Object[][] stringData = new Object[][] { + { "empty", 0, toString, r0 }, + { "one", 1, toString, r1 }, + { "ten", 10, toString, r10 }, + { "fill", 3, fillString, new String[] { "X", "X", "X" }} + }; + + private static final IntUnaryOperator toInt = i -> i << 1; + private static final IntUnaryOperator fillInt = i -> 99; + private static final int[] ir0 = {}; + private static final int[] ir1 = { 0 }; + private static final int[] ir10 = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 }; + private Object[][] intData = new Object[][] { + { "empty", 0, toInt, ir0 }, + { "one", 1, toInt, ir1 }, + { "ten", 10, toInt, ir10 }, + { "fill", 3, fillInt, new int[] { 99, 99, 99 }} + }; + + private static final IntToLongFunction toLong = i -> i << 1; + private static final IntToLongFunction fillLong = i -> 9999L; + private static final long[] lr0 = {}; + private static final long[] lr1 = { 0L }; + private static final long[] lr10 = { 0L, 2L, 4L, 6L, 8L, 10L, 12L, 14L, 16L, 18L }; + private Object[][] longData = new Object[][] { + { "empty", 0, toLong, lr0 }, + { "one", 1, toLong, lr1 }, + { "ten", 10, toLong, lr10 }, + { "fill", 3, fillLong, new long[] { 9999L, 9999L, 9999L }} + }; + + private static final IntToDoubleFunction toDouble = i -> i * 1.1; + private static final IntToDoubleFunction fillDouble = i -> 3.14; + private static final double[] dr0 = {}; + private static final double[] dr1 = { 0.0 }; + private static final double[] dr10 = { 0.0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 }; + private Object[][] doubleData = new Object[][] { + { "empty", 0, toDouble, dr0 }, + { "one", 1, toDouble, dr1 }, + { "ten", 10, toDouble, dr10 }, + { "fill", 3, fillDouble, new double[] { 3.14, 3.14, 3.14 }} + }; + + @DataProvider(name="string") + public Object[][] stringTests() { return stringData; } + + @DataProvider(name="int") + public Object[][] intTests() { return intData; } + + @DataProvider(name="long") + public Object[][] longTests() { return longData; } + + @DataProvider(name="double") + public Object[][] doubleTests() { return doubleData; } + + @Test(dataProvider = "string") + public void testSetAllString(String name, int size, IntFunction generator, String[] expected) { + String[] result = new String[size]; + Arrays.setAll(result, generator); + assertEquals(result, expected, "setAll(String[], IntFunction) case " + name + " failed."); + + // ensure fresh array + result = new String[size]; + Arrays.parallelSetAll(result, generator); + assertEquals(result, expected, "parallelSetAll(String[], IntFunction) case " + name + " failed."); + } + + @Test(dataProvider = "int") + public void testSetAllInt(String name, int size, IntUnaryOperator generator, int[] expected) { + int[] result = new int[size]; + Arrays.setAll(result, generator); + assertEquals(result, expected, "setAll(int[], IntUnaryOperator) case " + name + " failed."); + + // ensure fresh array + result = new int[size]; + Arrays.parallelSetAll(result, generator); + assertEquals(result, expected, "parallelSetAll(int[], IntUnaryOperator) case " + name + " failed."); + } + + @Test(dataProvider = "long") + public void testSetAllLong(String name, int size, IntToLongFunction generator, long[] expected) { + long[] result = new long[size]; + Arrays.setAll(result, generator); + assertEquals(result, expected, "setAll(long[], IntToLongFunction) case " + name + " failed."); + + // ensure fresh array + result = new long[size]; + Arrays.parallelSetAll(result, generator); + assertEquals(result, expected, "parallelSetAll(long[], IntToLongFunction) case " + name + " failed."); + } + + private void assertDoubleArrayEquals(double[] actual, double[] expected, double delta, String msg) { + if (actual.length != expected.length) { + fail(msg + ": length mismatch, expected " + expected.length + ", got " + actual.length); + } + + for (int i = 0; i < actual.length; i++) { + assertEquals(actual[i], expected[i], delta, msg + "(mismatch at index " + i + ")"); + } + } + + @Test(dataProvider = "double") + public void testSetAllDouble(String name, int size, IntToDoubleFunction generator, double[] expected) { + double[] result = new double[size]; + Arrays.setAll(result, generator); + assertDoubleArrayEquals(result, expected, 0.05, "setAll(double[], IntToDoubleFunction) case " + name + " failed."); + + // ensure fresh array + result = new double[size]; + Arrays.parallelSetAll(result, generator); + assertDoubleArrayEquals(result, expected, 0.05, "setAll(double[], IntToDoubleFunction) case " + name + " failed."); + } + + @Test + public void testStringSetNulls() { + String[] ar = new String[2]; + try { + Arrays.setAll(null, i -> "X"); + fail("Arrays.setAll(null, foo) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.parallelSetAll(null, i -> "X"); + fail("Arrays.parallelSetAll(null, foo) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.setAll(ar, null); + fail("Arrays.setAll(array, null) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.parallelSetAll(ar, null); + fail("Arrays.parallelSetAll(array, null) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + } + + @Test + public void testIntSetNulls() { + int[] ar = new int[2]; + try { + Arrays.setAll(null, (IntUnaryOperator) i -> i); + fail("Arrays.setAll(null, foo) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.parallelSetAll(null, (IntUnaryOperator) i -> i); + fail("Arrays.parallelSetAll(null, foo) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.setAll(ar, null); + fail("Arrays.setAll(array, null) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.parallelSetAll(ar, null); + fail("Arrays.parallelSetAll(array, null) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + } + + @Test + public void testLongSetNulls() { + long[] ar = new long[2]; + try { + Arrays.setAll(null, (IntToLongFunction) i -> Long.MAX_VALUE); + fail("Arrays.setAll(null, foo) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.parallelSetAll(null, (IntToLongFunction) i -> Long.MAX_VALUE); + fail("Arrays.parallelSetAll(null, foo) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.setAll(ar, null); + fail("Arrays.setAll(array, null) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.parallelSetAll(ar, null); + fail("Arrays.parallelSetAll(array, null) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + } + + @Test + public void testDoubleSetNulls() { + double[] ar = new double[2]; + try { + Arrays.setAll(null, (IntToDoubleFunction) i -> Math.E); + fail("Arrays.setAll(null, foo) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.parallelSetAll(null, (IntToDoubleFunction) i -> Math.E); + fail("Arrays.parallelSetAll(null, foo) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.setAll(ar, null); + fail("Arrays.setAll(array, null) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + try { + Arrays.parallelSetAll(ar, null); + fail("Arrays.parallelSetAll(array, null) should throw NPE"); + } catch (NullPointerException npe) { + // expected + } + } +} \ No newline at end of file