diff --git a/jdk/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java b/jdk/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java index dfd5d4e8da4..713df8e5dc5 100644 --- a/jdk/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java +++ b/jdk/src/macosx/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -520,14 +520,22 @@ public class HostLocaleProviderAdapterImpl { } private static boolean isSupportedCalendarLocale(Locale locale) { - Locale base = locale.stripExtensions(); + Locale base = locale; + + if (base.hasExtensions() || base.getVariant() != "") { + base = new Locale.Builder() + .setLocale(locale) + .clearExtensions() + .build(); + } + if (!supportedLocaleSet.contains(base)) { return false; } String requestedCalType = locale.getUnicodeLocaleType("ca"); String nativeCalType = - getCalendarID(locale.toLanguageTag()).replaceFirst("gregorian", "gregory"); + getCalendarID(base.toLanguageTag()).replaceFirst("gregorian", "gregory"); if (requestedCalType == null) { return Calendar.getAvailableCalendarTypes().contains(nativeCalType); diff --git a/jdk/src/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java b/jdk/src/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java index 137ba255c33..f09b7d875de 100644 --- a/jdk/src/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java +++ b/jdk/src/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 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 @@ -241,14 +241,29 @@ abstract class TlsPrfGenerator extends KeyGeneratorSpi { int off = secret.length >> 1; int seclen = off + (secret.length & 1); + byte[] secKey = secret; + int keyLen = seclen; byte[] output = new byte[outputLength]; // P_MD5(S1, label + seed) - expand(md5, 16, secret, 0, seclen, labelBytes, seed, output, + // If we have a long secret, digest it first. + if (seclen > 64) { // 64: block size of HMAC-MD5 + md5.update(secret, 0, seclen); + secKey = md5.digest(); + keyLen = secKey.length; + } + expand(md5, 16, secKey, 0, keyLen, labelBytes, seed, output, HMAC_ipad64.clone(), HMAC_opad64.clone()); // P_SHA-1(S2, label + seed) - expand(sha, 20, secret, off, seclen, labelBytes, seed, output, + // If we have a long secret, digest it first. + if (seclen > 64) { // 64: block size of HMAC-SHA1 + sha.update(secret, off, seclen); + secKey = sha.digest(); + keyLen = secKey.length; + off = 0; + } + expand(sha, 20, secKey, off, keyLen, labelBytes, seed, output, HMAC_ipad64.clone(), HMAC_opad64.clone()); return output; diff --git a/jdk/src/share/classes/com/sun/jarsigner/ContentSignerParameters.java b/jdk/src/share/classes/com/sun/jarsigner/ContentSignerParameters.java index fe48bc51739..bcf73013597 100644 --- a/jdk/src/share/classes/com/sun/jarsigner/ContentSignerParameters.java +++ b/jdk/src/share/classes/com/sun/jarsigner/ContentSignerParameters.java @@ -59,6 +59,13 @@ public interface ContentSignerParameters { */ public X509Certificate getTimestampingAuthorityCertificate(); + /** + * Retrieves the TSAPolicyID for a Timestamping Authority (TSA). + * + * @return The TSAPolicyID. May be null. + */ + public String getTSAPolicyID(); + /** * Retrieves the JAR file's signature. * diff --git a/jdk/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java b/jdk/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java index 836b99e268e..2a8e2c9585b 100644 --- a/jdk/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java +++ b/jdk/src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java @@ -147,11 +147,11 @@ public interface BasicFileAttributeView * this method has no effect. * *

Usage Example: - * Suppose we want to change a file's creation time. + * Suppose we want to change a file's last access time. *

      *    Path path = ...
      *    FileTime time = ...
-     *    Files.getFileAttributeView(path, BasicFileAttributeView.class).setTimes(null, null, time);
+     *    Files.getFileAttributeView(path, BasicFileAttributeView.class).setTimes(null, time, null);
      * 
* * @param lastModifiedTime diff --git a/jdk/src/share/classes/java/util/ArrayDeque.java b/jdk/src/share/classes/java/util/ArrayDeque.java index 476b2233298..0e20ffc52ef 100644 --- a/jdk/src/share/classes/java/util/ArrayDeque.java +++ b/jdk/src/share/classes/java/util/ArrayDeque.java @@ -33,7 +33,9 @@ */ package java.util; -import java.io.*; + +import java.io.Serializable; +import java.util.function.Consumer; /** * Resizable-array implementation of the {@link Deque} interface. Array @@ -44,16 +46,16 @@ import java.io.*; * {@link Stack} when used as a stack, and faster than {@link LinkedList} * when used as a queue. * - *

Most ArrayDeque operations run in amortized constant time. + *

Most {@code ArrayDeque} operations run in amortized constant time. * Exceptions include {@link #remove(Object) remove}, {@link * #removeFirstOccurrence removeFirstOccurrence}, {@link #removeLastOccurrence * removeLastOccurrence}, {@link #contains contains}, {@link #iterator * iterator.remove()}, and the bulk operations, all of which run in linear * time. * - *

The iterators returned by this class's iterator method are + *

The iterators returned by this class's {@code iterator} method are * fail-fast: If the deque is modified at any time after the iterator - * is created, in any way except through the iterator's own remove + * is created, in any way except through the iterator's own {@code remove} * method, the iterator will generally throw a {@link * ConcurrentModificationException}. Thus, in the face of concurrent * modification, the iterator fails quickly and cleanly, rather than risking @@ -63,7 +65,7 @@ import java.io.*; *

Note that the fail-fast behavior of an iterator cannot be guaranteed * as it is, generally speaking, impossible to make any hard guarantees in the * presence of unsynchronized concurrent modification. Fail-fast iterators - * throw ConcurrentModificationException on a best-effort basis. + * throw {@code ConcurrentModificationException} on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this * exception for its correctness: the fail-fast behavior of iterators * should be used only to detect bugs. @@ -93,20 +95,20 @@ public class ArrayDeque extends AbstractCollection * other. We also guarantee that all array cells not holding * deque elements are always null. */ - private transient E[] elements; + transient Object[] elements; // non-private to simplify nested class access /** * The index of the element at the head of the deque (which is the * element that would be removed by remove() or pop()); or an * arbitrary number equal to tail if the deque is empty. */ - private transient int head; + transient int head; /** * The index at which the next element would be added to the tail * of the deque (via addLast(E), add(E), or push(E)). */ - private transient int tail; + transient int tail; /** * The minimum capacity that we'll use for a newly created deque. @@ -117,11 +119,10 @@ public class ArrayDeque extends AbstractCollection // ****** Array allocation and resizing utilities ****** /** - * Allocate empty array to hold the given number of elements. + * Allocates empty array to hold the given number of elements. * * @param numElements the number of elements to hold */ - @SuppressWarnings("unchecked") private void allocateElements(int numElements) { int initialCapacity = MIN_INITIAL_CAPACITY; // Find the best power of two to hold elements. @@ -138,11 +139,11 @@ public class ArrayDeque extends AbstractCollection if (initialCapacity < 0) // Too many elements, must back off initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements } - elements = (E[]) new Object[initialCapacity]; + elements = new Object[initialCapacity]; } /** - * Double the capacity of this deque. Call only when full, i.e., + * Doubles the capacity of this deque. Call only when full, i.e., * when head and tail have wrapped around to become equal. */ private void doubleCapacity() { @@ -153,8 +154,7 @@ public class ArrayDeque extends AbstractCollection int newCapacity = n << 1; if (newCapacity < 0) throw new IllegalStateException("Sorry, deque too big"); - @SuppressWarnings("unchecked") - E[] a = (E[]) new Object[newCapacity]; + Object[] a = new Object[newCapacity]; System.arraycopy(elements, p, a, 0, r); System.arraycopy(elements, 0, a, r, p); elements = a; @@ -184,9 +184,8 @@ public class ArrayDeque extends AbstractCollection * Constructs an empty array deque with an initial capacity * sufficient to hold 16 elements. */ - @SuppressWarnings("unchecked") public ArrayDeque() { - elements = (E[]) new Object[16]; + elements = new Object[16]; } /** @@ -252,7 +251,7 @@ public class ArrayDeque extends AbstractCollection * Inserts the specified element at the front of this deque. * * @param e the element to add - * @return true (as specified by {@link Deque#offerFirst}) + * @return {@code true} (as specified by {@link Deque#offerFirst}) * @throws NullPointerException if the specified element is null */ public boolean offerFirst(E e) { @@ -264,7 +263,7 @@ public class ArrayDeque extends AbstractCollection * Inserts the specified element at the end of this deque. * * @param e the element to add - * @return true (as specified by {@link Deque#offerLast}) + * @return {@code true} (as specified by {@link Deque#offerLast}) * @throws NullPointerException if the specified element is null */ public boolean offerLast(E e) { @@ -294,7 +293,9 @@ public class ArrayDeque extends AbstractCollection public E pollFirst() { int h = head; - E result = elements[h]; // Element is null if deque empty + @SuppressWarnings("unchecked") + E result = (E) elements[h]; + // Element is null if deque empty if (result == null) return null; elements[h] = null; // Must null out slot @@ -304,7 +305,8 @@ public class ArrayDeque extends AbstractCollection public E pollLast() { int t = (tail - 1) & (elements.length - 1); - E result = elements[t]; + @SuppressWarnings("unchecked") + E result = (E) elements[t]; if (result == null) return null; elements[t] = null; @@ -316,48 +318,53 @@ public class ArrayDeque extends AbstractCollection * @throws NoSuchElementException {@inheritDoc} */ public E getFirst() { - E x = elements[head]; - if (x == null) + @SuppressWarnings("unchecked") + E result = (E) elements[head]; + if (result == null) throw new NoSuchElementException(); - return x; + return result; } /** * @throws NoSuchElementException {@inheritDoc} */ public E getLast() { - E x = elements[(tail - 1) & (elements.length - 1)]; - if (x == null) + @SuppressWarnings("unchecked") + E result = (E) elements[(tail - 1) & (elements.length - 1)]; + if (result == null) throw new NoSuchElementException(); - return x; + return result; } + @SuppressWarnings("unchecked") public E peekFirst() { - return elements[head]; // elements[head] is null if deque empty + // elements[head] is null if deque empty + return (E) elements[head]; } + @SuppressWarnings("unchecked") public E peekLast() { - return elements[(tail - 1) & (elements.length - 1)]; + return (E) elements[(tail - 1) & (elements.length - 1)]; } /** * Removes the first occurrence of the specified element in this * deque (when traversing the deque from head to tail). * If the deque does not contain the element, it is unchanged. - * More formally, removes the first element e such that - * o.equals(e) (if such an element exists). - * Returns true if this deque contained the specified element + * More formally, removes the first element {@code e} such that + * {@code o.equals(e)} (if such an element exists). + * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * * @param o element to be removed from this deque, if present - * @return true if the deque contained the specified element + * @return {@code true} if the deque contained the specified element */ public boolean removeFirstOccurrence(Object o) { if (o == null) return false; int mask = elements.length - 1; int i = head; - E x; + Object x; while ( (x = elements[i]) != null) { if (o.equals(x)) { delete(i); @@ -372,20 +379,20 @@ public class ArrayDeque extends AbstractCollection * Removes the last occurrence of the specified element in this * deque (when traversing the deque from head to tail). * If the deque does not contain the element, it is unchanged. - * More formally, removes the last element e such that - * o.equals(e) (if such an element exists). - * Returns true if this deque contained the specified element + * More formally, removes the last element {@code e} such that + * {@code o.equals(e)} (if such an element exists). + * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * * @param o element to be removed from this deque, if present - * @return true if the deque contained the specified element + * @return {@code true} if the deque contained the specified element */ public boolean removeLastOccurrence(Object o) { if (o == null) return false; int mask = elements.length - 1; int i = (tail - 1) & mask; - E x; + Object x; while ( (x = elements[i]) != null) { if (o.equals(x)) { delete(i); @@ -404,7 +411,7 @@ public class ArrayDeque extends AbstractCollection *

This method is equivalent to {@link #addLast}. * * @param e the element to add - * @return true (as specified by {@link Collection#add}) + * @return {@code true} (as specified by {@link Collection#add}) * @throws NullPointerException if the specified element is null */ public boolean add(E e) { @@ -418,7 +425,7 @@ public class ArrayDeque extends AbstractCollection *

This method is equivalent to {@link #offerLast}. * * @param e the element to add - * @return true (as specified by {@link Queue#offer}) + * @return {@code true} (as specified by {@link Queue#offer}) * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { @@ -443,12 +450,12 @@ public class ArrayDeque extends AbstractCollection /** * Retrieves and removes the head of the queue represented by this deque * (in other words, the first element of this deque), or returns - * null if this deque is empty. + * {@code null} if this deque is empty. * *

This method is equivalent to {@link #pollFirst}. * * @return the head of the queue represented by this deque, or - * null if this deque is empty + * {@code null} if this deque is empty */ public E poll() { return pollFirst(); @@ -470,12 +477,12 @@ public class ArrayDeque extends AbstractCollection /** * Retrieves, but does not remove, the head of the queue represented by - * this deque, or returns null if this deque is empty. + * this deque, or returns {@code null} if this deque is empty. * *

This method is equivalent to {@link #peekFirst}. * * @return the head of the queue represented by this deque, or - * null if this deque is empty + * {@code null} if this deque is empty */ public E peek() { return peekFirst(); @@ -530,7 +537,7 @@ public class ArrayDeque extends AbstractCollection */ private boolean delete(int i) { checkInvariants(); - final E[] elements = this.elements; + final Object[] elements = this.elements; final int mask = elements.length - 1; final int h = head; final int t = tail; @@ -579,9 +586,9 @@ public class ArrayDeque extends AbstractCollection } /** - * Returns true if this deque contains no elements. + * Returns {@code true} if this deque contains no elements. * - * @return true if this deque contains no elements + * @return {@code true} if this deque contains no elements */ public boolean isEmpty() { return head == tail; @@ -628,7 +635,8 @@ public class ArrayDeque extends AbstractCollection public E next() { if (cursor == fence) throw new NoSuchElementException(); - E result = elements[cursor]; + @SuppressWarnings("unchecked") + E result = (E) elements[cursor]; // This check doesn't catch all possible comodifications, // but does catch the ones that corrupt traversal if (tail != fence || result == null) @@ -647,6 +655,20 @@ public class ArrayDeque extends AbstractCollection } lastRet = -1; } + + public void forEachRemaining(Consumer action) { + Objects.requireNonNull(action); + Object[] a = elements; + int m = a.length - 1, f = fence, i = cursor; + cursor = f; + while (i != f) { + @SuppressWarnings("unchecked") E e = (E)a[i]; + i = (i + 1) & m; + if (e == null) + throw new ConcurrentModificationException(); + action.accept(e); + } + } } private class DescendingIterator implements Iterator { @@ -667,7 +689,8 @@ public class ArrayDeque extends AbstractCollection if (cursor == fence) throw new NoSuchElementException(); cursor = (cursor - 1) & (elements.length - 1); - E result = elements[cursor]; + @SuppressWarnings("unchecked") + E result = (E) elements[cursor]; if (head != fence || result == null) throw new ConcurrentModificationException(); lastRet = cursor; @@ -686,19 +709,19 @@ public class ArrayDeque extends AbstractCollection } /** - * Returns true if this deque contains the specified element. - * More formally, returns true if and only if this deque contains - * at least one element e such that o.equals(e). + * Returns {@code true} if this deque contains the specified element. + * More formally, returns {@code true} if and only if this deque contains + * at least one element {@code e} such that {@code o.equals(e)}. * * @param o object to be checked for containment in this deque - * @return true if this deque contains the specified element + * @return {@code true} if this deque contains the specified element */ public boolean contains(Object o) { if (o == null) return false; int mask = elements.length - 1; int i = head; - E x; + Object x; while ( (x = elements[i]) != null) { if (o.equals(x)) return true; @@ -710,15 +733,15 @@ public class ArrayDeque extends AbstractCollection /** * Removes a single instance of the specified element from this deque. * If the deque does not contain the element, it is unchanged. - * More formally, removes the first element e such that - * o.equals(e) (if such an element exists). - * Returns true if this deque contained the specified element + * More formally, removes the first element {@code e} such that + * {@code o.equals(e)} (if such an element exists). + * Returns {@code true} if this deque contained the specified element * (or equivalently, if this deque changed as a result of the call). * - *

This method is equivalent to {@link #removeFirstOccurrence}. + *

This method is equivalent to {@link #removeFirstOccurrence(Object)}. * * @param o element to be removed from this deque, if present - * @return true if this deque contained the specified element + * @return {@code true} if this deque contained the specified element */ public boolean remove(Object o) { return removeFirstOccurrence(o); @@ -770,22 +793,21 @@ public class ArrayDeque extends AbstractCollection *

If this deque fits in the specified array with room to spare * (i.e., the array has more elements than this deque), the element in * the array immediately following the end of the deque is set to - * null. + * {@code null}. * *

Like the {@link #toArray()} method, this method acts as bridge between * array-based and collection-based APIs. Further, this method allows * precise control over the runtime type of the output array, and may, * under certain circumstances, be used to save allocation costs. * - *

Suppose x is a deque known to contain only strings. + *

Suppose {@code x} is a deque known to contain only strings. * The following code can be used to dump the deque into a newly - * allocated array of String: + * allocated array of {@code String}: * - *

-     *     String[] y = x.toArray(new String[0]);
+ *
 {@code String[] y = x.toArray(new String[0]);}
* - * Note that toArray(new Object[0]) is identical in function to - * toArray(). + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. * * @param a the array into which the elements of the deque are to * be stored, if it is big enough; otherwise, a new array of the @@ -818,28 +840,25 @@ public class ArrayDeque extends AbstractCollection public ArrayDeque clone() { try { @SuppressWarnings("unchecked") - ArrayDeque result = (ArrayDeque) super.clone(); + ArrayDeque result = (ArrayDeque) super.clone(); result.elements = Arrays.copyOf(elements, elements.length); return result; - } catch (CloneNotSupportedException e) { throw new AssertionError(); } } - /** - * Appease the serialization gods. - */ private static final long serialVersionUID = 2340985798034038923L; /** - * Serialize this deque. + * Saves this deque to a stream (that is, serializes it). * - * @serialData The current size (int) of the deque, + * @serialData The current size ({@code int}) of the deque, * followed by all of its elements (each an object reference) in * first-to-last order. */ - private void writeObject(ObjectOutputStream s) throws IOException { + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException { s.defaultWriteObject(); // Write out size @@ -852,11 +871,10 @@ public class ArrayDeque extends AbstractCollection } /** - * Deserialize this deque. + * Reconstitutes this deque from a stream (that is, deserializes it). */ - @SuppressWarnings("unchecked") - private void readObject(ObjectInputStream s) - throws IOException, ClassNotFoundException { + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); // Read in size and allocate array @@ -867,6 +885,88 @@ public class ArrayDeque extends AbstractCollection // Read in all elements in the proper order. for (int i = 0; i < size; i++) - elements[i] = (E)s.readObject(); + elements[i] = s.readObject(); } + + public Spliterator spliterator() { + return new DeqSpliterator(this, -1, -1); + } + + static final class DeqSpliterator implements Spliterator { + private final ArrayDeque deq; + private int fence; // -1 until first use + private int index; // current index, modified on traverse/split + + /** Creates new spliterator covering the given array and range */ + DeqSpliterator(ArrayDeque deq, int origin, int fence) { + this.deq = deq; + this.index = origin; + this.fence = fence; + } + + private int getFence() { // force initialization + int t; + if ((t = fence) < 0) { + t = fence = deq.tail; + index = deq.head; + } + return t; + } + + public DeqSpliterator trySplit() { + int t = getFence(), h = index, n = deq.elements.length; + if (h != t && ((h + 1) & (n - 1)) != t) { + if (h > t) + t += n; + int m = ((h + t) >>> 1) & (n - 1); + return new DeqSpliterator<>(deq, h, index = m); + } + return null; + } + + public void forEachRemaining(Consumer consumer) { + if (consumer == null) + throw new NullPointerException(); + Object[] a = deq.elements; + int m = a.length - 1, f = getFence(), i = index; + index = f; + while (i != f) { + @SuppressWarnings("unchecked") E e = (E)a[i]; + i = (i + 1) & m; + if (e == null) + throw new ConcurrentModificationException(); + consumer.accept(e); + } + } + + public boolean tryAdvance(Consumer consumer) { + if (consumer == null) + throw new NullPointerException(); + Object[] a = deq.elements; + int m = a.length - 1, f = getFence(), i = index; + if (i != fence) { + @SuppressWarnings("unchecked") E e = (E)a[i]; + index = (i + 1) & m; + if (e == null) + throw new ConcurrentModificationException(); + consumer.accept(e); + return true; + } + return false; + } + + public long estimateSize() { + int n = getFence() - index; + if (n < 0) + n += deq.elements.length; + return (long) n; + } + + @Override + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | + Spliterator.NONNULL | Spliterator.SUBSIZED; + } + } + } diff --git a/jdk/src/share/classes/java/util/ArrayList.java b/jdk/src/share/classes/java/util/ArrayList.java index 46bd55760a7..dde4332e78f 100644 --- a/jdk/src/share/classes/java/util/ArrayList.java +++ b/jdk/src/share/classes/java/util/ArrayList.java @@ -25,6 +25,14 @@ package java.util; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + /** * Resizable-array implementation of the List interface. Implements * all optional list operations, and permits all elements, including @@ -120,7 +128,7 @@ public class ArrayList extends AbstractList * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to * DEFAULT_CAPACITY when the first element is added. */ - private transient Object[] elementData; + transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). @@ -853,6 +861,27 @@ public class ArrayList extends AbstractList } } + @Override + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer consumer) { + Objects.requireNonNull(consumer); + final int size = ArrayList.this.size; + int i = cursor; + if (i >= size) { + return; + } + final Object[] elementData = ArrayList.this.elementData; + if (i >= elementData.length) { + throw new ConcurrentModificationException(); + } + while (i != size && modCount == expectedModCount) { + consumer.accept((E) elementData[i++]); + } + // update once at end of iteration to reduce heap write traffic + lastRet = cursor = i; + checkForComodification(); + } + final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); @@ -1088,6 +1117,26 @@ public class ArrayList extends AbstractList return (E) elementData[offset + (lastRet = i)]; } + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer consumer) { + Objects.requireNonNull(consumer); + final int size = SubList.this.size; + int i = cursor; + if (i >= size) { + return; + } + final Object[] elementData = ArrayList.this.elementData; + if (offset + i >= elementData.length) { + throw new ConcurrentModificationException(); + } + while (i != size && modCount == expectedModCount) { + consumer.accept((E) elementData[offset + (i++)]); + } + // update once at end of iteration to reduce heap write traffic + lastRet = cursor = i; + checkForComodification(); + } + public int nextIndex() { return cursor; } @@ -1167,5 +1216,217 @@ public class ArrayList extends AbstractList if (ArrayList.this.modCount != this.modCount) throw new ConcurrentModificationException(); } + + public Spliterator spliterator() { + checkForComodification(); + return new ArrayListSpliterator(ArrayList.this, offset, + offset + this.size, this.modCount); + } + } + + @Override + public void forEach(Consumer action) { + Objects.requireNonNull(action); + final int expectedModCount = modCount; + @SuppressWarnings("unchecked") + final E[] elementData = (E[]) this.elementData; + final int size = this.size; + for (int i=0; modCount == expectedModCount && i < size; i++) { + action.accept(elementData[i]); + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + public Spliterator spliterator() { + return new ArrayListSpliterator<>(this, 0, -1, 0); + } + + /** Index-based split-by-two, lazily initialized Spliterator */ + static final class ArrayListSpliterator implements Spliterator { + + /* + * If ArrayLists were immutable, or structurally immutable (no + * adds, removes, etc), we could implement their spliterators + * with Arrays.spliterator. Instead we detect as much + * interference during traversal as practical without + * sacrificing much performance. We rely primarily on + * modCounts. These are not guaranteed to detect concurrency + * violations, and are sometimes overly conservative about + * within-thread interference, but detect enough problems to + * be worthwhile in practice. To carry this out, we (1) lazily + * initialize fence and expectedModCount until the latest + * point that we need to commit to the state we are checking + * against; thus improving precision. (This doesn't apply to + * SubLists, that create spliterators with current non-lazy + * values). (2) We perform only a single + * ConcurrentModificationException check at the end of forEach + * (the most performance-sensitive method). When using forEach + * (as opposed to iterators), we can normally only detect + * interference after actions, not before. Further + * CME-triggering checks apply to all other possible + * violations of assumptions for example null or too-small + * elementData array given its size(), that could only have + * occurred due to interference. This allows the inner loop + * of forEach to run without any further checks, and + * simplifies lambda-resolution. While this does entail a + * number of checks, note that in the common case of + * list.stream().forEach(a), no checks or other computation + * occur anywhere other than inside forEach itself. The other + * less-often-used methods cannot take advantage of most of + * these streamlinings. + */ + + private final ArrayList list; + private int index; // current index, modified on advance/split + private int fence; // -1 until used; then one past last index + private int expectedModCount; // initialized when fence set + + /** Create new spliterator covering the given range */ + ArrayListSpliterator(ArrayList list, int origin, int fence, + int expectedModCount) { + this.list = list; // OK if null unless traversed + this.index = origin; + this.fence = fence; + this.expectedModCount = expectedModCount; + } + + private int getFence() { // initialize fence to size on first use + int hi; // (a specialized variant appears in method forEach) + ArrayList lst; + if ((hi = fence) < 0) { + if ((lst = list) == null) + hi = fence = 0; + else { + expectedModCount = lst.modCount; + hi = fence = lst.size; + } + } + return hi; + } + + public ArrayListSpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + return (lo >= mid) ? null : // divide range in half unless too small + new ArrayListSpliterator(list, lo, index = mid, + expectedModCount); + } + + public boolean tryAdvance(Consumer action) { + if (action == null) + throw new NullPointerException(); + int hi = getFence(), i = index; + if (i < hi) { + index = i + 1; + @SuppressWarnings("unchecked") E e = (E)list.elementData[i]; + action.accept(e); + if (list.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + return false; + } + + public void forEachRemaining(Consumer action) { + int i, hi, mc; // hoist accesses and checks from loop + ArrayList lst; Object[] a; + if (action == null) + throw new NullPointerException(); + if ((lst = list) != null && (a = lst.elementData) != null) { + if ((hi = fence) < 0) { + mc = lst.modCount; + hi = lst.size; + } + else + mc = expectedModCount; + if ((i = index) >= 0 && (index = hi) <= a.length) { + for (; i < hi; ++i) { + @SuppressWarnings("unchecked") E e = (E) a[i]; + action.accept(e); + } + if (lst.modCount == mc) + return; + } + } + throw new ConcurrentModificationException(); + } + + public long estimateSize() { + return (long) (getFence() - index); + } + + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; + } + } + + @Override + public boolean removeIf(Predicate filter) { + Objects.requireNonNull(filter); + // figure out which elements are to be removed + // any exception thrown from the filter predicate at this stage + // will leave the collection unmodified + int removeCount = 0; + final BitSet removeSet = new BitSet(size); + final int expectedModCount = modCount; + final int size = this.size; + for (int i=0; modCount == expectedModCount && i < size; i++) { + @SuppressWarnings("unchecked") + final E element = (E) elementData[i]; + if (filter.test(element)) { + removeSet.set(i); + removeCount++; + } + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + // shift surviving elements left over the spaces left by removed elements + final boolean anyToRemove = removeCount > 0; + if (anyToRemove) { + final int newSize = size - removeCount; + for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) { + i = removeSet.nextClearBit(i); + elementData[j] = elementData[i]; + } + for (int k=newSize; k < size; k++) { + elementData[k] = null; // Let gc do its work + } + this.size = newSize; + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } + + return anyToRemove; + } + + @Override + @SuppressWarnings("unchecked") + public void replaceAll(UnaryOperator operator) { + Objects.requireNonNull(operator); + final int expectedModCount = modCount; + final int size = this.size; + for (int i=0; modCount == expectedModCount && i < size; i++) { + elementData[i] = operator.apply((E) elementData[i]); + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } + + @Override + @SuppressWarnings("unchecked") + public void sort(Comparator c) { + final int expectedModCount = modCount; + Arrays.sort((E[]) elementData, 0, size, c); + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; } } diff --git a/jdk/src/share/classes/java/util/Collection.java b/jdk/src/share/classes/java/util/Collection.java index a09e8c362d9..966324780de 100644 --- a/jdk/src/share/classes/java/util/Collection.java +++ b/jdk/src/share/classes/java/util/Collection.java @@ -25,6 +25,8 @@ package java.util; +import java.util.function.Predicate; + /** * The root interface in the collection hierarchy. A collection * represents a group of objects, known as its elements. Some @@ -372,6 +374,40 @@ public interface Collection extends Iterable { */ boolean removeAll(Collection c); + /** + * Removes all of the elements of this collection that satisfy the given + * predicate. Errors or runtime exceptions thrown by the predicate are + * relayed to the caller. + * + * @implSpec + * The default implementation traverses all elements of the collection using + * its {@link #iterator}. Each matching element is removed using + * {@link Iterator#remove()}. If the collection's iterator does not + * support removal then an {@code UnsupportedOperationException} will be + * thrown on the first matching element. + * + * @param filter a predicate which returns {@code true} for elements to be + * removed + * @return {@code true} if any elements were removed + * @throws NullPointerException if the specified filter is null + * @throws UnsupportedOperationException if the {@code remove} + * method is not supported by this collection's + * {@link #iterator} + * @since 1.8 + */ + default boolean removeIf(Predicate filter) { + Objects.requireNonNull(filter); + boolean removed = false; + final Iterator each = iterator(); + while (each.hasNext()) { + if (filter.test(each.next())) { + each.remove(); + removed = true; + } + } + return removed; + } + /** * Retains only the elements in this collection that are contained in the * specified collection (optional operation). In other words, removes from diff --git a/jdk/src/share/classes/java/util/Collections.java b/jdk/src/share/classes/java/util/Collections.java index ad36637452b..30d589cdc7d 100644 --- a/jdk/src/share/classes/java/util/Collections.java +++ b/jdk/src/share/classes/java/util/Collections.java @@ -30,7 +30,10 @@ import java.io.IOException; import java.lang.reflect.Array; import java.util.function.BiConsumer; import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; /** * This class consists exclusively of static methods that operate on or return @@ -1085,6 +1088,11 @@ public class Collections { public void remove() { throw new UnsupportedOperationException(); } + @Override + public void forEachRemaining(Consumer action) { + // Use backing collection version + i.forEachRemaining(action); + } }; } @@ -1110,6 +1118,21 @@ public class Collections { public void clear() { throw new UnsupportedOperationException(); } + + // Override default methods in Collection + @Override + public void forEach(Consumer action) { + c.forEach(action); + } + @Override + public boolean removeIf(Predicate filter) { + throw new UnsupportedOperationException(); + } + @Override + public Spliterator spliterator() { + return (Spliterator)c.spliterator(); + } + } /** @@ -1240,6 +1263,16 @@ public class Collections { public boolean addAll(int index, Collection c) { throw new UnsupportedOperationException(); } + + @Override + public void replaceAll(UnaryOperator operator) { + throw new UnsupportedOperationException(); + } + @Override + public void sort(Comparator c) { + throw new UnsupportedOperationException(); + } + public ListIterator listIterator() {return listIterator(0);} public ListIterator listIterator(final int index) { @@ -1263,6 +1296,11 @@ public class Collections { public void add(E e) { throw new UnsupportedOperationException(); } + + @Override + public void forEachRemaining(Consumer action) { + i.forEachRemaining(action); + } }; } @@ -1642,7 +1680,8 @@ public class Collections { * through the returned collection.

* * It is imperative that the user manually synchronize on the returned - * collection when iterating over it: + * collection when traversing it via {@link Iterator} or + * {@link Spliterator}: *

      *  Collection c = Collections.synchronizedCollection(myCollection);
      *     ...
@@ -1739,6 +1778,19 @@ public class Collections {
         public String toString() {
             synchronized (mutex) {return c.toString();}
         }
+        // Override default methods in Collection
+        @Override
+        public void forEach(Consumer consumer) {
+            synchronized (mutex) {c.forEach(consumer);}
+        }
+        @Override
+        public boolean removeIf(Predicate filter) {
+            synchronized (mutex) {return c.removeIf(filter);}
+        }
+        @Override
+        public Spliterator spliterator() {
+            return c.spliterator(); // Must be manually synched by user!
+        }
         private void writeObject(ObjectOutputStream s) throws IOException {
             synchronized (mutex) {s.defaultWriteObject();}
         }
@@ -1996,6 +2048,15 @@ public class Collections {
             }
         }
 
+        @Override
+        public void replaceAll(UnaryOperator operator) {
+            synchronized (mutex) {list.replaceAll(operator);}
+        }
+        @Override
+        public void sort(Comparator c) {
+            synchronized (mutex) {list.sort(c);}
+        }
+
         /**
          * SynchronizedRandomAccessList instances are serialized as
          * SynchronizedList instances to allow them to be deserialized
@@ -2492,6 +2553,16 @@ public class Collections {
             // element as we added it)
             return c.addAll(checkedCopyOf(coll));
         }
+
+        // Override default methods in Collection
+        @Override
+        public void forEach(Consumer action) {c.forEach(action);}
+        @Override
+        public boolean removeIf(Predicate filter) {
+            return c.removeIf(filter);
+        }
+        @Override
+        public Spliterator spliterator() {return c.spliterator();}
     }
 
     /**
@@ -2747,12 +2818,26 @@ public class Collections {
                     typeCheck(e);
                     i.add(e);
                 }
+
+                @Override
+                public void forEachRemaining(Consumer action) {
+                    i.forEachRemaining(action);
+                }
             };
         }
 
         public List subList(int fromIndex, int toIndex) {
             return new CheckedList<>(list.subList(fromIndex, toIndex), type);
         }
+
+        @Override
+        public void replaceAll(UnaryOperator operator) {
+            list.replaceAll(operator);
+        }
+        @Override
+        public void sort(Comparator c) {
+            list.sort(c);
+        }
     }
 
     /**
@@ -3276,6 +3361,10 @@ public class Collections {
         public boolean hasNext() { return false; }
         public E next() { throw new NoSuchElementException(); }
         public void remove() { throw new IllegalStateException(); }
+        @Override
+        public void forEachRemaining(Consumer action) {
+            Objects.requireNonNull(action);
+        }
     }
 
     /**
@@ -3416,6 +3505,19 @@ public class Collections {
             return a;
         }
 
+        // Override default methods in Collection
+        @Override
+        public void forEach(Consumer action) {
+            Objects.requireNonNull(action);
+        }
+        @Override
+        public boolean removeIf(Predicate filter) {
+            Objects.requireNonNull(filter);
+            return false;
+        }
+        @Override
+        public Spliterator spliterator() { return Spliterators.emptySpliterator(); }
+
         // Preserves singleton property
         private Object readResolve() {
             return EMPTY_SET;
@@ -3523,6 +3625,21 @@ public class Collections {
         public E last() {
             throw new NoSuchElementException();
         }
+
+        // Override default methods in Collection
+        @Override
+        public void forEach(Consumer action) {
+            Objects.requireNonNull(action);
+        }
+
+        @Override
+        public boolean removeIf(Predicate filter) {
+            Objects.requireNonNull(filter);
+            return false;
+        }
+
+        @Override
+        public Spliterator spliterator() { return Spliterators.emptySpliterator(); }
     }
 
     /**
@@ -3592,6 +3709,29 @@ public class Collections {
 
         public int hashCode() { return 1; }
 
+        @Override
+        public boolean removeIf(Predicate filter) {
+            Objects.requireNonNull(filter);
+            return false;
+        }
+        @Override
+        public void replaceAll(UnaryOperator operator) {
+            Objects.requireNonNull(operator);
+        }
+        @Override
+        public void sort(Comparator c) {
+            Objects.requireNonNull(c);
+        }
+
+        // Override default methods in Collection
+        @Override
+        public void forEach(Consumer action) {
+            Objects.requireNonNull(action);
+        }
+
+        @Override
+        public Spliterator spliterator() { return Spliterators.emptySpliterator(); }
+
         // Preserves singleton property
         private Object readResolve() {
             return EMPTY_LIST;
@@ -3747,6 +3887,60 @@ public class Collections {
             public void remove() {
                 throw new UnsupportedOperationException();
             }
+            @Override
+            public void forEachRemaining(Consumer action) {
+                Objects.requireNonNull(action);
+                if (hasNext) {
+                    action.accept(e);
+                    hasNext = false;
+                }
+            }
+        };
+    }
+
+    /**
+     * Creates a {@code Spliterator} with only the specified element
+     *
+     * @param  Type of elements
+     * @return A singleton {@code Spliterator}
+     */
+    static  Spliterator singletonSpliterator(final T element) {
+        return new Spliterator() {
+            long est = 1;
+
+            @Override
+            public Spliterator trySplit() {
+                return null;
+            }
+
+            @Override
+            public boolean tryAdvance(Consumer consumer) {
+                Objects.requireNonNull(consumer);
+                if (est > 0) {
+                    est--;
+                    consumer.accept(element);
+                    return true;
+                }
+                return false;
+            }
+
+            @Override
+            public void forEachRemaining(Consumer consumer) {
+                tryAdvance(consumer);
+            }
+
+            @Override
+            public long estimateSize() {
+                return est;
+            }
+
+            @Override
+            public int characteristics() {
+                int value = (element != null) ? Spliterator.NONNULL : 0;
+
+                return value | Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.IMMUTABLE |
+                       Spliterator.DISTINCT | Spliterator.ORDERED;
+            }
         };
     }
 
@@ -3770,6 +3964,20 @@ public class Collections {
         public int size() {return 1;}
 
         public boolean contains(Object o) {return eq(o, element);}
+
+        // Override default methods for Collection
+        @Override
+        public void forEach(Consumer action) {
+            action.accept(element);
+        }
+        @Override
+        public Spliterator spliterator() {
+            return singletonSpliterator(element);
+        }
+        @Override
+        public boolean removeIf(Predicate filter) {
+            throw new UnsupportedOperationException();
+        }
     }
 
     /**
@@ -3810,6 +4018,27 @@ public class Collections {
               throw new IndexOutOfBoundsException("Index: "+index+", Size: 1");
             return element;
         }
+
+        // Override default methods for Collection
+        @Override
+        public void forEach(Consumer action) {
+            action.accept(element);
+        }
+        @Override
+        public boolean removeIf(Predicate filter) {
+            throw new UnsupportedOperationException();
+        }
+        @Override
+        public void replaceAll(UnaryOperator operator) {
+            throw new UnsupportedOperationException();
+        }
+        @Override
+        public void sort(Comparator c) {
+        }
+        @Override
+        public Spliterator spliterator() {
+            return singletonSpliterator(element);
+        }
     }
 
     /**
@@ -4408,6 +4637,19 @@ public class Collections {
         public boolean retainAll(Collection c)   {return s.retainAll(c);}
         // addAll is the only inherited implementation
 
+        // Override default methods in Collection
+        @Override
+        public void forEach(Consumer action) {
+            s.forEach(action);
+        }
+        @Override
+        public boolean removeIf(Predicate filter) {
+            return s.removeIf(filter);
+        }
+
+        @Override
+        public Spliterator spliterator() {return s.spliterator();}
+
         private static final long serialVersionUID = 2454657854757543876L;
 
         private void readObject(java.io.ObjectInputStream stream)
@@ -4466,5 +4708,15 @@ public class Collections {
         public boolean removeAll(Collection c)   {return q.removeAll(c);}
         public boolean retainAll(Collection c)   {return q.retainAll(c);}
         // We use inherited addAll; forwarding addAll would be wrong
+
+        // Override default methods in Collection
+        @Override
+        public void forEach(Consumer action) {q.forEach(action);}
+        @Override
+        public Spliterator spliterator() {return q.spliterator();}
+        @Override
+        public boolean removeIf(Predicate filter) {
+            return q.removeIf(filter);
+        }
     }
 }
diff --git a/jdk/src/share/classes/java/util/Currency.java b/jdk/src/share/classes/java/util/Currency.java
index 09e7046849b..94b72dc4285 100644
--- a/jdk/src/share/classes/java/util/Currency.java
+++ b/jdk/src/share/classes/java/util/Currency.java
@@ -47,9 +47,8 @@ import sun.util.logging.PlatformLogger;
 
 /**
  * Represents a currency. Currencies are identified by their ISO 4217 currency
- * codes. Visit the 
- * ISO web site for more information, including a table of
- * currency codes.
+ * codes. Visit the 
+ * ISO web site for more information.
  * 

* The class is designed so that there's never more than one * Currency instance for any given currency. Therefore, there's diff --git a/jdk/src/share/classes/java/util/HashMap.java b/jdk/src/share/classes/java/util/HashMap.java index b40c5c92ffa..5e79498da9f 100644 --- a/jdk/src/share/classes/java/util/HashMap.java +++ b/jdk/src/share/classes/java/util/HashMap.java @@ -1230,6 +1230,14 @@ public class HashMap public void clear() { HashMap.this.clear(); } + + public Spliterator spliterator() { + if (HashMap.this.getClass() == HashMap.class) + return new KeySpliterator(HashMap.this, 0, -1, 0, 0); + else + return Spliterators.spliterator + (this, Spliterator.SIZED | Spliterator.DISTINCT); + } } /** @@ -1263,6 +1271,14 @@ public class HashMap public void clear() { HashMap.this.clear(); } + + public Spliterator spliterator() { + if (HashMap.this.getClass() == HashMap.class) + return new ValueSpliterator(HashMap.this, 0, -1, 0, 0); + else + return Spliterators.spliterator + (this, Spliterator.SIZED); + } } /** @@ -1310,6 +1326,14 @@ public class HashMap public void clear() { HashMap.this.clear(); } + + public Spliterator> spliterator() { + if (HashMap.this.getClass() == HashMap.class) + return new EntrySpliterator(HashMap.this, 0, -1, 0, 0); + else + return Spliterators.spliterator + (this, Spliterator.SIZED | Spliterator.DISTINCT); + } } /** @@ -1406,4 +1430,257 @@ public class HashMap // These methods are used when serializing HashSets int capacity() { return table.length; } float loadFactor() { return loadFactor; } + + /** + * Standin until HM overhaul; based loosely on Weak and Identity HM. + */ + static class HashMapSpliterator { + final HashMap map; + HashMap.Entry current; // current node + int index; // current index, modified on advance/split + int fence; // one past last index + int est; // size estimate + int expectedModCount; // for comodification checks + + HashMapSpliterator(HashMap m, int origin, + int fence, int est, + int expectedModCount) { + this.map = m; + this.index = origin; + this.fence = fence; + this.est = est; + this.expectedModCount = expectedModCount; + } + + final int getFence() { // initialize fence and size on first use + int hi; + if ((hi = fence) < 0) { + HashMap m = map; + est = m.size; + expectedModCount = m.modCount; + hi = fence = m.table.length; + } + return hi; + } + + public final long estimateSize() { + getFence(); // force init + return (long) est; + } + } + + static final class KeySpliterator + extends HashMapSpliterator + implements Spliterator { + KeySpliterator(HashMap m, int origin, int fence, int est, + int expectedModCount) { + super(m, origin, fence, est, expectedModCount); + } + + public KeySpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + return (lo >= mid || current != null) ? null : + new KeySpliterator(map, lo, index = mid, est >>>= 1, + expectedModCount); + } + + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer action) { + int i, hi, mc; + if (action == null) + throw new NullPointerException(); + HashMap m = map; + HashMap.Entry[] tab = (HashMap.Entry[])m.table; + if ((hi = fence) < 0) { + mc = expectedModCount = m.modCount; + hi = fence = tab.length; + } + else + mc = expectedModCount; + if (tab.length >= hi && (i = index) >= 0 && i < (index = hi)) { + HashMap.Entry p = current; + do { + if (p == null) + p = tab[i++]; + else { + action.accept(p.getKey()); + p = p.next; + } + } while (p != null || i < hi); + if (m.modCount != mc) + throw new ConcurrentModificationException(); + } + } + + @SuppressWarnings("unchecked") + public boolean tryAdvance(Consumer action) { + int hi; + if (action == null) + throw new NullPointerException(); + HashMap.Entry[] tab = (HashMap.Entry[])map.table; + if (tab.length >= (hi = getFence()) && index >= 0) { + while (current != null || index < hi) { + if (current == null) + current = tab[index++]; + else { + K k = current.getKey(); + current = current.next; + action.accept(k); + if (map.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + } + } + return false; + } + + public int characteristics() { + return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) | + Spliterator.DISTINCT; + } + } + + static final class ValueSpliterator + extends HashMapSpliterator + implements Spliterator { + ValueSpliterator(HashMap m, int origin, int fence, int est, + int expectedModCount) { + super(m, origin, fence, est, expectedModCount); + } + + public ValueSpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + return (lo >= mid || current != null) ? null : + new ValueSpliterator(map, lo, index = mid, est >>>= 1, + expectedModCount); + } + + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer action) { + int i, hi, mc; + if (action == null) + throw new NullPointerException(); + HashMap m = map; + HashMap.Entry[] tab = (HashMap.Entry[])m.table; + if ((hi = fence) < 0) { + mc = expectedModCount = m.modCount; + hi = fence = tab.length; + } + else + mc = expectedModCount; + if (tab.length >= hi && (i = index) >= 0 && i < (index = hi)) { + HashMap.Entry p = current; + do { + if (p == null) + p = tab[i++]; + else { + action.accept(p.getValue()); + p = p.next; + } + } while (p != null || i < hi); + if (m.modCount != mc) + throw new ConcurrentModificationException(); + } + } + + @SuppressWarnings("unchecked") + public boolean tryAdvance(Consumer action) { + int hi; + if (action == null) + throw new NullPointerException(); + HashMap.Entry[] tab = (HashMap.Entry[])map.table; + if (tab.length >= (hi = getFence()) && index >= 0) { + while (current != null || index < hi) { + if (current == null) + current = tab[index++]; + else { + V v = current.getValue(); + current = current.next; + action.accept(v); + if (map.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + } + } + return false; + } + + public int characteristics() { + return (fence < 0 || est == map.size ? Spliterator.SIZED : 0); + } + } + + static final class EntrySpliterator + extends HashMapSpliterator + implements Spliterator> { + EntrySpliterator(HashMap m, int origin, int fence, int est, + int expectedModCount) { + super(m, origin, fence, est, expectedModCount); + } + + public EntrySpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + return (lo >= mid || current != null) ? null : + new EntrySpliterator(map, lo, index = mid, est >>>= 1, + expectedModCount); + } + + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer> action) { + int i, hi, mc; + if (action == null) + throw new NullPointerException(); + HashMap m = map; + HashMap.Entry[] tab = (HashMap.Entry[])m.table; + if ((hi = fence) < 0) { + mc = expectedModCount = m.modCount; + hi = fence = tab.length; + } + else + mc = expectedModCount; + if (tab.length >= hi && (i = index) >= 0 && i < (index = hi)) { + HashMap.Entry p = current; + do { + if (p == null) + p = tab[i++]; + else { + action.accept(p); + p = p.next; + } + } while (p != null || i < hi); + if (m.modCount != mc) + throw new ConcurrentModificationException(); + } + } + + @SuppressWarnings("unchecked") + public boolean tryAdvance(Consumer> action) { + int hi; + if (action == null) + throw new NullPointerException(); + HashMap.Entry[] tab = (HashMap.Entry[])map.table; + if (tab.length >= (hi = getFence()) && index >= 0) { + while (current != null || index < hi) { + if (current == null) + current = tab[index++]; + else { + HashMap.Entry e = current; + current = current.next; + action.accept(e); + if (map.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + } + } + return false; + } + + public int characteristics() { + return (fence < 0 || est == map.size ? Spliterator.SIZED : 0) | + Spliterator.DISTINCT; + } + } } diff --git a/jdk/src/share/classes/java/util/HashSet.java b/jdk/src/share/classes/java/util/HashSet.java index f7e987143fc..002b80cbe68 100644 --- a/jdk/src/share/classes/java/util/HashSet.java +++ b/jdk/src/share/classes/java/util/HashSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -311,4 +311,8 @@ public class HashSet map.put(e, PRESENT); } } + + public Spliterator spliterator() { + return new HashMap.KeySpliterator(map, 0, -1, 0, 0); + } } diff --git a/jdk/src/share/classes/java/util/IdentityHashMap.java b/jdk/src/share/classes/java/util/IdentityHashMap.java index 49bb9943ff6..eb54db623e4 100644 --- a/jdk/src/share/classes/java/util/IdentityHashMap.java +++ b/jdk/src/share/classes/java/util/IdentityHashMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, 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 @@ -24,8 +24,10 @@ */ package java.util; + import java.io.*; import java.lang.reflect.Array; +import java.util.function.Consumer; /** * This class implements the Map interface with a hash table, using @@ -162,19 +164,19 @@ public class IdentityHashMap /** * The table, resized as necessary. Length MUST always be a power of two. */ - private transient Object[] table; + transient Object[] table; // non-private to simplify nested class access /** * The number of key-value mappings contained in this identity hash map. * * @serial */ - private int size; + int size; /** * The number of modifications, to support fast-fail iterators */ - private transient int modCount; + transient int modCount; /** * The next size value at which to resize (capacity * load factor). @@ -184,7 +186,7 @@ public class IdentityHashMap /** * Value representing null keys inside tables. */ - private static final Object NULL_KEY = new Object(); + static final Object NULL_KEY = new Object(); /** * Use NULL_KEY for key if it is null. @@ -196,7 +198,7 @@ public class IdentityHashMap /** * Returns internal representation of null key back to caller as null. */ - private static Object unmaskNull(Object key) { + static final Object unmaskNull(Object key) { return (key == NULL_KEY ? null : key); } @@ -1012,7 +1014,7 @@ public class IdentityHashMap return result; } public Object[] toArray() { - return toArray(new Object[size()]); + return toArray(new Object[0]); } @SuppressWarnings("unchecked") public T[] toArray(T[] a) { @@ -1042,6 +1044,10 @@ public class IdentityHashMap } return a; } + + public Spliterator spliterator() { + return new KeySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0); + } } /** @@ -1095,7 +1101,7 @@ public class IdentityHashMap IdentityHashMap.this.clear(); } public Object[] toArray() { - return toArray(new Object[size()]); + return toArray(new Object[0]); } @SuppressWarnings("unchecked") public T[] toArray(T[] a) { @@ -1124,6 +1130,10 @@ public class IdentityHashMap } return a; } + + public Spliterator spliterator() { + return new ValueSpliterator<>(IdentityHashMap.this, 0, -1, 0, 0); + } } /** @@ -1211,7 +1221,7 @@ public class IdentityHashMap } public Object[] toArray() { - return toArray(new Object[size()]); + return toArray(new Object[0]); } @SuppressWarnings("unchecked") @@ -1242,6 +1252,10 @@ public class IdentityHashMap } return a; } + + public Spliterator> spliterator() { + return new EntrySpliterator<>(IdentityHashMap.this, 0, -1, 0, 0); + } } @@ -1322,4 +1336,223 @@ public class IdentityHashMap tab[i] = k; tab[i + 1] = value; } + + /** + * Similar form as array-based Spliterators, but skips blank elements, + * and guestimates size as decreasing by half per split. + */ + static class IdentityHashMapSpliterator { + final IdentityHashMap map; + int index; // current index, modified on advance/split + int fence; // -1 until first use; then one past last index + int est; // size estimate + int expectedModCount; // initialized when fence set + + IdentityHashMapSpliterator(IdentityHashMap map, int origin, + int fence, int est, int expectedModCount) { + this.map = map; + this.index = origin; + this.fence = fence; + this.est = est; + this.expectedModCount = expectedModCount; + } + + final int getFence() { // initialize fence and size on first use + int hi; + if ((hi = fence) < 0) { + est = map.size; + expectedModCount = map.modCount; + hi = fence = map.table.length; + } + return hi; + } + + public final long estimateSize() { + getFence(); // force init + return (long) est; + } + } + + static final class KeySpliterator + extends IdentityHashMapSpliterator + implements Spliterator { + KeySpliterator(IdentityHashMap map, int origin, int fence, int est, + int expectedModCount) { + super(map, origin, fence, est, expectedModCount); + } + + public KeySpliterator trySplit() { + int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1; + return (lo >= mid) ? null : + new KeySpliterator(map, lo, index = mid, est >>>= 1, + expectedModCount); + } + + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer action) { + if (action == null) + throw new NullPointerException(); + int i, hi, mc; Object key; + IdentityHashMap m; Object[] a; + if ((m = map) != null && (a = m.table) != null && + (i = index) >= 0 && (index = hi = getFence()) <= a.length) { + for (; i < hi; i += 2) { + if ((key = a[i]) != null) + action.accept((K)unmaskNull(key)); + } + if (m.modCount == expectedModCount) + return; + } + throw new ConcurrentModificationException(); + } + + @SuppressWarnings("unchecked") + public boolean tryAdvance(Consumer action) { + if (action == null) + throw new NullPointerException(); + Object[] a = map.table; + int hi = getFence(); + while (index < hi) { + Object key = a[index]; + index += 2; + if (key != null) { + action.accept((K)unmaskNull(key)); + if (map.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + } + return false; + } + + public int characteristics() { + return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT; + } + } + + static final class ValueSpliterator + extends IdentityHashMapSpliterator + implements Spliterator { + ValueSpliterator(IdentityHashMap m, int origin, int fence, int est, + int expectedModCount) { + super(m, origin, fence, est, expectedModCount); + } + + public ValueSpliterator trySplit() { + int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1; + return (lo >= mid) ? null : + new ValueSpliterator(map, lo, index = mid, est >>>= 1, + expectedModCount); + } + + public void forEachRemaining(Consumer action) { + if (action == null) + throw new NullPointerException(); + int i, hi, mc; + IdentityHashMap m; Object[] a; + if ((m = map) != null && (a = m.table) != null && + (i = index) >= 0 && (index = hi = getFence()) <= a.length) { + for (; i < hi; i += 2) { + if (a[i] != null) { + @SuppressWarnings("unchecked") V v = (V)a[i+1]; + action.accept(v); + } + } + if (m.modCount == expectedModCount) + return; + } + throw new ConcurrentModificationException(); + } + + public boolean tryAdvance(Consumer action) { + if (action == null) + throw new NullPointerException(); + Object[] a = map.table; + int hi = getFence(); + while (index < hi) { + Object key = a[index]; + @SuppressWarnings("unchecked") V v = (V)a[index+1]; + index += 2; + if (key != null) { + action.accept(v); + if (map.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + } + return false; + } + + public int characteristics() { + return (fence < 0 || est == map.size ? SIZED : 0); + } + + } + + static final class EntrySpliterator + extends IdentityHashMapSpliterator + implements Spliterator> { + EntrySpliterator(IdentityHashMap m, int origin, int fence, int est, + int expectedModCount) { + super(m, origin, fence, est, expectedModCount); + } + + public EntrySpliterator trySplit() { + int hi = getFence(), lo = index, mid = ((lo + hi) >>> 1) & ~1; + return (lo >= mid) ? null : + new EntrySpliterator(map, lo, index = mid, est >>>= 1, + expectedModCount); + } + + public void forEachRemaining(Consumer> action) { + if (action == null) + throw new NullPointerException(); + int i, hi, mc; + IdentityHashMap m; Object[] a; + if ((m = map) != null && (a = m.table) != null && + (i = index) >= 0 && (index = hi = getFence()) <= a.length) { + for (; i < hi; i += 2) { + Object key = a[i]; + if (key != null) { + @SuppressWarnings("unchecked") K k = + (K)unmaskNull(key); + @SuppressWarnings("unchecked") V v = (V)a[i+1]; + action.accept + (new AbstractMap.SimpleImmutableEntry(k, v)); + + } + } + if (m.modCount == expectedModCount) + return; + } + throw new ConcurrentModificationException(); + } + + public boolean tryAdvance(Consumer> action) { + if (action == null) + throw new NullPointerException(); + Object[] a = map.table; + int hi = getFence(); + while (index < hi) { + Object key = a[index]; + @SuppressWarnings("unchecked") V v = (V)a[index+1]; + index += 2; + if (key != null) { + @SuppressWarnings("unchecked") K k = + (K)unmaskNull(key); + action.accept + (new AbstractMap.SimpleImmutableEntry(k, v)); + if (map.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + } + return false; + } + + public int characteristics() { + return (fence < 0 || est == map.size ? SIZED : 0) | Spliterator.DISTINCT; + } + } + } diff --git a/jdk/src/share/classes/java/util/LinkedHashSet.java b/jdk/src/share/classes/java/util/LinkedHashSet.java index 399ba80fe99..c66015aac6f 100644 --- a/jdk/src/share/classes/java/util/LinkedHashSet.java +++ b/jdk/src/share/classes/java/util/LinkedHashSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2006, 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 @@ -168,4 +168,18 @@ public class LinkedHashSet super(Math.max(2*c.size(), 11), .75f, true); addAll(c); } + + /** + * Creates a {@code Spliterator}, over the elements in this set, that + * reports {@code SIZED}, {@code DISTINCT} and {@code ORDERED}. + * Overriding implementations are expected to document if the + * {@code Spliterator} reports any additional and relevant characteristic + * values. + * + * @return a {@code Spliterator} over the elements in this set + */ + @Override + public Spliterator spliterator() { + return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED); + } } diff --git a/jdk/src/share/classes/java/util/LinkedList.java b/jdk/src/share/classes/java/util/LinkedList.java index fd8f181f17f..5db6874e255 100644 --- a/jdk/src/share/classes/java/util/LinkedList.java +++ b/jdk/src/share/classes/java/util/LinkedList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -25,6 +25,8 @@ package java.util; +import java.util.function.Consumer; + /** * Doubly-linked list implementation of the {@code List} and {@code Deque} * interfaces. Implements all optional list operations, and permits all @@ -948,6 +950,16 @@ public class LinkedList expectedModCount++; } + public void forEachRemaining(Consumer action) { + Objects.requireNonNull(action); + while (modCount == expectedModCount && nextIndex < size) { + action.accept(next.item); + next = next.next; + nextIndex++; + } + checkForComodification(); + } + final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); @@ -1135,4 +1147,103 @@ public class LinkedList for (int i = 0; i < size; i++) linkLast((E)s.readObject()); } + + public Spliterator spliterator() { + return new LLSpliterator(this, -1, 0); + } + + /** A customized variant of Spliterators.IteratorSpliterator */ + static final class LLSpliterator implements Spliterator { + static final int BATCH_UNIT = 1 << 10; // batch array size increment + static final int MAX_BATCH = 1 << 25; // max batch array size; + final LinkedList list; // null OK unless traversed + Node current; // current node; null until initialized + int est; // size estimate; -1 until first needed + int expectedModCount; // initialized when est set + int batch; // batch size for splits + + LLSpliterator(LinkedList list, int est, int expectedModCount) { + this.list = list; + this.est = est; + this.expectedModCount = expectedModCount; + } + + final int getEst() { + int s; // force initialization + final LinkedList lst; + if ((s = est) < 0) { + if ((lst = list) == null) + s = est = 0; + else { + expectedModCount = lst.modCount; + current = lst.first; + s = est = lst.size; + } + } + return s; + } + + public long estimateSize() { return (long) getEst(); } + + public Spliterator trySplit() { + Node p; + int s = getEst(); + if (s > 1 && (p = current) != null) { + int n = batch + BATCH_UNIT; + if (n > s) + n = s; + if (n > MAX_BATCH) + n = MAX_BATCH; + Object[] a; + try { + a = new Object[n]; + } catch (OutOfMemoryError oome) { + return null; + } + int j = 0; + do { a[j++] = p.item; } while ((p = p.next) != null && j < n); + current = p; + batch = j; + est = s - j; + return Spliterators.spliterator(a, 0, j, Spliterator.ORDERED); + } + return null; + } + + public void forEachRemaining(Consumer action) { + Node p; int n; + if (action == null) throw new NullPointerException(); + if ((n = getEst()) > 0 && (p = current) != null) { + current = null; + est = 0; + do { + E e = p.item; + p = p.next; + action.accept(e); + } while (p != null && --n > 0); + } + if (list.modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + + public boolean tryAdvance(Consumer action) { + Node p; + if (action == null) throw new NullPointerException(); + if (getEst() > 0 && (p = current) != null) { + --est; + E e = p.item; + current = p.next; + action.accept(e); + if (list.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + return false; + } + + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; + } + } + } diff --git a/jdk/src/share/classes/java/util/List.java b/jdk/src/share/classes/java/util/List.java index 734b231646e..daee8acd641 100644 --- a/jdk/src/share/classes/java/util/List.java +++ b/jdk/src/share/classes/java/util/List.java @@ -25,6 +25,8 @@ package java.util; +import java.util.function.UnaryOperator; + /** * An ordered collection (also known as a sequence). The user of this * interface has precise control over where in the list each element is @@ -374,6 +376,64 @@ public interface List extends Collection { */ boolean retainAll(Collection c); + /** + * Replaces each element of this list with the result of applying the + * operator to that element. Errors or runtime exceptions thrown by + * the operator are relayed to the caller. + * + * @implSpec + * The default implementation is equivalent to, for this {@code list}: + *

+     * final ListIterator li = list.listIterator();
+     * while (li.hasNext()) {
+     *   li.set(operator.apply(li.next()));
+     * }
+     * 
+ * If the list's list-iterator does not support the {@code set} operation + * then an {@code UnsupportedOperationException} will be thrown when + * replacing the first element. + * + * @param operator the operator to apply to each element + * @throws UnsupportedOperationException if the {@code set} + * operation is not supported by this list + * @throws NullPointerException if the specified operator is null or + * if the element is replaced with a null value and this list + * does not permit null elements + * (optional) + * @since 1.8 + */ + default void replaceAll(UnaryOperator operator) { + Objects.requireNonNull(operator); + final ListIterator li = this.listIterator(); + while (li.hasNext()) { + li.set(operator.apply(li.next())); + } + } + + /** + * Sorts this list using the supplied {@code Comparator} to compare elements. + * + * @implSpec + * The default implementation is equivalent to, for this {@code list}: + *
Collections.sort(list, c)
+ * + * @param c the {@code Comparator} used to compare list elements. + * A {@code null} value indicates that the elements' + * {@linkplain Comparable natural ordering} should be used + * @throws ClassCastException if the list contains elements that are not + * mutually comparable using the specified comparator + * @throws UnsupportedOperationException if the list's list-iterator does + * not support the {@code set} operation + * @throws IllegalArgumentException + * (optional) + * if the comparator is found to violate the {@link Comparator} + * contract + * @since 1.8 + */ + default void sort(Comparator c) { + Collections.sort(this, c); + } + /** * Removes all of the elements from this list (optional operation). * The list will be empty after this call returns. diff --git a/jdk/src/share/classes/java/util/PriorityQueue.java b/jdk/src/share/classes/java/util/PriorityQueue.java index dd67a0c5ba4..c28647b58bb 100644 --- a/jdk/src/share/classes/java/util/PriorityQueue.java +++ b/jdk/src/share/classes/java/util/PriorityQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package java.util; +import java.util.function.Consumer; + /** * An unbounded priority {@linkplain Queue queue} based on a priority heap. * The elements of the priority queue are ordered according to their @@ -56,7 +58,7 @@ package java.util; * the priority queue in any particular order. If you need ordered * traversal, consider using {@code Arrays.sort(pq.toArray())}. * - *

Note that this implementation is not synchronized. + *

Note that this implementation is not synchronized. * Multiple threads should not access a {@code PriorityQueue} * instance concurrently if any of the threads modifies the queue. * Instead, use the thread-safe {@link @@ -92,7 +94,7 @@ public class PriorityQueue extends AbstractQueue * heap and each descendant d of n, n <= d. The element with the * lowest value is in queue[0], assuming the queue is nonempty. */ - private transient Object[] queue; + transient Object[] queue; // non-private to simplify nested class access /** * The number of elements in the priority queue. @@ -109,7 +111,7 @@ public class PriorityQueue extends AbstractQueue * The number of times this priority queue has been * structurally modified. See AbstractList for gory details. */ - private transient int modCount = 0; + transient int modCount = 0; // non-private to simplify nested class access /** * Creates a {@code PriorityQueue} with the default initial @@ -332,9 +334,7 @@ public class PriorityQueue extends AbstractQueue @SuppressWarnings("unchecked") public E peek() { - if (size == 0) - return null; - return (E) queue[0]; + return (size == 0) ? null : (E) queue[0]; } private int indexOf(Object o) { @@ -431,15 +431,14 @@ public class PriorityQueue extends AbstractQueue * precise control over the runtime type of the output array, and may, * under certain circumstances, be used to save allocation costs. * - *

Suppose x is a queue known to contain only strings. + *

Suppose {@code x} is a queue known to contain only strings. * The following code can be used to dump the queue into a newly - * allocated array of String: + * allocated array of {@code String}: * - *

-     *     String[] y = x.toArray(new String[0]);
+ *
 {@code String[] y = x.toArray(new String[0]);}
* - * Note that toArray(new Object[0]) is identical in function to - * toArray(). + * Note that {@code toArray(new Object[0])} is identical in function to + * {@code toArray()}. * * @param a the array into which the elements of the queue are to * be stored, if it is big enough; otherwise, a new array of the @@ -452,6 +451,7 @@ public class PriorityQueue extends AbstractQueue */ @SuppressWarnings("unchecked") public T[] toArray(T[] a) { + final int size = this.size; if (a.length < size) // Make a new array of a's runtime type, but my contents: return (T[]) Arrays.copyOf(queue, size, a.getClass()); @@ -569,15 +569,14 @@ public class PriorityQueue extends AbstractQueue size = 0; } + @SuppressWarnings("unchecked") public E poll() { if (size == 0) return null; int s = --size; modCount++; - @SuppressWarnings("unchecked") - E result = (E) queue[0]; - @SuppressWarnings("unchecked") - E x = (E) queue[s]; + E result = (E) queue[0]; + E x = (E) queue[s]; queue[s] = null; if (s != 0) siftDown(0, x); @@ -596,15 +595,15 @@ public class PriorityQueue extends AbstractQueue * position before i. This fact is used by iterator.remove so as to * avoid missing traversing elements. */ + @SuppressWarnings("unchecked") private E removeAt(int i) { - assert i >= 0 && i < size; + // assert i >= 0 && i < size; modCount++; int s = --size; if (s == i) // removed last element queue[i] = null; else { - @SuppressWarnings("unchecked") - E moved = (E) queue[s]; + E moved = (E) queue[s]; queue[s] = null; siftDown(i, moved); if (queue[i] == moved) { @@ -649,12 +648,12 @@ public class PriorityQueue extends AbstractQueue queue[k] = key; } + @SuppressWarnings("unchecked") private void siftUpUsingComparator(int k, E x) { while (k > 0) { int parent = (k - 1) >>> 1; - @SuppressWarnings("unchecked") - E e = (E) queue[parent]; - if (comparator.compare(x, e) >= 0) + Object e = queue[parent]; + if (comparator.compare(x, (E) e) >= 0) break; queue[k] = e; k = parent; @@ -738,8 +737,7 @@ public class PriorityQueue extends AbstractQueue } /** - * Saves the state of the instance to a stream (that - * is, serializes it). + * Saves this queue to a stream (that is, serializes it). * * @serialData The length of the array backing the instance is * emitted (int), followed by all of its elements @@ -747,7 +745,7 @@ public class PriorityQueue extends AbstractQueue * @param s the stream */ private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException{ + throws java.io.IOException { // Write out element count, and any hidden stuff s.defaultWriteObject(); @@ -783,4 +781,99 @@ public class PriorityQueue extends AbstractQueue // spec has never explained what that might be. heapify(); } + + public final Spliterator spliterator() { + return new PriorityQueueSpliterator(this, 0, -1, 0); + } + + static final class PriorityQueueSpliterator implements Spliterator { + /* + * This is very similar to ArrayList Spliterator, except for + * extra null checks. + */ + private final PriorityQueue pq; + private int index; // current index, modified on advance/split + private int fence; // -1 until first use + private int expectedModCount; // initialized when fence set + + /** Creates new spliterator covering the given range */ + PriorityQueueSpliterator(PriorityQueue pq, int origin, int fence, + int expectedModCount) { + this.pq = pq; + this.index = origin; + this.fence = fence; + this.expectedModCount = expectedModCount; + } + + private int getFence() { // initialize fence to size on first use + int hi; + if ((hi = fence) < 0) { + expectedModCount = pq.modCount; + hi = fence = pq.size; + } + return hi; + } + + public PriorityQueueSpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + return (lo >= mid) ? null : + new PriorityQueueSpliterator(pq, lo, index = mid, + expectedModCount); + } + + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer action) { + int i, hi, mc; // hoist accesses and checks from loop + PriorityQueue q; Object[] a; + if (action == null) + throw new NullPointerException(); + if ((q = pq) != null && (a = q.queue) != null) { + if ((hi = fence) < 0) { + mc = q.modCount; + hi = q.size; + } + else + mc = expectedModCount; + if ((i = index) >= 0 && (index = hi) <= a.length) { + for (E e;; ++i) { + if (i < hi) { + if ((e = (E) a[i]) == null) // must be CME + break; + action.accept(e); + } + else if (q.modCount != mc) + break; + else + return; + } + } + } + throw new ConcurrentModificationException(); + } + + public boolean tryAdvance(Consumer action) { + if (action == null) + throw new NullPointerException(); + int hi = getFence(), lo = index; + if (lo >= 0 && lo < hi) { + index = lo + 1; + @SuppressWarnings("unchecked") E e = (E)pq.queue[lo]; + if (e == null) + throw new ConcurrentModificationException(); + action.accept(e); + if (pq.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + return false; + } + + public long estimateSize() { + return (long) (getFence() - index); + } + + public int characteristics() { + return Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.NONNULL; + } + } } diff --git a/jdk/src/share/classes/java/util/TreeMap.java b/jdk/src/share/classes/java/util/TreeMap.java index 14d75e7187b..c96fa41652d 100644 --- a/jdk/src/share/classes/java/util/TreeMap.java +++ b/jdk/src/share/classes/java/util/TreeMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -25,6 +25,8 @@ package java.util; +import java.util.function.Consumer; + /** * A Red-Black tree based {@link NavigableMap} implementation. * The map is sorted according to the {@linkplain Comparable natural @@ -971,6 +973,10 @@ public class TreeMap public void clear() { TreeMap.this.clear(); } + + public Spliterator spliterator() { + return new ValueSpliterator(TreeMap.this, null, null, 0, -1, 0); + } } class EntrySet extends AbstractSet> { @@ -1007,6 +1013,10 @@ public class TreeMap public void clear() { TreeMap.this.clear(); } + + public Spliterator> spliterator() { + return new EntrySpliterator(TreeMap.this, null, null, 0, -1, 0); + } } /* @@ -1090,6 +1100,10 @@ public class TreeMap public NavigableSet descendingSet() { return new KeySet<>(m.descendingMap()); } + + public Spliterator spliterator() { + return keySpliteratorFor(m); + } } /** @@ -1389,6 +1403,8 @@ public class TreeMap /** Returns ascending iterator from the perspective of this submap */ abstract Iterator keyIterator(); + abstract Spliterator keySpliterator(); + /** Returns descending iterator from the perspective of this submap */ abstract Iterator descendingKeyIterator(); @@ -1650,19 +1666,6 @@ public class TreeMap } } - final class SubMapKeyIterator extends SubMapIterator { - SubMapKeyIterator(TreeMap.Entry first, - TreeMap.Entry fence) { - super(first, fence); - } - public K next() { - return nextEntry().key; - } - public void remove() { - removeAscending(); - } - } - final class DescendingSubMapEntryIterator extends SubMapIterator> { DescendingSubMapEntryIterator(TreeMap.Entry last, TreeMap.Entry fence) { @@ -1677,7 +1680,47 @@ public class TreeMap } } - final class DescendingSubMapKeyIterator extends SubMapIterator { + // Implement minimal Spliterator as KeySpliterator backup + final class SubMapKeyIterator extends SubMapIterator + implements Spliterator { + SubMapKeyIterator(TreeMap.Entry first, + TreeMap.Entry fence) { + super(first, fence); + } + public K next() { + return nextEntry().key; + } + public void remove() { + removeAscending(); + } + public Spliterator trySplit() { + return null; + } + public void forEachRemaining(Consumer action) { + while (hasNext()) + action.accept(next()); + } + public boolean tryAdvance(Consumer action) { + if (hasNext()) { + action.accept(next()); + return true; + } + return false; + } + public long estimateSize() { + return Long.MAX_VALUE; + } + public int characteristics() { + return Spliterator.DISTINCT | Spliterator.ORDERED | + Spliterator.SORTED; + } + public final Comparator getComparator() { + return NavigableSubMap.this.comparator(); + } + } + + final class DescendingSubMapKeyIterator extends SubMapIterator + implements Spliterator { DescendingSubMapKeyIterator(TreeMap.Entry last, TreeMap.Entry fence) { super(last, fence); @@ -1688,6 +1731,26 @@ public class TreeMap public void remove() { removeDescending(); } + public Spliterator trySplit() { + return null; + } + public void forEachRemaining(Consumer action) { + while (hasNext()) + action.accept(next()); + } + public boolean tryAdvance(Consumer action) { + if (hasNext()) { + action.accept(next()); + return true; + } + return false; + } + public long estimateSize() { + return Long.MAX_VALUE; + } + public int characteristics() { + return Spliterator.DISTINCT | Spliterator.ORDERED; + } } } @@ -1747,6 +1810,10 @@ public class TreeMap return new SubMapKeyIterator(absLowest(), absHighFence()); } + Spliterator keySpliterator() { + return new SubMapKeyIterator(absLowest(), absHighFence()); + } + Iterator descendingKeyIterator() { return new DescendingSubMapKeyIterator(absHighest(), absLowFence()); } @@ -1828,6 +1895,10 @@ public class TreeMap return new DescendingSubMapKeyIterator(absHighest(), absLowFence()); } + Spliterator keySpliterator() { + return new DescendingSubMapKeyIterator(absHighest(), absLowFence()); + } + Iterator descendingKeyIterator() { return new SubMapKeyIterator(absLowest(), absHighFence()); } @@ -2444,4 +2515,407 @@ public class TreeMap level++; return level; } + + /** + * Currently, we support Spliterator-based versions only for the + * full map, in either plain of descending form, otherwise relying + * on defaults because size estimation for submaps would dominate + * costs. The type tests needed to check these for key views are + * not very nice but avoid disrupting existing class + * structures. Callers must use plain default spliterators if this + * returns null. + */ + static Spliterator keySpliteratorFor(NavigableMap m) { + if (m instanceof TreeMap) { + @SuppressWarnings("unchecked") TreeMap t = + (TreeMap) m; + return t.keySpliterator(); + } + if (m instanceof DescendingSubMap) { + @SuppressWarnings("unchecked") DescendingSubMap dm = + (DescendingSubMap) m; + TreeMap tm = dm.m; + if (dm == tm.descendingMap) { + @SuppressWarnings("unchecked") TreeMap t = + (TreeMap) tm; + return t.descendingKeySpliterator(); + } + } + @SuppressWarnings("unchecked") NavigableSubMap sm = + (NavigableSubMap) m; + return sm.keySpliterator(); + } + + final Spliterator keySpliterator() { + return new KeySpliterator(this, null, null, 0, -1, 0); + } + + final Spliterator descendingKeySpliterator() { + return new DescendingKeySpliterator(this, null, null, 0, -2, 0); + } + + /** + * Base class for spliterators. Iteration starts at a given + * origin and continues up to but not including a given fence (or + * null for end). At top-level, for ascending cases, the first + * split uses the root as left-fence/right-origin. From there, + * right-hand splits replace the current fence with its left + * child, also serving as origin for the split-off spliterator. + * Left-hands are symmetric. Descending versions place the origin + * at the end and invert ascending split rules. This base class + * is non-commital about directionality, or whether the top-level + * spliterator covers the whole tree. This means that the actual + * split mechanics are located in subclasses. Some of the subclass + * trySplit methods are identical (except for return types), but + * not nicely factorable. + * + * Currently, subclass versions exist only for the full map + * (including descending keys via its descendingMap). Others are + * possible but currently not worthwhile because submaps require + * O(n) computations to determine size, which substantially limits + * potential speed-ups of using custom Spliterators versus default + * mechanics. + * + * To boostrap initialization, external constructors use + * negative size estimates: -1 for ascend, -2 for descend. + */ + static class TreeMapSpliterator { + final TreeMap tree; + TreeMap.Entry current; // traverser; initially first node in range + TreeMap.Entry fence; // one past last, or null + int side; // 0: top, -1: is a left split, +1: right + int est; // size estimate (exact only for top-level) + int expectedModCount; // for CME checks + + TreeMapSpliterator(TreeMap tree, + TreeMap.Entry origin, TreeMap.Entry fence, + int side, int est, int expectedModCount) { + this.tree = tree; + this.current = origin; + this.fence = fence; + this.side = side; + this.est = est; + this.expectedModCount = expectedModCount; + } + + final int getEstimate() { // force initialization + int s; TreeMap t; + if ((s = est) < 0) { + if ((t = tree) != null) { + current = (s == -1) ? t.getFirstEntry() : t.getLastEntry(); + s = est = t.size; + expectedModCount = t.modCount; + } + else + s = est = 0; + } + return s; + } + + public final long estimateSize() { + return (long)getEstimate(); + } + } + + static final class KeySpliterator + extends TreeMapSpliterator + implements Spliterator { + KeySpliterator(TreeMap tree, + TreeMap.Entry origin, TreeMap.Entry fence, + int side, int est, int expectedModCount) { + super(tree, origin, fence, side, est, expectedModCount); + } + + public KeySpliterator trySplit() { + if (est < 0) + getEstimate(); // force initialization + int d = side; + TreeMap.Entry e = current, f = fence, + s = ((e == null || e == f) ? null : // empty + (d == 0) ? tree.root : // was top + (d > 0) ? e.right : // was right + (d < 0 && f != null) ? f.left : // was left + null); + if (s != null && s != e && s != f && + tree.compare(e.key, s.key) < 0) { // e not already past s + side = 1; + return new KeySpliterator<> + (tree, e, current = s, -1, est >>>= 1, expectedModCount); + } + return null; + } + + public void forEachRemaining(Consumer action) { + if (action == null) + throw new NullPointerException(); + if (est < 0) + getEstimate(); // force initialization + TreeMap.Entry f = fence, e, p, pl; + if ((e = current) != null && e != f) { + current = f; // exhaust + do { + action.accept(e.key); + if ((p = e.right) != null) { + while ((pl = p.left) != null) + p = pl; + } + else { + while ((p = e.parent) != null && e == p.right) + e = p; + } + } while ((e = p) != null && e != f); + if (tree.modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + } + + public boolean tryAdvance(Consumer action) { + TreeMap.Entry e; + if (action == null) + throw new NullPointerException(); + if (est < 0) + getEstimate(); // force initialization + if ((e = current) == null || e == fence) + return false; + current = successor(e); + action.accept(e.key); + if (tree.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + + public int characteristics() { + return (side == 0 ? Spliterator.SIZED : 0) | + Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED; + } + + public final Comparator getComparator() { + return tree.comparator; + } + + } + + static final class DescendingKeySpliterator + extends TreeMapSpliterator + implements Spliterator { + DescendingKeySpliterator(TreeMap tree, + TreeMap.Entry origin, TreeMap.Entry fence, + int side, int est, int expectedModCount) { + super(tree, origin, fence, side, est, expectedModCount); + } + + public DescendingKeySpliterator trySplit() { + if (est < 0) + getEstimate(); // force initialization + int d = side; + TreeMap.Entry e = current, f = fence, + s = ((e == null || e == f) ? null : // empty + (d == 0) ? tree.root : // was top + (d < 0) ? e.left : // was left + (d > 0 && f != null) ? f.right : // was right + null); + if (s != null && s != e && s != f && + tree.compare(e.key, s.key) > 0) { // e not already past s + side = 1; + return new DescendingKeySpliterator<> + (tree, e, current = s, -1, est >>>= 1, expectedModCount); + } + return null; + } + + public void forEachRemaining(Consumer action) { + if (action == null) + throw new NullPointerException(); + if (est < 0) + getEstimate(); // force initialization + TreeMap.Entry f = fence, e, p, pr; + if ((e = current) != null && e != f) { + current = f; // exhaust + do { + action.accept(e.key); + if ((p = e.left) != null) { + while ((pr = p.right) != null) + p = pr; + } + else { + while ((p = e.parent) != null && e == p.left) + e = p; + } + } while ((e = p) != null && e != f); + if (tree.modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + } + + public boolean tryAdvance(Consumer action) { + TreeMap.Entry e; + if (action == null) + throw new NullPointerException(); + if (est < 0) + getEstimate(); // force initialization + if ((e = current) == null || e == fence) + return false; + current = predecessor(e); + action.accept(e.key); + if (tree.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + + public int characteristics() { + return (side == 0 ? Spliterator.SIZED : 0) | + Spliterator.DISTINCT | Spliterator.ORDERED; + } + } + + static final class ValueSpliterator + extends TreeMapSpliterator + implements Spliterator { + ValueSpliterator(TreeMap tree, + TreeMap.Entry origin, TreeMap.Entry fence, + int side, int est, int expectedModCount) { + super(tree, origin, fence, side, est, expectedModCount); + } + + public ValueSpliterator trySplit() { + if (est < 0) + getEstimate(); // force initialization + int d = side; + TreeMap.Entry e = current, f = fence, + s = ((e == null || e == f) ? null : // empty + (d == 0) ? tree.root : // was top + (d > 0) ? e.right : // was right + (d < 0 && f != null) ? f.left : // was left + null); + if (s != null && s != e && s != f && + tree.compare(e.key, s.key) < 0) { // e not already past s + side = 1; + return new ValueSpliterator<> + (tree, e, current = s, -1, est >>>= 1, expectedModCount); + } + return null; + } + + public void forEachRemaining(Consumer action) { + if (action == null) + throw new NullPointerException(); + if (est < 0) + getEstimate(); // force initialization + TreeMap.Entry f = fence, e, p, pl; + if ((e = current) != null && e != f) { + current = f; // exhaust + do { + action.accept(e.value); + if ((p = e.right) != null) { + while ((pl = p.left) != null) + p = pl; + } + else { + while ((p = e.parent) != null && e == p.right) + e = p; + } + } while ((e = p) != null && e != f); + if (tree.modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + } + + public boolean tryAdvance(Consumer action) { + TreeMap.Entry e; + if (action == null) + throw new NullPointerException(); + if (est < 0) + getEstimate(); // force initialization + if ((e = current) == null || e == fence) + return false; + current = successor(e); + action.accept(e.value); + if (tree.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + + public int characteristics() { + return (side == 0 ? Spliterator.SIZED : 0); + } + } + + static final class EntrySpliterator + extends TreeMapSpliterator + implements Spliterator> { + EntrySpliterator(TreeMap tree, + TreeMap.Entry origin, TreeMap.Entry fence, + int side, int est, int expectedModCount) { + super(tree, origin, fence, side, est, expectedModCount); + } + + public EntrySpliterator trySplit() { + if (est < 0) + getEstimate(); // force initialization + int d = side; + TreeMap.Entry e = current, f = fence, + s = ((e == null || e == f) ? null : // empty + (d == 0) ? tree.root : // was top + (d > 0) ? e.right : // was right + (d < 0 && f != null) ? f.left : // was left + null); + if (s != null && s != e && s != f && + tree.compare(e.key, s.key) < 0) { // e not already past s + side = 1; + return new EntrySpliterator<> + (tree, e, current = s, -1, est >>>= 1, expectedModCount); + } + return null; + } + + public void forEachRemaining(Consumer> action) { + if (action == null) + throw new NullPointerException(); + if (est < 0) + getEstimate(); // force initialization + TreeMap.Entry f = fence, e, p, pl; + if ((e = current) != null && e != f) { + current = f; // exhaust + do { + action.accept(e); + if ((p = e.right) != null) { + while ((pl = p.left) != null) + p = pl; + } + else { + while ((p = e.parent) != null && e == p.right) + e = p; + } + } while ((e = p) != null && e != f); + if (tree.modCount != expectedModCount) + throw new ConcurrentModificationException(); + } + } + + public boolean tryAdvance(Consumer> action) { + TreeMap.Entry e; + if (action == null) + throw new NullPointerException(); + if (est < 0) + getEstimate(); // force initialization + if ((e = current) == null || e == fence) + return false; + current = successor(e); + action.accept(e); + if (tree.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + + public int characteristics() { + return (side == 0 ? Spliterator.SIZED : 0) | + Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED; + } + + @Override + public Comparator> getComparator() { + return tree.comparator != null ? + Comparators.byKey(tree.comparator) : null; + } + } } diff --git a/jdk/src/share/classes/java/util/TreeSet.java b/jdk/src/share/classes/java/util/TreeSet.java index a369af25c6c..9484d5b87c7 100644 --- a/jdk/src/share/classes/java/util/TreeSet.java +++ b/jdk/src/share/classes/java/util/TreeSet.java @@ -533,5 +533,9 @@ public class TreeSet extends AbstractSet tm.readTreeSet(size, s, PRESENT); } + public Spliterator spliterator() { + return TreeMap.keySpliteratorFor(m); + } + private static final long serialVersionUID = -2479143000061671589L; } diff --git a/jdk/src/share/classes/java/util/Vector.java b/jdk/src/share/classes/java/util/Vector.java index be74f617f5c..1d742c801e5 100644 --- a/jdk/src/share/classes/java/util/Vector.java +++ b/jdk/src/share/classes/java/util/Vector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 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 @@ -25,6 +25,12 @@ package java.util; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; + +import java.util.function.Consumer; + /** * The {@code Vector} class implements a growable array of * objects. Like an array, it contains components that can be @@ -1151,6 +1157,28 @@ public class Vector lastRet = -1; } + @Override + public void forEachRemaining(Consumer action) { + Objects.requireNonNull(action); + synchronized (Vector.this) { + final int size = Vector.this.elementCount; + int i = cursor; + if (i >= size) { + return; + } + final Object[] elementData = Vector.this.elementData; + if (i >= elementData.length) { + throw new ConcurrentModificationException(); + } + while (i != size && modCount == expectedModCount) { + action.accept((E) elementData[i++]); + } + // update once at end of iteration to reduce heap write traffic + lastRet = cursor = i; + checkForComodification(); + } + } + final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); @@ -1209,4 +1237,181 @@ public class Vector lastRet = -1; } } + + @Override + public synchronized void forEach(Consumer action) { + Objects.requireNonNull(action); + final int expectedModCount = modCount; + @SuppressWarnings("unchecked") + final E[] elementData = (E[]) this.elementData; + final int elementCount = this.elementCount; + for (int i=0; modCount == expectedModCount && i < elementCount; i++) { + action.accept(elementData[i]); + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + @Override + @SuppressWarnings("unchecked") + public synchronized boolean removeIf(Predicate filter) { + Objects.requireNonNull(filter); + // figure out which elements are to be removed + // any exception thrown from the filter predicate at this stage + // will leave the collection unmodified + int removeCount = 0; + final int size = elementCount; + final BitSet removeSet = new BitSet(size); + final int expectedModCount = modCount; + for (int i=0; modCount == expectedModCount && i < size; i++) { + @SuppressWarnings("unchecked") + final E element = (E) elementData[i]; + if (filter.test(element)) { + removeSet.set(i); + removeCount++; + } + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + + // shift surviving elements left over the spaces left by removed elements + final boolean anyToRemove = removeCount > 0; + if (anyToRemove) { + final int newSize = size - removeCount; + for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) { + i = removeSet.nextClearBit(i); + elementData[j] = elementData[i]; + } + for (int k=newSize; k < size; k++) { + elementData[k] = null; // Let gc do its work + } + elementCount = newSize; + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } + + return anyToRemove; + } + + @Override + @SuppressWarnings("unchecked") + public synchronized void replaceAll(UnaryOperator operator) { + Objects.requireNonNull(operator); + final int expectedModCount = modCount; + final int size = elementCount; + for (int i=0; modCount == expectedModCount && i < size; i++) { + elementData[i] = operator.apply((E) elementData[i]); + } + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } + + @Override + @SuppressWarnings("unchecked") + public synchronized void sort(Comparator c) { + final int expectedModCount = modCount; + Arrays.sort((E[]) elementData, 0, elementCount, c); + if (modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + modCount++; + } + + @Override + public Spliterator spliterator() { + return new VectorSpliterator<>(this, null, 0, -1, 0); + } + + /** Similar to ArrayList Spliterator */ + static final class VectorSpliterator implements Spliterator { + private final Vector list; + private Object[] array; + private int index; // current index, modified on advance/split + private int fence; // -1 until used; then one past last index + private int expectedModCount; // initialized when fence set + + /** Create new spliterator covering the given range */ + VectorSpliterator(Vector list, Object[] array, int origin, int fence, + int expectedModCount) { + this.list = list; + this.array = array; + this.index = origin; + this.fence = fence; + this.expectedModCount = expectedModCount; + } + + private int getFence() { // initialize on first use + int hi; + if ((hi = fence) < 0) { + synchronized(list) { + array = list.elementData; + expectedModCount = list.modCount; + hi = fence = list.elementCount; + } + } + return hi; + } + + public Spliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + return (lo >= mid) ? null : + new VectorSpliterator(list, array, lo, index = mid, + expectedModCount); + } + + @SuppressWarnings("unchecked") + public boolean tryAdvance(Consumer action) { + int i; + if (action == null) + throw new NullPointerException(); + if (getFence() > (i = index)) { + index = i + 1; + action.accept((E)array[i]); + if (list.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + return false; + } + + @SuppressWarnings("unchecked") + public void forEachRemaining(Consumer action) { + int i, hi; // hoist accesses and checks from loop + Vector lst; Object[] a; + if (action == null) + throw new NullPointerException(); + if ((lst = list) != null) { + if ((hi = fence) < 0) { + synchronized(lst) { + expectedModCount = lst.modCount; + a = array = lst.elementData; + hi = fence = lst.elementCount; + } + } + else + a = array; + if (a != null && (i = index) >= 0 && (index = hi) <= a.length) { + while (i < hi) + action.accept((E) a[i++]); + if (lst.modCount == expectedModCount) + return; + } + } + throw new ConcurrentModificationException(); + } + + public long estimateSize() { + return (long) (getFence() - index); + } + + public int characteristics() { + return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED; + } + } } diff --git a/jdk/src/share/classes/java/util/WeakHashMap.java b/jdk/src/share/classes/java/util/WeakHashMap.java index eb24a18fc44..77f9e094c1a 100644 --- a/jdk/src/share/classes/java/util/WeakHashMap.java +++ b/jdk/src/share/classes/java/util/WeakHashMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -24,8 +24,10 @@ */ package java.util; + import java.lang.ref.WeakReference; import java.lang.ref.ReferenceQueue; +import java.util.function.Consumer; /** @@ -898,6 +900,10 @@ public class WeakHashMap public void clear() { WeakHashMap.this.clear(); } + + public Spliterator spliterator() { + return new KeySpliterator<>(WeakHashMap.this, 0, -1, 0, 0); + } } /** @@ -934,6 +940,10 @@ public class WeakHashMap public void clear() { WeakHashMap.this.clear(); } + + public Spliterator spliterator() { + return new ValueSpliterator<>(WeakHashMap.this, 0, -1, 0, 0); + } } /** @@ -994,5 +1004,288 @@ public class WeakHashMap public T[] toArray(T[] a) { return deepCopy().toArray(a); } + + public Spliterator> spliterator() { + return new EntrySpliterator<>(WeakHashMap.this, 0, -1, 0, 0); + } } + + /** + * Similar form as other hash Spliterators, but skips dead + * elements. + */ + static class WeakHashMapSpliterator { + final WeakHashMap map; + WeakHashMap.Entry current; // current node + int index; // current index, modified on advance/split + int fence; // -1 until first use; then one past last index + int est; // size estimate + int expectedModCount; // for comodification checks + + WeakHashMapSpliterator(WeakHashMap m, int origin, + int fence, int est, + int expectedModCount) { + this.map = m; + this.index = origin; + this.fence = fence; + this.est = est; + this.expectedModCount = expectedModCount; + } + + final int getFence() { // initialize fence and size on first use + int hi; + if ((hi = fence) < 0) { + WeakHashMap m = map; + est = m.size(); + expectedModCount = m.modCount; + hi = fence = m.table.length; + } + return hi; + } + + public final long estimateSize() { + getFence(); // force init + return (long) est; + } + } + + static final class KeySpliterator + extends WeakHashMapSpliterator + implements Spliterator { + KeySpliterator(WeakHashMap m, int origin, int fence, int est, + int expectedModCount) { + super(m, origin, fence, est, expectedModCount); + } + + public KeySpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + return (lo >= mid) ? null : + new KeySpliterator(map, lo, index = mid, est >>>= 1, + expectedModCount); + } + + public void forEachRemaining(Consumer action) { + int i, hi, mc; + if (action == null) + throw new NullPointerException(); + WeakHashMap m = map; + WeakHashMap.Entry[] tab = m.table; + if ((hi = fence) < 0) { + mc = expectedModCount = m.modCount; + hi = fence = tab.length; + } + else + mc = expectedModCount; + if (tab.length >= hi && (i = index) >= 0 && i < hi) { + index = hi; + WeakHashMap.Entry p = current; + do { + if (p == null) + p = tab[i++]; + else { + Object x = p.get(); + p = p.next; + if (x != null) { + @SuppressWarnings("unchecked") K k = + (K) WeakHashMap.unmaskNull(x); + action.accept(k); + } + } + } while (p != null || i < hi); + } + if (m.modCount != mc) + throw new ConcurrentModificationException(); + } + + public boolean tryAdvance(Consumer action) { + int hi; + if (action == null) + throw new NullPointerException(); + WeakHashMap.Entry[] tab = map.table; + if (tab.length >= (hi = getFence()) && index >= 0) { + while (current != null || index < hi) { + if (current == null) + current = tab[index++]; + else { + Object x = current.get(); + current = current.next; + if (x != null) { + @SuppressWarnings("unchecked") K k = + (K) WeakHashMap.unmaskNull(x); + action.accept(k); + if (map.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + } + } + } + return false; + } + + public int characteristics() { + return Spliterator.DISTINCT; + } + } + + static final class ValueSpliterator + extends WeakHashMapSpliterator + implements Spliterator { + ValueSpliterator(WeakHashMap m, int origin, int fence, int est, + int expectedModCount) { + super(m, origin, fence, est, expectedModCount); + } + + public ValueSpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + return (lo >= mid) ? null : + new ValueSpliterator(map, lo, index = mid, est >>>= 1, + expectedModCount); + } + + public void forEachRemaining(Consumer action) { + int i, hi, mc; + if (action == null) + throw new NullPointerException(); + WeakHashMap m = map; + WeakHashMap.Entry[] tab = m.table; + if ((hi = fence) < 0) { + mc = expectedModCount = m.modCount; + hi = fence = tab.length; + } + else + mc = expectedModCount; + if (tab.length >= hi && (i = index) >= 0 && i < hi) { + index = hi; + WeakHashMap.Entry p = current; + do { + if (p == null) + p = tab[i++]; + else { + Object x = p.get(); + V v = p.value; + p = p.next; + if (x != null) + action.accept(v); + } + } while (p != null || i < hi); + } + if (m.modCount != mc) + throw new ConcurrentModificationException(); + } + + public boolean tryAdvance(Consumer action) { + int hi; + if (action == null) + throw new NullPointerException(); + WeakHashMap.Entry[] tab = map.table; + if (tab.length >= (hi = getFence()) && index >= 0) { + while (current != null || index < hi) { + if (current == null) + current = tab[index++]; + else { + Object x = current.get(); + V v = current.value; + current = current.next; + if (x != null) { + action.accept(v); + if (map.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + } + } + } + return false; + } + + public int characteristics() { + return 0; + } + } + + static final class EntrySpliterator + extends WeakHashMapSpliterator + implements Spliterator> { + EntrySpliterator(WeakHashMap m, int origin, int fence, int est, + int expectedModCount) { + super(m, origin, fence, est, expectedModCount); + } + + public EntrySpliterator trySplit() { + int hi = getFence(), lo = index, mid = (lo + hi) >>> 1; + return (lo >= mid) ? null : + new EntrySpliterator(map, lo, index = mid, est >>>= 1, + expectedModCount); + } + + + public void forEachRemaining(Consumer> action) { + int i, hi, mc; + if (action == null) + throw new NullPointerException(); + WeakHashMap m = map; + WeakHashMap.Entry[] tab = m.table; + if ((hi = fence) < 0) { + mc = expectedModCount = m.modCount; + hi = fence = tab.length; + } + else + mc = expectedModCount; + if (tab.length >= hi && (i = index) >= 0 && i < hi) { + index = hi; + WeakHashMap.Entry p = current; + do { + if (p == null) + p = tab[i++]; + else { + Object x = p.get(); + V v = p.value; + p = p.next; + if (x != null) { + @SuppressWarnings("unchecked") K k = + (K) WeakHashMap.unmaskNull(x); + action.accept + (new AbstractMap.SimpleImmutableEntry(k, v)); + } + } + } while (p != null || i < hi); + } + if (m.modCount != mc) + throw new ConcurrentModificationException(); + } + + public boolean tryAdvance(Consumer> action) { + int hi; + if (action == null) + throw new NullPointerException(); + WeakHashMap.Entry[] tab = map.table; + if (tab.length >= (hi = getFence()) && index >= 0) { + while (current != null || index < hi) { + if (current == null) + current = tab[index++]; + else { + Object x = current.get(); + V v = current.value; + current = current.next; + if (x != null) { + @SuppressWarnings("unchecked") K k = + (K) WeakHashMap.unmaskNull(x); + action.accept + (new AbstractMap.SimpleImmutableEntry(k, v)); + if (map.modCount != expectedModCount) + throw new ConcurrentModificationException(); + return true; + } + } + } + } + return false; + } + + public int characteristics() { + return Spliterator.DISTINCT; + } + } + } diff --git a/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java b/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java index 395f1cf89e3..f452a8abf04 100644 --- a/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java +++ b/jdk/src/share/classes/java/util/concurrent/CopyOnWriteArrayList.java @@ -36,6 +36,9 @@ package java.util.concurrent; import java.util.*; import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; /** * A thread-safe variant of {@link java.util.ArrayList} in which all mutative @@ -1260,8 +1263,57 @@ public class CopyOnWriteArrayList } } - } + @Override + public void forEach(Consumer action) { + @SuppressWarnings("unchecked") + final E[] elements = (E[]) l.getArray(); + checkForComodification(); + l.forEach(action, elements, offset, offset + size); + } + @Override + public void sort(Comparator c) { + final ReentrantLock lock = l.lock; + lock.lock(); + try { + checkForComodification(); + l.sort(c, offset, offset + size); + expectedArray = l.getArray(); + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeIf(Predicate filter) { + Objects.requireNonNull(filter); + final ReentrantLock lock = l.lock; + lock.lock(); + try { + checkForComodification(); + final int removeCount = + l.removeIf(filter, offset, offset + size); + expectedArray = l.getArray(); + size -= removeCount; + return removeCount > 0; + } finally { + lock.unlock(); + } + } + + @Override + public void replaceAll(UnaryOperator operator) { + final ReentrantLock lock = l.lock; + lock.lock(); + try { + checkForComodification(); + l.replaceAll(operator, offset, offset + size); + expectedArray = l.getArray(); + } finally { + lock.unlock(); + } + } + } private static class COWSubListIterator implements ListIterator { private final ListIterator it; @@ -1333,4 +1385,139 @@ public class CopyOnWriteArrayList throw new Error(e); } } + + @Override + @SuppressWarnings("unchecked") + public void forEach(Consumer action) { + forEach(action, (E[]) getArray(), 0, size()); + } + + private void forEach(Consumer action, + final E[] elements, + final int from, final int to) { + Objects.requireNonNull(action); + for (int i = from; i < to; i++) { + action.accept(elements[i]); + } + } + + @Override + public void sort(Comparator c) { + final ReentrantLock lock = this.lock; + lock.lock(); + try { + sort(c, 0, size()); + } finally { + lock.unlock(); + } + } + + // must be called with this.lock held + @SuppressWarnings("unchecked") + private void sort(Comparator c, final int from, final int to) { + final E[] elements = (E[]) getArray(); + final E[] newElements = Arrays.copyOf(elements, elements.length); + // only elements [from, to) are sorted + Arrays.sort(newElements, from, to, c); + setArray(newElements); + } + + @Override + public boolean removeIf(Predicate filter) { + Objects.requireNonNull(filter); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + return removeIf(filter, 0, size()) > 0; + } finally { + lock.unlock(); + } + } + + // must be called with this.lock held + private int removeIf(Predicate filter, final int from, final int to) { + Objects.requireNonNull(filter); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + @SuppressWarnings("unchecked") + final E[] elements = (E[]) getArray(); + + // figure out which elements are to be removed + // any exception thrown from the filter predicate at this stage + // will leave the collection unmodified + int removeCount = 0; + final int range = to - from; + final BitSet removeSet = new BitSet(range); + for (int i = 0; i < range; i++) { + final E element = elements[from + i]; + if (filter.test(element)) { + // removeSet is zero-based to keep its size small + removeSet.set(i); + removeCount++; + } + } + + // copy surviving elements into a new array + if (removeCount > 0) { + final int newSize = elements.length - removeCount; + final int newRange = newSize - from; + @SuppressWarnings("unchecked") + final E[] newElements = (E[]) new Object[newSize]; + // copy elements before [from, to) unmodified + for (int i = 0; i < from; i++) { + newElements[i] = elements[i]; + } + // elements [from, to) are subject to removal + int j = 0; + for (int i = 0; (i < range) && (j < newRange); i++) { + i = removeSet.nextClearBit(i); + if (i >= range) { + break; + } + newElements[from + (j++)] = elements[from + i]; + } + // copy any remaining elements beyond [from, to) + j += from; + for (int i = to; (i < elements.length) && (j < newSize); i++) { + newElements[j++] = elements[i]; + } + setArray(newElements); + } + + return removeCount; + } finally { + lock.unlock(); + } + } + + @Override + public void replaceAll(UnaryOperator operator) { + Objects.requireNonNull(operator); + final ReentrantLock lock = this.lock; + lock.lock(); + try { + replaceAll(operator, 0, size()); + } finally { + lock.unlock(); + } + } + + // must be called with this.lock held + @SuppressWarnings("unchecked") + private void replaceAll(UnaryOperator operator, final int from, final int to) { + final E[] elements = (E[]) getArray(); + final E[] newElements = (E[]) new Object[elements.length]; + for (int i = 0; i < from; i++) { + newElements[i] = elements[i]; + } + // the operator is only applied to elements [from, to) + for (int i = from; i < to; i++) { + newElements[i] = operator.apply(elements[i]); + } + for (int i = to; i < elements.length; i++) { + newElements[i] = elements[i]; + } + setArray(newElements); + } } diff --git a/jdk/src/share/classes/java/util/logging/LogManager.java b/jdk/src/share/classes/java/util/logging/LogManager.java index f7f9c3c4add..6f34eaaf6db 100644 --- a/jdk/src/share/classes/java/util/logging/LogManager.java +++ b/jdk/src/share/classes/java/util/logging/LogManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, 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 @@ -35,10 +35,8 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.beans.PropertyChangeListener; -import java.net.URL; import sun.misc.JavaAWTAccess; import sun.misc.SharedSecrets; -import sun.security.action.GetPropertyAction; /** * There is a single global LogManager object that is used to @@ -148,7 +146,6 @@ public class LogManager { // The global LogManager object private static LogManager manager; - private final static Handler[] emptyHandlers = { }; private Properties props = new Properties(); private final static Level defaultLevel = Level.INFO; @@ -555,14 +552,10 @@ public class LogManager { if (name == null) { throw new NullPointerException(); } - - // cleanup some Loggers that have been GC'ed - manager.drainLoggerRefQueueBounded(); - LoggerWeakRef ref = namedLoggers.get(name); if (ref != null) { if (ref.get() == null) { - // It's possible that the Logger was GC'ed after the + // It's possible that the Logger was GC'ed after a // drainLoggerRefQueueBounded() call above so allow // a new one to be registered. removeLogger(name); @@ -614,6 +607,8 @@ public class LogManager { return true; } + // note: all calls to removeLogger are synchronized on LogManager's + // intrinsic lock void removeLogger(String name) { namedLoggers.remove(name); } @@ -896,6 +891,7 @@ public class LogManager { if (name == null) { throw new NullPointerException(); } + drainLoggerRefQueueBounded(); LoggerContext cx = getUserContext(); if (cx.addLocalLogger(logger)) { // Do we have a per logger handler too? diff --git a/jdk/src/share/classes/java/util/stream/AbstractShortCircuitTask.java b/jdk/src/share/classes/java/util/stream/AbstractShortCircuitTask.java new file mode 100644 index 00000000000..06da65485dd --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/AbstractShortCircuitTask.java @@ -0,0 +1,203 @@ +/* + * 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.Spliterator; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Abstract class for fork-join tasks used to implement short-circuiting + * stream ops, which can produce a result without processing all elements of the + * stream. + * + * @param type of input elements to the pipeline + * @param type of output elements from the pipeline + * @param type of intermediate result, may be different from operation + * result type + * @param type of child and sibling tasks + * @since 1.8 + */ +abstract class AbstractShortCircuitTask> + extends AbstractTask { + /** + * The result for this computation; this is shared among all tasks and set + * exactly once + */ + protected final AtomicReference sharedResult; + + /** + * Indicates whether this task has been canceled. Tasks may cancel other + * tasks in the computation under various conditions, such as in a + * find-first operation, a task that finds a value will cancel all tasks + * that are later in the encounter order. + */ + protected volatile boolean canceled; + + /** + * Constructor for root tasks. + * + * @param helper the {@code PipelineHelper} describing the stream pipeline + * up to this operation + * @param spliterator the {@code Spliterator} describing the source for this + * pipeline + */ + protected AbstractShortCircuitTask(PipelineHelper helper, + Spliterator spliterator) { + super(helper, spliterator); + sharedResult = new AtomicReference<>(null); + } + + /** + * Constructor for non-root nodes. + * + * @param parent parent task in the computation tree + * @param spliterator the {@code Spliterator} for the portion of the + * computation tree described by this task + */ + protected AbstractShortCircuitTask(K parent, + Spliterator spliterator) { + super(parent, spliterator); + sharedResult = parent.sharedResult; + } + + /** + * Returns the value indicating the computation completed with no task + * finding a short-circuitable result. For example, for a "find" operation, + * this might be null or an empty {@code Optional}. + * + * @return the result to return when no task finds a result + */ + protected abstract R getEmptyResult(); + + @Override + protected boolean canCompute() { + // Have we already found an answer? + if (sharedResult.get() != null) { + tryComplete(); + return false; + } else if (taskCanceled()) { + setLocalResult(getEmptyResult()); + tryComplete(); + return false; + } + else { + return true; + } + } + + /** + * Declares that a globally valid result has been found. If another task has + * not already found the answer, the result is installed in + * {@code sharedResult}. The {@code compute()} method will check + * {@code sharedResult} before proceeding with computation, so this causes + * the computation to terminate early. + * + * @param result the result found + */ + protected void shortCircuit(R result) { + if (result != null) + sharedResult.compareAndSet(null, result); + } + + /** + * Sets a local result for this task. If this task is the root, set the + * shared result instead (if not already set). + * + * @param localResult The result to set for this task + */ + @Override + protected void setLocalResult(R localResult) { + if (isRoot()) { + if (localResult != null) + sharedResult.compareAndSet(null, localResult); + } + else + super.setLocalResult(localResult); + } + + /** + * Retrieves the local result for this task + */ + @Override + public R getRawResult() { + return getLocalResult(); + } + + /** + * Retrieves the local result for this task. If this task is the root, + * retrieves the shared result instead. + */ + @Override + public R getLocalResult() { + if (isRoot()) { + R answer = sharedResult.get(); + return (answer == null) ? getEmptyResult() : answer; + } + else + return super.getLocalResult(); + } + + /** + * Mark this task as canceled + */ + protected void cancel() { + canceled = true; + } + + /** + * Queries whether this task is canceled. A task is considered canceled if + * it or any of its parents have been canceled. + * + * @return {@code true} if this task or any parent is canceled. + */ + protected boolean taskCanceled() { + boolean cancel = canceled; + if (!cancel) { + for (K parent = getParent(); !cancel && parent != null; parent = parent.getParent()) + cancel = parent.canceled; + } + + return cancel; + } + + /** + * Cancels all tasks which succeed this one in the encounter order. This + * includes canceling all the current task's right sibling, as well as the + * later right siblings of all its parents. + */ + protected void cancelLaterNodes() { + // Go up the tree, cancel right siblings of this node and all parents + for (K parent = getParent(), node = (K) this; parent != null; + node = parent, parent = parent.getParent()) { + // If node is a left child of parent, then has a right sibling + if (parent.leftChild == node) { + K rightSibling = parent.rightChild; + if (!rightSibling.canceled) + rightSibling.cancel(); + } + } + } +} diff --git a/jdk/src/share/classes/java/util/stream/AbstractTask.java b/jdk/src/share/classes/java/util/stream/AbstractTask.java new file mode 100644 index 00000000000..1b7ad212286 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/AbstractTask.java @@ -0,0 +1,373 @@ +/* + * 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.Spliterator; +import java.util.concurrent.CountedCompleter; +import java.util.concurrent.ForkJoinPool; + +/** + * Abstract base class for most fork-join tasks used to implement stream ops. + * Manages splitting logic, tracking of child tasks, and intermediate results. + * Each task is associated with a {@link Spliterator} that describes the portion + * of the input associated with the subtree rooted at this task. + * Tasks may be leaf nodes (which will traverse the elements of + * the {@code Spliterator}) or internal nodes (which split the + * {@code Spliterator} into multiple child tasks). + * + * @implNote + *

This class is based on {@link CountedCompleter}, a form of fork-join task + * where each task has a semaphore-like count of uncompleted children, and the + * task is implicitly completed and notified when its last child completes. + * Internal node tasks will likely override the {@code onCompletion} method from + * {@code CountedCompleter} to merge the results from child tasks into the + * current task's result. + * + *

Splitting and setting up the child task links is done by {@code compute()} + * for internal nodes. At {@code compute()} time for leaf nodes, it is + * guaranteed that the parent's child-related fields (including sibling links + * for the parent's children) will be set up for all children. + * + *

For example, a task that performs a reduce would override {@code doLeaf()} + * to perform a reduction on that leaf node's chunk using the + * {@code Spliterator}, and override {@code onCompletion()} to merge the results + * of the child tasks for internal nodes: + * + *

{@code
+ *     protected S doLeaf() {
+ *         spliterator.forEach(...);
+ *         return localReductionResult;
+ *     }
+ *
+ *     public void onCompletion(CountedCompleter caller) {
+ *         if (!isLeaf()) {
+ *             ReduceTask child = children;
+ *             R result = child.getLocalResult();
+ *             child = child.nextSibling;
+ *             for (; child != null; child = child.nextSibling)
+ *                 result = combine(result, child.getLocalResult());
+ *             setLocalResult(result);
+ *         }
+ *     }
+ * }
+ * + * @param Type of elements input to the pipeline + * @param Type of elements output from the pipeline + * @param Type of intermediate result, which may be different from operation + * result type + * @param Type of parent, child and sibling tasks + * @since 1.8 + */ +abstract class AbstractTask> + extends CountedCompleter { + + /** + * Default target factor of leaf tasks for parallel decomposition. + * To allow load balancing, we over-partition, currently to approximately + * four tasks per processor, which enables others to help out + * if leaf tasks are uneven or some processors are otherwise busy. + */ + static final int LEAF_TARGET = ForkJoinPool.getCommonPoolParallelism() << 2; + + /** The pipeline helper, common to all tasks in a computation */ + protected final PipelineHelper helper; + + /** + * The spliterator for the portion of the input associated with the subtree + * rooted at this task + */ + protected Spliterator spliterator; + + /** Target leaf size, common to all tasks in a computation */ + protected final long targetSize; + + /** + * The left child. + * null if no children + * if non-null rightChild is non-null + */ + protected K leftChild; + + /** + * The right child. + * null if no children + * if non-null leftChild is non-null + */ + protected K rightChild; + + /** The result of this node, if completed */ + private R localResult; + + /** + * Constructor for root nodes. + * + * @param helper The {@code PipelineHelper} describing the stream pipeline + * up to this operation + * @param spliterator The {@code Spliterator} describing the source for this + * pipeline + */ + protected AbstractTask(PipelineHelper helper, + Spliterator spliterator) { + super(null); + this.helper = helper; + this.spliterator = spliterator; + this.targetSize = suggestTargetSize(spliterator.estimateSize()); + } + + /** + * Constructor for non-root nodes. + * + * @param parent this node's parent task + * @param spliterator {@code Spliterator} describing the subtree rooted at + * this node, obtained by splitting the parent {@code Spliterator} + */ + protected AbstractTask(K parent, + Spliterator spliterator) { + super(parent); + this.spliterator = spliterator; + this.helper = parent.helper; + this.targetSize = parent.targetSize; + } + + /** + * Constructs a new node of type T whose parent is the receiver; must call + * the AbstractTask(T, Spliterator) constructor with the receiver and the + * provided Spliterator. + * + * @param spliterator {@code Spliterator} describing the subtree rooted at + * this node, obtained by splitting the parent {@code Spliterator} + * @return newly constructed child node + */ + protected abstract K makeChild(Spliterator spliterator); + + /** + * Computes the result associated with a leaf node. Will be called by + * {@code compute()} and the result passed to @{code setLocalResult()} + * + * @return the computed result of a leaf node + */ + protected abstract R doLeaf(); + + /** + * Returns a suggested target leaf size based on the initial size estimate. + * + * @return suggested target leaf size + */ + public static long suggestTargetSize(long sizeEstimate) { + long est = sizeEstimate / LEAF_TARGET; + return est > 0L ? est : 1L; + } + + /** + * Returns a suggestion whether it is advisable to split the provided + * spliterator based on target size and other considerations, such as pool + * state. + * + * @return {@code true} if a split is advised otherwise {@code false} + */ + public static boolean suggestSplit(Spliterator spliterator, + long targetSize) { + long remaining = spliterator.estimateSize(); + return (remaining > targetSize); + // @@@ May additionally want to fold in pool characteristics such as surplus task count + } + + /** + * Returns a suggestion whether it is adviseable to split this task based on + * target size and other considerations. + * + * @return {@code true} if a split is advised otherwise {@code false} + */ + public boolean suggestSplit() { + return suggestSplit(spliterator, targetSize); + } + + /** + * Returns the local result, if any. Subclasses should use + * {@link #setLocalResult(Object)} and {@link #getLocalResult()} to manage + * results. This returns the local result so that calls from within the + * fork-join framework will return the correct result. + * + * @return local result for this node previously stored with + * {@link #setLocalResult} + */ + @Override + public R getRawResult() { + return localResult; + } + + /** + * Does nothing; instead, subclasses should use + * {@link #setLocalResult(Object)}} to manage results. + * + * @param result must be null, or an exception is thrown (this is a safety + * tripwire to detect when {@code setRawResult()} is being used + * instead of {@code setLocalResult()} + */ + @Override + protected void setRawResult(R result) { + if (result != null) + throw new IllegalStateException(); + } + + /** + * Retrieves a result previously stored with {@link #setLocalResult} + * + * @return local result for this node previously stored with + * {@link #setLocalResult} + */ + protected R getLocalResult() { + return localResult; + } + + /** + * Associates the result with the task, can be retrieved with + * {@link #getLocalResult} + * + * @param localResult local result for this node + */ + protected void setLocalResult(R localResult) { + this.localResult = localResult; + } + + /** + * Indicates whether this task is a leaf node. (Only valid after + * {@link #compute} has been called on this node). If the node is not a + * leaf node, then children will be non-null and numChildren will be + * positive. + * + * @return {@code true} if this task is a leaf node + */ + protected boolean isLeaf() { + return leftChild == null; + } + + /** + * Indicates whether this task is the root node + * + * @return {@code true} if this task is the root node. + */ + protected boolean isRoot() { + return getParent() == null; + } + + /** + * Returns the parent of this task, or null if this task is the root + * + * @return the parent of this task, or null if this task is the root + */ + @SuppressWarnings("unchecked") + protected K getParent() { + return (K) getCompleter(); + } + + /** + * Decides whether or not to split a task further or compute it directly. If + * computing directly, call {@code doLeaf} and pass the result to + * {@code setRawResult}. If splitting, set up the child-related fields, + * create the child tasks, fork the leftmost (prefix) child tasks, and + * compute the rightmost (remaining) child tasks. + * + *

+ * Computing will continue for rightmost tasks while a task can be computed + * as determined by {@link #canCompute()} and that task should and can be + * split into left and right tasks. + * + *

+ * The rightmost tasks are computed in a loop rather than recursively to + * avoid potential stack overflows when computing with a right-balanced + * tree, such as that produced when splitting with a {@link Spliterator} + * created from an {@link java.util.Iterator}. + */ + @Override + public final void compute() { + @SuppressWarnings("unchecked") + K task = (K) this; + while (task.canCompute()) { + Spliterator split; + if (!task.suggestSplit() || (split = task.spliterator.trySplit()) == null) { + task.setLocalResult(task.doLeaf()); + task.tryComplete(); + return; + } + else { + K l = task.leftChild = task.makeChild(split); + K r = task.rightChild = task.makeChild(task.spliterator); + task.setPendingCount(1); + l.fork(); + task = r; + } + } + } + + /** + * {@inheritDoc} + * + * @implNote + * Clears spliterator and children fields. Overriders MUST call + * {@code super.onCompletion} as the last thing they do if they want these + * cleared. + */ + @Override + public void onCompletion(CountedCompleter caller) { + spliterator = null; + leftChild = rightChild = null; + } + + /** + * Determines if the task can be computed. + * + * @implSpec The default always returns true + * + * @return {@code true} if this task can be computed to either calculate the + * leaf via {@link #doLeaf()} or split, otherwise false if this task + * cannot be computed, for example if this task has been canceled + * and/or a result for the computation has been found by another + * task. + */ + protected boolean canCompute() { + return true; + } + + /** + * Returns whether this node is a "leftmost" node -- whether the path from + * the root to this node involves only traversing leftmost child links. For + * a leaf node, this means it is the first leaf node in the encounter order. + * + * @return {@code true} if this node is a "leftmost" node + */ + protected boolean isLeftmostNode() { + @SuppressWarnings("unchecked") + K node = (K) this; + while (node != null) { + K parent = node.getParent(); + if (parent != null && parent.leftChild != node) + return false; + node = parent; + } + return true; + } +} diff --git a/jdk/src/share/classes/java/util/stream/BaseStream.java b/jdk/src/share/classes/java/util/stream/BaseStream.java new file mode 100644 index 00000000000..db002b5c031 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/BaseStream.java @@ -0,0 +1,108 @@ +/* + * 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.Iterator; +import java.util.Spliterator; + +/** + * Base interface for stream types such as {@link Stream}, {@link IntStream}, + * etc. Contains methods common to all stream types. Many of these methods + * are implemented by {@link AbstractPipeline}, even though + * {@code AbstractPipeline} does not directly implement {@code BaseStream}. + * + * @param type of stream elements + * @param type of stream implementing {@code BaseStream} + * @since 1.8 + */ +interface BaseStream> { + /** + * Returns an iterator for the elements of this stream. + * + *

This is a terminal + * operation. + * + * @return the element iterator for this stream + */ + Iterator iterator(); + + /** + * Returns a spliterator for the elements of this stream. + * + *

This is a terminal + * operation. + * + * @return the element spliterator for this stream + */ + Spliterator spliterator(); + + /** + * Returns whether this stream, when executed, would execute in parallel + * (assuming no further modification of the stream, such as appending + * further intermediate operations or changing its parallelism). Calling + * this method after invoking an intermediate or terminal stream operation + * method may yield unpredictable results. + * + * @return {@code true} if this stream would execute in parallel if executed + * without further modification otherwise {@code false} + */ + boolean isParallel(); + + /** + * Returns an equivalent stream that is sequential. May return + * itself, either because the stream was already sequential, or because + * the underlying stream state was modified to be sequential. + * + *

This is an intermediate + * operation. + * + * @return a sequential stream + */ + S sequential(); + + /** + * Returns an equivalent stream that is parallel. May return + * itself, either because the stream was already parallel, or because + * the underlying stream state was modified to be parallel. + * + *

This is an intermediate + * operation. + * + * @return a parallel stream + */ + S parallel(); + + /** + * Returns an equivalent stream that is + * unordered. May return + * itself if the stream was already unordered. + * + *

This is an intermediate + * operation. + * + * @return an unordered stream + */ + S unordered(); +} diff --git a/jdk/src/share/classes/java/util/stream/CloseableStream.java b/jdk/src/share/classes/java/util/stream/CloseableStream.java new file mode 100644 index 00000000000..bbcce516f99 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/CloseableStream.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.stream; + +/** + * A {@code CloseableStream} is a {@code Stream} that can be closed. + * The close method is invoked to release resources that the object is + * holding (such as open files). + * + * @param The type of stream elements + * @since 1.8 + */ +public interface CloseableStream extends Stream, AutoCloseable { + + /** + * Closes this resource, relinquishing any underlying resources. + * This method is invoked automatically on objects managed by the + * {@code try}-with-resources statement. Does nothing if called when + * the resource has already been closed. + * + * This method does not allow throwing checked {@code Exception}s like + * {@link AutoCloseable#close() AutoCloseable.close()}. Cases where the + * close operation may fail require careful attention by implementers. It + * is strongly advised to relinquish the underlying resources and to + * internally mark the resource as closed. The {@code close} + * method is unlikely to be invoked more than once and so this ensures + * that the resources are released in a timely manner. Furthermore it + * reduces problems that could arise when the resource wraps, or is + * wrapped, by another resource. + * + * @see AutoCloseable#close() + */ + void close(); +} diff --git a/jdk/src/share/classes/java/util/stream/Collector.java b/jdk/src/share/classes/java/util/stream/Collector.java new file mode 100644 index 00000000000..71bb2276f7b --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/Collector.java @@ -0,0 +1,249 @@ +/* + * 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.Collections; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Supplier; + +/** + * A reduction operation that + * supports folding input elements into a cumulative result. The result may be + * a value or may be a mutable result container. Examples of operations + * accumulating results into a mutable result container include: accumulating + * input elements into a {@code Collection}; concatenating strings into a + * {@code StringBuilder}; computing summary information about elements such as + * sum, min, max, or average; computing "pivot table" summaries such as "maximum + * valued transaction by seller", etc. Reduction operations can be performed + * either sequentially or in parallel. + * + *

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 = stream.collect(Collectors.toList());
+ *
+ *     // Accumulate elements into a TreeSet
+ *     Set list = stream.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);
+ *
+ * }
+ * + *

A {@code Collector} is specified by three functions that work together to + * manage a result or result container. They are: creation of an initial + * result, incorporating a new data element into a result, and combining two + * results into one. The last function -- combining two results into one -- is + * used during parallel operations, where subsets of the input are accumulated + * in parallel, and then the subresults merged into a combined result. The + * result may be a mutable container or a value. If the result is mutable, the + * accumulation and combination functions may either mutate their left argument + * and return that (such as adding elements to a collection), or return a new + * result, in which case it should not perform any mutation. + * + *

Collectors also have a set of characteristics, including + * {@link Characteristics#CONCURRENT} and + * {@link Characteristics#STRICTLY_MUTATIVE}. These characteristics provide + * hints that can be used by a reduction implementation to provide better + * performance. + * + *

Libraries that implement reduction based on {@code Collector}, such as + * {@link Stream#collect(Collector)}, must adhere to the following constraints: + *

    + *
  • The first argument passed to the accumulator function, and both + * arguments passed to the combiner function, must be the result of a + * previous invocation of {@link #resultSupplier()}, {@link #accumulator()}, + * or {@link #combiner()}.
  • + *
  • The implementation should not do anything with the result of any of + * the result supplier, accumulator, or combiner functions other than to + * pass them again to the accumulator or combiner functions, or return them + * to the caller of the reduction operation.
  • + *
  • If a result is passed to the accumulator or combiner function, and + * the same object is not returned from that function, it is never used + * again.
  • + *
  • Once a result is passed to the combiner function, it is never passed + * to the accumulator function again.
  • + *
  • For non-concurrent collectors, any result returned from the result + * supplier, accumulator, or combiner functions must be serially + * thread-confined. This enables collection to occur in parallel without + * the {@code Collector} needing to implement any additional synchronization. + * The reduction implementation must manage that the input is properly + * partitioned, that partitions are processed in isolation, and combining + * happens only after accumulation is complete.
  • + *
  • For concurrent collectors, an implementation is free to (but not + * required to) implement reduction concurrently. A concurrent reduction + * is one where the accumulator function is called concurrently from + * multiple threads, using the same concurrently-modifiable result container, + * rather than keeping the result isolated during accumulation. + * A concurrent reduction should only be applied if the collector has the + * {@link Characteristics#UNORDERED} characteristics or if the + * originating data is unordered.
  • + *
+ * + * @apiNote + * Performing a reduction operation with a {@code Collector} should produce a + * result equivalent to: + *
{@code
+ *     BiFunction accumulator = collector.accumulator();
+ *     R result = collector.resultSupplier().get();
+ *     for (T t : data)
+ *         result = accumulator.apply(result, t);
+ *     return result;
+ * }
+ * + *

However, the library is free to partition the input, perform the reduction + * on the partitions, and then use the combiner function to combine the partial + * results to achieve a parallel reduction. Depending on the specific reduction + * operation, this may perform better or worse, depending on the relative cost + * of the accumulator and combiner functions. + * + *

An example of an operation that can be easily modeled by {@code Collector} + * is accumulating elements into a {@code TreeSet}. In this case, the {@code + * resultSupplier()} function is {@code () -> new Treeset()}, the + * {@code accumulator} function is + * {@code (set, element) -> { set.add(element); return set; }}, and the combiner + * function is {@code (left, right) -> { left.addAll(right); return left; }}. + * (This behavior is implemented by + * {@code Collectors.toCollection(TreeSet::new)}). + * + * TODO Associativity and commutativity + * + * @see Stream#collect(Collector) + * @see Collectors + * + * @param the type of input element to the collect operation + * @param the result type of the collect operation + * @since 1.8 + */ +public interface Collector { + /** + * A function that creates and returns a new result that represents + * "no values". If the accumulator or combiner functions may mutate their + * arguments, this must be a new, empty result container. + * + * @return a function which, when invoked, returns a result representing + * "no values" + */ + Supplier resultSupplier(); + + /** + * A function that folds a new value into a cumulative result. The result + * may be a mutable result container or a value. The accumulator function + * may modify a mutable container and return it, or create a new result and + * return that, but if it returns a new result object, it must not modify + * any of its arguments. + * + *

If the collector has the {@link Characteristics#STRICTLY_MUTATIVE} + * characteristic, then the accumulator function must always return + * its first argument, after possibly mutating its state. + * + * @return a function which folds a new value into a cumulative result + */ + BiFunction accumulator(); + + /** + * A function that accepts two partial results and merges them. The + * combiner function may fold state from one argument into the other and + * return that, or may return a new result object, but if it returns + * a new result object, it must not modify the state of either of its + * arguments. + * + *

If the collector has the {@link Characteristics#STRICTLY_MUTATIVE} + * characteristic, then the combiner function must always return + * its first argument, after possibly mutating its state. + * + * @return a function which combines two partial results into a cumulative + * result + */ + BinaryOperator combiner(); + + /** + * Returns a {@code Set} of {@code Collector.Characteristics} indicating + * the characteristics of this Collector. This set should be immutable. + * + * @return an immutable set of collector characteristics + */ + Set characteristics(); + + /** + * Characteristics indicating properties of a {@code Collector}, which can + * be used to optimize reduction implementations. + */ + enum Characteristics { + /** + * Indicates that this collector is concurrent, meaning that + * the result container can support the accumulator function being + * called concurrently with the same result container from multiple + * threads. Concurrent collectors must also always have the + * {@code STRICTLY_MUTATIVE} characteristic. + * + *

If a {@code CONCURRENT} collector is not also {@code UNORDERED}, + * then it should only be evaluated concurrently if applied to an + * unordered data source. + */ + CONCURRENT, + + /** + * Indicates that the result container has no intrinsic order, such as + * a {@link Set}. + */ + UNORDERED, + + /** + * Indicates that this collector operates by strict mutation of its + * result container. This means that the {@link #accumulator()} and + * {@link #combiner()} functions will always modify the state of and + * return their first argument, rather than returning a different result + * container. + */ + STRICTLY_MUTATIVE + } +} diff --git a/jdk/src/share/classes/java/util/stream/DelegatingStream.java b/jdk/src/share/classes/java/util/stream/DelegatingStream.java new file mode 100644 index 00000000000..b3a0002e15c --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/DelegatingStream.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.util.stream; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.Objects; +import java.util.Optional; +import java.util.Spliterator; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; + +/** + * A {@code Stream} implementation that delegates operations to another {@code + * Stream}. + * + * @param type of stream elements for this stream and underlying delegate + * stream + * + * @since 1.8 + */ +public class DelegatingStream implements Stream { + final private Stream delegate; + + /** + * Construct a {@code Stream} that delegates operations to another {@code + * Stream}. + * + * @param delegate the underlying {@link Stream} to which we delegate all + * {@code Stream} methods + * @throws NullPointerException if the delegate is null + */ + public DelegatingStream(Stream delegate) { + this.delegate = Objects.requireNonNull(delegate); + } + + // -- BaseStream methods -- + + @Override + public Spliterator spliterator() { + return delegate.spliterator(); + } + + @Override + public boolean isParallel() { + return delegate.isParallel(); + } + + @Override + public Iterator iterator() { + return delegate.iterator(); + } + + // -- Stream methods -- + + @Override + public Stream filter(Predicate predicate) { + return delegate.filter(predicate); + } + + @Override + public Stream map(Function mapper) { + return delegate.map(mapper); + } + + @Override + public IntStream mapToInt(ToIntFunction mapper) { + return delegate.mapToInt(mapper); + } + + @Override + public LongStream mapToLong(ToLongFunction mapper) { + return delegate.mapToLong(mapper); + } + + @Override + public DoubleStream mapToDouble(ToDoubleFunction mapper) { + return delegate.mapToDouble(mapper); + } + + @Override + public Stream flatMap(Function> mapper) { + return delegate.flatMap(mapper); + } + + @Override + public IntStream flatMapToInt(Function mapper) { + return delegate.flatMapToInt(mapper); + } + + @Override + public LongStream flatMapToLong(Function mapper) { + return delegate.flatMapToLong(mapper); + } + + @Override + public DoubleStream flatMapToDouble(Function mapper) { + return delegate.flatMapToDouble(mapper); + } + + @Override + public Stream distinct() { + return delegate.distinct(); + } + + @Override + public Stream sorted() { + return delegate.sorted(); + } + + @Override + public Stream sorted(Comparator comparator) { + return delegate.sorted(comparator); + } + + @Override + public void forEach(Consumer action) { + delegate.forEach(action); + } + + @Override + public void forEachOrdered(Consumer action) { + delegate.forEachOrdered(action); + } + + @Override + public Stream peek(Consumer consumer) { + return delegate.peek(consumer); + } + + @Override + public Stream limit(long maxSize) { + return delegate.limit(maxSize); + } + + @Override + public Stream substream(long startingOffset) { + return delegate.substream(startingOffset); + } + + @Override + public Stream substream(long startingOffset, long endingOffset) { + return delegate.substream(startingOffset, endingOffset); + } + + @Override + public A[] toArray(IntFunction generator) { + return delegate.toArray(generator); + } + + @Override + public Object[] toArray() { + return delegate.toArray(); + } + + @Override + public T reduce(T identity, BinaryOperator accumulator) { + return delegate.reduce(identity, accumulator); + } + + @Override + public Optional reduce(BinaryOperator accumulator) { + return delegate.reduce(accumulator); + } + + @Override + public U reduce(U identity, BiFunction accumulator, + BinaryOperator combiner) { + return delegate.reduce(identity, accumulator, combiner); + } + + @Override + public R collect(Supplier resultFactory, + BiConsumer accumulator, + BiConsumer combiner) { + return delegate.collect(resultFactory, accumulator, combiner); + } + + @Override + public R collect(Collector collector) { + return delegate.collect(collector); + } + + @Override + public Optional max(Comparator comparator) { + return delegate.max(comparator); + } + + @Override + public Optional min(Comparator comparator) { + return delegate.min(comparator); + } + + @Override + public long count() { + return delegate.count(); + } + + @Override + public boolean anyMatch(Predicate predicate) { + return delegate.anyMatch(predicate); + } + + @Override + public boolean allMatch(Predicate predicate) { + return delegate.allMatch(predicate); + } + + @Override + public boolean noneMatch(Predicate predicate) { + return delegate.noneMatch(predicate); + } + + @Override + public Optional findFirst() { + return delegate.findFirst(); + } + + @Override + public Optional findAny() { + return delegate.findAny(); + } + + @Override + public Stream unordered() { + return delegate.unordered(); + } + + @Override + public Stream sequential() { + return delegate.sequential(); + } + + @Override + public Stream parallel() { + return delegate.parallel(); + } +} diff --git a/jdk/src/share/classes/java/util/stream/DoubleStream.java b/jdk/src/share/classes/java/util/stream/DoubleStream.java new file mode 100644 index 00000000000..e9d3fd15b0d --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/DoubleStream.java @@ -0,0 +1,652 @@ +/* + * 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.DoubleSummaryStatistics; +import java.util.OptionalDouble; +import java.util.PrimitiveIterator; +import java.util.Spliterator; +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.DoubleToIntFunction; +import java.util.function.DoubleToLongFunction; +import java.util.function.DoubleUnaryOperator; +import java.util.function.Function; +import java.util.function.ObjDoubleConsumer; +import java.util.function.Supplier; + +/** + * A sequence of primitive double elements supporting sequential and parallel + * bulk operations. Streams support lazy intermediate operations (transforming + * a stream to another stream) such as {@code filter} and {@code map}, and terminal + * operations (consuming the contents of a stream to produce a result or + * side-effect), such as {@code forEach}, {@code findFirst}, and {@code + * iterator}. Once an operation has been performed on a stream, it + * is considered consumed and no longer usable for other operations. + * + *

For sequential stream pipelines, all operations are performed in the + * encounter order of the pipeline + * source, if the pipeline source has a defined encounter order. + * + *

For parallel stream pipelines, unless otherwise specified, intermediate + * stream operations preserve the + * encounter order of their source, and terminal operations + * respect the encounter order of their source, if the source + * has an encounter order. Provided that and parameters to stream operations + * satisfy the non-interference + * requirements, and excepting differences arising from the absence of + * a defined encounter order, the result of a stream pipeline should be the + * stable across multiple executions of the same operations on the same source. + * However, the timing and thread in which side-effects occur (for those + * operations which are allowed to produce side-effects, such as + * {@link #forEach(DoubleConsumer)}), are explicitly nondeterministic for parallel + * execution of stream pipelines. + * + *

Unless otherwise noted, passing a {@code null} argument to any stream + * method may result in a {@link NullPointerException}. + * + * @apiNote + * Streams are not data structures; they do not manage the storage for their + * elements, nor do they support access to individual elements. However, + * you can use the {@link #iterator()} or {@link #spliterator()} operations to + * perform a controlled traversal. + * + * @since 1.8 + * @see java.util.stream + */ +public interface DoubleStream extends BaseStream { + + /** + * Returns a stream consisting of the elements of this stream that match + * the given predicate. + * + *

This is an intermediate + * operation. + * + * @param predicate a + * non-interfering, stateless predicate to apply to + * each element to determine if it should be included + * @return the new stream + */ + DoubleStream filter(DoublePredicate predicate); + + /** + * Returns a stream consisting of the results of applying the given + * function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to + * each element + * @return the new stream + */ + DoubleStream map(DoubleUnaryOperator mapper); + + /** + * Returns an object-valued {@code Stream} consisting of the results of + * applying the given function to the elements of this stream. + * + *

This is an + * intermediate operation. + * + * @param the element type of the new stream + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + Stream mapToObj(DoubleFunction mapper); + + /** + * Returns an {@code IntStream} consisting of the results of applying the + * given function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + IntStream mapToInt(DoubleToIntFunction mapper); + + /** + * Returns a {@code LongStream} consisting of the results of applying the + * given function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + LongStream mapToLong(DoubleToLongFunction mapper); + + /** + * Returns a stream consisting of the results of replacing each element of + * this stream with the contents of the stream produced by applying the + * provided mapping function to each element. + * + *

This is an intermediate + * operation. + * + * @apiNote + * The {@code flatMap()} operation has the effect of applying a one-to-many + * tranformation to the elements of the stream, and then flattening the + * resulting elements into a new stream. For example, if {@code orders} + * is a stream of purchase orders, and each purchase order contains a + * collection of line items, then the following produces a stream of line + * items: + *

{@code
+     *     orderStream.flatMap(order -> order.getLineItems().stream())...
+     * }
+ * + * @param mapper a + * non-interfering, stateless function to apply to + * each element which produces an {@code DoubleStream} of new + * values + * @return the new stream + * @see Stream#flatMap(Function) + */ + DoubleStream flatMap(DoubleFunction mapper); + + /** + * Returns a stream consisting of the distinct elements of this stream. The + * elements are compared for equality according to + * {@link java.lang.Double#compare(double, double)}. + * + *

This is a stateful + * intermediate operation. + * + * @return the result stream + */ + DoubleStream distinct(); + + /** + * Returns a stream consisting of the elements of this stream in sorted + * order. The elements are compared for equality according to + * {@link java.lang.Double#compare(double, double)}. + * + *

This is a stateful + * intermediate operation. + * + * @return the result stream + */ + DoubleStream sorted(); + + /** + * Returns a stream consisting of the elements of this stream, additionally + * performing the provided action on each element as elements are consumed + * from the resulting stream. + * + *

This is an intermediate + * operation. + * + *

For parallel stream pipelines, the action may be called at + * whatever time and in whatever thread the element is made available by the + * upstream operation. If the action modifies shared state, + * it is responsible for providing the required synchronization. + * + * @apiNote This method exists mainly to support debugging, where you want + * to see the elements as they flow past a certain point in a pipeline: + *

{@code
+     *     list.stream()
+     *         .filter(filteringFunction)
+     *         .peek(e -> {System.out.println("Filtered value: " + e); });
+     *         .map(mappingFunction)
+     *         .peek(e -> {System.out.println("Mapped value: " + e); });
+     *         .collect(Collectors.toDoubleSummaryStastistics());
+     * }
+ * + * @param consumer a + * non-interfering action to perform on the elements as + * they are consumed from the stream + * @return the new stream + */ + DoubleStream peek(DoubleConsumer consumer); + + /** + * Returns a stream consisting of the elements of this stream, truncated + * to be no longer than {@code maxSize} in length. + * + *

This is a short-circuiting + * stateful intermediate operation. + * + * @param maxSize the number of elements the stream should be limited to + * @return the new stream + * @throws IllegalArgumentException if {@code maxSize} is negative + */ + DoubleStream limit(long maxSize); + + /** + * Returns a stream consisting of the remaining elements of this stream + * after indexing {@code startInclusive} elements into the stream. If the + * {@code startInclusive} index lies past the end of this stream then an + * empty stream will be returned. + * + *

This is a stateful + * intermediate operation. + * + * @param startInclusive the number of leading elements to skip + * @return the new stream + * @throws IllegalArgumentException if {@code startInclusive} is negative + */ + DoubleStream substream(long startInclusive); + + /** + * Returns a stream consisting of the remaining elements of this stream + * after indexing {@code startInclusive} elements into the stream and + * truncated to contain no more than {@code endExclusive - startInclusive} + * elements. If the {@code startInclusive} index lies past the end + * of this stream then an empty stream will be returned. + * + *

This is a short-circuiting + * stateful intermediate operation. + * + * @param startInclusive the starting position of the substream, inclusive + * @param endExclusive the ending position of the substream, exclusive + * @return the new stream + * @throws IllegalArgumentException if {@code startInclusive} or + * {@code endExclusive} is negative or {@code startInclusive} is greater + * than {@code endExclusive} + */ + DoubleStream substream(long startInclusive, long endExclusive); + + /** + * Performs an action for each element of this stream. + * + *

This is a terminal + * operation. + * + *

For parallel stream pipelines, this operation does not + * guarantee to respect the encounter order of the stream, as doing so + * would sacrifice the benefit of parallelism. For any given element, the + * action may be performed at whatever time and in whatever thread the + * library chooses. If the action accesses shared state, it is + * responsible for providing the required synchronization. + * + * @param action a + * non-interfering action to perform on the elements + */ + void forEach(DoubleConsumer action); + + /** + * Performs an action for each element of this stream, guaranteeing that + * each element is processed in encounter order for streams that have a + * defined encounter order. + * + *

This is a terminal + * operation. + * + * @param action a + * non-interfering action to perform on the elements + * @see #forEach(DoubleConsumer) + */ + void forEachOrdered(DoubleConsumer action); + + /** + * Returns an array containing the elements of this stream. + * + *

This is a terminal + * operation. + * + * @return an array containing the elements of this stream + */ + double[] toArray(); + + /** + * Performs a reduction on the + * elements of this stream, using the provided identity value and an + * associative + * accumulation function, and returns the reduced value. This is equivalent + * to: + *

{@code
+     *     double result = identity;
+     *     for (double element : this stream)
+     *         result = accumulator.apply(result, element)
+     *     return result;
+     * }
+ * + * but is not constrained to execute sequentially. + * + *

The {@code identity} value must be an identity for the accumulator + * function. This means that for all {@code x}, + * {@code accumulator.apply(identity, x)} is equal to {@code x}. + * The {@code accumulator} function must be an + * associative function. + * + *

This is a terminal + * operation. + * + * @apiNote Sum, min, max, and average are all special cases of reduction. + * Summing a stream of numbers can be expressed as: + + *

{@code
+     *     double sum = numbers.reduce(0, (a, b) -> a+b);
+     * }
+ * + * or more compactly: + * + *
{@code
+     *     double sum = numbers.reduce(0, Double::sum);
+     * }
+ * + *

While this may seem a more roundabout way to perform an aggregation + * compared to simply mutating a running total in a loop, reduction + * operations parallelize more gracefully, without needing additional + * synchronization and with greatly reduced risk of data races. + * + * @param identity the identity value for the accumulating function + * @param op an associative + * non-interfering, + * stateless function for combining two values + * @return the result of the reduction + * @see #sum() + * @see #min() + * @see #max() + * @see #average() + */ + double reduce(double identity, DoubleBinaryOperator op); + + /** + * Performs a reduction on the + * elements of this stream, using an + * associative accumulation + * function, and returns an {@code OptionalDouble} describing the reduced + * value, if any. This is equivalent to: + *

{@code
+     *     boolean foundAny = false;
+     *     double result = null;
+     *     for (double element : this stream) {
+     *         if (!foundAny) {
+     *             foundAny = true;
+     *             result = element;
+     *         }
+     *         else
+     *             result = accumulator.apply(result, element);
+     *     }
+     *     return foundAny ? OptionalDouble.of(result) : OptionalDouble.empty();
+     * }
+ * + * but is not constrained to execute sequentially. + * + *

The {@code accumulator} function must be an + * associative function. + * + *

This is a terminal + * operation. + * + * @param op an associative + * non-interfering, + * stateless function for combining two values + * @return the result of the reduction + * @see #reduce(double, DoubleBinaryOperator) + */ + OptionalDouble reduce(DoubleBinaryOperator op); + + /** + * Performs a mutable + * reduction operation on the elements of this stream. A mutable + * reduction is one in which the reduced value is a mutable value holder, + * such as an {@code ArrayList}, and elements are incorporated by updating + * the state of the result, rather than by replacing the result. This + * produces a result equivalent to: + *

{@code
+     *     R result = resultFactory.get();
+     *     for (double element : this stream)
+     *         accumulator.accept(result, element);
+     *     return result;
+     * }
+ * + *

Like {@link #reduce(double, DoubleBinaryOperator)}, {@code collect} + * operations can be parallelized without requiring additional + * synchronization. + * + *

This is a terminal + * operation. + * + * @param type of the result + * @param resultFactory a function that creates a new result container. + * For a parallel execution, this function may be + * called multiple times and must return a fresh value + * each time. + * @param accumulator an associative + * non-interfering, + * stateless function for incorporating an additional + * element into a result + * @param combiner an associative + * non-interfering, + * stateless function for combining two values, which + * must be compatible with the accumulator function + * @return the result of the reduction + * @see Stream#collect(Supplier, BiConsumer, BiConsumer) + */ + R collect(Supplier resultFactory, + ObjDoubleConsumer accumulator, + BiConsumer combiner); + + /** + * Returns the sum of elements in this stream. The sum returned can vary + * depending upon the order in which elements are encountered. This is due + * to accumulated rounding error in addition of values of differing + * magnitudes. Elements sorted by increasing absolute magnitude tend to + * yield more accurate results. If any stream element is a {@code NaN} or + * the sum is at any point a {@code NaN} then the sum will be {@code NaN}. + * This is a special case of a + * reduction and is + * equivalent to: + *

{@code
+     *     return reduce(0, Double::sum);
+     * }
+ * + * @return the sum of elements in this stream + */ + double sum(); + + /** + * Returns an {@code OptionalDouble} describing the minimum element of this + * stream, or an empty OptionalDouble if this stream is empty. The minimum + * element will be {@code Double.NaN} if any stream element was NaN. Unlike + * the numerical comparison operators, this method considers negative zero + * to be strictly smaller than positive zero. This is a special case of a + * reduction and is + * equivalent to: + *
{@code
+     *     return reduce(Double::min);
+     * }
+ * + * @return an {@code OptionalDouble} containing the minimum element of this + * stream, or an empty optional if the stream is empty + */ + OptionalDouble min(); + + /** + * Returns an {@code OptionalDouble} describing the maximum element of this + * stream, or an empty OptionalDouble if this stream is empty. The maximum + * element will be {@code Double.NaN} if any stream element was NaN. Unlike + * the numerical comparison operators, this method considers negative zero + * to be strictly smaller than positive zero. This is a + * special case of a + * reduction and is + * equivalent to: + *
{@code
+     *     return reduce(Double::max);
+     * }
+ * + * @return an {@code OptionalDouble} containing the maximum element of this + * stream, or an empty optional if the stream is empty + */ + OptionalDouble max(); + + /** + * Returns the count of elements in this stream. This is a special case of + * a reduction and is + * equivalent to: + *
{@code
+     *     return mapToLong(e -> 1L).sum();
+     * }
+ * + *

This is a terminal operation. + * + * @return the count of elements in this stream + */ + long count(); + + /** + * Returns an {@code OptionalDouble} describing the average of elements of + * this stream, or an empty optional if this stream is empty. The average + * returned can vary depending upon the order in which elements are + * encountered. This is due to accumulated rounding error in addition of + * elements of differing magnitudes. Elements sorted by increasing absolute + * magnitude tend to yield more accurate results. If any recorded value is + * a {@code NaN} or the sum is at any point a {@code NaN} then the average + * will be {@code NaN}. This is a special case of a + * reduction. + * + * @return an {@code OptionalDouble} containing the average element of this + * stream, or an empty optional if the stream is empty + */ + OptionalDouble average(); + + /** + * Returns a {@code DoubleSummaryStatistics} describing various summary data + * about the elements of this stream. This is a special + * case of a reduction. + * + * @return a {@code DoubleSummaryStatistics} describing various summary data + * about the elements of this stream + */ + DoubleSummaryStatistics summaryStatistics(); + + /** + * Returns whether any elements of this stream match the provided + * predicate. May not evaluate the predicate on all elements if not + * necessary for determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if any elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean anyMatch(DoublePredicate predicate); + + /** + * Returns whether all elements of this stream match the provided predicate. + * May not evaluate the predicate on all elements if not necessary for + * determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if all elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean allMatch(DoublePredicate predicate); + + /** + * Returns whether no elements of this stream match the provided predicate. + * May not evaluate the predicate on all elements if not necessary for + * determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if no elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean noneMatch(DoublePredicate predicate); + + /** + * Returns an {@link OptionalDouble} describing the first element of this + * stream (in the encounter order), or an empty {@code OptionalDouble} if + * the stream is empty. If the stream has no encounter order, than any + * element may be returned. + * + *

This is a short-circuiting + * terminal operation. + * + * @return an {@code OptionalDouble} describing the first element of this + * stream, or an empty {@code OptionalDouble} if the stream is empty + */ + OptionalDouble findFirst(); + + /** + * Returns an {@link OptionalDouble} describing some element of the stream, + * or an empty {@code OptionalDouble} if the stream is empty. + * + *

This is a short-circuiting + * terminal operation. + * + *

The behavior of this operation is explicitly nondeterministic; it is + * free to select any element in the stream. This is to allow for maximal + * performance in parallel operations; the cost is that multiple invocations + * on the same source may not return the same result. (If the first element + * in the encounter order is desired, use {@link #findFirst()} instead.) + * + * @return an {@code OptionalDouble} describing some element of this stream, + * or an empty {@code OptionalDouble} if the stream is empty + * @see #findFirst() + */ + OptionalDouble findAny(); + + /** + * Returns a {@code Stream} consisting of the elements of this stream, + * boxed to {@code Double}. + * + * @return a {@code Stream} consistent of the elements of this stream, + * each boxed to a {@code Double} + */ + Stream boxed(); + + @Override + DoubleStream sequential(); + + @Override + DoubleStream parallel(); + + @Override + PrimitiveIterator.OfDouble iterator(); + + @Override + Spliterator.OfDouble spliterator(); +} diff --git a/jdk/src/share/classes/java/util/stream/FindOps.java b/jdk/src/share/classes/java/util/stream/FindOps.java new file mode 100644 index 00000000000..6b34b24244d --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/FindOps.java @@ -0,0 +1,317 @@ +/* + * 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.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; +import java.util.Spliterator; +import java.util.concurrent.CountedCompleter; +import java.util.function.Predicate; +import java.util.function.Supplier; + +/** + * Factory for instances of a short-circuiting {@code TerminalOp} that searches + * for an element in a stream pipeline, and terminates when it finds one. + * Supported variants include find-first (find the first element in the + * encounter order) and find-any (find any element, may not be the first in + * encounter order.) + * + * @since 1.8 + */ +final class FindOps { + + private FindOps() { } + + /** + * Constructs a {@code TerminalOp} for streams of objects. + * + * @param the type of elements of the stream + * @param mustFindFirst whether the {@code TerminalOp} must produce the + * first element in the encounter order + * @return a {@code TerminalOp} implementing the find operation + */ + public static TerminalOp> makeRef(boolean mustFindFirst) { + return new FindOp<>(mustFindFirst, StreamShape.REFERENCE, Optional.empty(), + Optional::isPresent, FindSink.OfRef::new); + } + + /** + * Constructs a {@code TerminalOp} for streams of ints. + * + * @param mustFindFirst whether the {@code TerminalOp} must produce the + * first element in the encounter order + * @return a {@code TerminalOp} implementing the find operation + */ + public static TerminalOp makeInt(boolean mustFindFirst) { + return new FindOp<>(mustFindFirst, StreamShape.INT_VALUE, OptionalInt.empty(), + OptionalInt::isPresent, FindSink.OfInt::new); + } + + /** + * Constructs a {@code TerminalOp} for streams of longs. + * + * @param mustFindFirst whether the {@code TerminalOp} must produce the + * first element in the encounter order + * @return a {@code TerminalOp} implementing the find operation + */ + public static TerminalOp makeLong(boolean mustFindFirst) { + return new FindOp<>(mustFindFirst, StreamShape.LONG_VALUE, OptionalLong.empty(), + OptionalLong::isPresent, FindSink.OfLong::new); + } + + /** + * Constructs a {@code FindOp} for streams of doubles. + * + * @param mustFindFirst whether the {@code TerminalOp} must produce the + * first element in the encounter order + * @return a {@code TerminalOp} implementing the find operation + */ + public static TerminalOp makeDouble(boolean mustFindFirst) { + return new FindOp<>(mustFindFirst, StreamShape.DOUBLE_VALUE, OptionalDouble.empty(), + OptionalDouble::isPresent, FindSink.OfDouble::new); + } + + /** + * A short-circuiting {@code TerminalOp} that searches for an element in a + * stream pipeline, and terminates when it finds one. Implements both + * find-first (find the first element in the encounter order) and find-any + * (find any element, may not be the first in encounter order.) + * + * @param the output type of the stream pipeline + * @param the result type of the find operation, typically an optional + * type + */ + private static final class FindOp implements TerminalOp { + private final StreamShape shape; + final boolean mustFindFirst; + final O emptyValue; + final Predicate presentPredicate; + final Supplier> sinkSupplier; + + /** + * Constructs a {@code FindOp}. + * + * @param mustFindFirst if true, must find the first element in + * encounter order, otherwise can find any element + * @param shape stream shape of elements to search + * @param emptyValue result value corresponding to "found nothing" + * @param presentPredicate {@code Predicate} on result value + * corresponding to "found something" + * @param sinkSupplier supplier for a {@code TerminalSink} implementing + * the matching functionality + */ + FindOp(boolean mustFindFirst, + StreamShape shape, + O emptyValue, + Predicate presentPredicate, + Supplier> sinkSupplier) { + this.mustFindFirst = mustFindFirst; + this.shape = shape; + this.emptyValue = emptyValue; + this.presentPredicate = presentPredicate; + this.sinkSupplier = sinkSupplier; + } + + @Override + public int getOpFlags() { + return StreamOpFlag.IS_SHORT_CIRCUIT | (mustFindFirst ? 0 : StreamOpFlag.NOT_ORDERED); + } + + @Override + public StreamShape inputShape() { + return shape; + } + + @Override + public O evaluateSequential(PipelineHelper helper, + Spliterator spliterator) { + O result = helper.wrapAndCopyInto(sinkSupplier.get(), spliterator).get(); + return result != null ? result : emptyValue; + } + + @Override + public O evaluateParallel(PipelineHelper helper, + Spliterator spliterator) { + return new FindTask<>(this, helper, spliterator).invoke(); + } + } + + /** + * Implementation of @{code TerminalSink} that implements the find + * functionality, requesting cancellation when something has been found + * + * @param The type of input element + * @param The result type, typically an optional type + */ + private static abstract class FindSink implements TerminalSink { + boolean hasValue; + T value; + + FindSink() {} // Avoid creation of special accessor + + @Override + public void accept(T value) { + if (!hasValue) { + hasValue = true; + this.value = value; + } + } + + @Override + public boolean cancellationRequested() { + return hasValue; + } + + /** Specialization of {@code FindSink} for reference streams */ + static final class OfRef extends FindSink> { + @Override + public Optional get() { + return hasValue ? Optional.of(value) : null; + } + } + + /** Specialization of {@code FindSink} for int streams */ + static final class OfInt extends FindSink + implements Sink.OfInt { + @Override + public void accept(int value) { + // Boxing is OK here, since few values will actually flow into the sink + accept((Integer) value); + } + + @Override + public OptionalInt get() { + return hasValue ? OptionalInt.of(value) : null; + } + } + + /** Specialization of {@code FindSink} for long streams */ + static final class OfLong extends FindSink + implements Sink.OfLong { + @Override + public void accept(long value) { + // Boxing is OK here, since few values will actually flow into the sink + accept((Long) value); + } + + @Override + public OptionalLong get() { + return hasValue ? OptionalLong.of(value) : null; + } + } + + /** Specialization of {@code FindSink} for double streams */ + static final class OfDouble extends FindSink + implements Sink.OfDouble { + @Override + public void accept(double value) { + // Boxing is OK here, since few values will actually flow into the sink + accept((Double) value); + } + + @Override + public OptionalDouble get() { + return hasValue ? OptionalDouble.of(value) : null; + } + } + } + + /** + * {@code ForkJoinTask} implementing parallel short-circuiting search + * @param Input element type to the stream pipeline + * @param Output element type from the stream pipeline + * @param Result type from the find operation + */ + private static final class FindTask + extends AbstractShortCircuitTask> { + private final FindOp op; + + FindTask(FindOp op, + PipelineHelper helper, + Spliterator spliterator) { + super(helper, spliterator); + this.op = op; + } + + FindTask(FindTask parent, Spliterator spliterator) { + super(parent, spliterator); + this.op = parent.op; + } + + @Override + protected FindTask makeChild(Spliterator spliterator) { + return new FindTask<>(this, spliterator); + } + + @Override + protected O getEmptyResult() { + return op.emptyValue; + } + + private void foundResult(O answer) { + if (isLeftmostNode()) + shortCircuit(answer); + else + cancelLaterNodes(); + } + + @Override + protected O doLeaf() { + O result = helper.wrapAndCopyInto(op.sinkSupplier.get(), spliterator).get(); + if (!op.mustFindFirst) { + if (result != null) + shortCircuit(result); + return null; + } + else { + if (result != null) { + foundResult(result); + return result; + } + else + return null; + } + } + + @Override + public void onCompletion(CountedCompleter caller) { + if (op.mustFindFirst) { + for (FindTask child = leftChild, p = null; child != p; + p = child, child = rightChild) { + O result = child.getLocalResult(); + if (result != null && op.presentPredicate.test(result)) { + setLocalResult(result); + foundResult(result); + break; + } + } + } + super.onCompletion(caller); + } + } +} + diff --git a/jdk/src/share/classes/java/util/stream/ForEachOps.java b/jdk/src/share/classes/java/util/stream/ForEachOps.java new file mode 100644 index 00000000000..944dec6211f --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/ForEachOps.java @@ -0,0 +1,396 @@ +/* + * 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.Objects; +import java.util.Spliterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountedCompleter; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; + +/** + * Factory for creating instances of {@code TerminalOp} that perform an + * action for every element of a stream. Supported variants include unordered + * traversal (elements are provided to the {@code Consumer} as soon as they are + * available), and ordered traversal (elements are provided to the + * {@code Consumer} in encounter order.) + * + *

Elements are provided to the {@code Consumer} on whatever thread and + * whatever order they become available. For ordered traversals, it is + * guaranteed that processing an element happens-before processing + * subsequent elements in the encounter order. + * + *

Exceptions occurring as a result of sending an element to the + * {@code Consumer} will be relayed to the caller and traversal will be + * prematurely terminated. + * + * @since 1.8 + */ +final class ForEachOps { + + private ForEachOps() { } + + /** + * Constructs a {@code TerminalOp} that perform an action for every element + * of a stream. + * + * @param action the {@code Consumer} that receives all elements of a + * stream + * @param ordered whether an ordered traversal is requested + * @param the type of the stream elements + * @return the {@code TerminalOp} instance + */ + public static TerminalOp makeRef(Consumer action, + boolean ordered) { + Objects.requireNonNull(action); + return new ForEachOp.OfRef<>(action, ordered); + } + + /** + * Constructs a {@code TerminalOp} that perform an action for every element + * of an {@code IntStream}. + * + * @param action the {@code IntConsumer} that receives all elements of a + * stream + * @param ordered whether an ordered traversal is requested + * @return the {@code TerminalOp} instance + */ + public static TerminalOp makeInt(IntConsumer action, + boolean ordered) { + Objects.requireNonNull(action); + return new ForEachOp.OfInt(action, ordered); + } + + /** + * Constructs a {@code TerminalOp} that perform an action for every element + * of a {@code LongStream}. + * + * @param action the {@code LongConsumer} that receives all elements of a + * stream + * @param ordered whether an ordered traversal is requested + * @return the {@code TerminalOp} instance + */ + public static TerminalOp makeLong(LongConsumer action, + boolean ordered) { + Objects.requireNonNull(action); + return new ForEachOp.OfLong(action, ordered); + } + + /** + * Constructs a {@code TerminalOp} that perform an action for every element + * of a {@code DoubleStream}. + * + * @param action the {@code DoubleConsumer} that receives all elements of + * a stream + * @param ordered whether an ordered traversal is requested + * @return the {@code TerminalOp} instance + */ + public static TerminalOp makeDouble(DoubleConsumer action, + boolean ordered) { + Objects.requireNonNull(action); + return new ForEachOp.OfDouble(action, ordered); + } + + /** + * A {@code TerminalOp} that evaluates a stream pipeline and sends the + * output to itself as a {@code TerminalSink}. Elements will be sent in + * whatever thread they become available. If the traversal is unordered, + * they will be sent independent of the stream's encounter order. + * + *

This terminal operation is stateless. For parallel evaluation, each + * leaf instance of a {@code ForEachTask} will send elements to the same + * {@code TerminalSink} reference that is an instance of this class. + * + * @param the output type of the stream pipeline + */ + private static abstract class ForEachOp + implements TerminalOp, TerminalSink { + private final boolean ordered; + + protected ForEachOp(boolean ordered) { + this.ordered = ordered; + } + + // TerminalOp + + @Override + public int getOpFlags() { + return ordered ? 0 : StreamOpFlag.NOT_ORDERED; + } + + @Override + public Void evaluateSequential(PipelineHelper helper, + Spliterator spliterator) { + return helper.wrapAndCopyInto(this, spliterator).get(); + } + + @Override + public Void evaluateParallel(PipelineHelper helper, + Spliterator spliterator) { + if (ordered) + new ForEachOrderedTask<>(helper, spliterator, this).invoke(); + else + new ForEachTask<>(helper, spliterator, helper.wrapSink(this)).invoke(); + return null; + } + + // TerminalSink + + @Override + public Void get() { + return null; + } + + // Implementations + + /** Implementation class for reference streams */ + private static class OfRef extends ForEachOp { + final Consumer consumer; + + OfRef(Consumer consumer, boolean ordered) { + super(ordered); + this.consumer = consumer; + } + + @Override + public void accept(T t) { + consumer.accept(t); + } + } + + /** Implementation class for {@code IntStream} */ + private static class OfInt extends ForEachOp + implements Sink.OfInt { + final IntConsumer consumer; + + OfInt(IntConsumer consumer, boolean ordered) { + super(ordered); + this.consumer = consumer; + } + + @Override + public StreamShape inputShape() { + return StreamShape.INT_VALUE; + } + + @Override + public void accept(int t) { + consumer.accept(t); + } + } + + /** Implementation class for {@code LongStream} */ + private static class OfLong extends ForEachOp + implements Sink.OfLong { + final LongConsumer consumer; + + OfLong(LongConsumer consumer, boolean ordered) { + super(ordered); + this.consumer = consumer; + } + + @Override + public StreamShape inputShape() { + return StreamShape.LONG_VALUE; + } + + @Override + public void accept(long t) { + consumer.accept(t); + } + } + + /** Implementation class for {@code DoubleStream} */ + private static class OfDouble extends ForEachOp + implements Sink.OfDouble { + final DoubleConsumer consumer; + + OfDouble(DoubleConsumer consumer, boolean ordered) { + super(ordered); + this.consumer = consumer; + } + + @Override + public StreamShape inputShape() { + return StreamShape.DOUBLE_VALUE; + } + + @Override + public void accept(double t) { + consumer.accept(t); + } + } + } + + /** A {@code ForkJoinTask} for performing a parallel for-each operation */ + private static class ForEachTask extends CountedCompleter { + private Spliterator spliterator; + private final Sink sink; + private final PipelineHelper helper; + private final long targetSize; + + ForEachTask(PipelineHelper helper, + Spliterator spliterator, + Sink sink) { + super(null); + this.spliterator = spliterator; + this.sink = sink; + this.targetSize = AbstractTask.suggestTargetSize(spliterator.estimateSize()); + this.helper = helper; + } + + ForEachTask(ForEachTask parent, Spliterator spliterator) { + super(parent); + this.spliterator = spliterator; + this.sink = parent.sink; + this.targetSize = parent.targetSize; + this.helper = parent.helper; + } + + public void compute() { + boolean isShortCircuit = StreamOpFlag.SHORT_CIRCUIT.isKnown(helper.getStreamAndOpFlags()); + while (true) { + if (isShortCircuit && sink.cancellationRequested()) { + propagateCompletion(); + spliterator = null; + return; + } + + Spliterator split; + if (!AbstractTask.suggestSplit(spliterator, targetSize) + || (split = spliterator.trySplit()) == null) { + helper.copyInto(sink, spliterator); + propagateCompletion(); + spliterator = null; + return; + } + else { + addToPendingCount(1); + new ForEachTask<>(this, split).fork(); + } + } + } + } + + /** + * A {@code ForkJoinTask} for performing a parallel for-each operation + * which visits the elements in encounter order + */ + private static class ForEachOrderedTask extends CountedCompleter { + private final PipelineHelper helper; + private Spliterator spliterator; + private final long targetSize; + private final ConcurrentHashMap, ForEachOrderedTask> completionMap; + private final Sink action; + private final Object lock; + private final ForEachOrderedTask leftPredecessor; + private Node node; + + protected ForEachOrderedTask(PipelineHelper helper, + Spliterator spliterator, + Sink action) { + super(null); + this.helper = helper; + this.spliterator = spliterator; + this.targetSize = AbstractTask.suggestTargetSize(spliterator.estimateSize()); + this.completionMap = new ConcurrentHashMap<>(); + this.action = action; + this.lock = new Object(); + this.leftPredecessor = null; + } + + ForEachOrderedTask(ForEachOrderedTask parent, + Spliterator spliterator, + ForEachOrderedTask leftPredecessor) { + super(parent); + this.helper = parent.helper; + this.spliterator = spliterator; + this.targetSize = parent.targetSize; + this.completionMap = parent.completionMap; + this.action = parent.action; + this.lock = parent.lock; + this.leftPredecessor = leftPredecessor; + } + + @Override + public final void compute() { + doCompute(this); + } + + private static void doCompute(ForEachOrderedTask task) { + while (true) { + Spliterator split; + if (!AbstractTask.suggestSplit(task.spliterator, task.targetSize) + || (split = task.spliterator.trySplit()) == null) { + if (task.getPendingCount() == 0) { + task.helper.wrapAndCopyInto(task.action, task.spliterator); + } + else { + Node.Builder nb = task.helper.makeNodeBuilder( + task.helper.exactOutputSizeIfKnown(task.spliterator), + size -> (T[]) new Object[size]); + task.node = task.helper.wrapAndCopyInto(nb, task.spliterator).build(); + } + task.tryComplete(); + return; + } + else { + ForEachOrderedTask leftChild = new ForEachOrderedTask<>(task, split, task.leftPredecessor); + ForEachOrderedTask rightChild = new ForEachOrderedTask<>(task, task.spliterator, leftChild); + task.completionMap.put(leftChild, rightChild); + task.addToPendingCount(1); // forking + rightChild.addToPendingCount(1); // right pending on left child + if (task.leftPredecessor != null) { + leftChild.addToPendingCount(1); // left pending on previous subtree, except left spine + if (task.completionMap.replace(task.leftPredecessor, task, leftChild)) + task.addToPendingCount(-1); // transfer my "right child" count to my left child + else + leftChild.addToPendingCount(-1); // left child is ready to go when ready + } + leftChild.fork(); + task = rightChild; + } + } + } + + @Override + public void onCompletion(CountedCompleter caller) { + spliterator = null; + if (node != null) { + // Dump any data from this leaf into the sink + synchronized (lock) { + node.forEach(action); + } + node = null; + } + ForEachOrderedTask victim = completionMap.remove(this); + if (victim != null) + victim.tryComplete(); + } + } +} diff --git a/jdk/src/share/classes/java/util/stream/IntStream.java b/jdk/src/share/classes/java/util/stream/IntStream.java new file mode 100644 index 00000000000..65fd16df4cc --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/IntStream.java @@ -0,0 +1,655 @@ +/* + * 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.IntSummaryStatistics; +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.PrimitiveIterator; +import java.util.Spliterator; +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.IntToDoubleFunction; +import java.util.function.IntToLongFunction; +import java.util.function.IntUnaryOperator; +import java.util.function.ObjIntConsumer; +import java.util.function.Supplier; + +/** + * A sequence of primitive integer elements supporting sequential and parallel + * bulk operations. Streams support lazy intermediate operations (transforming + * a stream to another stream) such as {@code filter} and {@code map}, and terminal + * operations (consuming the contents of a stream to produce a result or + * side-effect), such as {@code forEach}, {@code findFirst}, and {@code + * iterator}. Once an operation has been performed on a stream, it + * is considered consumed and no longer usable for other operations. + * + *

For sequential stream pipelines, all operations are performed in the + * encounter order of the pipeline + * source, if the pipeline source has a defined encounter order. + * + *

For parallel stream pipelines, unless otherwise specified, intermediate + * stream operations preserve the + * encounter order of their source, and terminal operations + * respect the encounter order of their source, if the source + * has an encounter order. Provided that and parameters to stream operations + * satisfy the non-interference + * requirements, and excepting differences arising from the absence of + * a defined encounter order, the result of a stream pipeline should be the + * stable across multiple executions of the same operations on the same source. + * However, the timing and thread in which side-effects occur (for those + * operations which are allowed to produce side-effects, such as + * {@link #forEach(IntConsumer)}), are explicitly nondeterministic for parallel + * execution of stream pipelines. + * + *

Unless otherwise noted, passing a {@code null} argument to any stream + * method may result in a {@link NullPointerException}. + * + * @apiNote + * Streams are not data structures; they do not manage the storage for their + * elements, nor do they support access to individual elements. However, + * you can use the {@link #iterator()} or {@link #spliterator()} operations to + * perform a controlled traversal. + * + * @since 1.8 + * @see java.util.stream + */ +public interface IntStream extends BaseStream { + + /** + * Returns a stream consisting of the elements of this stream that match + * the given predicate. + * + *

This is an intermediate + * operation. + * + * @param predicate a + * non-interfering, stateless predicate to apply to + * each element to determine if it should be included + * @return the new stream + */ + IntStream filter(IntPredicate predicate); + + /** + * Returns a stream consisting of the results of applying the given + * function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + IntStream map(IntUnaryOperator mapper); + + /** + * Returns an object-valued {@code Stream} consisting of the results of + * applying the given function to the elements of this stream. + * + *

This is an + * intermediate operation. + * + * @param the element type of the new stream + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + Stream mapToObj(IntFunction mapper); + + /** + * Returns a {@code LongStream} consisting of the results of applying the + * given function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + LongStream mapToLong(IntToLongFunction mapper); + + /** + * Returns a {@code DoubleStream} consisting of the results of applying the + * given function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + DoubleStream mapToDouble(IntToDoubleFunction mapper); + + /** + * Returns a stream consisting of the results of replacing each element of + * this stream with the contents of the stream produced by applying the + * provided mapping function to each element. + * + *

This is an intermediate + * operation. + * + * @apiNote + * The {@code flatMap()} operation has the effect of applying a one-to-many + * tranformation to the elements of the stream, and then flattening the + * resulting elements into a new stream. For example, if {@code orders} + * is a stream of purchase orders, and each purchase order contains a + * collection of line items, then the following produces a stream of line + * items: + *

{@code
+     *     orderStream.flatMap(order -> order.getLineItems().stream())...
+     * }
+ * + * @param mapper a + * non-interfering, stateless function to apply to + * each element which produces an {@code IntStream} of new + * values + * @return the new stream + * @see Stream#flatMap(Function) + */ + IntStream flatMap(IntFunction mapper); + + /** + * Returns a stream consisting of the distinct elements of this stream. + * + *

This is a stateful + * intermediate operation. + * + * @return the new stream + */ + IntStream distinct(); + + /** + * Returns a stream consisting of the elements of this stream in sorted + * order. + * + *

This is a stateful + * intermediate operation. + * + * @return the new stream + */ + IntStream sorted(); + + /** + * Returns a stream consisting of the elements of this stream, additionally + * performing the provided action on each element as elements are consumed + * from the resulting stream. + * + *

This is an intermediate + * operation. + * + *

For parallel stream pipelines, the action may be called at + * whatever time and in whatever thread the element is made available by the + * upstream operation. If the action modifies shared state, + * it is responsible for providing the required synchronization. + * + * @apiNote This method exists mainly to support debugging, where you want + * to see the elements as they flow past a certain point in a pipeline: + *

{@code
+     *     list.stream()
+     *         .filter(filteringFunction)
+     *         .peek(e -> {System.out.println("Filtered value: " + e); });
+     *         .map(mappingFunction)
+     *         .peek(e -> {System.out.println("Mapped value: " + e); });
+     *         .collect(Collectors.toIntSummaryStastistics());
+     * }
+ * + * @param consumer a + * non-interfering action to perform on the elements as + * they are consumed from the stream + * @return the new stream + */ + IntStream peek(IntConsumer consumer); + + /** + * Returns a stream consisting of the elements of this stream, truncated + * to be no longer than {@code maxSize} in length. + * + *

This is a short-circuiting + * stateful intermediate operation. + * + * @param maxSize the number of elements the stream should be limited to + * @return the new stream + * @throws IllegalArgumentException if {@code maxSize} is negative + */ + IntStream limit(long maxSize); + + /** + * Returns a stream consisting of the remaining elements of this stream + * after indexing {@code startInclusive} elements into the stream. If the + * {@code startInclusive} index lies past the end of this stream then an + * empty stream will be returned. + * + *

This is a stateful + * intermediate operation. + * + * @param startInclusive the number of leading elements to skip + * @return the new stream + * @throws IllegalArgumentException if {@code startInclusive} is negative + */ + IntStream substream(long startInclusive); + + /** + * Returns a stream consisting of the remaining elements of this stream + * after indexing {@code startInclusive} elements into the stream and + * truncated to contain no more than {@code endExclusive - startInclusive} + * elements. If the {@code startInclusive} index lies past the end + * of this stream then an empty stream will be returned. + * + *

This is a short-circuiting + * stateful intermediate operation. + * + * @param startInclusive the starting position of the substream, inclusive + * @param endExclusive the ending position of the substream, exclusive + * @return the new stream + * @throws IllegalArgumentException if {@code startInclusive} or + * {@code endExclusive} is negative or {@code startInclusive} is greater + * than {@code endExclusive} + */ + IntStream substream(long startInclusive, long endExclusive); + + /** + * Performs an action for each element of this stream. + * + *

This is a terminal + * operation. + * + *

For parallel stream pipelines, this operation does not + * guarantee to respect the encounter order of the stream, as doing so + * would sacrifice the benefit of parallelism. For any given element, the + * action may be performed at whatever time and in whatever thread the + * library chooses. If the action accesses shared state, it is + * responsible for providing the required synchronization. + * + * @param action a + * non-interfering action to perform on the elements + */ + void forEach(IntConsumer action); + + /** + * Performs an action for each element of this stream, guaranteeing that + * each element is processed in encounter order for streams that have a + * defined encounter order. + * + *

This is a terminal + * operation. + * + * @param action a + * non-interfering action to perform on the elements + * @see #forEach(IntConsumer) + */ + void forEachOrdered(IntConsumer action); + + /** + * Returns an array containing the elements of this stream. + * + *

This is a terminal + * operation. + * + * @return an array containing the elements of this stream + */ + int[] toArray(); + + /** + * Performs a reduction on the + * elements of this stream, using the provided identity value and an + * associative + * accumulation function, and returns the reduced value. This is equivalent + * to: + *

{@code
+     *     int result = identity;
+     *     for (int element : this stream)
+     *         result = accumulator.apply(result, element)
+     *     return result;
+     * }
+ * + * but is not constrained to execute sequentially. + * + *

The {@code identity} value must be an identity for the accumulator + * function. This means that for all {@code x}, + * {@code accumulator.apply(identity, x)} is equal to {@code x}. + * The {@code accumulator} function must be an + * associative function. + * + *

This is a terminal + * operation. + * + * @apiNote Sum, min, max, and average are all special cases of reduction. + * Summing a stream of numbers can be expressed as: + * + *

{@code
+     *     int sum = integers.reduce(0, (a, b) -> a+b);
+     * }
+ * + * or more compactly: + * + *
{@code
+     *     int sum = integers.reduce(0, Integer::sum);
+     * }
+ * + *

While this may seem a more roundabout way to perform an aggregation + * compared to simply mutating a running total in a loop, reduction + * operations parallelize more gracefully, without needing additional + * synchronization and with greatly reduced risk of data races. + * + * @param identity the identity value for the accumulating function + * @param op an associative + * non-interfering, + * stateless function for combining two values + * @return the result of the reduction + * @see #sum() + * @see #min() + * @see #max() + * @see #average() + */ + int reduce(int identity, IntBinaryOperator op); + + /** + * Performs a reduction on the + * elements of this stream, using an + * associative accumulation + * function, and returns an {@code OptionalInt} describing the reduced value, + * if any. This is equivalent to: + *

{@code
+     *     boolean foundAny = false;
+     *     int result = null;
+     *     for (int element : this stream) {
+     *         if (!foundAny) {
+     *             foundAny = true;
+     *             result = element;
+     *         }
+     *         else
+     *             result = accumulator.apply(result, element);
+     *     }
+     *     return foundAny ? OptionalInt.of(result) : OptionalInt.empty();
+     * }
+ * + * but is not constrained to execute sequentially. + * + *

The {@code accumulator} function must be an + * associative function. + * + *

This is a terminal + * operation. + * + * @param op an associative + * non-interfering, + * stateless function for combining two values + * @return the result of the reduction + * @see #reduce(int, IntBinaryOperator) + */ + OptionalInt reduce(IntBinaryOperator op); + + /** + * Performs a mutable + * reduction operation on the elements of this stream. A mutable + * reduction is one in which the reduced value is a mutable value holder, + * such as an {@code ArrayList}, and elements are incorporated by updating + * the state of the result, rather than by replacing the result. This + * produces a result equivalent to: + *

{@code
+     *     R result = resultFactory.get();
+     *     for (int element : this stream)
+     *         accumulator.accept(result, element);
+     *     return result;
+     * }
+ * + *

Like {@link #reduce(int, IntBinaryOperator)}, {@code collect} operations + * can be parallelized without requiring additional synchronization. + * + *

This is a terminal + * operation. + * + * @param type of the result + * @param resultFactory a function that creates a new result container. + * For a parallel execution, this function may be + * called multiple times and must return a fresh value + * each time. + * @param accumulator an associative + * non-interfering, + * stateless function for incorporating an additional + * element into a result + * @param combiner an associative + * non-interfering, + * stateless function for combining two values, which + * must be compatible with the accumulator function + * @return the result of the reduction + * @see Stream#collect(Supplier, BiConsumer, BiConsumer) + */ + R collect(Supplier resultFactory, + ObjIntConsumer accumulator, + BiConsumer combiner); + + /** + * Returns the sum of elements in this stream. This is a special case + * of a reduction + * and is equivalent to: + *

{@code
+     *     return reduce(0, Integer::sum);
+     * }
+ * + * @return the sum of elements in this stream + */ + int sum(); + + /** + * Returns an {@code OptionalInt} describing the minimum element of this + * stream, or an empty optional if this stream is empty. This is a special + * case of a reduction + * and is equivalent to: + *
{@code
+     *     return reduce(Integer::min);
+     * }
+ * + *

This is a terminal operation. + * + + * @return an {@code OptionalInt} containing the minimum element of this + * stream, or an empty {@code OptionalInt} if the stream is empty + */ + OptionalInt min(); + + /** + * Returns an {@code OptionalInt} describing the maximum element of this + * stream, or an empty optional if this stream is empty. This is a special + * case of a reduction + * and is equivalent to: + *

{@code
+     *     return reduce(Integer::max);
+     * }
+ * + *

This is a terminal + * operation. + * + * @return an {@code OptionalInt} containing the maximum element of this + * stream, or an empty {@code OptionalInt} if the stream is empty + */ + OptionalInt max(); + + /** + * Returns the count of elements in this stream. This is a special case of + * a reduction and is + * equivalent to: + *

{@code
+     *     return mapToLong(e -> 1L).sum();
+     * }
+ * + *

This is a terminal operation. + * + * @return the count of elements in this stream + */ + long count(); + + /** + * Returns an {@code OptionalDouble} describing the average of elements of + * this stream, or an empty optional if this stream is empty. This is a + * special case of a + * reduction. + * + * @return an {@code OptionalDouble} containing the average element of this + * stream, or an empty optional if the stream is empty + */ + OptionalDouble average(); + + /** + * Returns an {@code IntSummaryStatistics} describing various + * summary data about the elements of this stream. This is a special + * case of a reduction. + * + * @return an {@code IntSummaryStatistics} describing various summary data + * about the elements of this stream + */ + IntSummaryStatistics summaryStatistics(); + + /** + * Returns whether any elements of this stream match the provided + * predicate. May not evaluate the predicate on all elements if not + * necessary for determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if any elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean anyMatch(IntPredicate predicate); + + /** + * Returns whether all elements of this stream match the provided predicate. + * May not evaluate the predicate on all elements if not necessary for + * determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if all elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean allMatch(IntPredicate predicate); + + /** + * Returns whether no elements of this stream match the provided predicate. + * May not evaluate the predicate on all elements if not necessary for + * determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if no elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean noneMatch(IntPredicate predicate); + + /** + * Returns an {@link OptionalInt} describing the first element of this + * stream (in the encounter order), or an empty {@code OptionalInt} if the + * stream is empty. If the stream has no encounter order, than any element + * may be returned. + * + *

This is a short-circuiting + * terminal operation. + * + * @return an {@code OptionalInt} describing the first element of this stream, + * or an empty {@code OptionalInt} if the stream is empty + */ + OptionalInt findFirst(); + + /** + * Returns an {@link OptionalInt} describing some element of the stream, or + * an empty {@code OptionalInt} if the stream is empty. + * + *

This is a short-circuiting + * terminal operation. + * + *

The behavior of this operation is explicitly nondeterministic; it is + * free to select any element in the stream. This is to allow for maximal + * performance in parallel operations; the cost is that multiple invocations + * on the same source may not return the same result. (If the first element + * in the encounter order is desired, use {@link #findFirst()} instead.) + * + * @return an {@code OptionalInt} describing some element of this stream, or + * an empty {@code OptionalInt} if the stream is empty + * @see #findFirst() + */ + OptionalInt findAny(); + + /** + * Returns a {@code LongStream} consisting of the elements of this stream, + * converted to {@code long}. + * + * @return a {@code LongStream} consisting of the elements of this stream, + * converted to {@code long} + */ + LongStream longs(); + + /** + * Returns a {@code DoubleStream} consisting of the elements of this stream, + * converted to {@code double}. + * + * @return a {@code DoubleStream} consisting of the elements of this stream, + * converted to {@code double} + */ + DoubleStream doubles(); + + /** + * Returns a {@code Stream} consisting of the elements of this stream, + * each boxed to an {@code Integer}. + * + * @return a {@code Stream} consistent of the elements of this stream, + * each boxed to an {@code Integer} + */ + Stream boxed(); + + @Override + IntStream sequential(); + + @Override + IntStream parallel(); + + @Override + PrimitiveIterator.OfInt iterator(); + + @Override + Spliterator.OfInt spliterator(); +} diff --git a/jdk/src/share/classes/java/util/stream/LongStream.java b/jdk/src/share/classes/java/util/stream/LongStream.java new file mode 100644 index 00000000000..94425dcbb44 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/LongStream.java @@ -0,0 +1,646 @@ +/* + * 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.LongSummaryStatistics; +import java.util.OptionalDouble; +import java.util.OptionalLong; +import java.util.PrimitiveIterator; +import java.util.Spliterator; +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.LongToDoubleFunction; +import java.util.function.LongToIntFunction; +import java.util.function.LongUnaryOperator; +import java.util.function.ObjLongConsumer; +import java.util.function.Supplier; + +/** + * A sequence of primitive long elements supporting sequential and parallel + * bulk operations. Streams support lazy intermediate operations (transforming + * a stream to another stream) such as {@code filter} and {@code map}, and terminal + * operations (consuming the contents of a stream to produce a result or + * side-effect), such as {@code forEach}, {@code findFirst}, and {@code + * iterator}. Once an operation has been performed on a stream, it + * is considered consumed and no longer usable for other operations. + * + *

For sequential stream pipelines, all operations are performed in the + * encounter order of the pipeline + * source, if the pipeline source has a defined encounter order. + * + *

For parallel stream pipelines, unless otherwise specified, intermediate + * stream operations preserve the + * encounter order of their source, and terminal operations + * respect the encounter order of their source, if the source + * has an encounter order. Provided that and parameters to stream operations + * satisfy the non-interference + * requirements, and excepting differences arising from the absence of + * a defined encounter order, the result of a stream pipeline should be the + * stable across multiple executions of the same operations on the same source. + * However, the timing and thread in which side-effects occur (for those + * operations which are allowed to produce side-effects, such as + * {@link #forEach(LongConsumer)}), are explicitly nondeterministic for parallel + * execution of stream pipelines. + * + *

Unless otherwise noted, passing a {@code null} argument to any stream + * method may result in a {@link NullPointerException}. + * + * @apiNote + * Streams are not data structures; they do not manage the storage for their + * elements, nor do they support access to individual elements. However, + * you can use the {@link #iterator()} or {@link #spliterator()} operations to + * perform a controlled traversal. + * + * @since 1.8 + * @see java.util.stream + */ +public interface LongStream extends BaseStream { + + /** + * Returns a stream consisting of the elements of this stream that match + * the given predicate. + * + *

This is an intermediate + * operation. + * + * @param predicate a + * non-interfering, stateless predicate to apply to + * each element to determine if it should be included + * @return the new stream + */ + LongStream filter(LongPredicate predicate); + + /** + * Returns a stream consisting of the results of applying the given + * function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + LongStream map(LongUnaryOperator mapper); + + /** + * Returns an object-valued {@code Stream} consisting of the results of + * applying the given function to the elements of this stream. + * + *

This is an + * intermediate operation. + * + * @param the element type of the new stream + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + Stream mapToObj(LongFunction mapper); + + /** + * Returns an {@code IntStream} consisting of the results of applying the + * given function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + IntStream mapToInt(LongToIntFunction mapper); + + /** + * Returns a {@code DoubleStream} consisting of the results of applying the + * given function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + DoubleStream mapToDouble(LongToDoubleFunction mapper); + + /** + * Returns a stream consisting of the results of replacing each element of + * this stream with the contents of the stream produced by applying the + * provided mapping function to each element. + * + *

This is an intermediate + * operation. + * + * @apiNote + * The {@code flatMap()} operation has the effect of applying a one-to-many + * tranformation to the elements of the stream, and then flattening the + * resulting elements into a new stream. For example, if {@code orders} + * is a stream of purchase orders, and each purchase order contains a + * collection of line items, then the following produces a stream of line + * items: + *

{@code
+     *     orderStream.flatMap(order -> order.getLineItems().stream())...
+     * }
+ * + * @param mapper a + * non-interfering, stateless function to apply to + * each element which produces an {@code LongStream} of new + * values + * @return the new stream + * @see Stream#flatMap(Function) + */ + LongStream flatMap(LongFunction mapper); + + /** + * Returns a stream consisting of the distinct elements of this stream. + * + *

This is a stateful + * intermediate operation. + * + * @return the new stream + */ + LongStream distinct(); + + /** + * Returns a stream consisting of the elements of this stream in sorted + * order. + * + *

This is a stateful + * intermediate operation. + * + * @return the new stream + */ + LongStream sorted(); + + /** + * Returns a stream consisting of the elements of this stream, additionally + * performing the provided action on each element as elements are consumed + * from the resulting stream. + * + *

This is an intermediate + * operation. + * + *

For parallel stream pipelines, the action may be called at + * whatever time and in whatever thread the element is made available by the + * upstream operation. If the action modifies shared state, + * it is responsible for providing the required synchronization. + * + * @apiNote This method exists mainly to support debugging, where you want + * to see the elements as they flow past a certain point in a pipeline: + *

{@code
+     *     list.stream()
+     *         .filter(filteringFunction)
+     *         .peek(e -> {System.out.println("Filtered value: " + e); });
+     *         .map(mappingFunction)
+     *         .peek(e -> {System.out.println("Mapped value: " + e); });
+     *         .collect(Collectors.toLongSummaryStastistics());
+     * }
+ * + * @param consumer a + * non-interfering action to perform on the elements as + * they are consumed from the stream + * @return the new stream + */ + LongStream peek(LongConsumer consumer); + + /** + * Returns a stream consisting of the elements of this stream, truncated + * to be no longer than {@code maxSize} in length. + * + *

This is a short-circuiting + * stateful intermediate operation. + * + * @param maxSize the number of elements the stream should be limited to + * @return the new stream + * @throws IllegalArgumentException if {@code maxSize} is negative + */ + LongStream limit(long maxSize); + + /** + * Returns a stream consisting of the remaining elements of this stream + * after indexing {@code startInclusive} elements into the stream. If the + * {@code startInclusive} index lies past the end of this stream then an + * empty stream will be returned. + * + *

This is a stateful + * intermediate operation. + * + * @param startInclusive the number of leading elements to skip + * @return the new stream + * @throws IllegalArgumentException if {@code startInclusive} is negative + */ + LongStream substream(long startInclusive); + + /** + * Returns a stream consisting of the remaining elements of this stream + * after indexing {@code startInclusive} elements into the stream and + * truncated to contain no more than {@code endExclusive - startInclusive} + * elements. If the {@code startInclusive} index lies past the end + * of this stream then an empty stream will be returned. + * + *

This is a short-circuiting + * stateful intermediate operation. + * + * @param startInclusive the starting position of the substream, inclusive + * @param endExclusive the ending position of the substream, exclusive + * @return the new stream + * @throws IllegalArgumentException if {@code startInclusive} or + * {@code endExclusive} is negative or {@code startInclusive} is greater + * than {@code endExclusive} + */ + LongStream substream(long startInclusive, long endExclusive); + + /** + * Performs an action for each element of this stream. + * + *

This is a terminal + * operation. + * + *

For parallel stream pipelines, this operation does not + * guarantee to respect the encounter order of the stream, as doing so + * would sacrifice the benefit of parallelism. For any given element, the + * action may be performed at whatever time and in whatever thread the + * library chooses. If the action accesses shared state, it is + * responsible for providing the required synchronization. + * + * @param action a + * non-interfering action to perform on the elements + */ + void forEach(LongConsumer action); + + /** + * Performs an action for each element of this stream, guaranteeing that + * each element is processed in encounter order for streams that have a + * defined encounter order. + * + *

This is a terminal + * operation. + * + * @param action a + * non-interfering action to perform on the elements + * @see #forEach(LongConsumer) + */ + void forEachOrdered(LongConsumer action); + + /** + * Returns an array containing the elements of this stream. + * + *

This is a terminal + * operation. + * + * @return an array containing the elements of this stream + */ + long[] toArray(); + + /** + * Performs a reduction on the + * elements of this stream, using the provided identity value and an + * associative + * accumulation function, and returns the reduced value. This is equivalent + * to: + *

{@code
+     *     long result = identity;
+     *     for (long element : this stream)
+     *         result = accumulator.apply(result, element)
+     *     return result;
+     * }
+ * + * but is not constrained to execute sequentially. + * + *

The {@code identity} value must be an identity for the accumulator + * function. This means that for all {@code x}, + * {@code accumulator.apply(identity, x)} is equal to {@code x}. + * The {@code accumulator} function must be an + * associative function. + * + *

This is a terminal + * operation. + * + * @apiNote Sum, min, max, and average are all special cases of reduction. + * Summing a stream of numbers can be expressed as: + * + *

{@code
+     *     long sum = integers.reduce(0, (a, b) -> a+b);
+     * }
+ * + * or more compactly: + * + *
{@code
+     *     long sum = integers.reduce(0, Long::sum);
+     * }
+ * + *

While this may seem a more roundabout way to perform an aggregation + * compared to simply mutating a running total in a loop, reduction + * operations parallelize more gracefully, without needing additional + * synchronization and with greatly reduced risk of data races. + * + * @param identity the identity value for the accumulating function + * @param op an associative + * non-interfering, + * stateless function for combining two values + * @return the result of the reduction + * @see #sum() + * @see #min() + * @see #max() + * @see #average() + */ + long reduce(long identity, LongBinaryOperator op); + + /** + * Performs a reduction on the + * elements of this stream, using an + * associative accumulation + * function, and returns an {@code OptionalLong} describing the reduced value, + * if any. This is equivalent to: + *

{@code
+     *     boolean foundAny = false;
+     *     long result = null;
+     *     for (long element : this stream) {
+     *         if (!foundAny) {
+     *             foundAny = true;
+     *             result = element;
+     *         }
+     *         else
+     *             result = accumulator.apply(result, element);
+     *     }
+     *     return foundAny ? OptionalLong.of(result) : OptionalLong.empty();
+     * }
+ * + * but is not constrained to execute sequentially. + * + *

The {@code accumulator} function must be an + * associative function. + * + *

This is a terminal + * operation. + * + * @param op an associative + * non-interfering, + * stateless function for combining two values + * @return the result of the reduction + * @see #reduce(long, LongBinaryOperator) + */ + OptionalLong reduce(LongBinaryOperator op); + + /** + * Performs a mutable + * reduction operation on the elements of this stream. A mutable + * reduction is one in which the reduced value is a mutable value holder, + * such as an {@code ArrayList}, and elements are incorporated by updating + * the state of the result, rather than by replacing the result. This + * produces a result equivalent to: + *

{@code
+     *     R result = resultFactory.get();
+     *     for (long element : this stream)
+     *         accumulator.accept(result, element);
+     *     return result;
+     * }
+ * + *

Like {@link #reduce(long, LongBinaryOperator)}, {@code collect} operations + * can be parallelized without requiring additional synchronization. + * + *

This is a terminal + * operation. + * + * @param type of the result + * @param resultFactory a function that creates a new result container. + * For a parallel execution, this function may be + * called multiple times and must return a fresh value + * each time. + * @param accumulator an associative + * non-interfering, + * stateless function for incorporating an additional + * element into a result + * @param combiner an associative + * non-interfering, + * stateless function for combining two values, which + * must be compatible with the accumulator function + * @return the result of the reduction + * @see Stream#collect(Supplier, BiConsumer, BiConsumer) + */ + R collect(Supplier resultFactory, + ObjLongConsumer accumulator, + BiConsumer combiner); + + /** + * Returns the sum of elements in this stream. This is a special case + * of a reduction + * and is equivalent to: + *

{@code
+     *     return reduce(0, Long::sum);
+     * }
+ * + * @return the sum of elements in this stream + */ + long sum(); + + /** + * Returns an {@code OptionalLong} describing the minimum element of this + * stream, or an empty optional if this stream is empty. This is a special + * case of a reduction + * and is equivalent to: + *
{@code
+     *     return reduce(Long::min);
+     * }
+ * + *

This is a terminal operation. + * + + * @return an {@code OptionalLong} containing the minimum element of this + * stream, or an empty {@code OptionalLong} if the stream is empty + */ + OptionalLong min(); + + /** + * Returns an {@code OptionalLong} describing the maximum element of this + * stream, or an empty optional if this stream is empty. This is a special + * case of a reduction + * and is equivalent to: + *

{@code
+     *     return reduce(Long::max);
+     * }
+ * + *

This is a terminal + * operation. + * + * @return an {@code OptionalLong} containing the maximum element of this + * stream, or an empty {@code OptionalLong} if the stream is empty + */ + OptionalLong max(); + + /** + * Returns the count of elements in this stream. This is a special case of + * a reduction and is + * equivalent to: + *

{@code
+     *     return map(e -> 1L).sum();
+     * }
+ * + *

This is a terminal operation. + * + * @return the count of elements in this stream + */ + long count(); + + /** + * Returns an {@code OptionalDouble} describing the average of elements of + * this stream, or an empty optional if this stream is empty. This is a + * special case of a + * reduction. + * + * @return an {@code OptionalDouble} containing the average element of this + * stream, or an empty optional if the stream is empty + */ + OptionalDouble average(); + + /** + * Returns a {@code LongSummaryStatistics} describing various summary data + * about the elements of this stream. This is a special case of a + * reduction. + * + * @return a {@code LongSummaryStatistics} describing various summary data + * about the elements of this stream + */ + LongSummaryStatistics summaryStatistics(); + + /** + * Returns whether any elements of this stream match the provided + * predicate. May not evaluate the predicate on all elements if not + * necessary for determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if any elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean anyMatch(LongPredicate predicate); + + /** + * Returns whether all elements of this stream match the provided predicate. + * May not evaluate the predicate on all elements if not necessary for + * determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if all elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean allMatch(LongPredicate predicate); + + /** + * Returns whether no elements of this stream match the provided predicate. + * May not evaluate the predicate on all elements if not necessary for + * determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if no elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean noneMatch(LongPredicate predicate); + + /** + * Returns an {@link OptionalLong} describing the first element of this + * stream (in the encounter order), or an empty {@code OptionalLong} if the + * stream is empty. If the stream has no encounter order, than any element + * may be returned. + * + *

This is a short-circuiting + * terminal operation. + * + * @return an {@code OptionalLong} describing the first element of this + * stream, or an empty {@code OptionalLong} if the stream is empty + */ + OptionalLong findFirst(); + + /** + * Returns an {@link OptionalLong} describing some element of the stream, or + * an empty {@code OptionalLong} if the stream is empty. + * + *

This is a short-circuiting + * terminal operation. + * + *

The behavior of this operation is explicitly nondeterministic; it is + * free to select any element in the stream. This is to allow for maximal + * performance in parallel operations; the cost is that multiple invocations + * on the same source may not return the same result. (If the first element + * in the encounter order is desired, use {@link #findFirst()} instead.) + * + * @return an {@code OptionalLong} describing some element of this stream, + * or an empty {@code OptionalLong} if the stream is empty + * @see #findFirst() + */ + OptionalLong findAny(); + + /** + * Returns a {@code DoubleStream} consisting of the elements of this stream, + * converted to {@code double}. + * + * @return a {@code DoubleStream} consisting of the elements of this stream, + * converted to {@code double} + */ + DoubleStream doubles(); + + /** + * Returns a {@code Stream} consisting of the elements of this stream, + * each boxed to a {@code Long}. + * + * @return a {@code Stream} consistent of the elements of this stream, + * each boxed to {@code Long} + */ + Stream boxed(); + + @Override + LongStream sequential(); + + @Override + LongStream parallel(); + + @Override + PrimitiveIterator.OfLong iterator(); + + @Override + Spliterator.OfLong spliterator(); +} diff --git a/jdk/src/share/classes/java/util/stream/MatchOps.java b/jdk/src/share/classes/java/util/stream/MatchOps.java new file mode 100644 index 00000000000..3fdef943606 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/MatchOps.java @@ -0,0 +1,337 @@ +/* + * 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.Objects; +import java.util.Spliterator; +import java.util.function.DoublePredicate; +import java.util.function.IntPredicate; +import java.util.function.LongPredicate; +import java.util.function.Predicate; +import java.util.function.Supplier; + +/** + * Factory for instances of a short-circuiting {@code TerminalOp} that implement + * quantified predicate matching on the elements of a stream. Supported variants + * include match-all, match-any, and match-none. + * + * @since 1.8 + */ +final class MatchOps { + + private MatchOps() { } + + /** + * Enum describing quantified match options -- all match, any match, none + * match. + */ + enum MatchKind { + /** Do all elements match the predicate? */ + ANY(true, true), + + /** Do any elements match the predicate? */ + ALL(false, false), + + /** Do no elements match the predicate? */ + NONE(true, false); + + private final boolean stopOnPredicateMatches; + private final boolean shortCircuitResult; + + private MatchKind(boolean stopOnPredicateMatches, + boolean shortCircuitResult) { + this.stopOnPredicateMatches = stopOnPredicateMatches; + this.shortCircuitResult = shortCircuitResult; + } + } + + /** + * Constructs a quantified predicate matcher for a Stream. + * + * @param the type of stream elements + * @param predicate the {@code Predicate} to apply to stream elements + * @param matchKind the kind of quantified match (all, any, none) + * @return a {@code TerminalOp} implementing the desired quantified match + * criteria + */ + public static TerminalOp makeRef(Predicate predicate, + MatchKind matchKind) { + Objects.requireNonNull(predicate); + Objects.requireNonNull(matchKind); + class MatchSink extends BooleanTerminalSink { + MatchSink() { + super(matchKind); + } + + @Override + public void accept(T t) { + if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { + stop = true; + value = matchKind.shortCircuitResult; + } + } + } + + // @@@ Workaround for JDK-8011591 -- when fixed, replace s with constructor ref + Supplier> s = new Supplier>() { + @Override + public BooleanTerminalSink get() {return new MatchSink();} + }; + return new MatchOp<>(StreamShape.REFERENCE, matchKind, s); + } + + /** + * Constructs a quantified predicate matcher for an {@code IntStream}. + * + * @param predicate the {@code Predicate} to apply to stream elements + * @param matchKind the kind of quantified match (all, any, none) + * @return a {@code TerminalOp} implementing the desired quantified match + * criteria + */ + public static TerminalOp makeInt(IntPredicate predicate, + MatchKind matchKind) { + Objects.requireNonNull(predicate); + Objects.requireNonNull(matchKind); + class MatchSink extends BooleanTerminalSink implements Sink.OfInt { + MatchSink() { + super(matchKind); + } + + @Override + public void accept(int t) { + if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { + stop = true; + value = matchKind.shortCircuitResult; + } + } + } + + // @@@ Workaround for JDK-8011591 -- when fixed, replace s with constructor ref + Supplier> s = new Supplier>() { + @Override + public BooleanTerminalSink get() {return new MatchSink();} + }; + return new MatchOp<>(StreamShape.INT_VALUE, matchKind, s); + } + + /** + * Constructs a quantified predicate matcher for a {@code LongStream}. + * + * @param predicate the {@code Predicate} to apply to stream elements + * @param matchKind the kind of quantified match (all, any, none) + * @return a {@code TerminalOp} implementing the desired quantified match + * criteria + */ + public static TerminalOp makeLong(LongPredicate predicate, + MatchKind matchKind) { + Objects.requireNonNull(predicate); + Objects.requireNonNull(matchKind); + class MatchSink extends BooleanTerminalSink implements Sink.OfLong { + + MatchSink() { + super(matchKind); + } + + @Override + public void accept(long t) { + if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { + stop = true; + value = matchKind.shortCircuitResult; + } + } + } + + // @@@ Workaround for JDK-8011591 -- when fixed, replace s with constructor ref + Supplier> s = new Supplier>() { + @Override + public BooleanTerminalSink get() {return new MatchSink();} + }; + return new MatchOp<>(StreamShape.LONG_VALUE, matchKind, s); + } + + /** + * Constructs a quantified predicate matcher for a {@code DoubleStream}. + * + * @param predicate the {@code Predicate} to apply to stream elements + * @param matchKind the kind of quantified match (all, any, none) + * @return a {@code TerminalOp} implementing the desired quantified match + * criteria + */ + public static TerminalOp makeDouble(DoublePredicate predicate, + MatchKind matchKind) { + Objects.requireNonNull(predicate); + Objects.requireNonNull(matchKind); + class MatchSink extends BooleanTerminalSink implements Sink.OfDouble { + + MatchSink() { + super(matchKind); + } + + @Override + public void accept(double t) { + if (!stop && predicate.test(t) == matchKind.stopOnPredicateMatches) { + stop = true; + value = matchKind.shortCircuitResult; + } + } + } + + // @@@ Workaround for JDK-8011591 -- when fixed, replace s with constructor ref + Supplier> s = new Supplier>() { + @Override + public BooleanTerminalSink get() {return new MatchSink();} + }; + return new MatchOp<>(StreamShape.DOUBLE_VALUE, matchKind, s); + } + + /** + * A short-circuiting {@code TerminalOp} that evaluates a predicate on the + * elements of a stream and determines whether all, any or none of those + * elements match the predicate. + * + * @param the output type of the stream pipeline + */ + private static final class MatchOp implements TerminalOp { + private final StreamShape inputShape; + final MatchKind matchKind; + final Supplier> sinkSupplier; + + /** + * Constructs a {@code MatchOp}. + * + * @param shape the output shape of the stream pipeline + * @param matchKind the kind of quantified match (all, any, none) + * @param sinkSupplier {@code Supplier} for a {@code Sink} of the + * appropriate shape which implements the matching operation + */ + MatchOp(StreamShape shape, + MatchKind matchKind, + Supplier> sinkSupplier) { + this.inputShape = shape; + this.matchKind = matchKind; + this.sinkSupplier = sinkSupplier; + } + + @Override + public int getOpFlags() { + return StreamOpFlag.IS_SHORT_CIRCUIT | StreamOpFlag.NOT_ORDERED; + } + + @Override + public StreamShape inputShape() { + return inputShape; + } + + @Override + public Boolean evaluateSequential(PipelineHelper helper, + Spliterator spliterator) { + return helper.wrapAndCopyInto(sinkSupplier.get(), spliterator).getAndClearState(); + } + + @Override + public Boolean evaluateParallel(PipelineHelper helper, + Spliterator spliterator) { + // Approach for parallel implementation: + // - Decompose as per usual + // - run match on leaf chunks, call result "b" + // - if b == matchKind.shortCircuitOn, complete early and return b + // - else if we complete normally, return !shortCircuitOn + + return new MatchTask<>(this, helper, spliterator).invoke(); + } + } + + /** + * Boolean specific terminal sink to avoid the boxing costs when returning + * results. Subclasses implement the shape-specific functionality. + * + * @param The output type of the stream pipeline + */ + private static abstract class BooleanTerminalSink implements Sink { + boolean stop; + boolean value; + + BooleanTerminalSink(MatchKind matchKind) { + value = !matchKind.shortCircuitResult; + } + + public boolean getAndClearState() { + return value; + } + + @Override + public boolean cancellationRequested() { + return stop; + } + } + + /** + * ForkJoinTask implementation to implement a parallel short-circuiting + * quantified match + * + * @param the type of source elements for the pipeline + * @param the type of output elements for the pipeline + */ + private static final class MatchTask + extends AbstractShortCircuitTask> { + private final MatchOp op; + + /** + * Constructor for root node + */ + MatchTask(MatchOp op, PipelineHelper helper, + Spliterator spliterator) { + super(helper, spliterator); + this.op = op; + } + + /** + * Constructor for non-root node + */ + MatchTask(MatchTask parent, Spliterator spliterator) { + super(parent, spliterator); + this.op = parent.op; + } + + @Override + protected MatchTask makeChild(Spliterator spliterator) { + return new MatchTask<>(this, spliterator); + } + + @Override + protected Boolean doLeaf() { + boolean b = helper.wrapAndCopyInto(op.sinkSupplier.get(), spliterator).getAndClearState(); + if (b == op.matchKind.shortCircuitResult) + shortCircuit(b); + return null; + } + + @Override + protected Boolean getEmptyResult() { + return !op.matchKind.shortCircuitResult; + } + } +} + diff --git a/jdk/src/share/classes/java/util/stream/Node.java b/jdk/src/share/classes/java/util/stream/Node.java new file mode 100644 index 00000000000..528c3a6cca2 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/Node.java @@ -0,0 +1,557 @@ +/* + * 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.Spliterator; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.IntFunction; +import java.util.function.LongConsumer; + +/** + * An immutable container for describing an ordered sequence of elements of some + * type {@code T}. + * + *

A {@code Node} contains a fixed number of elements, which can be accessed + * via the {@link #count}, {@link #spliterator}, {@link #forEach}, + * {@link #asArray}, or {@link #copyInto} methods. A {@code Node} may have zero + * or more child {@code Node}s; if it has no children (accessed via + * {@link #getChildCount} and {@link #getChild(int)}, it is considered flat + * or a leaf; if it has children, it is considered an + * internal node. The size of an internal node is the sum of sizes of + * its children. + * + * @apiNote + *

A {@code Node} typically does not store the elements directly, but instead + * mediates access to one or more existing (effectively immutable) data + * structures such as a {@code Collection}, array, or a set of other + * {@code Node}s. Commonly {@code Node}s are formed into a tree whose shape + * corresponds to the computation tree that produced the elements that are + * contained in the leaf nodes. The use of {@code Node} within the stream + * framework is largely to avoid copying data unnecessarily during parallel + * operations. + * + * @param the type of elements. + * @since 1.8 + */ +interface Node { + + /** + * Returns a {@link Spliterator} describing the elements contained in this + * {@code Node}. + * + * @return a {@code Spliterator} describing the elements contained in this + * {@code Node} + */ + Spliterator spliterator(); + + /** + * Traverses the elements of this node, and invoke the provided + * {@code Consumer} with each element. Elements are provided in encounter + * order if the source for the {@code Node} has a defined encounter order. + * + * @param consumer a {@code Consumer} that is to be invoked with each + * element in this {@code Node} + */ + void forEach(Consumer consumer); + + /** + * Returns the number of child nodes of this node. + * + * @implSpec The default implementation returns zero. + * + * @return the number of child nodes + */ + default int getChildCount() { + return 0; + } + + /** + * Retrieves the child {@code Node} at a given index. + * + * @implSpec The default implementation always throws + * {@code IndexOutOfBoundsException}. + * + * @param i the index to the child node + * @return the child node + * @throws IndexOutOfBoundsException if the index is less than 0 or greater + * than or equal to the number of child nodes + */ + default Node getChild(int i) { + throw new IndexOutOfBoundsException(); + } + + /** + * Provides an array view of the contents of this node. + * + *

Depending on the underlying implementation, this may return a + * reference to an internal array rather than a copy. Since the returned + * array may be shared, the returned array should not be modified. The + * {@code generator} function may be consulted to create the array if a new + * array needs to be created. + * + * @param generator a factory function which takes an integer parameter and + * returns a new, empty array of that size and of the appropriate + * array type + * @return an array containing the contents of this {@code Node} + */ + T[] asArray(IntFunction generator); + + /** + * Copies the content of this {@code Node} into an array, starting at a + * given offset into the array. It is the caller's responsibility to ensure + * there is sufficient room in the array. + * + * @param array the array into which to copy the contents of this + * {@code Node} + * @param offset the starting offset within the array + * @throws IndexOutOfBoundsException if copying would cause access of data + * outside array bounds + * @throws NullPointerException if {@code array} is {@code null} + */ + void copyInto(T[] array, int offset); + + /** + * Gets the {@code StreamShape} associated with this {@code Node}. + * + * @implSpec The default in {@code Node} returns + * {@code StreamShape.REFERENCE} + * + * @return the stream shape associated with this node + */ + default StreamShape getShape() { + return StreamShape.REFERENCE; + } + + /** + * Returns the number of elements contained in this node. + * + * @return the number of elements contained in this node + */ + long count(); + + /** + * A mutable builder for a {@code Node} that implements {@link Sink}, which + * builds a flat node containing the elements that have been pushed to it. + */ + interface Builder extends Sink { + + /** + * Builds the node. Should be called after all elements have been + * pushed and signalled with an invocation of {@link Sink#end()}. + * + * @return the resulting {@code Node} + */ + Node build(); + + /** + * Specialized @{code Node.Builder} for int elements + */ + interface OfInt extends Node.Builder, Sink.OfInt { + @Override + Node.OfInt build(); + } + + /** + * Specialized @{code Node.Builder} for long elements + */ + interface OfLong extends Node.Builder, Sink.OfLong { + @Override + Node.OfLong build(); + } + + /** + * Specialized @{code Node.Builder} for double elements + */ + interface OfDouble extends Node.Builder, Sink.OfDouble { + @Override + Node.OfDouble build(); + } + } + + /** + * Specialized {@code Node} for int elements + */ + interface OfInt extends Node { + + /** + * {@inheritDoc} + * + * @return a {@link Spliterator.OfInt} describing the elements of this + * node + */ + @Override + Spliterator.OfInt spliterator(); + + /** + * {@inheritDoc} + * + * @param consumer a {@code Consumer} that is to be invoked with each + * element in this {@code Node}. If this is an + * {@code IntConsumer}, it is cast to {@code IntConsumer} so the + * elements may be processed without boxing. + */ + @Override + default void forEach(Consumer consumer) { + if (consumer instanceof IntConsumer) { + forEach((IntConsumer) consumer); + } + else { + if (Tripwire.ENABLED) + Tripwire.trip(getClass(), "{0} calling Node.OfInt.forEachRemaining(Consumer)"); + spliterator().forEachRemaining(consumer); + } + } + + /** + * Traverses the elements of this node, and invoke the provided + * {@code IntConsumer} with each element. + * + * @param consumer a {@code IntConsumer} that is to be invoked with each + * element in this {@code Node} + */ + void forEach(IntConsumer consumer); + + /** + * {@inheritDoc} + * + * @implSpec the default implementation invokes the generator to create + * an instance of an Integer[] array with a length of {@link #count()} + * and then invokes {@link #copyInto(Integer[], int)} with that + * Integer[] array at an offset of 0. This is not efficient and it is + * recommended to invoke {@link #asIntArray()}. + */ + @Override + default Integer[] asArray(IntFunction generator) { + Integer[] boxed = generator.apply((int) count()); + copyInto(boxed, 0); + return boxed; + } + + /** + * {@inheritDoc} + * + * @implSpec the default implementation invokes {@link #asIntArray()} to + * obtain an int[] array then and copies the elements from that int[] + * array into the boxed Integer[] array. This is not efficient and it + * is recommended to invoke {@link #copyInto(int[], int)}. + */ + @Override + default void copyInto(Integer[] boxed, int offset) { + if (Tripwire.ENABLED) + Tripwire.trip(getClass(), "{0} calling Node.OfInt.copyInto(Integer[], int)"); + + int[] array = asIntArray(); + for (int i = 0; i < array.length; i++) { + boxed[offset + i] = array[i]; + } + } + + @Override + default Node.OfInt getChild(int i) { + throw new IndexOutOfBoundsException(); + } + + /** + * Views this node as an int[] array. + * + *

Depending on the underlying implementation this may return a + * reference to an internal array rather than a copy. It is the callers + * responsibility to decide if either this node or the array is utilized + * as the primary reference for the data.

+ * + * @return an array containing the contents of this {@code Node} + */ + int[] asIntArray(); + + /** + * Copies the content of this {@code Node} into an int[] array, starting + * at a given offset into the array. It is the caller's responsibility + * to ensure there is sufficient room in the array. + * + * @param array the array into which to copy the contents of this + * {@code Node} + * @param offset the starting offset within the array + * @throws IndexOutOfBoundsException if copying would cause access of + * data outside array bounds + * @throws NullPointerException if {@code array} is {@code null} + */ + void copyInto(int[] array, int offset); + + /** + * {@inheritDoc} + * @implSpec The default in {@code Node.OfInt} returns + * {@code StreamShape.INT_VALUE} + */ + default StreamShape getShape() { + return StreamShape.INT_VALUE; + } + + } + + /** + * Specialized {@code Node} for long elements + */ + interface OfLong extends Node { + + /** + * {@inheritDoc} + * + * @return a {@link Spliterator.OfLong} describing the elements of this + * node + */ + @Override + Spliterator.OfLong spliterator(); + + /** + * {@inheritDoc} + * + * @param consumer A {@code Consumer} that is to be invoked with each + * element in this {@code Node}. If this is an + * {@code LongConsumer}, it is cast to {@code LongConsumer} so + * the elements may be processed without boxing. + */ + @Override + default void forEach(Consumer consumer) { + if (consumer instanceof LongConsumer) { + forEach((LongConsumer) consumer); + } + else { + if (Tripwire.ENABLED) + Tripwire.trip(getClass(), "{0} calling Node.OfLong.forEachRemaining(Consumer)"); + spliterator().forEachRemaining(consumer); + } + } + + /** + * Traverses the elements of this node, and invoke the provided + * {@code LongConsumer} with each element. + * + * @param consumer a {@code LongConsumer} that is to be invoked with + * each element in this {@code Node} + */ + void forEach(LongConsumer consumer); + + /** + * {@inheritDoc} + * + * @implSpec the default implementation invokes the generator to create + * an instance of a Long[] array with a length of {@link #count()} and + * then invokes {@link #copyInto(Long[], int)} with that Long[] array at + * an offset of 0. This is not efficient and it is recommended to + * invoke {@link #asLongArray()}. + */ + @Override + default Long[] asArray(IntFunction generator) { + Long[] boxed = generator.apply((int) count()); + copyInto(boxed, 0); + return boxed; + } + + /** + * {@inheritDoc} + * + * @implSpec the default implementation invokes {@link #asLongArray()} + * to obtain a long[] array then and copies the elements from that + * long[] array into the boxed Long[] array. This is not efficient and + * it is recommended to invoke {@link #copyInto(long[], int)}. + */ + @Override + default void copyInto(Long[] boxed, int offset) { + if (Tripwire.ENABLED) + Tripwire.trip(getClass(), "{0} calling Node.OfInt.copyInto(Long[], int)"); + + long[] array = asLongArray(); + for (int i = 0; i < array.length; i++) { + boxed[offset + i] = array[i]; + } + } + + @Override + default Node.OfLong getChild(int i) { + throw new IndexOutOfBoundsException(); + } + + /** + * Views this node as a long[] array. + * + *

Depending on the underlying implementation this may return a + * reference to an internal array rather than a copy. It is the callers + * responsibility to decide if either this node or the array is utilized + * as the primary reference for the data. + * + * @return an array containing the contents of this {@code Node} + */ + long[] asLongArray(); + + /** + * Copies the content of this {@code Node} into a long[] array, starting + * at a given offset into the array. It is the caller's responsibility + * to ensure there is sufficient room in the array. + * + * @param array the array into which to copy the contents of this + * {@code Node} + * @param offset the starting offset within the array + * @throws IndexOutOfBoundsException if copying would cause access of + * data outside array bounds + * @throws NullPointerException if {@code array} is {@code null} + */ + void copyInto(long[] array, int offset); + + /** + * {@inheritDoc} + * @implSpec The default in {@code Node.OfLong} returns + * {@code StreamShape.LONG_VALUE} + */ + default StreamShape getShape() { + return StreamShape.LONG_VALUE; + } + + + } + + /** + * Specialized {@code Node} for double elements + */ + interface OfDouble extends Node { + + /** + * {@inheritDoc} + * + * @return A {@link Spliterator.OfDouble} describing the elements of + * this node + */ + @Override + Spliterator.OfDouble spliterator(); + + /** + * {@inheritDoc} + * + * @param consumer A {@code Consumer} that is to be invoked with each + * element in this {@code Node}. If this is an + * {@code DoubleConsumer}, it is cast to {@code DoubleConsumer} + * so the elements may be processed without boxing. + */ + @Override + default void forEach(Consumer consumer) { + if (consumer instanceof DoubleConsumer) { + forEach((DoubleConsumer) consumer); + } + else { + if (Tripwire.ENABLED) + Tripwire.trip(getClass(), "{0} calling Node.OfLong.forEachRemaining(Consumer)"); + spliterator().forEachRemaining(consumer); + } + } + + /** + * Traverses the elements of this node, and invoke the provided + * {@code DoubleConsumer} with each element. + * + * @param consumer A {@code DoubleConsumer} that is to be invoked with + * each element in this {@code Node} + */ + void forEach(DoubleConsumer consumer); + + // + + /** + * {@inheritDoc} + * + * @implSpec the default implementation invokes the generator to create + * an instance of a Double[] array with a length of {@link #count()} and + * then invokes {@link #copyInto(Double[], int)} with that Double[] + * array at an offset of 0. This is not efficient and it is recommended + * to invoke {@link #asDoubleArray()}. + */ + @Override + default Double[] asArray(IntFunction generator) { + Double[] boxed = generator.apply((int) count()); + copyInto(boxed, 0); + return boxed; + } + + /** + * {@inheritDoc} + * + * @implSpec the default implementation invokes {@link #asDoubleArray()} + * to obtain a double[] array then and copies the elements from that + * double[] array into the boxed Double[] array. This is not efficient + * and it is recommended to invoke {@link #copyInto(double[], int)}. + */ + @Override + default void copyInto(Double[] boxed, int offset) { + if (Tripwire.ENABLED) + Tripwire.trip(getClass(), "{0} calling Node.OfDouble.copyInto(Double[], int)"); + + double[] array = asDoubleArray(); + for (int i = 0; i < array.length; i++) { + boxed[offset + i] = array[i]; + } + } + + @Override + default Node.OfDouble getChild(int i) { + throw new IndexOutOfBoundsException(); + } + + /** + * Views this node as a double[] array. + * + *

Depending on the underlying implementation this may return a + * reference to an internal array rather than a copy. It is the callers + * responsibility to decide if either this node or the array is utilized + * as the primary reference for the data. + * + * @return an array containing the contents of this {@code Node} + */ + double[] asDoubleArray(); + + /** + * Copies the content of this {@code Node} into a double[] array, starting + * at a given offset into the array. It is the caller's responsibility + * to ensure there is sufficient room in the array. + * + * @param array the array into which to copy the contents of this + * {@code Node} + * @param offset the starting offset within the array + * @throws IndexOutOfBoundsException if copying would cause access of + * data outside array bounds + * @throws NullPointerException if {@code array} is {@code null} + */ + void copyInto(double[] array, int offset); + + /** + * {@inheritDoc} + * + * @implSpec The default in {@code Node.OfDouble} returns + * {@code StreamShape.DOUBLE_VALUE} + */ + default StreamShape getShape() { + return StreamShape.DOUBLE_VALUE; + } + } +} diff --git a/jdk/src/share/classes/java/util/stream/PipelineHelper.java b/jdk/src/share/classes/java/util/stream/PipelineHelper.java new file mode 100644 index 00000000000..853a1ffac48 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/PipelineHelper.java @@ -0,0 +1,188 @@ +/* + * 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.Spliterator; +import java.util.function.IntFunction; + +/** + * Helper class for executing + * stream pipelines, capturing all of the information about a stream + * pipeline (output shape, intermediate operations, stream flags, parallelism, + * etc) in one place. + * + *

+ * A {@code PipelineHelper} describes the initial segment of a stream pipeline, + * including its source, intermediate operations, and may additionally + * incorporate information about the terminal (or stateful) operation which + * follows the last intermediate operation described by this + * {@code PipelineHelper}. The {@code PipelineHelper} is passed to the + * {@link TerminalOp#evaluateParallel(PipelineHelper, java.util.Spliterator)}, + * {@link TerminalOp#evaluateSequential(PipelineHelper, java.util.Spliterator)}, + * and {@link AbstractPipeline#opEvaluateParallel(PipelineHelper, java.util.Spliterator, + * java.util.function.IntFunction)}, methods, which can use the + * {@code PipelineHelper} to access information about the pipeline such as + * input shape, output shape, stream flags, and size, and use the helper methods + * such as {@link #wrapAndCopyInto(Sink, Spliterator)}, + * {@link #copyInto(Sink, Spliterator)}, and {@link #wrapSink(Sink)} to execute + * pipeline operations. + * + * @param type of output elements from the pipeline + * @since 1.8 + */ +abstract class PipelineHelper { + + /** + * Gets the combined stream and operation flags for the output of the described + * pipeline. This will incorporate stream flags from the stream source, all + * the intermediate operations and the terminal operation. + * + * @return the combined stream and operation flags + * @see StreamOpFlag + */ + abstract int getStreamAndOpFlags(); + + /** + * Returns the exact output size of the portion of the output resulting from + * applying the pipeline stages described by this {@code PipelineHelper} to + * the the portion of the input described by the provided + * {@code Spliterator}, if known. If not known or known infinite, will + * return {@code -1}. + * + * @apiNote + * The exact output size is known if the {@code Spliterator} has the + * {@code SIZED} characteristic, and the operation flags + * {@link StreamOpFlag#SIZED} is known on the combined stream and operation + * flags. + * + * @param spliterator the spliterator describing the relevant portion of the + * source data + * @return the exact size if known, or -1 if infinite or unknown + */ + abstract long exactOutputSizeIfKnown(Spliterator spliterator); + + /** + * Applies the pipeline stages described by this {@code PipelineHelper} to + * the provided {@code Spliterator} and send the results to the provided + * {@code Sink}. + * + * @implSpec + * The implementation behaves as if: + *

{@code
+     *     intoWrapped(wrapSink(sink), spliterator);
+     * }
+ * + * @param sink the {@code Sink} to receive the results + * @param spliterator the spliterator describing the source input to process + */ + abstract> S wrapAndCopyInto(S sink, Spliterator spliterator); + + /** + * Pushes elements obtained from the {@code Spliterator} into the provided + * {@code Sink}. If the stream pipeline is known to have short-circuiting + * stages in it (see {@link StreamOpFlag#SHORT_CIRCUIT}), the + * {@link Sink#cancellationRequested()} is checked after each + * element, stopping if cancellation is requested. + * + * @implSpec + * This method conforms to the {@code Sink} protocol of calling + * {@code Sink.begin} before pushing elements, via {@code Sink.accept}, and + * calling {@code Sink.end} after all elements have been pushed. + * + * @param wrappedSink the destination {@code Sink} + * @param spliterator the source {@code Spliterator} + */ + abstract void copyInto(Sink wrappedSink, Spliterator spliterator); + + /** + * Pushes elements obtained from the {@code Spliterator} into the provided + * {@code Sink}, checking {@link Sink#cancellationRequested()} after each + * element, and stopping if cancellation is requested. + * + * @implSpec + * This method conforms to the {@code Sink} protocol of calling + * {@code Sink.begin} before pushing elements, via {@code Sink.accept}, and + * calling {@code Sink.end} after all elements have been pushed or if + * cancellation is requested. + * + * @param wrappedSink the destination {@code Sink} + * @param spliterator the source {@code Spliterator} + */ + abstract void copyIntoWithCancel(Sink wrappedSink, Spliterator spliterator); + + /** + * Takes a {@code Sink} that accepts elements of the output type of the + * {@code PipelineHelper}, and wrap it with a {@code Sink} that accepts + * elements of the input type and implements all the intermediate operations + * described by this {@code PipelineHelper}, delivering the result into the + * provided {@code Sink}. + * + * @param sink the {@code Sink} to receive the results + * @return a {@code Sink} that implements the pipeline stages and sends + * results to the provided {@code Sink} + */ + abstract Sink wrapSink(Sink sink); + + /** + * Constructs a @{link Node.Builder} compatible with the output shape of + * this {@code PipelineHelper}. + * + * @param exactSizeIfKnown if >=0 then a builder will be created that has a + * fixed capacity of exactly sizeIfKnown elements; if < 0 then the + * builder has variable capacity. A fixed capacity builder will fail + * if an element is added after the builder has reached capacity. + * @param generator a factory function for array instances + * @return a {@code Node.Builder} compatible with the output shape of this + * {@code PipelineHelper} + */ + abstract Node.Builder makeNodeBuilder(long exactSizeIfKnown, + IntFunction generator); + + /** + * Collects all output elements resulting from applying the pipeline stages + * to the source {@code Spliterator} into a {@code Node}. + * + * @implNote + * If the pipeline has no intermediate operations and the source is backed + * by a {@code Node} then that {@code Node} will be returned (or flattened + * and then returned). This reduces copying for a pipeline consisting of a + * stateful operation followed by a terminal operation that returns an + * array, such as: + *
{@code
+     *     stream.sorted().toArray();
+     * }
+ * + * @param spliterator the source {@code Spliterator} + * @param flatten if true and the pipeline is a parallel pipeline then the + * {@code Node} returned will contain no children, otherwise the + * {@code Node} may represent the root in a tree that reflects the + * shape of the computation tree. + * @param generator a factory function for array instances + * @return the {@code Node} containing all output elements + */ + abstract Node evaluate(Spliterator spliterator, + boolean flatten, + IntFunction generator); +} diff --git a/jdk/src/share/classes/java/util/stream/Sink.java b/jdk/src/share/classes/java/util/stream/Sink.java new file mode 100644 index 00000000000..33e256c7b9b --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/Sink.java @@ -0,0 +1,362 @@ +/* + * 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.Objects; +import java.util.function.Consumer; +import java.util.function.DoubleConsumer; +import java.util.function.IntConsumer; +import java.util.function.LongConsumer; + +/** + * An extension of {@link Consumer} used to conduct values through the stages of + * a stream pipeline, with additional methods to manage size information, + * control flow, etc. Before calling the {@code accept()} method on a + * {@code Sink} for the first time, you must first call the {@code begin()} + * method to inform it that data is coming (optionally informing the sink how + * much data is coming), and after all data has been sent, you must call the + * {@code end()} method. After calling {@code end()}, you should not call + * {@code accept()} without again calling {@code begin()}. {@code Sink} also + * offers a mechanism by which the sink can cooperatively signal that it does + * not wish to receive any more data (the {@code cancellationRequested()} + * method), which a source can poll before sending more data to the + * {@code Sink}. + * + *

A sink may be in one of two states: an initial state and an active state. + * It starts out in the initial state; the {@code begin()} method transitions + * it to the active state, and the {@code end()} method transitions it back into + * the initial state, where it can be re-used. Data-accepting methods (such as + * {@code accept()} are only valid in the active state. + * + * @apiNote + * A stream pipeline consists of a source, zero or more intermediate stages + * (such as filtering or mapping), and a terminal stage, such as reduction or + * for-each. For concreteness, consider the pipeline: + * + *

{@code
+ *     int longestStringLengthStartingWithA
+ *         = strings.stream()
+ *                  .filter(s -> s.startsWith("A"))
+ *                  .mapToInt(String::length)
+ *                  .max();
+ * }
+ * + *

Here, we have three stages, filtering, mapping, and reducing. The + * filtering stage consumes strings and emits a subset of those strings; the + * mapping stage consumes strings and emits ints; the reduction stage consumes + * those ints and computes the maximal value. + * + *

A {@code Sink} instance is used to represent each stage of this pipeline, + * whether the stage accepts objects, ints, longs, or doubles. Sink has entry + * points for {@code accept(Object)}, {@code accept(int)}, etc, so that we do + * not need a specialized interface for each primitive specialization. (It + * might be called a "kitchen sink" for this omnivorous tendency.) The entry + * point to the pipeline is the {@code Sink} for the filtering stage, which + * sends some elements "downstream" -- into the {@code Sink} for the mapping + * stage, which in turn sends integral values downstream into the {@code Sink} + * for the reduction stage. The {@code Sink} implementations associated with a + * given stage is expected to know the data type for the next stage, and call + * the correct {@code accept} method on its downstream {@code Sink}. Similarly, + * each stage must implement the correct {@code accept} method corresponding to + * the data type it accepts. + * + *

The specialized subtypes such as {@link Sink.OfInt} override + * {@code accept(Object)} to call the appropriate primitive specialization of + * {@code accept}, implement the appropriate primitive specialization of + * {@code Consumer}, and re-abstract the appropriate primitive specialization of + * {@code accept}. + * + *

The chaining subtypes such as {@link ChainedInt} not only implement + * {@code Sink.OfInt}, but also maintain a {@code downstream} field which + * represents the downstream {@code Sink}, and implement the methods + * {@code begin()}, {@code end()}, and {@code cancellationRequested()} to + * delegate to the downstream {@code Sink}. Most implementations of + * intermediate operations will use these chaining wrappers. For example, the + * mapping stage in the above example would look like: + * + *

{@code
+ *     IntSink is = new Sink.ChainedReference(sink) {
+ *         public void accept(U u) {
+ *             downstream.accept(mapper.applyAsInt(u));
+ *         }
+ *     };
+ * }
+ * + *

Here, we implement {@code Sink.ChainedReference}, meaning that we expect + * to receive elements of type {@code U} as input, and pass the downstream sink + * to the constructor. Because the next stage expects to receive integers, we + * must call the {@code accept(int)} method when emitting values to the downstream. + * The {@code accept()} method applies the mapping function from {@code U} to + * {@code int} and passes the resulting value to the downstream {@code Sink}. + * + * @param type of elements for value streams + * @since 1.8 + */ +interface Sink extends Consumer { + /** + * Resets the sink state to receive a fresh data set. This must be called + * before sending any data to the sink. After calling {@link #end()}, + * you may call this method to reset the sink for another calculation. + * @param size The exact size of the data to be pushed downstream, if + * known or {@code -1} if unknown or infinite. + * + *

Prior to this call, the sink must be in the initial state, and after + * this call it is in the active state. + */ + default void begin(long size) {} + + /** + * Indicates that all elements have been pushed. If the {@code Sink} is + * stateful, it should send any stored state downstream at this time, and + * should clear any accumulated state (and associated resources). + * + *

Prior to this call, the sink must be in the active state, and after + * this call it is returned to the initial state. + */ + default void end() {} + + /** + * Indicates that this {@code Sink} does not wish to receive any more data. + * + * @implSpec The default implementation always returns false. + * + * @return true if cancellation is requested + */ + default boolean cancellationRequested() { + return false; + } + + /** + * Accepts an int value. + * + * @implSpec The default implementation throws IllegalStateException. + * + * @throws IllegalStateException if this sink does not accept int values + */ + default void accept(int value) { + throw new IllegalStateException("called wrong accept method"); + } + + /** + * Accepts a long value. + * + * @implSpec The default implementation throws IllegalStateException. + * + * @throws IllegalStateException if this sink does not accept long values + */ + default void accept(long value) { + throw new IllegalStateException("called wrong accept method"); + } + + /** + * Accepts a double value. + * + * @implSpec The default implementation throws IllegalStateException. + * + * @throws IllegalStateException if this sink does not accept double values + */ + default void accept(double value) { + throw new IllegalStateException("called wrong accept method"); + } + + /** + * {@code Sink} that implements {@code Sink}, re-abstracts + * {@code accept(int)}, and wires {@code accept(Integer)} to bridge to + * {@code accept(int)}. + */ + interface OfInt extends Sink, IntConsumer { + @Override + void accept(int value); + + @Override + default void accept(Integer i) { + if (Tripwire.ENABLED) + Tripwire.trip(getClass(), "{0} calling Sink.OfInt.accept(Integer)"); + accept(i.intValue()); + } + } + + /** + * {@code Sink} that implements {@code Sink}, re-abstracts + * {@code accept(long)}, and wires {@code accept(Long)} to bridge to + * {@code accept(long)}. + */ + interface OfLong extends Sink, LongConsumer { + @Override + void accept(long value); + + @Override + default void accept(Long i) { + if (Tripwire.ENABLED) + Tripwire.trip(getClass(), "{0} calling Sink.OfLong.accept(Long)"); + accept(i.longValue()); + } + } + + /** + * {@code Sink} that implements {@code Sink}, re-abstracts + * {@code accept(double)}, and wires {@code accept(Double)} to bridge to + * {@code accept(double)}. + */ + interface OfDouble extends Sink, DoubleConsumer { + @Override + void accept(double value); + + @Override + default void accept(Double i) { + if (Tripwire.ENABLED) + Tripwire.trip(getClass(), "{0} calling Sink.OfDouble.accept(Double)"); + accept(i.doubleValue()); + } + } + + /** + * Abstract {@code Sink} implementation for creating chains of + * sinks. The {@code begin}, {@code end}, and + * {@code cancellationRequested} methods are wired to chain to the + * downstream {@code Sink}. This implementation takes a downstream + * {@code Sink} of unknown input shape and produces a {@code Sink}. The + * implementation of the {@code accept()} method must call the correct + * {@code accept()} method on the downstream {@code Sink}. + */ + static abstract class ChainedReference implements Sink { + protected final Sink downstream; + + public ChainedReference(Sink downstream) { + this.downstream = Objects.requireNonNull(downstream); + } + + @Override + public void begin(long size) { + downstream.begin(size); + } + + @Override + public void end() { + downstream.end(); + } + + @Override + public boolean cancellationRequested() { + return downstream.cancellationRequested(); + } + } + + /** + * Abstract {@code Sink} implementation designed for creating chains of + * sinks. The {@code begin}, {@code end}, and + * {@code cancellationRequested} methods are wired to chain to the + * downstream {@code Sink}. This implementation takes a downstream + * {@code Sink} of unknown input shape and produces a {@code Sink.OfInt}. + * The implementation of the {@code accept()} method must call the correct + * {@code accept()} method on the downstream {@code Sink}. + */ + static abstract class ChainedInt implements Sink.OfInt { + protected final Sink downstream; + + public ChainedInt(Sink downstream) { + this.downstream = Objects.requireNonNull(downstream); + } + + @Override + public void begin(long size) { + downstream.begin(size); + } + + @Override + public void end() { + downstream.end(); + } + + @Override + public boolean cancellationRequested() { + return downstream.cancellationRequested(); + } + } + + /** + * Abstract {@code Sink} implementation designed for creating chains of + * sinks. The {@code begin}, {@code end}, and + * {@code cancellationRequested} methods are wired to chain to the + * downstream {@code Sink}. This implementation takes a downstream + * {@code Sink} of unknown input shape and produces a {@code Sink.OfLong}. + * The implementation of the {@code accept()} method must call the correct + * {@code accept()} method on the downstream {@code Sink}. + */ + static abstract class ChainedLong implements Sink.OfLong { + protected final Sink downstream; + + public ChainedLong(Sink downstream) { + this.downstream = Objects.requireNonNull(downstream); + } + + @Override + public void begin(long size) { + downstream.begin(size); + } + + @Override + public void end() { + downstream.end(); + } + + @Override + public boolean cancellationRequested() { + return downstream.cancellationRequested(); + } + } + + /** + * Abstract {@code Sink} implementation designed for creating chains of + * sinks. The {@code begin}, {@code end}, and + * {@code cancellationRequested} methods are wired to chain to the + * downstream {@code Sink}. This implementation takes a downstream + * {@code Sink} of unknown input shape and produces a {@code Sink.OfDouble}. + * The implementation of the {@code accept()} method must call the correct + * {@code accept()} method on the downstream {@code Sink}. + */ + static abstract class ChainedDouble implements Sink.OfDouble { + protected final Sink downstream; + + public ChainedDouble(Sink downstream) { + this.downstream = Objects.requireNonNull(downstream); + } + + @Override + public void begin(long size) { + downstream.begin(size); + } + + @Override + public void end() { + downstream.end(); + } + + @Override + public boolean cancellationRequested() { + return downstream.cancellationRequested(); + } + } +} diff --git a/jdk/src/share/classes/java/util/stream/Stream.java b/jdk/src/share/classes/java/util/stream/Stream.java new file mode 100644 index 00000000000..0624f50b29d --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/Stream.java @@ -0,0 +1,782 @@ +/* + * 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.Optional; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToDoubleFunction; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; + +// @@@ Specification to-do list @@@ +// - Describe the difference between sequential and parallel streams +// - More general information about reduce, better definitions for associativity, more description of +// how reduce employs parallelism, more examples +// - Role of stream flags in various operations, specifically ordering +// - Whether each op preserves encounter order +// @@@ Specification to-do list @@@ + +/** + * A sequence of elements supporting sequential and parallel bulk operations. + * Streams support lazy intermediate operations (transforming a stream to + * another stream) such as {@code filter} and {@code map}, and terminal + * operations (consuming the contents of a stream to produce a result or + * side-effect), such as {@code forEach}, {@code findFirst}, and {@code + * iterator}. Once an operation has been performed on a stream, it + * is considered consumed and no longer usable for other operations. + * + *

For sequential stream pipelines, all operations are performed in the + * encounter order of the pipeline + * source, if the pipeline source has a defined encounter order. + * + *

For parallel stream pipelines, unless otherwise specified, intermediate + * stream operations preserve the + * encounter order of their source, and terminal operations + * respect the encounter order of their source, if the source + * has an encounter order. Provided that and parameters to stream operations + * satisfy the non-interference + * requirements, and excepting differences arising from the absence of + * a defined encounter order, the result of a stream pipeline should be the + * stable across multiple executions of the same operations on the same source. + * However, the timing and thread in which side-effects occur (for those + * operations which are allowed to produce side-effects, such as + * {@link #forEach(Consumer)}), are explicitly nondeterministic for parallel + * execution of stream pipelines. + * + *

Unless otherwise noted, passing a {@code null} argument to any stream + * method may result in a {@link NullPointerException}. + * + * @apiNote + * Streams are not data structures; they do not manage the storage for their + * elements, nor do they support access to individual elements. However, + * you can use the {@link #iterator()} or {@link #spliterator()} operations to + * perform a controlled traversal. + * + * @param type of elements + * @since 1.8 + * @see java.util.stream + */ +public interface Stream extends BaseStream> { + + /** + * Returns a stream consisting of the elements of this stream that match + * the given predicate. + * + *

This is an intermediate + * operation. + * + * @param predicate a + * non-interfering, stateless predicate to apply to + * each element to determine if it should be included + * @return the new stream + */ + Stream filter(Predicate predicate); + + /** + * Returns a stream consisting of the results of applying the given + * function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param The element type of the new stream + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + Stream map(Function mapper); + + /** + * Returns an {@code IntStream} consisting of the results of applying the + * given function to the elements of this stream. + * + *

This is an + * intermediate operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + IntStream mapToInt(ToIntFunction mapper); + + /** + * Returns a {@code LongStream} consisting of the results of applying the + * given function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + LongStream mapToLong(ToLongFunction mapper); + + /** + * Returns a {@code DoubleStream} consisting of the results of applying the + * given function to the elements of this stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element + * @return the new stream + */ + DoubleStream mapToDouble(ToDoubleFunction mapper); + + /** + * Returns a stream consisting of the results of replacing each element of + * this stream with the contents of the stream produced by applying the + * provided mapping function to each element. If the result of the mapping + * function is {@code null}, this is treated as if the result is an empty + * stream. + * + *

This is an intermediate + * operation. + * + * @apiNote + * The {@code flatMap()} operation has the effect of applying a one-to-many + * tranformation to the elements of the stream, and then flattening the + * resulting elements into a new stream. For example, if {@code orders} + * is a stream of purchase orders, and each purchase order contains a + * collection of line items, then the following produces a stream of line + * items: + *

{@code
+     *     orderStream.flatMap(order -> order.getLineItems().stream())...
+     * }
+ * + * @param The element type of the new stream + * @param mapper a + * non-interfering, stateless function to apply to each + * element which produces a stream of new values + * @return the new stream + */ + Stream flatMap(Function> mapper); + + /** + * Returns an {@code IntStream} consisting of the results of replacing each + * element of this stream with the contents of the stream produced by + * applying the provided mapping function to each element. If the result of + * the mapping function is {@code null}, this is treated as if the result is + * an empty stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element which produces a stream of new values + * @return the new stream + */ + IntStream flatMapToInt(Function mapper); + + /** + * Returns a {@code LongStream} consisting of the results of replacing each + * element of this stream with the contents of the stream produced + * by applying the provided mapping function to each element. If the result + * of the mapping function is {@code null}, this is treated as if the + * result is an empty stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to + * each element which produces a stream of new values + * @return the new stream + */ + LongStream flatMapToLong(Function mapper); + + /** + * Returns a {@code DoubleStream} consisting of the results of replacing each + * element of this stream with the contents of the stream produced + * by applying the provided mapping function to each element. If the result + * of the mapping function is {@code null}, this is treated as if the result + * is an empty stream. + * + *

This is an intermediate + * operation. + * + * @param mapper a + * non-interfering, stateless function to apply to each + * element which produces a stream of new values + * @return the new stream + */ + DoubleStream flatMapToDouble(Function mapper); + + /** + * Returns a stream consisting of the distinct elements (according to + * {@link Object#equals(Object)}) of this stream. + * + *

This is a stateful + * intermediate operation. + * + * @return the new stream + */ + Stream distinct(); + + /** + * Returns a stream consisting of the elements of this stream, sorted + * according to natural order. If the elements of this stream are not + * {@code Comparable}, a {@code java.lang.ClassCastException} may be thrown + * when the stream pipeline is executed. + * + *

This is a stateful + * intermediate operation. + * + * @return the new stream + */ + Stream sorted(); + + /** + * Returns a stream consisting of the elements of this stream, sorted + * according to the provided {@code Comparator}. + * + *

This is a stateful + * intermediate operation. + * + * @param comparator a + * non-interfering, stateless {@code Comparator} to + * be used to compare stream elements + * @return the new stream + */ + Stream sorted(Comparator comparator); + + /** + * Returns a stream consisting of the elements of this stream, additionally + * performing the provided action on each element as elements are consumed + * from the resulting stream. + * + *

This is an intermediate + * operation. + * + *

For parallel stream pipelines, the action may be called at + * whatever time and in whatever thread the element is made available by the + * upstream operation. If the action modifies shared state, + * it is responsible for providing the required synchronization. + * + * @apiNote This method exists mainly to support debugging, where you want + * to see the elements as they flow past a certain point in a pipeline: + *

{@code
+     *     list.stream()
+     *         .filter(filteringFunction)
+     *         .peek(e -> {System.out.println("Filtered value: " + e); });
+     *         .map(mappingFunction)
+     *         .peek(e -> {System.out.println("Mapped value: " + e); });
+     *         .collect(Collectors.intoList());
+     * }
+ * + * @param consumer a + * non-interfering action to perform on the elements as + * they are consumed from the stream + * @return the new stream + */ + Stream peek(Consumer consumer); + + /** + * Returns a stream consisting of the elements of this stream, truncated + * to be no longer than {@code maxSize} in length. + * + *

This is a short-circuiting + * stateful intermediate operation. + * + * @param maxSize the number of elements the stream should be limited to + * @return the new stream + * @throws IllegalArgumentException if {@code maxSize} is negative + */ + Stream limit(long maxSize); + + /** + * Returns a stream consisting of the remaining elements of this stream + * after indexing {@code startInclusive} elements into the stream. If the + * {@code startInclusive} index lies past the end of this stream then an + * empty stream will be returned. + * + *

This is a stateful + * intermediate operation. + * + * @param startInclusive the number of leading elements to skip + * @return the new stream + * @throws IllegalArgumentException if {@code startInclusive} is negative + */ + Stream substream(long startInclusive); + + /** + * Returns a stream consisting of the remaining elements of this stream + * after indexing {@code startInclusive} elements into the stream and + * truncated to contain no more than {@code endExclusive - startInclusive} + * elements. If the {@code startInclusive} index lies past the end + * of this stream then an empty stream will be returned. + * + *

This is a short-circuiting + * stateful intermediate operation. + * + * @param startInclusive the starting position of the substream, inclusive + * @param endExclusive the ending position of the substream, exclusive + * @return the new stream + * @throws IllegalArgumentException if {@code startInclusive} or + * {@code endExclusive} is negative or {@code startInclusive} is greater + * than {@code endExclusive} + */ + Stream substream(long startInclusive, long endExclusive); + + /** + * Performs an action for each element of this stream. + * + *

This is a terminal + * operation. + * + *

For parallel stream pipelines, this operation does not + * guarantee to respect the encounter order of the stream, as doing so + * would sacrifice the benefit of parallelism. For any given element, the + * action may be performed at whatever time and in whatever thread the + * library chooses. If the action accesses shared state, it is + * responsible for providing the required synchronization. + * + * @param action a + * non-interfering action to perform on the elements + */ + void forEach(Consumer action); + + /** + * Performs an action for each element of this stream, guaranteeing that + * each element is processed in encounter order for streams that have a + * defined encounter order. + * + *

This is a terminal + * operation. + * + * @param action a + * non-interfering action to perform on the elements + * @see #forEach(Consumer) + */ + void forEachOrdered(Consumer action); + + /** + * Returns an array containing the elements of this stream. + * + *

This is a terminal + * operation. + * + * @return an array containing the elements of this stream + */ + Object[] toArray(); + + /** + * Returns an array containing the elements of this stream, using the + * provided {@code generator} function to allocate the returned array. + * + *

This is a terminal + * operation. + * + * @param the element type of the resulting array + * @param generator a function which produces a new array of the desired + * type and the provided length + * @return an array containing the elements in this stream + * @throws ArrayStoreException if the runtime type of the array returned + * from the array generator is not a supertype of the runtime type of every + * element in this stream + */ + A[] toArray(IntFunction generator); + + /** + * Performs a reduction on the + * elements of this stream, using the provided identity value and an + * associative + * accumulation function, and returns the reduced value. This is equivalent + * to: + *

{@code
+     *     T result = identity;
+     *     for (T element : this stream)
+     *         result = accumulator.apply(result, element)
+     *     return result;
+     * }
+ * + * but is not constrained to execute sequentially. + * + *

The {@code identity} value must be an identity for the accumulator + * function. This means that for all {@code t}, + * {@code accumulator.apply(identity, t)} is equal to {@code t}. + * The {@code accumulator} function must be an + * associative function. + * + *

This is a terminal + * operation. + * + * @apiNote Sum, min, max, average, and string concatenation are all special + * cases of reduction. Summing a stream of numbers can be expressed as: + * + *

{@code
+     *     Integer sum = integers.reduce(0, (a, b) -> a+b);
+     * }
+ * + * or more compactly: + * + *
{@code
+     *     Integer sum = integers.reduce(0, Integer::sum);
+     * }
+ * + *

While this may seem a more roundabout way to perform an aggregation + * compared to simply mutating a running total in a loop, reduction + * operations parallelize more gracefully, without needing additional + * synchronization and with greatly reduced risk of data races. + * + * @param identity the identity value for the accumulating function + * @param accumulator an associative + * non-interfering, + * stateless function for combining two values + * @return the result of the reduction + */ + T reduce(T identity, BinaryOperator accumulator); + + /** + * Performs a reduction on the + * elements of this stream, using an + * associative accumulation + * function, and returns an {@code Optional} describing the reduced value, + * if any. This is equivalent to: + *

{@code
+     *     boolean foundAny = false;
+     *     T result = null;
+     *     for (T element : this stream) {
+     *         if (!foundAny) {
+     *             foundAny = true;
+     *             result = element;
+     *         }
+     *         else
+     *             result = accumulator.apply(result, element);
+     *     }
+     *     return foundAny ? Optional.of(result) : Optional.empty();
+     * }
+ * + * but is not constrained to execute sequentially. + * + *

The {@code accumulator} function must be an + * associative function. + * + *

This is a terminal + * operation. + * + * @param accumulator an associative + * non-interfering, + * stateless function for combining two values + * @return the result of the reduction + * @see #reduce(Object, BinaryOperator) + * @see #min(java.util.Comparator) + * @see #max(java.util.Comparator) + */ + Optional reduce(BinaryOperator accumulator); + + /** + * Performs a reduction on the + * elements of this stream, using the provided identity, accumulation + * function, and a combining functions. This is equivalent to: + *

{@code
+     *     U result = identity;
+     *     for (T element : this stream)
+     *         result = accumulator.apply(result, element)
+     *     return result;
+     * }
+ * + * but is not constrained to execute sequentially. + * + *

The {@code identity} value must be an identity for the combiner + * function. This means that for all {@code u}, {@code combiner(identity, u)} + * is equal to {@code u}. Additionally, the {@code combiner} function + * must be compatible with the {@code accumulator} function; for all + * {@code u} and {@code t}, the following must hold: + *

{@code
+     *     combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
+     * }
+ * + *

This is a terminal + * operation. + * + * @apiNote Many reductions using this form can be represented more simply + * by an explicit combination of {@code map} and {@code reduce} operations. + * The {@code accumulator} function acts as a fused mapper and accumulator, + * which can sometimes be more efficient than separate mapping and reduction, + * such as in the case where knowing the previously reduced value allows you + * to avoid some computation. + * + * @param The type of the result + * @param identity the identity value for the combiner function + * @param accumulator an associative + * non-interfering, + * stateless function for incorporating an additional + * element into a result + * @param combiner an associative + * non-interfering, + * stateless function for combining two values, which + * must be compatible with the accumulator function + * @return the result of the reduction + * @see #reduce(BinaryOperator) + * @see #reduce(Object, BinaryOperator) + */ + U reduce(U identity, + BiFunction accumulator, + BinaryOperator combiner); + + /** + * Performs a mutable + * reduction operation on the elements of this stream. A mutable + * reduction is one in which the reduced value is a mutable value holder, + * such as an {@code ArrayList}, and elements are incorporated by updating + * the state of the result, rather than by replacing the result. This + * produces a result equivalent to: + *

{@code
+     *     R result = resultFactory.get();
+     *     for (T element : this stream)
+     *         accumulator.accept(result, element);
+     *     return result;
+     * }
+ * + *

Like {@link #reduce(Object, BinaryOperator)}, {@code collect} operations + * can be parallelized without requiring additional synchronization. + * + *

This is a terminal + * operation. + * + * @apiNote There are many existing classes in the JDK whose signatures are + * a good match for use as arguments to {@code collect()}. For example, + * the following will accumulate strings into an ArrayList: + *

{@code
+     *     List asList = stringStream.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
+     * }
+ * + *

The following will take a stream of strings and concatenates them into a + * single string: + *

{@code
+     *     String concat = stringStream.collect(StringBuilder::new, StringBuilder::append,
+     *                                          StringBuilder::append)
+     *                                 .toString();
+     * }
+ * + * @param type of the result + * @param resultFactory a function that creates a new result container. + * For a parallel execution, this function may be + * called multiple times and must return a fresh value + * each time. + * @param accumulator an associative + * non-interfering, + * stateless function for incorporating an additional + * element into a result + * @param combiner an associative + * non-interfering, + * stateless function for combining two values, which + * must be compatible with the accumulator function + * @return the result of the reduction + */ + R collect(Supplier resultFactory, + BiConsumer accumulator, + BiConsumer combiner); + + /** + * Performs a mutable + * reduction operation on the elements of this stream using a + * {@code Collector} object to describe the reduction. A {@code Collector} + * encapsulates the functions used as arguments to + * {@link #collect(Supplier, BiConsumer, BiConsumer)}, allowing for reuse of + * collection strategies, and composition of collect operations such as + * multiple-level grouping or partitioning. + * + *

This is a terminal + * operation. + * + *

When executed in parallel, multiple intermediate results may be + * instantiated, populated, and merged, so as to maintain isolation of + * mutable data structures. Therefore, even when executed in parallel + * with non-thread-safe data structures (such as {@code ArrayList}), no + * additional synchronization is needed for a parallel reduction. + * + * @apiNote + * The following will accumulate strings into an ArrayList: + *

{@code
+     *     List asList = stringStream.collect(Collectors.toList());
+     * }
+ * + *

The following will classify {@code Person} objects by city: + *

{@code
+     *     Map> peopleByCity
+     *         = personStream.collect(Collectors.groupBy(Person::getCity));
+     * }
+ * + *

The following will classify {@code Person} objects by state and city, + * cascading two {@code Collector}s together: + *

{@code
+     *     Map>> peopleByStateAndCity
+     *         = personStream.collect(Collectors.groupBy(Person::getState,
+     *                                                   Collectors.groupBy(Person::getCity)));
+     * }
+ * + * @param the type of the result + * @param collector the {@code Collector} describing the reduction + * @return the result of the reduction + * @see #collect(Supplier, BiConsumer, BiConsumer) + * @see Collectors + */ + R collect(Collector collector); + + /** + * Returns the minimum element of this stream according to the provided + * {@code Comparator}. This is a special case of a + * reduction. + * + *

This is a terminal operation. + * + * @param comparator a non-interfering, + * stateless {@code Comparator} to use to compare + * elements of this stream + * @return an {@code Optional} describing the minimum element of this stream, + * or an empty {@code Optional} if the stream is empty + */ + Optional min(Comparator comparator); + + /** + * Returns the maximum element of this stream according to the provided + * {@code Comparator}. This is a special case of a + * reduction. + * + *

This is a terminal + * operation. + * + * @param comparator a non-interfering, + * stateless {@code Comparator} to use to compare + * elements of this stream + * @return an {@code Optional} describing the maximum element of this stream, + * or an empty {@code Optional} if the stream is empty + */ + Optional max(Comparator comparator); + + /** + * Returns the count of elements in this stream. This is a special case of + * a reduction and is + * equivalent to: + *

{@code
+     *     return mapToLong(e -> 1L).sum();
+     * }
+ * + *

This is a terminal operation. + * + * @return the count of elements in this stream + */ + long count(); + + /** + * Returns whether any elements of this stream match the provided + * predicate. May not evaluate the predicate on all elements if not + * necessary for determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if any elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean anyMatch(Predicate predicate); + + /** + * Returns whether all elements of this stream match the provided predicate. + * May not evaluate the predicate on all elements if not necessary for + * determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if all elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean allMatch(Predicate predicate); + + /** + * Returns whether no elements of this stream match the provided predicate. + * May not evaluate the predicate on all elements if not necessary for + * determining the result. + * + *

This is a short-circuiting + * terminal operation. + * + * @param predicate a non-interfering, + * stateless predicate to apply to elements of this + * stream + * @return {@code true} if no elements of the stream match the provided + * predicate otherwise {@code false} + */ + boolean noneMatch(Predicate predicate); + + /** + * Returns an {@link Optional} describing the first element of this stream + * (in the encounter order), or an empty {@code Optional} if the stream is + * empty. If the stream has no encounter order, than any element may be + * returned. + * + *

This is a short-circuiting + * terminal operation. + * + * @return an {@code Optional} describing the first element of this stream, + * or an empty {@code Optional} if the stream is empty + * @throws NullPointerException if the element selected is null + */ + Optional findFirst(); + + /** + * Returns an {@link Optional} describing some element of the stream, or an + * empty {@code Optional} if the stream is empty. + * + *

This is a short-circuiting + * terminal operation. + * + *

The behavior of this operation is explicitly nondeterministic; it is + * free to select any element in the stream. This is to allow for maximal + * performance in parallel operations; the cost is that multiple invocations + * on the same source may not return the same result. (If the first element + * in the encounter order is desired, use {@link #findFirst()} instead.) + * + * @return an {@code Optional} describing some element of this stream, or an + * empty {@code Optional} if the stream is empty + * @throws NullPointerException if the element selected is null + * @see #findFirst() + */ + Optional findAny(); +} diff --git a/jdk/src/share/classes/java/util/stream/StreamOpFlag.java b/jdk/src/share/classes/java/util/stream/StreamOpFlag.java new file mode 100644 index 00000000000..8fecfed4df4 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/StreamOpFlag.java @@ -0,0 +1,753 @@ +/* + * 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.EnumMap; +import java.util.Map; +import java.util.Spliterator; + +/** + * Flags corresponding to characteristics of streams and operations. Flags are + * utilized by the stream framework to control, specialize or optimize + * computation. + * + *

+ * Stream flags may be used to describe characteristics of several different + * entities associated with streams: stream sources, intermediate operations, + * and terminal operations. Not all stream flags are meaningful for all + * entities; the following table summarizes which flags are meaningful in what + * contexts: + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Type Characteristics
 {@code DISTINCT}{@code SORTED}{@code ORDERED}{@code SIZED}{@code SHORT_CIRCUIT}
Stream sourceYYYYN
Intermediate operationPCIPCIPCIPCPI
Terminal operationNNPCNPI
Legend 
FlagMeaning
YAllowed
NInvalid
PPreserves
CClears
IInjects
+ *
+ * + *

In the above table, "PCI" means "may preserve, clear, or inject"; "PC" + * means "may preserve or clear", "PI" means "may preserve or inject", and "N" + * means "not valid". + * + *

Stream flags are represented by unioned bit sets, so that a single word + * may describe all the characteristics of a given stream entity, and that, for + * example, the flags for a stream source can be efficiently combined with the + * flags for later operations on that stream. + * + *

The bit masks {@link #STREAM_MASK}, {@link #OP_MASK}, and + * {@link #TERMINAL_OP_MASK} can be ANDed with a bit set of stream flags to + * produce a mask containing only the valid flags for that entity type. + * + *

When describing a stream source, one only need describe what + * characteristics that stream has; when describing a stream operation, one need + * describe whether the operation preserves, injects, or clears that + * characteristic. Accordingly, two bits are used for each flag, so as to allow + * representing not only the presence of of a characteristic, but how an + * operation modifies that characteristic. There are two common forms in which + * flag bits are combined into an {@code int} bit set. Stream flags + * are a unioned bit set constructed by ORing the enum characteristic values of + * {@link #set()} (or, more commonly, ORing the corresponding static named + * constants prefixed with {@code IS_}). Operation flags are a unioned + * bit set constructed by ORing the enum characteristic values of {@link #set()} + * or {@link #clear()} (to inject, or clear, respectively, the corresponding + * flag), or more commonly ORing the corresponding named constants prefixed with + * {@code IS_} or {@code NOT_}. Flags that are not marked with {@code IS_} or + * {@code NOT_} are implicitly treated as preserved. Care must be taken when + * combining bitsets that the correct combining operations are applied in the + * correct order. + * + *

+ * With the exception of {@link #SHORT_CIRCUIT}, stream characteristics can be + * derived from the equivalent {@link java.util.Spliterator} characteristics: + * {@link java.util.Spliterator#DISTINCT}, {@link java.util.Spliterator#SORTED}, + * {@link java.util.Spliterator#ORDERED}, and + * {@link java.util.Spliterator#SIZED}. A spliterator characteristics bit set + * can be converted to stream flags using the method + * {@link #fromCharacteristics(java.util.Spliterator)} and converted back using + * {@link #toCharacteristics(int)}. (The bit set + * {@link #SPLITERATOR_CHARACTERISTICS_MASK} is used to AND with a bit set to + * produce a valid spliterator characteristics bit set that can be converted to + * stream flags.) + * + *

+ * The source of a stream encapsulates a spliterator. The characteristics of + * that source spliterator when transformed to stream flags will be a proper + * subset of stream flags of that stream. + * For example: + *

 {@code
+ *     Spliterator s = ...;
+ *     Stream stream = Streams.stream(s);
+ *     flagsFromSplitr = fromCharacteristics(s.characteristics());
+ *     assert(flagsFromSplitr & stream.getStreamFlags() == flagsFromSplitr);
+ * }
+ * + *

+ * An intermediate operation, performed on an input stream to create a new + * output stream, may preserve, clear or inject stream or operation + * characteristics. Similarly, a terminal operation, performed on an input + * stream to produce an output result may preserve, clear or inject stream or + * operation characteristics. Preservation means that if that characteristic + * is present on the input, then it is also present on the output. Clearing + * means that the characteristic is not present on the output regardless of the + * input. Injection means that the characteristic is present on the output + * regardless of the input. If a characteristic is not cleared or injected then + * it is implicitly preserved. + * + *

+ * A pipeline consists of a stream source encapsulating a spliterator, one or + * more intermediate operations, and finally a terminal operation that produces + * a result. At each stage of the pipeline, a combined stream and operation + * flags can be calculated, using {@link #combineOpFlags(int, int)}. Such flags + * ensure that preservation, clearing and injecting information is retained at + * each stage. + * + * The combined stream and operation flags for the source stage of the pipeline + * is calculated as follows: + *

 {@code
+ *     int flagsForSourceStage = combineOpFlags(sourceFlags, INITIAL_OPS_VALUE);
+ * }
+ * + * The combined stream and operation flags of each subsequent intermediate + * operation stage in the pipeline is calculated as follows: + *
 {@code
+ *     int flagsForThisStage = combineOpFlags(flagsForPreviousStage, thisOpFlags);
+ * }
+ * + * Finally the flags output from the last intermediate operation of the pipeline + * are combined with the operation flags of the terminal operation to produce + * the flags output from the pipeline. + * + *

Those flags can then be used to apply optimizations. For example, if + * {@code SIZED.isKnown(flags)} returns true then the stream size remains + * constant throughout the pipeline, this information can be utilized to + * pre-allocate data structures and combined with + * {@link java.util.Spliterator#SUBSIZED} that information can be utilized to + * perform concurrent in-place updates into a shared array. + * + * For specific details see the {@link AbstractPipeline} constructors. + * + * @since 1.8 + */ +enum StreamOpFlag { + + /* + * Each characteristic takes up 2 bits in a bit set to accommodate + * preserving, clearing and setting/injecting information. + * + * This applies to stream flags, intermediate/terminal operation flags, and + * combined stream and operation flags. Even though the former only requires + * 1 bit of information per characteristic, is it more efficient when + * combining flags to align set and inject bits. + * + * Characteristics belong to certain types, see the Type enum. Bit masks for + * the types are constructed as per the following table: + * + * DISTINCT SORTED ORDERED SIZED SHORT_CIRCUIT + * SPLITERATOR 01 01 01 01 00 + * STREAM 01 01 01 01 00 + * OP 11 11 11 10 01 + * TERMINAL_OP 00 00 10 00 01 + * UPSTREAM_TERMINAL_OP 00 00 10 00 00 + * + * 01 = set/inject + * 10 = clear + * 11 = preserve + * + * Construction of the columns is performed using a simple builder for + * non-zero values. + */ + + + // The following flags correspond to characteristics on Spliterator + // and the values MUST be equal. + // + + /** + * Characteristic value signifying that, for each pair of + * encountered elements in a stream {@code x, y}, {@code !x.equals(y)}. + *

+ * A stream may have this value or an intermediate operation can preserve, + * clear or inject this value. + */ + // 0, 0x00000001 + // Matches Spliterator.DISTINCT + DISTINCT(0, + set(Type.SPLITERATOR).set(Type.STREAM).setAndClear(Type.OP)), + + /** + * Characteristic value signifying that encounter order follows a natural + * sort order of comparable elements. + *

+ * A stream can have this value or an intermediate operation can preserve, + * clear or inject this value. + *

+ * Note: The {@link java.util.Spliterator#SORTED} characteristic can define + * a sort order with an associated non-null comparator. Augmenting flag + * state with addition properties such that those properties can be passed + * to operations requires some disruptive changes for a singular use-case. + * Furthermore, comparing comparators for equality beyond that of identity + * is likely to be unreliable. Therefore the {@code SORTED} characteristic + * for a defined non-natural sort order is not mapped internally to the + * {@code SORTED} flag. + */ + // 1, 0x00000004 + // Matches Spliterator.SORTED + SORTED(1, + set(Type.SPLITERATOR).set(Type.STREAM).setAndClear(Type.OP)), + + /** + * Characteristic value signifying that an encounter order is + * defined for stream elements. + *

+ * A stream can have this value, an intermediate operation can preserve, + * clear or inject this value, or a terminal operation can preserve or clear + * this value. + */ + // 2, 0x00000010 + // Matches Spliterator.ORDERED + ORDERED(2, + set(Type.SPLITERATOR).set(Type.STREAM).setAndClear(Type.OP).clear(Type.TERMINAL_OP) + .clear(Type.UPSTREAM_TERMINAL_OP)), + + /** + * Characteristic value signifying that size of the stream + * is of a known finite size that is equal to the known finite + * size of the source spliterator input to the first stream + * in the pipeline. + *

+ * A stream can have this value or an intermediate operation can preserve or + * clear this value. + */ + // 3, 0x00000040 + // Matches Spliterator.SIZED + SIZED(3, + set(Type.SPLITERATOR).set(Type.STREAM).clear(Type.OP)), + + // The following Spliterator characteristics are not currently used but a + // gap in the bit set is deliberately retained to enable corresponding + // stream flags if//when required without modification to other flag values. + // + // 4, 0x00000100 NONNULL(4, ... + // 5, 0x00000400 IMMUTABLE(5, ... + // 6, 0x00001000 CONCURRENT(6, ... + // 7, 0x00004000 SUBSIZED(7, ... + + // The following 4 flags are currently undefined and a free for any further + // spliterator characteristics. + // + // 8, 0x00010000 + // 9, 0x00040000 + // 10, 0x00100000 + // 11, 0x00400000 + + // The following flags are specific to streams and operations + // + + /** + * Characteristic value signifying that an operation may short-circuit the + * stream. + *

+ * An intermediate operation can preserve or inject this value, + * or a terminal operation can preserve or inject this value. + */ + // 12, 0x01000000 + SHORT_CIRCUIT(12, + set(Type.OP).set(Type.TERMINAL_OP)); + + // The following 2 flags are currently undefined and a free for any further + // stream flags if/when required + // + // 13, 0x04000000 + // 14, 0x10000000 + // 15, 0x40000000 + + /** + * Type of a flag + */ + enum Type { + /** + * The flag is associated with spliterator characteristics. + */ + SPLITERATOR, + + /** + * The flag is associated with stream flags. + */ + STREAM, + + /** + * The flag is associated with intermediate operation flags. + */ + OP, + + /** + * The flag is associated with terminal operation flags. + */ + TERMINAL_OP, + + /** + * The flag is associated with terminal operation flags that are + * propagated upstream across the last stateful operation boundary + */ + UPSTREAM_TERMINAL_OP + } + + /** + * The bit pattern for setting/injecting a flag. + */ + private static final int SET_BITS = 0b01; + + /** + * The bit pattern for clearing a flag. + */ + private static final int CLEAR_BITS = 0b10; + + /** + * The bit pattern for preserving a flag. + */ + private static final int PRESERVE_BITS = 0b11; + + private static MaskBuilder set(Type t) { + return new MaskBuilder(new EnumMap<>(Type.class)).set(t); + } + + private static class MaskBuilder { + final Map map; + + MaskBuilder(Map map) { + this.map = map; + } + + MaskBuilder mask(Type t, Integer i) { + map.put(t, i); + return this; + } + + MaskBuilder set(Type t) { + return mask(t, SET_BITS); + } + + MaskBuilder clear(Type t) { + return mask(t, CLEAR_BITS); + } + + MaskBuilder setAndClear(Type t) { + return mask(t, PRESERVE_BITS); + } + + Map build() { + for (Type t : Type.values()) { + map.putIfAbsent(t, 0b00); + } + return map; + } + } + + /** + * The mask table for a flag, this is used to determine if a flag + * corresponds to a certain flag type and for creating mask constants. + */ + private final Map maskTable; + + /** + * The bit position in the bit mask. + */ + private final int bitPosition; + + /** + * The set 2 bit set offset at the bit position. + */ + private final int set; + + /** + * The clear 2 bit set offset at the bit position. + */ + private final int clear; + + /** + * The preserve 2 bit set offset at the bit position. + */ + private final int preserve; + + private StreamOpFlag(int position, MaskBuilder maskBuilder) { + this.maskTable = maskBuilder.build(); + // Two bits per flag + position *= 2; + this.bitPosition = position; + this.set = SET_BITS << position; + this.clear = CLEAR_BITS << position; + this.preserve = PRESERVE_BITS << position; + } + + /** + * Gets the bitmap associated with setting this characteristic. + * + * @return the bitmap for setting this characteristic + */ + int set() { + return set; + } + + /** + * Gets the bitmap associated with clearing this characteristic. + * + * @return the bitmap for clearing this characteristic + */ + int clear() { + return clear; + } + + /** + * Determines if this flag is a stream-based flag. + * + * @return true if a stream-based flag, otherwise false. + */ + boolean isStreamFlag() { + return maskTable.get(Type.STREAM) > 0; + } + + /** + * Checks if this flag is set on stream flags, injected on operation flags, + * and injected on combined stream and operation flags. + * + * @param flags the stream flags, operation flags, or combined stream and + * operation flags + * @return true if this flag is known, otherwise false. + */ + boolean isKnown(int flags) { + return (flags & preserve) == set; + } + + /** + * Checks if this flag is cleared on operation flags or combined stream and + * operation flags. + * + * @param flags the operation flags or combined stream and operations flags. + * @return true if this flag is preserved, otherwise false. + */ + boolean isCleared(int flags) { + return (flags & preserve) == clear; + } + + /** + * Checks if this flag is preserved on combined stream and operation flags. + * + * @param flags the combined stream and operations flags. + * @return true if this flag is preserved, otherwise false. + */ + boolean isPreserved(int flags) { + return (flags & preserve) == preserve; + } + + /** + * Determines if this flag can be set for a flag type. + * + * @param t the flag type. + * @return true if this flag can be set for the flag type, otherwise false. + */ + boolean canSet(Type t) { + return (maskTable.get(t) & SET_BITS) > 0; + } + + /** + * The bit mask for spliterator characteristics + */ + static final int SPLITERATOR_CHARACTERISTICS_MASK = createMask(Type.SPLITERATOR); + + /** + * The bit mask for source stream flags. + */ + static final int STREAM_MASK = createMask(Type.STREAM); + + /** + * The bit mask for intermediate operation flags. + */ + static final int OP_MASK = createMask(Type.OP); + + /** + * The bit mask for terminal operation flags. + */ + static final int TERMINAL_OP_MASK = createMask(Type.TERMINAL_OP); + + /** + * The bit mask for upstream terminal operation flags. + */ + static final int UPSTREAM_TERMINAL_OP_MASK = createMask(Type.UPSTREAM_TERMINAL_OP); + + private static int createMask(Type t) { + int mask = 0; + for (StreamOpFlag flag : StreamOpFlag.values()) { + mask |= flag.maskTable.get(t) << flag.bitPosition; + } + return mask; + } + + /** + * Complete flag mask. + */ + private static final int FLAG_MASK = createFlagMask(); + + private static int createFlagMask() { + int mask = 0; + for (StreamOpFlag flag : StreamOpFlag.values()) { + mask |= flag.preserve; + } + return mask; + } + + /** + * Flag mask for stream flags that are set. + */ + private static final int FLAG_MASK_IS = STREAM_MASK; + + /** + * Flag mask for stream flags that are cleared. + */ + private static final int FLAG_MASK_NOT = STREAM_MASK << 1; + + /** + * The initial value to be combined with the stream flags of the first + * stream in the pipeline. + */ + static final int INITIAL_OPS_VALUE = FLAG_MASK_IS | FLAG_MASK_NOT; + + /** + * The bit value to set or inject {@link #DISTINCT}. + */ + static final int IS_DISTINCT = DISTINCT.set; + + /** + * The bit value to clear {@link #DISTINCT}. + */ + static final int NOT_DISTINCT = DISTINCT.clear; + + /** + * The bit value to set or inject {@link #SORTED}. + */ + static final int IS_SORTED = SORTED.set; + + /** + * The bit value to clear {@link #SORTED}. + */ + static final int NOT_SORTED = SORTED.clear; + + /** + * The bit value to set or inject {@link #ORDERED}. + */ + static final int IS_ORDERED = ORDERED.set; + + /** + * The bit value to clear {@link #ORDERED}. + */ + static final int NOT_ORDERED = ORDERED.clear; + + /** + * The bit value to set {@link #SIZED}. + */ + static final int IS_SIZED = SIZED.set; + + /** + * The bit value to clear {@link #SIZED}. + */ + static final int NOT_SIZED = SIZED.clear; + + /** + * The bit value to inject {@link #SHORT_CIRCUIT}. + */ + static final int IS_SHORT_CIRCUIT = SHORT_CIRCUIT.set; + + private static int getMask(int flags) { + return (flags == 0) + ? FLAG_MASK + : ~(flags | ((FLAG_MASK_IS & flags) << 1) | ((FLAG_MASK_NOT & flags) >> 1)); + } + + /** + * Combines stream or operation flags with previously combined stream and + * operation flags to produce updated combined stream and operation flags. + *

+ * A flag set on stream flags or injected on operation flags, + * and injected combined stream and operation flags, + * will be injected on the updated combined stream and operation flags. + * + *

+ * A flag set on stream flags or injected on operation flags, + * and cleared on the combined stream and operation flags, + * will be cleared on the updated combined stream and operation flags. + * + *

+ * A flag set on the stream flags or injected on operation flags, + * and preserved on the combined stream and operation flags, + * will be injected on the updated combined stream and operation flags. + * + *

+ * A flag not set on the stream flags or cleared/preserved on operation + * flags, and injected on the combined stream and operation flags, + * will be injected on the updated combined stream and operation flags. + * + *

+ * A flag not set on the stream flags or cleared/preserved on operation + * flags, and cleared on the combined stream and operation flags, + * will be cleared on the updated combined stream and operation flags. + * + *

+ * A flag not set on the stream flags, + * and preserved on the combined stream and operation flags + * will be preserved on the updated combined stream and operation flags. + * + *

+ * A flag cleared on operation flags, + * and preserved on the combined stream and operation flags + * will be cleared on the updated combined stream and operation flags. + * + *

+ * A flag preserved on operation flags, + * and preserved on the combined stream and operation flags + * will be preserved on the updated combined stream and operation flags. + * + * @param newStreamOrOpFlags the stream or operation flags. + * @param prevCombOpFlags previously combined stream and operation flags. + * The value {#link INITIAL_OPS_VALUE} must be used as the seed value. + * @return the updated combined stream and operation flags. + */ + static int combineOpFlags(int newStreamOrOpFlags, int prevCombOpFlags) { + // 0x01 or 0x10 nibbles are transformed to 0x11 + // 0x00 nibbles remain unchanged + // Then all the bits are flipped + // Then the result is logically or'ed with the operation flags. + return (prevCombOpFlags & StreamOpFlag.getMask(newStreamOrOpFlags)) | newStreamOrOpFlags; + } + + /** + * Converts combined stream and operation flags to stream flags. + * + *

Each flag injected on the combined stream and operation flags will be + * set on the stream flags. + * + * @param combOpFlags the combined stream and operation flags. + * @return the stream flags. + */ + static int toStreamFlags(int combOpFlags) { + // By flipping the nibbles 0x11 become 0x00 and 0x01 become 0x10 + // Shift left 1 to restore set flags and mask off anything other than the set flags + return ((~combOpFlags) >> 1) & FLAG_MASK_IS & combOpFlags; + } + + /** + * Converts stream flags to a spliterator characteristic bit set. + * + * @param streamFlags the stream flags. + * @return the spliterator characteristic bit set. + */ + static int toCharacteristics(int streamFlags) { + return streamFlags & SPLITERATOR_CHARACTERISTICS_MASK; + } + + /** + * Converts a spliterator characteristic bit set to stream flags. + * + * @implSpec + * If the spliterator is naturally {@code SORTED} (the associated + * {@code Comparator} is {@code null}) then the characteristic is converted + * to the {@link #SORTED} flag, otherwise the characteristic is not + * converted. + * + * @param spliterator the spliterator from which to obtain characteristic + * bit set. + * @return the stream flags. + */ + static int fromCharacteristics(Spliterator spliterator) { + int characteristics = spliterator.characteristics(); + if ((characteristics & Spliterator.SORTED) != 0 && spliterator.getComparator() != null) { + // Do not propagate the SORTED characteristic if it does not correspond + // to a natural sort order + return characteristics & SPLITERATOR_CHARACTERISTICS_MASK & ~Spliterator.SORTED; + } + else { + return characteristics & SPLITERATOR_CHARACTERISTICS_MASK; + } + } + + /** + * Converts a spliterator characteristic bit set to stream flags. + * + * @param characteristics the spliterator characteristic bit set. + * @return the stream flags. + */ + static int fromCharacteristics(int characteristics) { + return characteristics & SPLITERATOR_CHARACTERISTICS_MASK; + } +} diff --git a/jdk/src/share/classes/java/util/stream/StreamShape.java b/jdk/src/share/classes/java/util/stream/StreamShape.java new file mode 100644 index 00000000000..9051be2a27e --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/StreamShape.java @@ -0,0 +1,70 @@ +/* + * 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; + +/** + * An enum describing the known shape specializations for stream abstractions. + * Each will correspond to a specific subinterface of {@link BaseStream} + * (e.g., {@code REFERENCE} corresponds to {@code Stream}, {@code INT_VALUE} + * corresponds to {@code IntStream}). Each may also correspond to + * specializations of value-handling abstractions such as {@code Spliterator}, + * {@code Consumer}, etc. + * + * @apiNote + * This enum is used by implementations to determine compatibility between + * streams and operations (i.e., if the output shape of a stream is compatible + * with the input shape of the next operation). + * + *

Some APIs require you to specify both a generic type and a stream shape + * for input or output elements, such as {@link TerminalOp} which has both + * generic type parameters for its input types, and a getter for the + * input shape. When representing primitive streams in this way, the + * generic type parameter should correspond to the wrapper type for that + * primitive type. + * + * @since 1.8 + */ +enum StreamShape { + /** + * The shape specialization corresponding to {@code Stream} and elements + * that are object references. + */ + REFERENCE, + /** + * The shape specialization corresponding to {@code IntStream} and elements + * that are {@code int} values. + */ + INT_VALUE, + /** + * The shape specialization corresponding to {@code LongStream} and elements + * that are {@code long} values. + */ + LONG_VALUE, + /** + * The shape specialization corresponding to {@code DoubleStream} and + * elements that are {@code double} values. + */ + DOUBLE_VALUE +} diff --git a/jdk/src/share/classes/java/util/stream/TerminalOp.java b/jdk/src/share/classes/java/util/stream/TerminalOp.java new file mode 100644 index 00000000000..a6e8ae1c65a --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/TerminalOp.java @@ -0,0 +1,98 @@ +/* + * 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.Spliterator; + +/** + * An operation in a stream pipeline that takes a stream as input and produces + * a result or side-effect. A {@code TerminalOp} has an input type and stream + * shape, and a result type. A {@code TerminalOp} also has a set of + * operation flags that describes how the operation processes elements + * of the stream (such as short-circuiting or respecting encounter order; see + * {@link StreamOpFlag}). + * + *

A {@code TerminalOp} must provide a sequential and parallel implementation + * of the operation relative to a given stream source and set of intermediate + * operations. + * + * @param the type of input elements + * @param the type of the result + * @since 1.8 + */ +interface TerminalOp { + /** + * Gets the shape of the input type of this operation. + * + * @implSpec The default returns {@code StreamShape.REFERENCE}. + * + * @return StreamShape of the input type of this operation + */ + default StreamShape inputShape() { return StreamShape.REFERENCE; } + + /** + * Gets the stream flags of the operation. Terminal operations may set a + * limited subset of the stream flags defined in {@link StreamOpFlag}, and + * these flags are combined with the previously combined stream and + * intermediate operation flags for the pipeline. + * + * @implSpec The default implementation returns zero. + * + * @return the stream flags for this operation + * @see StreamOpFlag + */ + default int getOpFlags() { return 0; } + + /** + * Performs a parallel evaluation of the operation using the specified + * {@code PipelineHelper}, which describes the upstream intermediate + * operations. + * + * @implSpec The default performs a sequential evaluation of the operation + * using the specified {@code PipelineHelper}. + * + * @param helper the pipeline helper + * @param spliterator the source spliterator + * @return the result of the evaluation + */ + default R evaluateParallel(PipelineHelper helper, + Spliterator spliterator) { + if (Tripwire.ENABLED) + Tripwire.trip(getClass(), "{0} triggering TerminalOp.evaluateParallel serial default"); + return evaluateSequential(helper, spliterator); + } + + /** + * Performs a sequential evaluation of the operation using the specified + * {@code PipelineHelper}, which describes the upstream intermediate + * operations. + * + * @param helper the pipeline helper + * @param spliterator the source spliterator + * @return the result of the evaluation + */ + R evaluateSequential(PipelineHelper helper, + Spliterator spliterator); +} diff --git a/jdk/src/share/classes/java/util/stream/TerminalSink.java b/jdk/src/share/classes/java/util/stream/TerminalSink.java new file mode 100644 index 00000000000..9808d54802d --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/TerminalSink.java @@ -0,0 +1,38 @@ +/* + * 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.function.Supplier; + +/** + * A {@link Sink} which accumulates state as elements are accepted, and allows + * a result to be retrieved after the computation is finished. + * + * @param the type of elements to be accepted + * @param the type of the result + * + * @since 1.8 + */ +interface TerminalSink extends Sink, Supplier { } diff --git a/jdk/src/share/classes/java/util/stream/Tripwire.java b/jdk/src/share/classes/java/util/stream/Tripwire.java new file mode 100644 index 00000000000..c6558b9de48 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/Tripwire.java @@ -0,0 +1,69 @@ +/* + * 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.security.AccessController; +import java.security.PrivilegedAction; + +import sun.util.logging.PlatformLogger; + +/** + * Utility class for detecting inadvertent uses of boxing in + * {@code java.util.stream} classes. The detection is turned on or off based on + * whether the system property {@code org.openjdk.java.util.stream.tripwire} is + * considered {@code true} according to {@link Boolean#getBoolean(String)}. + * This should normally be turned off for production use. + * + * @apiNote + * Typical usage would be for boxing code to do: + *

{@code
+ *     if (Tripwire.ENABLED)
+ *         Tripwire.trip(getClass(), "{0} calling Sink.OfInt.accept(Integer)");
+ * }
+ * + * @since 1.8 + */ +final class Tripwire { + private static final String TRIPWIRE_PROPERTY = "org.openjdk.java.util.stream.tripwire"; + + /** Should debugging checks be enabled? */ + static final boolean ENABLED = AccessController.doPrivileged( + (PrivilegedAction) () -> Boolean.getBoolean(TRIPWIRE_PROPERTY)); + + private Tripwire() { } + + /** + * Produces a log warning, using {@code PlatformLogger.getLogger(className)}, + * using the supplied message. The class name of {@code trippingClass} will + * be used as the first parameter to the message. + * + * @param trippingClass Name of the class generating the message + * @param msg A message format string of the type expected by + * {@link PlatformLogger} + */ + static void trip(Class trippingClass, String msg) { + PlatformLogger.getLogger(trippingClass.getName()).warning(msg, trippingClass.getName()); + } +} diff --git a/jdk/src/share/classes/java/util/stream/package-info.java b/jdk/src/share/classes/java/util/stream/package-info.java new file mode 100644 index 00000000000..4dfb1e998d2 --- /dev/null +++ b/jdk/src/share/classes/java/util/stream/package-info.java @@ -0,0 +1,566 @@ +/* + * 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. + */ + +/** + *

java.util.stream

+ * + * Classes to support functional-style operations on streams of values, as in the following: + * + *
{@code
+ *     int sumOfWeights = blocks.stream().filter(b -> b.getColor() == RED)
+ *                                       .mapToInt(b -> b.getWeight())
+ *                                       .sum();
+ * }
+ * + *

Here we use {@code blocks}, which might be a {@code Collection}, as a source for a stream, + * and then perform a filter-map-reduce ({@code sum()} is an example of a reduction + * operation) on the stream to obtain the sum of the weights of the red blocks. + * + *

The key abstraction used in this approach is {@link java.util.stream.Stream}, as well as its primitive + * specializations {@link java.util.stream.IntStream}, {@link java.util.stream.LongStream}, + * and {@link java.util.stream.DoubleStream}. Streams differ from Collections in several ways: + * + *

    + *
  • No storage. A stream is not a data structure that stores elements; instead, they + * carry values from a source (which could be a data structure, a generator, an IO channel, etc) + * through a pipeline of computational operations.
  • + *
  • Functional in nature. An operation on a stream produces a result, but does not modify + * its underlying data source. For example, filtering a {@code Stream} produces a new {@code Stream}, + * rather than removing elements from the underlying source.
  • + *
  • Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, + * can be implemented lazily, exposing opportunities for optimization. (For example, "find the first + * {@code String} matching a pattern" need not examine all the input strings.) Stream operations + * are divided into intermediate ({@code Stream}-producing) operations and terminal (value-producing) + * operations; all intermediate operations are lazy.
  • + *
  • Possibly unbounded. While collections have a finite size, streams need not. Operations + * such as {@code limit(n)} or {@code findFirst()} can allow computations on infinite streams + * to complete in finite time.
  • + *
+ * + *

Stream pipelines

+ * + *

Streams are used to create pipelines of operations. A + * complete stream pipeline has several components: a source (which may be a {@code Collection}, + * an array, a generator function, or an IO channel); zero or more intermediate operations + * such as {@code Stream.filter} or {@code Stream.map}; and a terminal operation such + * as {@code Stream.forEach} or {@code java.util.stream.Stream.reduce}. Stream operations may take as parameters + * function values (which are often lambda expressions, but could be method references + * or objects) which parameterize the behavior of the operation, such as a {@code Predicate} + * passed to the {@code Stream#filter} method. + * + *

Intermediate operations return a new {@code Stream}. They are lazy; executing an + * intermediate operation such as {@link java.util.stream.Stream#filter Stream.filter} does + * not actually perform any filtering, instead creating a new {@code Stream} that, when + * traversed, contains the elements of the initial {@code Stream} that match the + * given {@code Predicate}. Consuming elements from the stream source does not + * begin until the terminal operation is executed. + * + *

Terminal operations consume the {@code Stream} and produce a result or a side-effect. + * After a terminal operation is performed, the stream can no longer be used and you must + * return to the data source, or select a new data source, to get a new stream. For example, + * obtaining the sum of weights of all red blocks, and then of all blue blocks, requires a + * filter-map-reduce on two different streams: + *

{@code
+ *     int sumOfRedWeights  = blocks.stream().filter(b -> b.getColor() == RED)
+ *                                           .mapToInt(b -> b.getWeight())
+ *                                           .sum();
+ *     int sumOfBlueWeights = blocks.stream().filter(b -> b.getColor() == BLUE)
+ *                                           .mapToInt(b -> b.getWeight())
+ *                                           .sum();
+ * }
+ * + *

However, there are other techniques that allow you to obtain both results in a single + * pass if multiple traversal is impractical or inefficient. TODO provide link + * + *

Stream operations

+ * + *

Intermediate stream operation (such as {@code filter} or {@code sorted}) always produce a + * new {@code Stream}, and are alwayslazy. Executing a lazy operations does not + * trigger processing of the stream contents; all processing is deferred until the terminal + * operation commences. Processing streams lazily allows for significant efficiencies; in a + * pipeline such as the filter-map-sum example above, filtering, mapping, and addition can be + * fused into a single pass, with minimal intermediate state. Laziness also enables us to avoid + * examining all the data when it is not necessary; for operations such as "find the first + * string longer than 1000 characters", one need not examine all the input strings, just enough + * to find one that has the desired characteristics. (This behavior becomes even more important + * when the input stream is infinite and not merely large.) + * + *

Intermediate operations are further divided into stateless and stateful + * operations. Stateless operations retain no state from previously seen values when processing + * a new value; examples of stateless intermediate operations include {@code filter} and + * {@code map}. Stateful operations may incorporate state from previously seen elements in + * processing new values; examples of stateful intermediate operations include {@code distinct} + * and {@code sorted}. Stateful operations may need to process the entire input before + * producing a result; for example, one cannot produce any results from sorting a stream until + * one has seen all elements of the stream. As a result, under parallel computation, some + * pipelines containing stateful intermediate operations have to be executed in multiple passes. + * Pipelines containing exclusively stateless intermediate operations can be processed in a + * single pass, whether sequential or parallel. + * + *

Further, some operations are deemed short-circuiting operations. An intermediate + * operation is short-circuiting if, when presented with infinite input, it may produce a + * finite stream as a result. A terminal operation is short-circuiting if, when presented with + * infinite input, it may terminate in finite time. (Having a short-circuiting operation is a + * necessary, but not sufficient, condition for the processing of an infinite stream to + * terminate normally in finite time.) + * + * Terminal operations (such as {@code forEach} or {@code findFirst}) are always eager + * (they execute completely before returning), and produce a non-{@code Stream} result, such + * as a primitive value or a {@code Collection}, or have side-effects. + * + *

Parallelism

+ * + *

By recasting aggregate operations as a pipeline of operations on a stream of values, many + * aggregate operations can be more easily parallelized. A {@code Stream} can execute either + * in serial or in parallel. When streams are created, they are either created as sequential + * or parallel streams; the parallel-ness of streams can also be switched by the + * {@link java.util.stream Stream#sequential()} and {@link java.util.stream.Stream#parallel()} + * operations. The {@code Stream} implementations in the JDK create serial streams unless + * parallelism is explicitly requested. For example, {@code Collection} has methods + * {@link java.util.Collection#stream} and {@link java.util.Collection#parallelStream}, + * which produce sequential and parallel streams respectively; other stream-bearing methods + * such as {@link java.util.stream.Streams#intRange(int, int)} produce sequential + * streams but these can be efficiently parallelized by calling {@code parallel()} on the + * result. The set of operations on serial and parallel streams is identical. To execute the + * "sum of weights of blocks" query in parallel, we would do: + * + *

{@code
+ *     int sumOfWeights = blocks.parallelStream().filter(b -> b.getColor() == RED)
+ *                                               .mapToInt(b -> b.getWeight())
+ *                                               .sum();
+ * }
+ * + *

The only difference between the serial and parallel versions of this example code is + * the creation of the initial {@code Stream}. Whether a {@code Stream} will execute in serial + * or parallel can be determined by the {@code Stream#isParallel} method. When the terminal + * operation is initiated, the entire stream pipeline is either executed sequentially or in + * parallel, determined by the last operation that affected the stream's serial-parallel + * orientation (which could be the stream source, or the {@code sequential()} or + * {@code parallel()} methods.) + * + *

In order for the results of parallel operations to be deterministic and consistent with + * their serial equivalent, the function values passed into the various stream operations should + * be stateless. + * + *

Ordering

+ * + *

Streams may or may not have an encounter order. An encounter + * order specifies the order in which elements are provided by the stream to the + * operations pipeline. Whether or not there is an encounter order depends on + * the source, the intermediate operations, and the terminal operation. + * Certain stream sources (such as {@code List} or arrays) are intrinsically + * ordered, whereas others (such as {@code HashSet}) are not. Some intermediate + * operations may impose an encounter order on an otherwise unordered stream, + * such as {@link java.util.stream.Stream#sorted()}, and others may render an + * ordered stream unordered (such as {@link java.util.stream.Stream#unordered()}). + * Some terminal operations may ignore encounter order, such as + * {@link java.util.stream.Stream#forEach}. + * + *

If a Stream is ordered, most operations are constrained to operate on the + * elements in their encounter order; if the source of a stream is a {@code List} + * containing {@code [1, 2, 3]}, then the result of executing {@code map(x -> x*2)} + * must be {@code [2, 4, 6]}. However, if the source has no defined encounter + * order, than any of the six permutations of the values {@code [2, 4, 6]} would + * be a valid result. Many operations can still be efficiently parallelized even + * under ordering constraints. + * + *

For sequential streams, ordering is only relevant to the determinism + * of operations performed repeatedly on the same source. (An {@code ArrayList} + * is constrained to iterate elements in order; a {@code HashSet} is not, and + * repeated iteration might produce a different order.) + * + *

For parallel streams, relaxing the ordering constraint can enable + * optimized implementation for some operations. For example, duplicate + * filtration on an ordered stream must completely process the first partition + * before it can return any elements from a subsequent partition, even if those + * elements are available earlier. On the other hand, without the constraint of + * ordering, duplicate filtration can be done more efficiently by using + * a shared {@code ConcurrentHashSet}. There will be cases where the stream + * is structurally ordered (the source is ordered and the intermediate + * operations are order-preserving), but the user does not particularly care + * about the encounter order. In some cases, explicitly de-ordering the stream + * with the {@link java.util.stream.Stream#unordered()} method may result in + * improved parallel performance for some stateful or terminal operations. + * + *

Non-interference

+ * + * The {@code java.util.stream} package enables you to execute possibly-parallel + * bulk-data operations over a variety of data sources, including even non-thread-safe + * collections such as {@code ArrayList}. This is possible only if we can + * prevent interference with the data source during the execution of a + * stream pipeline. (Execution begins when the terminal operation is invoked, and ends + * when the terminal operation completes.) For most data sources, preventing interference + * means ensuring that the data source is not modified at all during the execution + * of the stream pipeline. (Some data sources, such as concurrent collections, are + * specifically designed to handle concurrent modification.) + * + *

Accordingly, lambda expressions (or other objects implementing the appropriate functional + * interface) passed to stream methods should never modify the stream's data source. An + * implementation is said to interfere with the data source if it modifies, or causes + * to be modified, the stream's data source. The need for non-interference applies to all + * pipelines, not just parallel ones. Unless the stream source is concurrent, modifying a + * stream's data source during execution of a stream pipeline can cause exceptions, incorrect + * answers, or nonconformant results. + * + *

Further, results may be nondeterministic or incorrect if the lambda expressions passed to + * stream operations are stateful. A stateful lambda (or other object implementing the + * appropriate functional interface) is one whose result depends on any state which might change + * during the execution of the stream pipeline. An example of a stateful lambda is: + *

{@code
+ *     Set seen = Collections.synchronizedSet(new HashSet<>());
+ *     stream.parallel().map(e -> { if (seen.add(e)) return 0; else return e; })...
+ * }
+ * Here, if the mapping operation is performed in parallel, the results for the same input + * could vary from run to run, due to thread scheduling differences, whereas, with a stateless + * lambda expression the results would always be the same. + * + *

Side-effects

+ * + *

Reduction operations

+ * + * A reduction operation takes a stream of elements and processes them in a way + * that reduces to a single value or summary description, such as finding the sum or maximum + * of a set of numbers. (In more complex scenarios, the reduction operation might need to + * extract data from the elements before reducing that data to a single value, such as + * finding the sum of weights of a set of blocks. This would require extracting the weight + * from each block before summing up the weights.) + * + *

Of course, such operations can be readily implemented as simple sequential loops, as in: + *

{@code
+ *    int sum = 0;
+ *    for (int x : numbers) {
+ *       sum += x;
+ *    }
+ * }
+ * However, there may be a significant advantage to preferring a {@link java.util.stream.Stream#reduce reduce operation} + * over a mutative accumulation such as the above -- a properly constructed reduce operation is + * inherently parallelizable so long as the + * {@link java.util.function.BinaryOperator reduction operaterator} + * has the right characteristics. Specifically the operator must be + * associative. For example, given a + * stream of numbers for which we want to find the sum, we can write: + *
{@code
+ *    int sum = numbers.reduce(0, (x,y) -> x+y);
+ * }
+ * or more succinctly: + *
{@code
+ *    int sum = numbers.reduce(0, Integer::sum);
+ * }
+ * + *

(The primitive specializations of {@link java.util.stream.Stream}, such as + * {@link java.util.stream.IntStream}, even have convenience methods for common reductions, + * such as {@link java.util.stream.IntStream#sum() sum} and {@link java.util.stream.IntStream#max() max}, + * which are implemented as simple wrappers around reduce.) + * + *

Reduction parallellizes well since the implementation of {@code reduce} can operate on + * subsets of the stream in parallel, and then combine the intermediate results to get the final + * correct answer. Even if you were to use a parallelizable form of the + * {@link java.util.stream.Stream#forEach(Consumer) forEach()} method + * in place of the original for-each loop above, you would still have to provide thread-safe + * updates to the shared accumulating variable {@code sum}, and the required synchronization + * would likely eliminate any performance gain from parallelism. Using a {@code reduce} method + * instead removes all of the burden of parallelizing the reduction operation, and the library + * can provide an efficient parallel implementation with no additional synchronization needed. + * + *

The "blocks" examples shown earlier shows how reduction combines with other operations + * to replace for loops with bulk operations. If {@code blocks} is a collection of {@code Block} + * objects, which have a {@code getWeight} method, we can find the heaviest block with: + *

{@code
+ *     OptionalInt heaviest = blocks.stream()
+ *                                  .mapToInt(Block::getWeight)
+ *                                  .reduce(Integer::max);
+ * }
+ * + *

In its more general form, a {@code reduce} operation on elements of type {@code } + * yielding a result of type {@code } requires three parameters: + *

{@code
+ *  U reduce(U identity,
+ *              BiFunction accumlator,
+ *              BinaryOperator combiner);
+ * }
+ * Here, the identity element is both an initial seed for the reduction, and a default + * result if there are no elements. The accumulator function takes a partial result and + * the next element, and produce a new partial result. The combiner function combines + * the partial results of two accumulators to produce a new partial result, and eventually the + * final result. + * + *

This form is a generalization of the two-argument form, and is also a generalization of + * the map-reduce construct illustrated above. If we wanted to re-cast the simple {@code sum} + * example using the more general form, {@code 0} would be the identity element, while + * {@code Integer::sum} would be both the accumulator and combiner. For the sum-of-weights + * example, this could be re-cast as: + *

{@code
+ *     int sumOfWeights = blocks.stream().reduce(0,
+ *                                               (sum, b) -> sum + b.getWeight())
+ *                                               Integer::sum);
+ * }
+ * though the map-reduce form is more readable and generally preferable. The generalized form + * is provided for cases where significant work can be optimized away by combining mapping and + * reducing into a single function. + * + *

More formally, the {@code identity} value must be an identity for the combiner + * function. This means that for all {@code u}, {@code combiner.apply(identity, u)} is equal + * to {@code u}. Additionally, the {@code combiner} function must be + * associative and must be compatible with the {@code accumulator} + * function; for all {@code u} and {@code t}, the following must hold: + *

{@code
+ *     combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)
+ * }
+ * + *

Mutable Reduction

+ * + * A mutable reduction operation is similar to an ordinary reduction, in that it reduces + * a stream of values to a single value, but instead of producing a distinct single-valued result, it + * mutates a general result container, such as a {@code Collection} or {@code StringBuilder}, + * as it processes the elements in the stream. + * + *

For example, if we wanted to take a stream of strings and concatenate them into a single + * long string, we could achieve this with ordinary reduction: + *

{@code
+ *     String concatenated = strings.reduce("", String::concat)
+ * }
+ * + * We would get the desired result, and it would even work in parallel. However, we might not + * be happy about the performance! Such an implementation would do a great deal of string + * copying, and the run time would be O(n^2) in the number of elements. A more + * performant approach would be to accumulate the results into a {@link java.lang.StringBuilder}, which + * is a mutable container for accumulating strings. We can use the same technique to + * parallelize mutable reduction as we do with ordinary reduction. + * + *

The mutable reduction operation is called {@link java.util.stream.Stream#collect(Collector) collect()}, as it + * collects together the desired results into a result container such as {@code StringBuilder}. + * A {@code collect} operation requires three things: a factory function which will construct + * new instances of the result container, an accumulating function that will update a result + * container by incorporating a new element, and a combining function that can take two + * result containers and merge their contents. The form of this is very similar to the general + * form of ordinary reduction: + *

{@code
+ *  R collect(Supplier resultFactory,
+ *               BiConsumer accumulator,
+ *               BiConsumer combiner);
+ * }
+ * As with {@code reduce()}, the benefit of expressing {@code collect} in this abstract way is + * that it is directly amenable to parallelization: we can accumulate partial results in parallel + * and then combine them. For example, to collect the String representations of the elements + * in a stream into an {@code ArrayList}, we could write the obvious sequential for-each form: + *
{@code
+ *     ArrayList strings = new ArrayList<>();
+ *     for (T element : stream) {
+ *         strings.add(element.toString());
+ *     }
+ * }
+ * Or we could use a parallelizable collect form: + *
{@code
+ *     ArrayList strings = stream.collect(() -> new ArrayList<>(),
+ *                                                (c, e) -> c.add(e.toString()),
+ *                                                (c1, c2) -> c1.addAll(c2));
+ * }
+ * or, noting that we have buried a mapping operation inside the accumulator function, more + * succinctly as: + *
{@code
+ *     ArrayList strings = stream.map(Object::toString)
+ *                                       .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
+ * }
+ * Here, our supplier is just the {@link java.util.ArrayList#ArrayList() ArrayList constructor}, the + * accumulator adds the stringified element to an {@code ArrayList}, and the combiner simply + * uses {@link java.util.ArrayList#addAll addAll} to copy the strings from one container into the other. + * + *

As with the regular reduction operation, the ability to parallelize only comes if an + * associativity condition is met. The {@code combiner} is associative + * if for result containers {@code r1}, {@code r2}, and {@code r3}: + *

{@code
+ *    combiner.accept(r1, r2);
+ *    combiner.accept(r1, r3);
+ * }
+ * is equivalent to + *
{@code
+ *    combiner.accept(r2, r3);
+ *    combiner.accept(r1, r2);
+ * }
+ * where equivalence means that {@code r1} is left in the same state (according to the meaning + * of {@link java.lang.Object#equals equals} for the element types). Similarly, the {@code resultFactory} + * must act as an identity with respect to the {@code combiner} so that for any result + * container {@code r}: + *
{@code
+ *     combiner.accept(r, resultFactory.get());
+ * }
+ * does not modify the state of {@code r} (again according to the meaning of + * {@link java.lang.Object#equals equals}). Finally, the {@code accumulator} and {@code combiner} must be + * compatible such that for a result container {@code r} and element {@code t}: + *
{@code
+ *    r2 = resultFactory.get();
+ *    accumulator.accept(r2, t);
+ *    combiner.accept(r, r2);
+ * }
+ * is equivalent to: + *
{@code
+ *    accumulator.accept(r,t);
+ * }
+ * where equivalence means that {@code r} is left in the same state (again according to the + * meaning of {@link java.lang.Object#equals equals}). + * + *

The three aspects of {@code collect}: supplier, accumulator, and combiner, are often very + * tightly coupled, and it is convenient to introduce the notion of a {@link java.util.stream.Collector} as + * being an object that embodies all three aspects. There is a {@link java.util.stream.Stream#collect(Collector) collect} + * method that simply takes a {@code Collector} and returns the resulting container. + * The above example for collecting strings into a {@code List} can be rewritten using a + * standard {@code Collector} as: + *

{@code
+ *     ArrayList strings = stream.map(Object::toString)
+ *                                       .collect(Collectors.toList());
+ * }
+ * + *

Reduction, Concurrency, and Ordering

+ * + * With some complex reduction operations, for example a collect that produces a + * {@code Map}, such as: + *
{@code
+ *     Map> salesByBuyer
+ *         = txns.parallelStream()
+ *               .collect(Collectors.groupingBy(Transaction::getBuyer));
+ * }
+ * (where {@link java.util.stream.Collectors#groupingBy} is a utility function + * that returns a {@link java.util.stream.Collector} for grouping sets of elements based on some key) + * it may actually be counterproductive to perform the operation in parallel. + * This is because the combining step (merging one {@code Map} into another by key) + * can be expensive for some {@code Map} implementations. + * + *

Suppose, however, that the result container used in this reduction + * was a concurrently modifiable collection -- such as a + * {@link java.util.concurrent.ConcurrentHashMap ConcurrentHashMap}. In that case, + * the parallel invocations of the accumulator could actually deposit their results + * concurrently into the same shared result container, eliminating the need for the combiner to + * merge distinct result containers. This potentially provides a boost + * to the parallel execution performance. We call this a concurrent reduction. + * + *

A {@link java.util.stream.Collector} that supports concurrent reduction is marked with the + * {@link java.util.stream.Collector.Characteristics#CONCURRENT} characteristic. + * Having a concurrent collector is a necessary condition for performing a + * concurrent reduction, but that alone is not sufficient. If you imagine multiple + * accumulators depositing results into a shared container, the order in which + * results are deposited is non-deterministic. Consequently, a concurrent reduction + * is only possible if ordering is not important for the stream being processed. + * The {@link java.util.stream.Stream#collect(Collector)} + * implementation will only perform a concurrent reduction if + *

    + *
  • The stream is parallel;
  • + *
  • The collector has the + * {@link java.util.stream.Collector.Characteristics#CONCURRENT} characteristic, + * and;
  • + *
  • Either the stream is unordered, or the collector has the + * {@link java.util.stream.Collector.Characteristics#UNORDERED} characteristic. + *
+ * For example: + *
{@code
+ *     Map> salesByBuyer
+ *         = txns.parallelStream()
+ *               .unordered()
+ *               .collect(groupingByConcurrent(Transaction::getBuyer));
+ * }
+ * (where {@link java.util.stream.Collectors#groupingByConcurrent} is the concurrent companion + * to {@code groupingBy}). + * + *

Note that if it is important that the elements for a given key appear in the + * order they appear in the source, then we cannot use a concurrent reduction, + * as ordering is one of the casualties of concurrent insertion. We would then + * be constrained to implement either a sequential reduction or a merge-based + * parallel reduction. + * + *

Associativity

+ * + * An operator or function {@code op} is associative if the following holds: + *
{@code
+ *     (a op b) op c == a op (b op c)
+ * }
+ * The importance of this to parallel evaluation can be seen if we expand this to four terms: + *
{@code
+ *     a op b op c op d == (a op b) op (c op d)
+ * }
+ * So we can evaluate {@code (a op b)} in parallel with {@code (c op d)} and then invoke {@code op} on + * the results. + * TODO what does associative mean for mutative combining functions? + * FIXME: we described mutative associativity above. + * + *

Stream sources

+ * TODO where does this section go? + * + * XXX - change to section to stream construction gradually introducing more + * complex ways to construct + * - construction from Collection + * - construction from Iterator + * - construction from array + * - construction from generators + * - construction from spliterator + * + * XXX - the following is quite low-level but important aspect of stream constriction + * + *

A pipeline is initially constructed from a spliterator (see {@link java.util.Spliterator}) supplied by a stream source. + * The spliterator covers elements of the source and provides element traversal operations + * for a possibly-parallel computation. See methods on {@link java.util.stream.Streams} for construction + * of pipelines using spliterators. + * + *

A source may directly supply a spliterator. If so, the spliterator is traversed, split, or queried + * for estimated size after, and never before, the terminal operation commences. It is strongly recommended + * that the spliterator report a characteristic of {@code IMMUTABLE} or {@code CONCURRENT}, or be + * late-binding and not bind to the elements it covers until traversed, split or queried for + * estimated size. + * + *

If a source cannot directly supply a recommended spliterator then it may indirectly supply a spliterator + * using a {@code Supplier}. The spliterator is obtained from the supplier after, and never before, the terminal + * operation of the stream pipeline commences. + * + *

Such requirements significantly reduce the scope of potential interference to the interval starting + * with the commencing of the terminal operation and ending with the producing a result or side-effect. See + * Non-Interference for + * more details. + * + * XXX - move the following to the non-interference section + * + *

A source can be modified before the terminal operation commences and those modifications will be reflected in + * the covered elements. Afterwards, and depending on the properties of the source, further modifications + * might not be reflected and the throwing of a {@code ConcurrentModificationException} may occur. + * + *

For example, consider the following code: + *

{@code
+ *     List l = new ArrayList(Arrays.asList("one", "two"));
+ *     Stream sl = l.stream();
+ *     l.add("three");
+ *     String s = sl.collect(toStringJoiner(" ")).toString();
+ * }
+ * First a list is created consisting of two strings: "one"; and "two". Then a stream is created from that list. + * Next the list is modified by adding a third string: "three". Finally the elements of the stream are collected + * and joined together. Since the list was modified before the terminal {@code collect} operation commenced + * the result will be a string of "one two three". However, if the list is modified after the terminal operation + * commences, as in: + *
{@code
+ *     List l = new ArrayList(Arrays.asList("one", "two"));
+ *     Stream sl = l.stream();
+ *     String s = sl.peek(s -> l.add("BAD LAMBDA")).collect(toStringJoiner(" ")).toString();
+ * }
+ * then a {@code ConcurrentModificationException} will be thrown since the {@code peek} operation will attempt + * to add the string "BAD LAMBDA" to the list after the terminal operation has commenced. + */ + +package java.util.stream; diff --git a/jdk/src/share/classes/sun/misc/PerfCounter.java b/jdk/src/share/classes/sun/misc/PerfCounter.java index 9d0b8fec7aa..980e34fe469 100644 --- a/jdk/src/share/classes/sun/misc/PerfCounter.java +++ b/jdk/src/share/classes/sun/misc/PerfCounter.java @@ -62,7 +62,7 @@ public class PerfCounter { private PerfCounter(String name, int type) { this.name = name; - ByteBuffer bb = perf.createLong(name, U_None, type, 0L); + ByteBuffer bb = perf.createLong(name, type, U_None, 0L); bb.order(ByteOrder.nativeOrder()); this.lb = bb.asLongBuffer(); } diff --git a/jdk/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java b/jdk/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java index 981967b6d35..fd999d36c8b 100644 --- a/jdk/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java +++ b/jdk/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java @@ -364,17 +364,18 @@ class DigestAuthentication extends AuthenticationInfo { + ncfield + ", uri=\"" + uri + "\", response=\"" + response - + "\", algorithm=\"" + algorithm; + + "\", algorithm=" + algorithm; if (opaque != null) { - value = value + "\", opaque=\"" + opaque; + value = value + ", opaque=\"" + opaque; + value = value + "\""; } if (cnonce != null) { - value = value + "\", cnonce=\"" + cnonce; + value = value + ", cnonce=\"" + cnonce; + value = value + "\""; } if (qop) { - value = value + "\", qop=\"auth"; + value = value + ", qop=auth"; } - value = value + "\""; return value; } diff --git a/jdk/src/share/classes/sun/security/pkcs/PKCS7.java b/jdk/src/share/classes/sun/security/pkcs/PKCS7.java index 8c3c947374e..f26f6ed7d41 100644 --- a/jdk/src/share/classes/sun/security/pkcs/PKCS7.java +++ b/jdk/src/share/classes/sun/security/pkcs/PKCS7.java @@ -784,6 +784,9 @@ public class PKCS7 { * @param signatureAlgorithm the name of the signature algorithm * @param tsaURI the URI of the Timestamping Authority; or null if no * timestamp is requested + * @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a + * numerical object identifier; or null if we leave the TSA server + * to choose one. This argument is only used when tsaURI is provided * @return the bytes of the encoded PKCS #7 signed data message * @throws NoSuchAlgorithmException The exception is thrown if the signature * algorithm is unrecognised. @@ -798,7 +801,8 @@ public class PKCS7 { X509Certificate[] signerChain, byte[] content, String signatureAlgorithm, - URI tsaURI) + URI tsaURI, + String tSAPolicyID) throws CertificateException, IOException, NoSuchAlgorithmException { @@ -807,7 +811,7 @@ public class PKCS7 { if (tsaURI != null) { // Timestamp the signature HttpTimestamper tsa = new HttpTimestamper(tsaURI); - byte[] tsToken = generateTimestampToken(tsa, signature); + byte[] tsToken = generateTimestampToken(tsa, tSAPolicyID, signature); // Insert the timestamp token into the PKCS #7 signer info element // (as an unsigned attribute) @@ -851,14 +855,20 @@ public class PKCS7 { * set to true. * * @param tsa the timestamping authority to use + * @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a + * numerical object identifier; or null if we leave the TSA server + * to choose one * @param toBeTimestamped the token that is to be timestamped * @return the encoded timestamp token * @throws IOException The exception is thrown if an error occurs while - * communicating with the TSA. + * communicating with the TSA, or a non-null + * TSAPolicyID is specified in the request but it + * does not match the one in the reply * @throws CertificateException The exception is thrown if the TSA's * certificate is not permitted for timestamping. */ private static byte[] generateTimestampToken(Timestamper tsa, + String tSAPolicyID, byte[] toBeTimestamped) throws IOException, CertificateException { @@ -868,7 +878,7 @@ public class PKCS7 { try { // SHA-1 is always used. messageDigest = MessageDigest.getInstance("SHA-1"); - tsQuery = new TSRequest(toBeTimestamped, messageDigest); + tsQuery = new TSRequest(tSAPolicyID, toBeTimestamped, messageDigest); } catch (NoSuchAlgorithmException e) { // ignore } @@ -889,6 +899,12 @@ public class PKCS7 { tsReply.getStatusCodeAsText() + " " + tsReply.getFailureCodeAsText()); } + + if (tSAPolicyID != null && + !tSAPolicyID.equals(tsReply.getTimestampToken().getPolicyID())) { + throw new IOException("TSAPolicyID changed in " + + "timestamp token"); + } PKCS7 tsToken = tsReply.getToken(); TimestampToken tst = tsReply.getTimestampToken(); diff --git a/jdk/src/share/classes/sun/security/timestamp/TSRequest.java b/jdk/src/share/classes/sun/security/timestamp/TSRequest.java index 9b322192cca..b2620bd032a 100644 --- a/jdk/src/share/classes/sun/security/timestamp/TSRequest.java +++ b/jdk/src/share/classes/sun/security/timestamp/TSRequest.java @@ -88,9 +88,10 @@ public class TSRequest { * @param messageDigest The MessageDigest of the hash algorithm to use. * @throws NoSuchAlgorithmException if the hash algorithm is not supported */ - public TSRequest(byte[] toBeTimeStamped, MessageDigest messageDigest) + public TSRequest(String tSAPolicyID, byte[] toBeTimeStamped, MessageDigest messageDigest) throws NoSuchAlgorithmException { + this.policyId = tSAPolicyID; this.hashAlgorithmId = AlgorithmId.get(messageDigest.getAlgorithm()); this.hashValue = messageDigest.digest(toBeTimeStamped); } diff --git a/jdk/src/share/classes/sun/security/timestamp/TimestampToken.java b/jdk/src/share/classes/sun/security/timestamp/TimestampToken.java index 5ca1d62fb98..9df8f1c0bd0 100644 --- a/jdk/src/share/classes/sun/security/timestamp/TimestampToken.java +++ b/jdk/src/share/classes/sun/security/timestamp/TimestampToken.java @@ -115,6 +115,10 @@ public class TimestampToken { return nonce; } + public String getPolicyID() { + return policy.toString(); + } + public BigInteger getSerialNumber() { return serialNumber; } diff --git a/jdk/src/share/classes/sun/security/tools/jarsigner/Main.java b/jdk/src/share/classes/sun/security/tools/jarsigner/Main.java index 9828a1a9a1d..c5ed6b1facf 100644 --- a/jdk/src/share/classes/sun/security/tools/jarsigner/Main.java +++ b/jdk/src/share/classes/sun/security/tools/jarsigner/Main.java @@ -141,6 +141,7 @@ public class Main { String tsaUrl; // location of the Timestamping Authority String tsaAlias; // alias for the Timestamping Authority's certificate String altCertChain; // file to read alternative cert chain from + String tSAPolicyID; boolean verify = false; // verify the jar String verbose = null; // verbose output when signing/verifying boolean showcerts = false; // show certs when verifying @@ -331,6 +332,9 @@ public class Main { } else if (collator.compare(flags, "-certchain") ==0) { if (++n == args.length) usageNoArg(); altCertChain = args[n]; + } else if (collator.compare(flags, "-tsapolicyid") ==0) { + if (++n == args.length) usageNoArg(); + tSAPolicyID = args[n]; } else if (collator.compare(flags, "-debug") ==0) { debug = true; } else if (collator.compare(flags, "-keypass") ==0) { @@ -530,6 +534,9 @@ public class Main { System.out.println(rb.getString (".tsacert.alias.public.key.certificate.for.Timestamping.Authority")); System.out.println(); + System.out.println(rb.getString + (".tsapolicyid.tsapolicyid.for.Timestamping.Authority")); + System.out.println(); System.out.println(rb.getString (".altsigner.class.class.name.of.an.alternative.signing.mechanism")); System.out.println(); @@ -1232,7 +1239,7 @@ public class Main { try { block = sf.generateBlock(privateKey, sigalg, certChain, - externalSF, tsaUrl, tsaCert, signingMechanism, args, + externalSF, tsaUrl, tsaCert, tSAPolicyID, signingMechanism, args, zipFile); } catch (SocketTimeoutException e) { // Provide a helpful message when TSA is beyond a firewall @@ -2206,13 +2213,14 @@ class SignatureFile { X509Certificate[] certChain, boolean externalSF, String tsaUrl, X509Certificate tsaCert, + String tSAPolicyID, ContentSigner signingMechanism, String[] args, ZipFile zipFile) throws NoSuchAlgorithmException, InvalidKeyException, IOException, SignatureException, CertificateException { return new Block(this, privateKey, sigalg, certChain, externalSF, - tsaUrl, tsaCert, signingMechanism, args, zipFile); + tsaUrl, tsaCert, tSAPolicyID, signingMechanism, args, zipFile); } @@ -2226,7 +2234,7 @@ class SignatureFile { */ Block(SignatureFile sfg, PrivateKey privateKey, String sigalg, X509Certificate[] certChain, boolean externalSF, String tsaUrl, - X509Certificate tsaCert, ContentSigner signingMechanism, + X509Certificate tsaCert, String tSAPolicyID, ContentSigner signingMechanism, String[] args, ZipFile zipFile) throws NoSuchAlgorithmException, InvalidKeyException, IOException, SignatureException, CertificateException { @@ -2309,7 +2317,7 @@ class SignatureFile { // Assemble parameters for the signing mechanism ContentSignerParameters params = - new JarSignerParameters(args, tsaUri, tsaCert, signature, + new JarSignerParameters(args, tsaUri, tsaCert, tSAPolicyID, signature, signatureAlgorithm, certChain, content, zipFile); // Generate the signature block @@ -2353,11 +2361,13 @@ class JarSignerParameters implements ContentSignerParameters { private X509Certificate[] signerCertificateChain; private byte[] content; private ZipFile source; + private String tSAPolicyID; /** * Create a new object. */ JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate, + String tSAPolicyID, byte[] signature, String signatureAlgorithm, X509Certificate[] signerCertificateChain, byte[] content, ZipFile source) { @@ -2369,6 +2379,7 @@ class JarSignerParameters implements ContentSignerParameters { this.args = args; this.tsa = tsa; this.tsaCertificate = tsaCertificate; + this.tSAPolicyID = tSAPolicyID; this.signature = signature; this.signatureAlgorithm = signatureAlgorithm; this.signerCertificateChain = signerCertificateChain; @@ -2403,6 +2414,10 @@ class JarSignerParameters implements ContentSignerParameters { return tsaCertificate; } + public String getTSAPolicyID() { + return tSAPolicyID; + } + /** * Retrieves the signature. * diff --git a/jdk/src/share/classes/sun/security/tools/jarsigner/Resources.java b/jdk/src/share/classes/sun/security/tools/jarsigner/Resources.java index 9c59b5085c8..9ccc070e003 100644 --- a/jdk/src/share/classes/sun/security/tools/jarsigner/Resources.java +++ b/jdk/src/share/classes/sun/security/tools/jarsigner/Resources.java @@ -86,6 +86,8 @@ public class Resources extends java.util.ListResourceBundle { "[-tsa ] location of the Timestamping Authority"}, {".tsacert.alias.public.key.certificate.for.Timestamping.Authority", "[-tsacert ] public key certificate for Timestamping Authority"}, + {".tsapolicyid.tsapolicyid.for.Timestamping.Authority", + "[-tsapolicyid ] TSAPolicyID for Timestamping Authority"}, {".altsigner.class.class.name.of.an.alternative.signing.mechanism", "[-altsigner ] class name of an alternative signing mechanism"}, {".altsignerpath.pathlist.location.of.an.alternative.signing.mechanism", diff --git a/jdk/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java b/jdk/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java index 4b952de1d5b..e52e246fe58 100644 --- a/jdk/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java +++ b/jdk/src/share/classes/sun/security/tools/jarsigner/TimestampedSigner.java @@ -133,7 +133,8 @@ public final class TimestampedSigner extends ContentSigner { } } return PKCS7.generateSignedData(signature, signerChain, content, - params.getSignatureAlgorithm(), tsaURI); + params.getSignatureAlgorithm(), tsaURI, + params.getTSAPolicyID()); } /** diff --git a/jdk/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java b/jdk/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java index df00a728f2c..ec44ee1514a 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixChannelFactory.java @@ -84,7 +84,7 @@ class UnixChannelFactory { } continue; } - if (option == LinkOption.NOFOLLOW_LINKS && supportsNoFollowLinks()) { + if (option == LinkOption.NOFOLLOW_LINKS && O_NOFOLLOW != 0) { flags.noFollowLinks = true; continue; } @@ -218,7 +218,7 @@ class UnixChannelFactory { // follow links by default boolean followLinks = true; if (!flags.createNew && (flags.noFollowLinks || flags.deleteOnClose)) { - if (flags.deleteOnClose && !supportsNoFollowLinks()) { + if (flags.deleteOnClose && O_NOFOLLOW == 0) { try { if (UnixFileAttributes.get(path, false).isSymbolicLink()) throw new UnixException("DELETE_ON_CLOSE specified and file is a symbolic link"); diff --git a/jdk/src/solaris/classes/sun/nio/fs/UnixCopyFile.java b/jdk/src/solaris/classes/sun/nio/fs/UnixCopyFile.java index c35133b202a..9c6e204414a 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/UnixCopyFile.java +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixCopyFile.java @@ -189,7 +189,7 @@ class UnixCopyFile { // copy time stamps last if (flags.copyBasicAttributes) { try { - if (dfd >= 0) { + if (dfd >= 0 && futimesSupported()) { futimes(dfd, attrs.lastAccessTime().to(TimeUnit.MICROSECONDS), attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS)); @@ -269,9 +269,15 @@ class UnixCopyFile { // copy time attributes if (flags.copyBasicAttributes) { try { - futimes(fo, - attrs.lastAccessTime().to(TimeUnit.MICROSECONDS), - attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS)); + if (futimesSupported()) { + futimes(fo, + attrs.lastAccessTime().to(TimeUnit.MICROSECONDS), + attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS)); + } else { + utimes(target, + attrs.lastAccessTime().to(TimeUnit.MICROSECONDS), + attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS)); + } } catch (UnixException x) { if (flags.failIfUnableToCopyBasic) x.rethrowAsIOException(target); diff --git a/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java b/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java index 3716e3fdef7..c650758a6cf 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java @@ -73,6 +73,8 @@ class UnixFileAttributeViews { int fd = file.openForAttributeAccess(followLinks); try { + // assert followLinks || !UnixFileAttributes.get(fd).isSymbolicLink(); + // if not changing both attributes then need existing attributes if (lastModifiedTime == null || lastAccessTime == null) { try { @@ -92,9 +94,13 @@ class UnixFileAttributeViews { boolean retry = false; try { - futimes(fd, accessValue, modValue); + if (futimesSupported()) { + futimes(fd, accessValue, modValue); + } else { + utimes(file, accessValue, modValue); + } } catch (UnixException x) { - // if futimes fails with EINVAL and one/both of the times is + // if futimes/utimes fails with EINVAL and one/both of the times is // negative then we adjust the value to the epoch and retry. if (x.errno() == UnixConstants.EINVAL && (modValue < 0L || accessValue < 0L)) { @@ -107,7 +113,11 @@ class UnixFileAttributeViews { if (modValue < 0L) modValue = 0L; if (accessValue < 0L) accessValue= 0L; try { - futimes(fd, accessValue, modValue); + if (futimesSupported()) { + futimes(fd, accessValue, modValue); + } else { + utimes(file, accessValue, modValue); + } } catch (UnixException x) { x.rethrowAsIOException(file); } diff --git a/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java b/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java index b61b5d6e4a0..47b61ac9181 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java @@ -51,6 +51,7 @@ class UnixFileAttributes private long st_mtime_nsec; private long st_ctime_sec; private long st_ctime_nsec; + private long st_birthtime_sec; // created lazily private volatile UserPrincipal owner; @@ -139,7 +140,12 @@ class UnixFileAttributes @Override public FileTime creationTime() { - return lastModifiedTime(); + if (UnixNativeDispatcher.birthtimeSupported()) { + return FileTime.from(st_birthtime_sec, TimeUnit.SECONDS); + } else { + // return last modified when birth time not supported + return lastModifiedTime(); + } } @Override diff --git a/jdk/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java b/jdk/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java index fc0f943a97a..553c4b2e8c8 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java @@ -394,9 +394,9 @@ public abstract class UnixFileSystemProvider if (filter == null) throw new NullPointerException(); - // can't return SecureDirectoryStream on kernels that don't support - // openat, etc. - if (!supportsAtSysCalls() || !supportsNoFollowLinks()) { + // can't return SecureDirectoryStream on kernels that don't support openat + // or O_NOFOLLOW + if (!openatSupported() || O_NOFOLLOW == 0) { try { long ptr = opendir(dir); return new UnixDirectoryStream(dir, ptr, filter); diff --git a/jdk/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java b/jdk/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java index 5255c660dd1..ec21e6df8ef 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java @@ -537,30 +537,42 @@ class UnixNativeDispatcher { */ static native byte[] strerror(int errnum); - // indicates if openat, unlinkat, etc. is supported - private static final boolean hasAtSysCalls; - static boolean supportsAtSysCalls() { - return hasAtSysCalls; + /** + * Capabilities + */ + private static final int SUPPORTS_OPENAT = 1 << 1; // syscalls + private static final int SUPPORTS_FUTIMES = 1 << 2; + private static final int SUPPORTS_BIRTHTIME = 1 << 16; // other features + private static final int capabilities; + + /** + * Supports openat and other *at calls. + */ + static boolean openatSupported() { + return (capabilities & SUPPORTS_OPENAT) != 0; } - static boolean supportsNoFollowLinks() { - return UnixConstants.O_NOFOLLOW != 0; + /** + * Supports futimes or futimesat + */ + static boolean futimesSupported() { + return (capabilities & SUPPORTS_FUTIMES) != 0; + } + + /** + * Supports file birth (creation) time attribute + */ + static boolean birthtimeSupported() { + return (capabilities & SUPPORTS_BIRTHTIME) != 0; } - // initialize syscalls and fieldIDs private static native int init(); - - // flags returned by init to indicate capabilities - private static final int HAS_AT_SYSCALLS = 0x1; - static { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { System.loadLibrary("nio"); return null; }}); - int flags = init(); - - hasAtSysCalls = (flags & HAS_AT_SYSCALLS) > 0; + capabilities = init(); } } diff --git a/jdk/src/solaris/classes/sun/nio/fs/UnixPath.java b/jdk/src/solaris/classes/sun/nio/fs/UnixPath.java index b9410b4ee60..5738292027d 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/UnixPath.java +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixPath.java @@ -769,7 +769,7 @@ class UnixPath int openForAttributeAccess(boolean followLinks) throws IOException { int flags = O_RDONLY; if (!followLinks) { - if (!supportsNoFollowLinks()) + if (O_NOFOLLOW == 0) throw new IOException("NOFOLLOW_LINKS is not supported on this platform"); flags |= O_NOFOLLOW; } diff --git a/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c b/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c index eb4698183df..8f408951e77 100644 --- a/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c +++ b/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c @@ -97,6 +97,10 @@ static jfieldID attrs_st_mtime_nsec; static jfieldID attrs_st_ctime_sec; static jfieldID attrs_st_ctime_nsec; +#ifdef _DARWIN_FEATURE_64_BIT_INODE +static jfieldID attrs_st_birthtime_sec; +#endif + static jfieldID attrs_f_frsize; static jfieldID attrs_f_blocks; static jfieldID attrs_f_bfree; @@ -171,7 +175,7 @@ static void throwUnixException(JNIEnv* env, int errnum) { JNIEXPORT jint JNICALL Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) { - jint flags = 0; + jint capabilities = 0; jclass clazz; clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileAttributes"); @@ -193,6 +197,10 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) attrs_st_ctime_sec = (*env)->GetFieldID(env, clazz, "st_ctime_sec", "J"); attrs_st_ctime_nsec = (*env)->GetFieldID(env, clazz, "st_ctime_nsec", "J"); +#ifdef _DARWIN_FEATURE_64_BIT_INODE + attrs_st_birthtime_sec = (*env)->GetFieldID(env, clazz, "st_birthtime_sec", "J"); +#endif + clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileStoreAttributes"); if (clazz == NULL) { return 0; @@ -233,14 +241,31 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) my_fstatat64_func = (fstatat64_func*)&fstatat64_wrapper; #endif + /* supports futimes or futimesat */ + +#ifdef _ALLBSD_SOURCE + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FUTIMES; +#else + if (my_futimesat_func != NULL) + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_FUTIMES; +#endif + + /* supports openat, etc. */ + if (my_openat64_func != NULL && my_fstatat64_func != NULL && my_unlinkat_func != NULL && my_renameat_func != NULL && my_futimesat_func != NULL && my_fdopendir_func != NULL) { - flags |= sun_nio_fs_UnixNativeDispatcher_HAS_AT_SYSCALLS; + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_OPENAT; } - return flags; + /* supports file birthtime */ + +#ifdef _DARWIN_FEATURE_64_BIT_INODE + capabilities |= sun_nio_fs_UnixNativeDispatcher_SUPPORTS_BIRTHTIME; +#endif + + return capabilities; } JNIEXPORT jbyteArray JNICALL @@ -405,6 +430,10 @@ static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) { (*env)->SetLongField(env, attrs, attrs_st_mtime_sec, (jlong)buf->st_mtime); (*env)->SetLongField(env, attrs, attrs_st_ctime_sec, (jlong)buf->st_ctime); +#ifdef _DARWIN_FEATURE_64_BIT_INODE + (*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, (jlong)buf->st_birthtime); +#endif + #if (_POSIX_C_SOURCE >= 200809L) || defined(__solaris__) (*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->st_atim.tv_nsec); (*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->st_mtim.tv_nsec); diff --git a/jdk/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java b/jdk/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java index fddffb925be..eae2da60d41 100644 --- a/jdk/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java +++ b/jdk/src/windows/classes/sun/util/locale/provider/HostLocaleProviderAdapterImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -37,6 +37,7 @@ import java.text.spi.DecimalFormatSymbolsProvider; import java.text.spi.NumberFormatProvider; import java.util.Calendar; import java.util.Collections; +import java.util.Currency; import java.util.HashSet; import java.util.Locale; import java.util.Map; @@ -48,6 +49,8 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.spi.CalendarDataProvider; import java.util.spi.CalendarNameProvider; +import java.util.spi.CurrencyNameProvider; +import java.util.spi.LocaleNameProvider; import sun.util.spi.CalendarProvider; /** @@ -72,6 +75,14 @@ public class HostLocaleProviderAdapterImpl { private static final int CD_FIRSTDAYOFWEEK = 0; private static final int CD_MINIMALDAYSINFIRSTWEEK = 1; + // Currency/Locale display name types + private static final int DN_CURRENCY_NAME = 0; + private static final int DN_CURRENCY_SYMBOL = 1; + private static final int DN_LOCALE_LANGUAGE = 2; + private static final int DN_LOCALE_SCRIPT = 3; + private static final int DN_LOCALE_REGION = 4; + private static final int DN_LOCALE_VARIANT = 5; + // Native Calendar ID to LDML calendar type map private static final String[] calIDToLDML = { "", @@ -96,15 +107,25 @@ public class HostLocaleProviderAdapterImpl { private static ConcurrentMap> decimalFormatSymbolsCache = new ConcurrentHashMap<>(); private static final Set supportedLocaleSet; + private static final String nativeDisplayLanguage; static { Set tmpSet = new HashSet<>(); if (initialize()) { // Assuming the default locales do not include any extensions, so // no stripping is needed here. - Locale l = Locale.forLanguageTag(getDefaultLocale(CAT_FORMAT).replace('_', '-')); - tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l)); - l = Locale.forLanguageTag(getDefaultLocale(CAT_DISPLAY).replace('_', '-')); - tmpSet.addAll(Control.getNoFallbackControl(Control.FORMAT_DEFAULT).getCandidateLocales("", l)); + Control c = Control.getNoFallbackControl(Control.FORMAT_DEFAULT); + String displayLocale = getDefaultLocale(CAT_DISPLAY); + Locale l = Locale.forLanguageTag(displayLocale.replace('_', '-')); + tmpSet.addAll(c.getCandidateLocales("", l)); + nativeDisplayLanguage = l.getLanguage(); + + String formatLocale = getDefaultLocale(CAT_FORMAT); + if (!formatLocale.equals(displayLocale)) { + l = Locale.forLanguageTag(formatLocale.replace('_', '-')); + tmpSet.addAll(c.getCandidateLocales("", l)); + } + } else { + nativeDisplayLanguage = ""; } supportedLocaleSet = Collections.unmodifiableSet(tmpSet); } @@ -392,6 +413,96 @@ public class HostLocaleProviderAdapterImpl { }; } + public static CurrencyNameProvider getCurrencyNameProvider() { + return new CurrencyNameProvider() { + @Override + public Locale[] getAvailableLocales() { + return supportedLocale; + } + + @Override + public boolean isSupportedLocale(Locale locale) { + // Ignore the extensions for now + return supportedLocaleSet.contains(locale.stripExtensions()) && + locale.getLanguage().equals(nativeDisplayLanguage); + } + + @Override + public String getSymbol(String currencyCode, Locale locale) { + // Retrieves the currency symbol by calling + // GetLocaleInfoEx(LOCALE_SCURRENCY). + // It only works with the "locale"'s currency in its native + // language. + try { + if (Currency.getInstance(locale).getCurrencyCode() + .equals(currencyCode)) { + return getDisplayString(locale.toLanguageTag(), + DN_CURRENCY_SYMBOL, currencyCode); + } + } catch (IllegalArgumentException iae) {} + return null; + } + + @Override + public String getDisplayName(String currencyCode, Locale locale) { + // Retrieves the display name by calling + // GetLocaleInfoEx(LOCALE_SNATIVECURRNAME). + // It only works with the "locale"'s currency in its native + // language. + try { + if (Currency.getInstance(locale).getCurrencyCode() + .equals(currencyCode)) { + return getDisplayString(locale.toLanguageTag(), + DN_CURRENCY_NAME, currencyCode); + } + } catch (IllegalArgumentException iae) {} + return null; + } + }; + } + + public static LocaleNameProvider getLocaleNameProvider() { + return new LocaleNameProvider() { + @Override + public Locale[] getAvailableLocales() { + return supportedLocale; + } + + @Override + public boolean isSupportedLocale(Locale locale) { + return supportedLocaleSet.contains(locale.stripExtensions()) && + locale.getLanguage().equals(nativeDisplayLanguage); + } + + @Override + public String getDisplayLanguage(String languageCode, Locale locale) { + // Retrieves the display language name by calling + // GetLocaleInfoEx(LOCALE_SLOCALIZEDLANGUAGENAME). + return getDisplayString(locale.toLanguageTag(), + DN_LOCALE_LANGUAGE, languageCode); + } + + @Override + public String getDisplayCountry(String countryCode, Locale locale) { + // Retrieves the display country name by calling + // GetLocaleInfoEx(LOCALE_SLOCALIZEDCOUNTRYNAME). + return getDisplayString(locale.toLanguageTag(), + DN_LOCALE_REGION, nativeDisplayLanguage+"-"+countryCode); + } + + @Override + public String getDisplayScript(String scriptCode, Locale locale) { + return null; + } + + @Override + public String getDisplayVariant(String variantCode, Locale locale) { + return null; + } + }; + } + + private static String convertDateTimePattern(String winPattern) { String ret = winPattern.replaceAll("dddd", "EEEE"); ret = ret.replaceAll("ddd", "EEE"); @@ -413,12 +524,21 @@ public class HostLocaleProviderAdapterImpl { } private static boolean isSupportedCalendarLocale(Locale locale) { - Locale base = locale.stripExtensions(); + Locale base = locale; + + if (base.hasExtensions() || base.getVariant() != "") { + // strip off extensions and variant. + base = new Locale.Builder() + .setLocale(locale) + .clearExtensions() + .build(); + } + if (!supportedLocaleSet.contains(base)) { return false; } - int calid = getCalendarID(locale.toLanguageTag()); + int calid = getCalendarID(base.toLanguageTag()); if (calid <= 0 || calid >= calIDToLDML.length) { return false; } @@ -546,4 +666,7 @@ public class HostLocaleProviderAdapterImpl { // For CalendarDataProvider private static native int getCalendarDataValue(String langTag, int type); + + // For Locale/CurrencyNameProvider + private static native String getDisplayString(String langTag, int key, String value); } diff --git a/jdk/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c b/jdk/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c index 037ea5ee578..148282cfadc 100644 --- a/jdk/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c +++ b/jdk/src/windows/native/sun/util/locale/provider/HostLocaleProviderAdapter_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -196,7 +196,7 @@ JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapte break; } - localeString = getJavaIDFromLangID(langid); + localeString = (char *)getJavaIDFromLangID(langid); ret = (*env)->NewStringUTF(env, localeString); free(localeString); return ret; @@ -366,12 +366,14 @@ JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapte */ JNIEXPORT jboolean JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_isNativeDigit (JNIEnv *env, jclass cls, jstring jlangtag) { - WCHAR buf[BUFLEN]; + DWORD num; const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE); - int got = getLocaleInfoWrapper(langtag, LOCALE_IDIGITSUBSTITUTION, buf, BUFLEN); + int got = getLocaleInfoWrapper(langtag, + LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, + (LPWSTR)&num, sizeof(num)); (*env)->ReleaseStringChars(env, jlangtag, langtag); - return got && buf[0] == L'2'; // 2: native digit substitution + return got && num == 2; // 2: native digit substitution } /* @@ -590,25 +592,72 @@ JNIEXPORT jchar JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterI */ JNIEXPORT jint JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getCalendarDataValue (JNIEnv *env, jclass cls, jstring jlangtag, jint type) { - WCHAR buf[BUFLEN]; + DWORD num; const jchar *langtag = (*env)->GetStringChars(env, jlangtag, JNI_FALSE); int got = 0; switch (type) { case sun_util_locale_provider_HostLocaleProviderAdapterImpl_CD_FIRSTDAYOFWEEK: - got = getLocaleInfoWrapper(langtag, LOCALE_IFIRSTDAYOFWEEK, buf, BUFLEN); + got = getLocaleInfoWrapper(langtag, + LOCALE_IFIRSTDAYOFWEEK | LOCALE_RETURN_NUMBER, + (LPWSTR)&num, sizeof(num)); break; } (*env)->ReleaseStringChars(env, jlangtag, langtag); if (got) { - return _wtoi(buf); + return num; } else { return -1; } } +/* + * Class: sun_util_locale_provider_HostLocaleProviderAdapterImpl + * Method: getDisplayString + * Signature: (Ljava/lang/String;ILjava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_sun_util_locale_provider_HostLocaleProviderAdapterImpl_getDisplayString + (JNIEnv *env, jclass cls, jstring jlangtag, jint type, jstring jvalue) { + LCTYPE lcType; + jstring jStr; + const jchar * pjChar; + WCHAR buf[BUFLEN]; + int got = 0; + + switch (type) { + case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_CURRENCY_NAME: + lcType = LOCALE_SNATIVECURRNAME; + jStr = jlangtag; + break; + case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_CURRENCY_SYMBOL: + lcType = LOCALE_SCURRENCY; + jStr = jlangtag; + break; + case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_LOCALE_LANGUAGE: + lcType = LOCALE_SLOCALIZEDLANGUAGENAME; + jStr = jvalue; + break; + case sun_util_locale_provider_HostLocaleProviderAdapterImpl_DN_LOCALE_REGION: + lcType = LOCALE_SLOCALIZEDCOUNTRYNAME; + jStr = jvalue; + break; + default: + return NULL; + } + + pjChar = (*env)->GetStringChars(env, jStr, JNI_FALSE); + got = getLocaleInfoWrapper(pjChar, lcType, buf, BUFLEN); + (*env)->ReleaseStringChars(env, jStr, pjChar); + + if (got) { + return (*env)->NewString(env, buf, wcslen(buf)); + } else { + return NULL; + } +} + int getLocaleInfoWrapper(const jchar *langtag, LCTYPE type, LPWSTR data, int buflen) { if (pGetLocaleInfoEx) { if (wcscmp(L"und", (LPWSTR)langtag) == 0) { @@ -642,11 +691,13 @@ int getCalendarInfoWrapper(const jchar *langtag, CALID id, LPCWSTR reserved, CAL } jint getCalendarID(const jchar *langtag) { - WCHAR type[BUFLEN]; - int got = getLocaleInfoWrapper(langtag, LOCALE_ICALENDARTYPE, type, BUFLEN); + DWORD type; + int got = getLocaleInfoWrapper(langtag, + LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER, + (LPWSTR)&type, sizeof(type)); if (got) { - return _wtoi(type); + return type; } else { return 0; } @@ -691,28 +742,37 @@ WCHAR * getNumberPattern(const jchar * langtag, const jint numberStyle) { } void getNumberPart(const jchar * langtag, const jint numberStyle, WCHAR * number) { - WCHAR buf[BUFLEN]; + DWORD digits = 0; + DWORD leadingZero = 0; WCHAR grouping[BUFLEN]; + int groupingLen; WCHAR fractionPattern[BUFLEN]; WCHAR * integerPattern = number; - int digits; - BOOL leadingZero; WCHAR * pDest; - int groupingLen; // Get info from Windows - if (numberStyle == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY) { - getLocaleInfoWrapper(langtag, LOCALE_ICURRDIGITS, buf, BUFLEN); - } else { - getLocaleInfoWrapper(langtag, LOCALE_IDIGITS, buf, BUFLEN); + switch (numberStyle) { + case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY: + getLocaleInfoWrapper(langtag, + LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER, + (LPWSTR)&digits, sizeof(digits)); + break; + + case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_INTEGER: + break; + + case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_NUMBER: + case sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_PERCENT: + default: + getLocaleInfoWrapper(langtag, + LOCALE_IDIGITS | LOCALE_RETURN_NUMBER, + (LPWSTR)&digits, sizeof(digits)); + break; } - if (numberStyle == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_INTEGER) { - digits = 0; - } else { - digits = _wtoi(buf); - } - getLocaleInfoWrapper(langtag, LOCALE_ILZERO, buf, BUFLEN); - leadingZero = _wtoi(buf) != 0; + + getLocaleInfoWrapper(langtag, + LOCALE_ILZERO | LOCALE_RETURN_NUMBER, + (LPWSTR)&leadingZero, sizeof(leadingZero)); groupingLen = getLocaleInfoWrapper(langtag, LOCALE_SGROUPING, grouping, BUFLEN); // fraction pattern @@ -749,7 +809,7 @@ void getNumberPart(const jchar * langtag, const jint numberStyle, WCHAR * number } } - if (leadingZero) { + if (leadingZero != 0) { *pDest++ = L'0'; } else { *pDest++ = L'#'; @@ -760,29 +820,35 @@ void getNumberPart(const jchar * langtag, const jint numberStyle, WCHAR * number } void getFixPart(const jchar * langtag, const jint numberStyle, BOOL positive, BOOL prefix, WCHAR * ret) { - WCHAR buf[BUFLEN]; - int pattern = 0; + DWORD pattern = 0; int style = numberStyle; int got = 0; if (positive) { if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY) { - got = getLocaleInfoWrapper(langtag, LOCALE_ICURRENCY, buf, BUFLEN); + got = getLocaleInfoWrapper(langtag, + LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER, + (LPWSTR)&pattern, sizeof(pattern)); } else if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_PERCENT) { - got = getLocaleInfoWrapper(langtag, LOCALE_IPOSITIVEPERCENT, buf, BUFLEN); + got = getLocaleInfoWrapper(langtag, + LOCALE_IPOSITIVEPERCENT | LOCALE_RETURN_NUMBER, + (LPWSTR)&pattern, sizeof(pattern)); } } else { if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_CURRENCY) { - got = getLocaleInfoWrapper(langtag, LOCALE_INEGCURR, buf, BUFLEN); + got = getLocaleInfoWrapper(langtag, + LOCALE_INEGCURR | LOCALE_RETURN_NUMBER, + (LPWSTR)&pattern, sizeof(pattern)); } else if (style == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_PERCENT) { - got = getLocaleInfoWrapper(langtag, LOCALE_INEGATIVEPERCENT, buf, BUFLEN); + got = getLocaleInfoWrapper(langtag, + LOCALE_INEGATIVEPERCENT | LOCALE_RETURN_NUMBER, + (LPWSTR)&pattern, sizeof(pattern)); } else { - got = getLocaleInfoWrapper(langtag, LOCALE_INEGNUMBER, buf, BUFLEN); + got = getLocaleInfoWrapper(langtag, + LOCALE_INEGNUMBER | LOCALE_RETURN_NUMBER, + (LPWSTR)&pattern, sizeof(pattern)); } } - if (got) { - pattern = _wtoi(buf); - } if (numberStyle == sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_INTEGER) { style = sun_util_locale_provider_HostLocaleProviderAdapterImpl_NF_NUMBER; diff --git a/jdk/test/Makefile b/jdk/test/Makefile index 289e208ee9d..2edcc8ffddc 100644 --- a/jdk/test/Makefile +++ b/jdk/test/Makefile @@ -502,7 +502,7 @@ jdk_math: $(call TestDirs, java/math) # Stable agentvm testruns (TestNG) JDK_DEFAULT_TARGETS += jdk_time jdk_time: $(call TestDirs, java/time) - $(call RunOthervmBatch) + $(call RunAgentvmBatch) # Stable agentvm testruns (minus items from PROBLEM_LIST) JDK_ALL_TARGETS += jdk_other diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index d13975499d8..065d08fd37f 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -144,6 +144,9 @@ vm/verifier/TestStaticIF.java generic-all # jdk_management +# 8010897 +sun/management/HotspotRuntimeMBean/GetSafepointSyncTime.java macosx-all + ############################################################################ # jdk_jmx diff --git a/jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java b/jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java index 40792dfde46..4459d071f24 100644 --- a/jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java +++ b/jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java @@ -70,22 +70,16 @@ public class Basic { check(f.lastModified()/1000 == attrs.lastModifiedTime().to(TimeUnit.SECONDS), "last-modified time should be the same"); - // copy last-modified time and file create time from directory to file, + // copy last-modified time from directory to file, // re-read attribtues, and check they match BasicFileAttributeView view = Files.getFileAttributeView(file, BasicFileAttributeView.class); BasicFileAttributes dirAttrs = Files.readAttributes(dir, BasicFileAttributes.class); view.setTimes(dirAttrs.lastModifiedTime(), null, null); - if (dirAttrs.creationTime() != null) { - view.setTimes(null, null, dirAttrs.creationTime()); - } + attrs = view.readAttributes(); check(attrs.lastModifiedTime().equals(dirAttrs.lastModifiedTime()), "last-modified time should be equal"); - if (dirAttrs.creationTime() != null) { - check(attrs.creationTime().equals(dirAttrs.creationTime()), - "create time should be the same"); - } // security tests check (!(attrs instanceof PosixFileAttributes), diff --git a/jdk/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java b/jdk/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java new file mode 100644 index 00000000000..16898feaec8 --- /dev/null +++ b/jdk/test/java/nio/file/attribute/BasicFileAttributeView/CreationTime.java @@ -0,0 +1,123 @@ +/* + * 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 8011536 + * @summary Basic test for creationTime attribute on platforms/file systems + * that support it. + * @library ../.. + */ + +import java.nio.file.Path; +import java.nio.file.Files; +import java.nio.file.attribute.*; +import java.time.Instant; +import java.io.IOException; + +public class CreationTime { + + private static final java.io.PrintStream err = System.err; + + /** + * Reads the creationTime attribute + */ + private static FileTime creationTime(Path file) throws IOException { + return Files.readAttributes(file, BasicFileAttributes.class).creationTime(); + } + + /** + * Sets the creationTime attribute + */ + private static void setCreationTime(Path file, FileTime time) throws IOException { + BasicFileAttributeView view = + Files.getFileAttributeView(file, BasicFileAttributeView.class); + view.setTimes(null, null, time); + } + + static void test(Path top) throws IOException { + Path file = Files.createFile(top.resolve("foo")); + + /** + * Check that creationTime reported + */ + FileTime creationTime = creationTime(file); + Instant now = Instant.now(); + if (Math.abs(creationTime.toMillis()-now.toEpochMilli()) > 10000L) { + err.println("File creation time reported as: " + creationTime); + throw new RuntimeException("Expected to be close to: " + now); + } + + /** + * Is the creationTime attribute supported here? + */ + boolean supportsCreationTimeRead = false; + boolean supportsCreationTimeWrite = false; + String os = System.getProperty("os.name"); + if (os.contains("OS X") && Files.getFileStore(file).type().equals("hfs")) { + supportsCreationTimeRead = true; + } else if (os.startsWith("Windows")) { + String type = Files.getFileStore(file).type(); + if (type.equals("NTFS") || type.equals("FAT")) { + supportsCreationTimeRead = true; + supportsCreationTimeWrite = true; + } + } + + /** + * If the creation-time attribute is supported then change the file's + * last modified and check that it doesn't change the creation-time. + */ + if (supportsCreationTimeRead) { + // change modified time by +1 hour + Instant plusHour = Instant.now().plusSeconds(60L * 60L); + Files.setLastModifiedTime(file, FileTime.from(plusHour)); + FileTime current = creationTime(file); + if (!current.equals(creationTime)) + throw new RuntimeException("Creation time should not have changed"); + } + + /** + * If the creation-time attribute is supported and can be changed then + * check that the change is effective. + */ + if (supportsCreationTimeWrite) { + // change creation time by -1 hour + Instant minusHour = Instant.now().minusSeconds(60L * 60L); + creationTime = FileTime.from(minusHour); + setCreationTime(file, creationTime); + FileTime current = creationTime(file); + if (Math.abs(creationTime.toMillis()-current.toMillis()) > 1000L) + throw new RuntimeException("Creation time not changed"); + } + } + + public static void main(String[] args) throws IOException { + // create temporary directory to run tests + Path dir = TestUtil.createTemporaryDirectory(); + try { + test(dir); + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/jdk/test/java/time/TEST.properties b/jdk/test/java/time/TEST.properties index ccf8ed60635..909c6193da8 100644 --- a/jdk/test/java/time/TEST.properties +++ b/jdk/test/java/time/TEST.properties @@ -1,3 +1,3 @@ # Threeten test uses TestNG TestNG.dirs = . - +othervm.dirs = tck/java/time/chrono test/java/time/chrono test/java/time/format diff --git a/jdk/test/java/util/Collection/CollectionDefaults.java b/jdk/test/java/util/Collection/CollectionDefaults.java new file mode 100644 index 00000000000..36fd8da5469 --- /dev/null +++ b/jdk/test/java/util/Collection/CollectionDefaults.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.function.Predicate; + +/** + * @test + * @library testlibrary + * @build CollectionAsserts CollectionSupplier + * @run testng CollectionDefaults + * @summary Unit tests for extension methods on Collection + */ +public class CollectionDefaults { + + public static final Predicate pEven = x -> 0 == x % 2; + public static final Predicate pOdd = x -> 1 == x % 2; + + private static final String[] SET_CLASSES = { + "java.util.HashSet", + "java.util.LinkedHashSet", + "java.util.TreeSet" + }; + + private static final int SIZE = 100; + + @DataProvider(name="setProvider", parallel=true) + public static Object[][] setCases() { + final List cases = new LinkedList<>(); + cases.add(new Object[] { new HashSet<>() }); + cases.add(new Object[] { new LinkedHashSet<>() }); + cases.add(new Object[] { new TreeSet<>() }); + + cases.add(new Object[] { Collections.newSetFromMap(new HashMap<>()) }); + cases.add(new Object[] { Collections.newSetFromMap(new LinkedHashMap()) }); + cases.add(new Object[] { Collections.newSetFromMap(new TreeMap<>()) }); + + cases.add(new Object[] { new HashSet(){{add(42);}} }); + cases.add(new Object[] { new LinkedHashSet(){{add(42);}} }); + cases.add(new Object[] { new TreeSet(){{add(42);}} }); + return cases.toArray(new Object[0][cases.size()]); + } + + @Test(dataProvider = "setProvider") + public void testProvidedWithNull(final Set set) throws Exception { + try { + set.forEach(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + try { + set.removeIf(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + } + + @Test + public void testForEach() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(SET_CLASSES, SIZE); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final Set original = ((Set) test.original); + final Set set = ((Set) test.collection); + + try { + set.forEach(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + if (test.className.equals("java.util.HashSet")) { + CollectionAsserts.assertContentsUnordered(set, original); + } else { + CollectionAsserts.assertContents(set, original); + } + + final List actual = new LinkedList<>(); + set.forEach(actual::add); + if (test.className.equals("java.util.HashSet")) { + CollectionAsserts.assertContentsUnordered(actual, set); + CollectionAsserts.assertContentsUnordered(actual, original); + } else { + CollectionAsserts.assertContents(actual, set); + CollectionAsserts.assertContents(actual, original); + } + } + } + + @Test + public void testRemoveIf() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(SET_CLASSES, SIZE); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final Set original = ((Set) test.original); + final Set set = ((Set) test.collection); + + try { + set.removeIf(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + if (test.className.equals("java.util.HashSet")) { + CollectionAsserts.assertContentsUnordered(set, original); + } else { + CollectionAsserts.assertContents(set, original); + } + + set.removeIf(pEven); + for (int i : set) { + assertTrue((i % 2) == 1); + } + for (int i : original) { + if (i % 2 == 1) { + assertTrue(set.contains(i)); + } + } + set.removeIf(pOdd); + assertTrue(set.isEmpty()); + } + } +} diff --git a/jdk/test/java/util/Collection/ListDefaults.java b/jdk/test/java/util/Collection/ListDefaults.java new file mode 100644 index 00000000000..6011fbf7804 --- /dev/null +++ b/jdk/test/java/util/Collection/ListDefaults.java @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Comparators; +import java.util.List; +import java.util.LinkedList; +import java.util.Stack; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.Vector; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.lang.reflect.Constructor; +import java.util.ConcurrentModificationException; +import java.util.function.Predicate; + +/** + * @test + * @library testlibrary + * @build CollectionAsserts CollectionSupplier + * @run testng ListDefaults + * @summary Unit tests for extension methods on List + */ +public class ListDefaults { + + private static final String[] LIST_CLASSES = { + "java.util.ArrayList", + "java.util.LinkedList", + "java.util.Vector", + "java.util.concurrent.CopyOnWriteArrayList" + }; + + private static final String[] LIST_CME_CLASSES = { + "java.util.ArrayList", + "java.util.Vector" + }; + + private static final Predicate pEven = x -> 0 == x % 2; + private static final Predicate pOdd = x -> 1 == x % 2; + + private static final Comparator BIT_COUNT_COMPARATOR = + (x, y) -> Integer.bitCount(x) - Integer.bitCount(y); + + private static final Comparator ATOMIC_INTEGER_COMPARATOR = + (x, y) -> x.intValue() - y.intValue(); + + private static final int SIZE = 100; + private static final int SUBLIST_FROM = 20; + private static final int SUBLIST_TO = SIZE - 5; + private static final int SUBLIST_SIZE = SUBLIST_TO - SUBLIST_FROM; + + private static interface Callback { + void call(List list); + } + + // call the callback for each recursive subList + private void trimmedSubList(final List list, final Callback callback) { + int size = list.size(); + if (size > 1) { + // trim 1 element from both ends + final List subList = list.subList(1, size - 1); + callback.call(subList); + trimmedSubList(subList, callback); + } + } + + @DataProvider(name="listProvider", parallel=true) + public static Object[][] listCases() { + final List cases = new LinkedList<>(); + cases.add(new Object[] { new ArrayList<>() }); + cases.add(new Object[] { new LinkedList<>() }); + cases.add(new Object[] { new Vector<>() }); + cases.add(new Object[] { new Stack<>() }); + cases.add(new Object[] { new CopyOnWriteArrayList<>() }); + + cases.add(new Object[] { new ArrayList(){{add(42);}} }); + cases.add(new Object[] { new LinkedList(){{add(42);}} }); + cases.add(new Object[] { new Vector(){{add(42);}} }); + cases.add(new Object[] { new Stack(){{add(42);}} }); + cases.add(new Object[] { new CopyOnWriteArrayList(){{add(42);}} }); + return cases.toArray(new Object[0][cases.size()]); + } + + @Test(dataProvider = "listProvider") + public void testProvidedWithNull(final List list) throws Exception { + try { + list.forEach(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + try { + list.replaceAll(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + try { + list.removeIf(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + } + + @Test + public void testForEach() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.original); + final List list = ((List) test.collection); + } + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.original); + final List list = ((List) test.collection); + + try { + list.forEach(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + CollectionAsserts.assertContents(list, original); + + final List actual = new LinkedList<>(); + list.forEach(actual::add); + CollectionAsserts.assertContents(actual, list); + CollectionAsserts.assertContents(actual, original); + + if (original.size() > SUBLIST_SIZE) { + final List subList = original.subList(SUBLIST_FROM, SUBLIST_TO); + final List actualSubList = new LinkedList<>(); + subList.forEach(actualSubList::add); + assertEquals(actualSubList.size(), SUBLIST_SIZE); + for (int i = 0; i < SUBLIST_SIZE; i++) { + assertEquals(actualSubList.get(i), original.get(i + SUBLIST_FROM)); + } + } + + trimmedSubList(list, new Callback() { + @Override + public void call(final List list) { + final List actual = new LinkedList<>(); + list.forEach(actual::add); + CollectionAsserts.assertContents(actual, list); + } + }); + } + } + + @Test + public void testRemoveIf() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE); + + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.original); + final List list = ((List) test.collection); + + try { + list.removeIf(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + CollectionAsserts.assertContents(list, original); + + final AtomicInteger offset = new AtomicInteger(1); + while (list.size() > 0) { + removeFirst(original, list, offset); + } + } + + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.original); + final List list = ((List) test.collection); + list.removeIf(pOdd); + for (int i : list) { + assertTrue((i % 2) == 0); + } + for (int i : original) { + if (i % 2 == 0) { + assertTrue(list.contains(i)); + } + } + list.removeIf(pEven); + assertTrue(list.isEmpty()); + } + + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.original); + final List list = ((List) test.collection); + final List listCopy = new ArrayList<>(list); + if (original.size() > SUBLIST_SIZE) { + final List subList = list.subList(SUBLIST_FROM, SUBLIST_TO); + final List subListCopy = new ArrayList<>(subList); + listCopy.removeAll(subList); + subList.removeIf(pOdd); + for (int i : subList) { + assertTrue((i % 2) == 0); + } + for (int i : subListCopy) { + if (i % 2 == 0) { + assertTrue(subList.contains(i)); + } else { + assertFalse(subList.contains(i)); + } + } + subList.removeIf(pEven); + assertTrue(subList.isEmpty()); + // elements outside the view should remain + CollectionAsserts.assertContents(list, listCopy); + } + } + + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + trimmedSubList(list, new Callback() { + @Override + public void call(final List list) { + final List copy = new ArrayList<>(list); + list.removeIf(pOdd); + for (int i : list) { + assertTrue((i % 2) == 0); + } + for (int i : copy) { + if (i % 2 == 0) { + assertTrue(list.contains(i)); + } else { + assertFalse(list.contains(i)); + } + } + } + }); + } + } + + // remove the first element + private void removeFirst(final List original, final List list, final AtomicInteger offset) { + final AtomicBoolean first = new AtomicBoolean(true); + list.removeIf(x -> first.getAndSet(false)); + CollectionAsserts.assertContents(original.subList(offset.getAndIncrement(), original.size()), list); + } + + @Test + public void testReplaceAll() throws Exception { + final int scale = 3; + final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.original); + final List list = ((List) test.collection); + + try { + list.replaceAll(null); + fail("expected NPE not thrown"); + } catch (NullPointerException npe) {} + CollectionAsserts.assertContents(list, original); + + list.replaceAll(x -> scale * x); + for (int i=0; i < original.size(); i++) { + assertTrue(list.get(i) == (scale * original.get(i)), "mismatch at index " + i); + } + + if (original.size() > SUBLIST_SIZE) { + final List subList = list.subList(SUBLIST_FROM, SUBLIST_TO); + subList.replaceAll(x -> x + 1); + // verify elements in view [from, to) were replaced + for (int i = 0; i < SUBLIST_SIZE; i++) { + assertTrue(subList.get(i) == ((scale * original.get(i + SUBLIST_FROM)) + 1), + "mismatch at sublist index " + i); + } + // verify that elements [0, from) remain unmodified + for (int i = 0; i < SUBLIST_FROM; i++) { + assertTrue(list.get(i) == (scale * original.get(i)), + "mismatch at original index " + i); + } + // verify that elements [to, size) remain unmodified + for (int i = SUBLIST_TO; i < list.size(); i++) { + assertTrue(list.get(i) == (scale * original.get(i)), + "mismatch at original index " + i); + } + } + } + + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + trimmedSubList(list, new Callback() { + @Override + public void call(final List list) { + final List copy = new ArrayList<>(list); + final int offset = 5; + list.replaceAll(x -> offset + x); + for (int i=0; i < copy.size(); i++) { + assertTrue(list.get(i) == (offset + copy.get(i)), "mismatch at index " + i); + } + } + }); + } + } + + @Test + public void testSort() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List original = ((List) test.original); + final List list = ((List) test.collection); + CollectionSupplier.shuffle(list); + list.sort(Integer::compare); + CollectionAsserts.assertSorted(list, Integer::compare); + if (test.name.startsWith("reverse")) { + Collections.reverse(list); + } + CollectionAsserts.assertContents(list, original); + + CollectionSupplier.shuffle(list); + list.sort(null); + CollectionAsserts.assertSorted(list, Comparators.naturalOrder()); + if (test.name.startsWith("reverse")) { + Collections.reverse(list); + } + CollectionAsserts.assertContents(list, original); + + CollectionSupplier.shuffle(list); + list.sort(Comparators.naturalOrder()); + CollectionAsserts.assertSorted(list, Comparators.naturalOrder()); + if (test.name.startsWith("reverse")) { + Collections.reverse(list); + } + CollectionAsserts.assertContents(list, original); + + CollectionSupplier.shuffle(list); + list.sort(Comparators.reverseOrder()); + CollectionAsserts.assertSorted(list, Comparators.reverseOrder()); + if (!test.name.startsWith("reverse")) { + Collections.reverse(list); + } + CollectionAsserts.assertContents(list, original); + + CollectionSupplier.shuffle(list); + list.sort(BIT_COUNT_COMPARATOR); + CollectionAsserts.assertSorted(list, BIT_COUNT_COMPARATOR); + // check sort by verifying that bitCount increases and never drops + int minBitCount = 0; + int bitCount = 0; + for (final Integer i : list) { + bitCount = Integer.bitCount(i); + assertTrue(bitCount >= minBitCount); + minBitCount = bitCount; + } + + @SuppressWarnings("unchecked") + final Class> type = + (Class>) Class.forName(test.className); + final Constructor> defaultConstructor = type.getConstructor(); + final List incomparables = (List) defaultConstructor.newInstance(); + + for (int i=0; i < test.original.size(); i++) { + incomparables.add(new AtomicInteger(i)); + } + CollectionSupplier.shuffle(incomparables); + incomparables.sort(ATOMIC_INTEGER_COMPARATOR); + for (int i=0; i < test.original.size(); i++) { + assertEquals(i, incomparables.get(i).intValue()); + } + + if (original.size() > SUBLIST_SIZE) { + final List copy = new ArrayList<>(list); + final List subList = list.subList(SUBLIST_FROM, SUBLIST_TO); + CollectionSupplier.shuffle(subList); + subList.sort(Comparators.naturalOrder()); + CollectionAsserts.assertSorted(subList, Comparators.naturalOrder()); + // verify that elements [0, from) remain unmodified + for (int i = 0; i < SUBLIST_FROM; i++) { + assertTrue(list.get(i) == copy.get(i), + "mismatch at index " + i); + } + // verify that elements [to, size) remain unmodified + for (int i = SUBLIST_TO; i < list.size(); i++) { + assertTrue(list.get(i) == copy.get(i), + "mismatch at index " + i); + } + } + } + + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + trimmedSubList(list, new Callback() { + @Override + public void call(final List list) { + final List copy = new ArrayList<>(list); + CollectionSupplier.shuffle(list); + list.sort(Comparators.naturalOrder()); + CollectionAsserts.assertSorted(list, Comparators.naturalOrder()); + } + }); + } + } + + @Test + public void testForEachThrowsCME() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + if (list.size() <= 1) { + continue; + } + boolean gotException = false; + try { + // bad predicate that modifies its list, should throw CME + list.forEach((x) -> {list.add(x);}); + } catch (ConcurrentModificationException cme) { + gotException = true; + } + if (!gotException) { + fail("expected CME was not thrown from " + test); + } + } + } + + @Test + public void testRemoveIfThrowsCME() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + if (list.size() <= 1) { + continue; + } + boolean gotException = false; + try { + // bad predicate that modifies its list, should throw CME + list.removeIf((x) -> {return list.add(x);}); + } catch (ConcurrentModificationException cme) { + gotException = true; + } + if (!gotException) { + fail("expected CME was not thrown from " + test); + } + } + } + + @Test + public void testReplaceAllThrowsCME() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + if (list.size() <= 1) { + continue; + } + boolean gotException = false; + try { + // bad predicate that modifies its list, should throw CME + list.replaceAll(x -> {int n = 3 * x; list.add(n); return n;}); + } catch (ConcurrentModificationException cme) { + gotException = true; + } + if (!gotException) { + fail("expected CME was not thrown from " + test); + } + } + } + + @Test + public void testSortThrowsCME() throws Exception { + final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE); + for (final CollectionSupplier.TestCase test : supplier.get()) { + final List list = ((List) test.collection); + if (list.size() <= 1) { + continue; + } + boolean gotException = false; + try { + // bad predicate that modifies its list, should throw CME + list.sort((x, y) -> {list.add(x); return x - y;}); + } catch (ConcurrentModificationException cme) { + gotException = true; + } + if (!gotException) { + fail("expected CME was not thrown from " + test); + } + } + } + + private static final List SLICED_EXPECTED = Arrays.asList(0, 1, 2, 3, 5, 6, 7, 8, 9); + private static final List SLICED_EXPECTED2 = Arrays.asList(0, 1, 2, 5, 6, 7, 8, 9); + + @DataProvider(name="shortIntListProvider", parallel=true) + public static Object[][] intListCases() { + final Integer[] DATA = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + final List cases = new LinkedList<>(); + cases.add(new Object[] { new ArrayList<>(Arrays.asList(DATA)) }); + cases.add(new Object[] { new LinkedList<>(Arrays.asList(DATA)) }); + cases.add(new Object[] { new Vector<>(Arrays.asList(DATA)) }); + cases.add(new Object[] { new CopyOnWriteArrayList<>(Arrays.asList(DATA)) }); + return cases.toArray(new Object[0][cases.size()]); + } + + @Test(dataProvider = "shortIntListProvider") + public void testRemoveIfFromSlice(final List list) throws Exception { + final List sublist = list.subList(3, 6); + assertTrue(sublist.removeIf(x -> x == 4)); + CollectionAsserts.assertContents(list, SLICED_EXPECTED); + + final List sublist2 = list.subList(2, 5); + assertTrue(sublist2.removeIf(x -> x == 3)); + CollectionAsserts.assertContents(list, SLICED_EXPECTED2); + } +} diff --git a/jdk/test/java/util/Collection/testlibrary/CollectionAsserts.java b/jdk/test/java/util/Collection/testlibrary/CollectionAsserts.java new file mode 100644 index 00000000000..a03f975152e --- /dev/null +++ b/jdk/test/java/util/Collection/testlibrary/CollectionAsserts.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +/** + * @library + * CollectionAssert -- assertion methods for lambda test cases + */ +public class CollectionAsserts { + + public static void assertCountSum(Iterable it, int count, int sum) { + assertCountSum(it.iterator(), count, sum); + } + + public static void assertCountSum(Iterator it, int count, int sum) { + int c = 0; + int s = 0; + while (it.hasNext()) { + int i = (Integer) it.next(); + c++; + s += i; + } + + assertEquals(c, count); + assertEquals(s, sum); + } + + public static void assertConcat(Iterator it, String result) { + StringBuilder sb = new StringBuilder(); + while (it.hasNext()) { + sb.append(it.next()); + } + + assertEquals(result, sb.toString()); + } + + public static> void assertSorted(Iterator i) { + if (!i.hasNext()) + return; + T last = i.next(); + while (i.hasNext()) { + T t = i.next(); + assertTrue(last.compareTo(t) <= 0); + assertTrue(t.compareTo(last) >= 0); + last = t; + } + } + + public static void assertSorted(Iterator i, Comparator comp) { + if (!i.hasNext()) + return; + T last = i.next(); + while (i.hasNext()) { + T t = i.next(); + assertTrue(comp.compare(last, t) <= 0); + assertTrue(comp.compare(t, last) >= 0); + last = t; + } + } + + public static> void assertSorted(Iterable iter) { + assertSorted(iter.iterator()); + } + + public static void assertSorted(Iterable iter, Comparator comp) { + assertSorted(iter.iterator(), comp); + } + + public static void assertUnique(Iterable iter) { + assertUnique(iter.iterator()); + } + + public static void assertUnique(Iterator iter) { + if (!iter.hasNext()) { + return; + } + + Set uniq = new HashSet<>(); + while(iter.hasNext()) { + T each = iter.next(); + assertTrue(!uniq.contains(each)); + uniq.add(each); + } + } + + public static void assertContents(Iterable actual, Iterable expected) { + assertContents(actual.iterator(), expected.iterator()); + } + + public static void assertContents(Iterator actual, Iterator expected) { + List history = new ArrayList<>(); + + while (expected.hasNext()) { + if (!actual.hasNext()) { + List expectedData = new ArrayList<>(history); + while (expected.hasNext()) + expectedData.add(expected.next()); + fail(String.format("Premature end of data; expected=%s, found=%s", expectedData, history)); + } + T a = actual.next(); + T e = expected.next(); + history.add(a); + + if (!Objects.equals(a, e)) + fail(String.format("Data mismatch; preceding=%s, nextExpected=%s, nextFound=%s", history, e, a)); + } + if (actual.hasNext()) { + List rest = new ArrayList<>(); + while (actual.hasNext()) + rest.add(actual.next()); + fail(String.format("Unexpected data %s after %s", rest, history)); + } + } + + @SafeVarargs + @SuppressWarnings("varargs") + public static void assertContents(Iterator actual, T... expected) { + assertContents(actual, Arrays.asList(expected).iterator()); + } + + public static boolean equalsContentsUnordered(Iterable a, Iterable b) { + Set sa = new HashSet<>(); + for (T t : a) { + sa.add(t); + } + + Set sb = new HashSet<>(); + for (T t : b) { + sb.add(t); + } + + return Objects.equals(sa, sb); + } + + public static> void assertContentsUnordered(Iterable actual, Iterable expected) { + ArrayList one = new ArrayList<>(); + for (T t : actual) + one.add(t); + ArrayList two = new ArrayList<>(); + for (T t : expected) + two.add(t); + Collections.sort(one); + Collections.sort(two); + assertContents(one, two); + } + + static void assertSplitContents(Iterable> splits, Iterable list) { + Iterator> mI = splits.iterator(); + Iterator pI = null; + Iterator lI = list.iterator(); + + while (lI.hasNext()) { + if (pI == null) + pI = mI.next().iterator(); + while (!pI.hasNext()) { + if (!mI.hasNext()) { + break; + } + else { + pI = mI.next().iterator(); + } + } + assertTrue(pI.hasNext()); + T pT = pI.next(); + T lT = lI.next(); + assertEquals(pT, lT); + } + + if (pI != null) { + assertTrue(!pI.hasNext()); + } + + while(mI.hasNext()) { + pI = mI.next().iterator(); + assertTrue(!pI.hasNext()); + } + } +} diff --git a/jdk/test/java/util/Collection/testlibrary/CollectionSupplier.java b/jdk/test/java/util/Collection/testlibrary/CollectionSupplier.java new file mode 100644 index 00000000000..c26aaa7385f --- /dev/null +++ b/jdk/test/java/util/Collection/testlibrary/CollectionSupplier.java @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.lang.Exception; +import java.lang.Integer; +import java.lang.Iterable; +import java.lang.Override; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import org.testng.TestException; + +import static org.testng.Assert.assertTrue; + +import java.lang.reflect.Constructor; +import java.util.Collection; +import java.util.Collections; +import java.util.function.Supplier; + +/** + * @library + * @summary A Supplier of test cases for Collection tests + */ +public final class CollectionSupplier implements Supplier> { + + private final String[] classNames; + private final int size; + + /** + * A Collection test case. + */ + public static final class TestCase { + + /** + * The name of the test case. + */ + public final String name; + + /** + * Class name of the instantiated Collection. + */ + public final String className; + + /** + * Unmodifiable reference collection, useful for comparisons. + */ + public final Collection original; + + /** + * A modifiable test collection. + */ + public final Collection collection; + + /** + * Create a Collection test case. + * @param name name of the test case + * @param className class name of the instantiated collection + * @param original reference collection + * @param collection the modifiable test collection + */ + public TestCase(String name, String className, + Collection original, Collection collection) { + this.name = name; + this.className = className; + this.original = + List.class.isAssignableFrom(original.getClass()) ? + Collections.unmodifiableList((List) original) : + Set.class.isAssignableFrom(original.getClass()) ? + Collections.unmodifiableSet((Set) original) : + Collections.unmodifiableCollection(original); + this.collection = collection; + } + + @Override + public String toString() { + return name + " " + className + + "\n original: " + original + + "\n target: " + collection; + } + } + + /** + * Shuffle a list using a PRNG with known seed for repeatability + * @param list the list to be shuffled + */ + public static void shuffle(final List list) { + // PRNG with known seed for repeatable tests + final Random prng = new Random(13); + final int size = list.size(); + for (int i=0; i < size; i++) { + // random index in interval [i, size) + final int j = i + prng.nextInt(size - i); + // swap elements at indices i & j + final E e = list.get(i); + list.set(i, list.get(j)); + list.set(j, e); + } + } + + /** + * Create a {@code Supplier} that creates instances of specified collection + * classes of specified length. + * + * @param classNames class names that implement {@code Collection} + * @param size the desired size of each collection + */ + public CollectionSupplier(String[] classNames, int size) { + this.classNames = Arrays.copyOf(classNames, classNames.length); + this.size = size; + } + + @Override + public Iterable get() { + try { + return getThrows(); + } catch (Exception e) { + throw new TestException(e); + } + } + + private Iterable getThrows() throws Exception { + final Collection collections = new LinkedList<>(); + for (final String className : classNames) { + @SuppressWarnings("unchecked") + final Class> type = + (Class>) Class.forName(className); + final Constructor> + defaultConstructor = type.getConstructor(); + final Constructor> + copyConstructor = type.getConstructor(Collection.class); + + final Collection empty = defaultConstructor.newInstance(); + collections.add(new TestCase("empty", + className, + copyConstructor.newInstance(empty), + empty)); + + final Collection single = defaultConstructor.newInstance(); + single.add(42); + collections.add(new TestCase("single", + className, + copyConstructor.newInstance(single), + single)); + + final Collection regular = defaultConstructor.newInstance(); + for (int i=0; i < size; i++) { + regular.add(i); + } + collections.add(new TestCase("regular", + className, + copyConstructor.newInstance(regular), + regular)); + + final Collection reverse = defaultConstructor.newInstance(); + for (int i=size; i >= 0; i--) { + reverse.add(i); + } + collections.add(new TestCase("reverse", + className, + copyConstructor.newInstance(reverse), + reverse)); + + final Collection odds = defaultConstructor.newInstance(); + for (int i=0; i < size; i++) { + odds.add((i * 2) + 1); + } + collections.add(new TestCase("odds", + className, + copyConstructor.newInstance(odds), + odds)); + + final Collection evens = defaultConstructor.newInstance(); + for (int i=0; i < size; i++) { + evens.add(i * 2); + } + collections.add(new TestCase("evens", + className, + copyConstructor.newInstance(evens), + evens)); + + final Collection fibonacci = defaultConstructor.newInstance(); + int prev2 = 0; + int prev1 = 1; + for (int i=0; i < size; i++) { + final int n = prev1 + prev2; + if (n < 0) { // stop on overflow + break; + } + fibonacci.add(n); + prev2 = prev1; + prev1 = n; + } + collections.add(new TestCase("fibonacci", + className, + copyConstructor.newInstance(fibonacci), + fibonacci)); + + // variants where the size of the backing storage != reported size + // created by removing half of the elements + + final Collection emptyWithSlack = defaultConstructor.newInstance(); + emptyWithSlack.add(42); + assertTrue(emptyWithSlack.remove(42)); + collections.add(new TestCase("emptyWithSlack", + className, + copyConstructor.newInstance(emptyWithSlack), + emptyWithSlack)); + + final Collection singleWithSlack = defaultConstructor.newInstance(); + singleWithSlack.add(42); + singleWithSlack.add(43); + assertTrue(singleWithSlack.remove(43)); + collections.add(new TestCase("singleWithSlack", + className, + copyConstructor.newInstance(singleWithSlack), + singleWithSlack)); + + final Collection regularWithSlack = defaultConstructor.newInstance(); + for (int i=0; i < (2 * size); i++) { + regularWithSlack.add(i); + } + assertTrue(regularWithSlack.removeIf((x) -> {return x >= size;})); + collections.add(new TestCase("regularWithSlack", + className, + copyConstructor.newInstance(regularWithSlack), + regularWithSlack)); + + final Collection reverseWithSlack = defaultConstructor.newInstance(); + for (int i=2 * size; i >= 0; i--) { + reverseWithSlack.add(i); + } + assertTrue(reverseWithSlack.removeIf((x) -> {return x < size;})); + collections.add(new TestCase("reverseWithSlack", + className, + copyConstructor.newInstance(reverseWithSlack), + reverseWithSlack)); + + final Collection oddsWithSlack = defaultConstructor.newInstance(); + for (int i = 0; i < 2 * size; i++) { + oddsWithSlack.add((i * 2) + 1); + } + assertTrue(oddsWithSlack.removeIf((x) -> {return x >= size;})); + collections.add(new TestCase("oddsWithSlack", + className, + copyConstructor.newInstance(oddsWithSlack), + oddsWithSlack)); + + final Collection evensWithSlack = defaultConstructor.newInstance(); + for (int i = 0; i < 2 * size; i++) { + evensWithSlack.add(i * 2); + } + assertTrue(evensWithSlack.removeIf((x) -> {return x >= size;})); + collections.add(new TestCase("evensWithSlack", + className, + copyConstructor.newInstance(evensWithSlack), + evensWithSlack)); + + final Collection fibonacciWithSlack = defaultConstructor.newInstance(); + prev2 = 0; + prev1 = 1; + for (int i=0; i < size; i++) { + final int n = prev1 + prev2; + if (n < 0) { // stop on overflow + break; + } + fibonacciWithSlack.add(n); + prev2 = prev1; + prev1 = n; + } + assertTrue(fibonacciWithSlack.removeIf((x) -> {return x < 20;})); + collections.add(new TestCase("fibonacciWithSlack", + className, + copyConstructor.newInstance(fibonacciWithSlack), + fibonacciWithSlack)); + + } + + return collections; + } + +} diff --git a/jdk/test/java/util/Locale/LocaleProviders.java b/jdk/test/java/util/Locale/LocaleProviders.java index b6320727943..3159b4b3bc1 100644 --- a/jdk/test/java/util/Locale/LocaleProviders.java +++ b/jdk/test/java/util/Locale/LocaleProviders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -23,6 +23,7 @@ import java.text.*; import java.text.spi.*; import java.util.*; +import java.util.spi.*; import sun.util.locale.provider.LocaleProviderAdapter; public class LocaleProviders { @@ -55,6 +56,10 @@ public class LocaleProviders { bug8001440Test(); break; + case "bug8010666Test": + bug8010666Test(); + break; + default: throw new RuntimeException("Test method '"+methodName+"' not found."); } @@ -103,4 +108,38 @@ public class LocaleProviders { NumberFormat nf = NumberFormat.getInstance(locale); String nu = nf.format(1234560); } + + // This test assumes Windows localized language/country display names. + static void bug8010666Test() { + if (System.getProperty("os.name").startsWith("Windows")) { + NumberFormat nf = NumberFormat.getInstance(Locale.US); + try { + double ver = nf.parse(System.getProperty("os.version")).doubleValue(); + System.out.printf("Windows version: %.1f\n", ver); + if (ver >= 6.0) { + LocaleProviderAdapter lda = LocaleProviderAdapter.getAdapter(LocaleNameProvider.class, Locale.ENGLISH); + LocaleProviderAdapter.Type type = lda.getAdapterType(); + if (type == LocaleProviderAdapter.Type.HOST) { + Locale mkmk = Locale.forLanguageTag("mk-MK"); + String result = mkmk.getDisplayLanguage(Locale.ENGLISH); + if (!"Macedonian (FYROM)".equals(result)) { + throw new RuntimeException("Windows locale name provider did not return expected localized language name for \"mk\". Returned name was \"" + result + "\""); + } + result = Locale.US.getDisplayLanguage(Locale.ENGLISH); + if (!"English".equals(result)) { + throw new RuntimeException("Windows locale name provider did not return expected localized language name for \"en\". Returned name was \"" + result + "\""); + } + result = Locale.US.getDisplayCountry(Locale.ENGLISH); + if (ver >= 6.1 && !"United States".equals(result)) { + throw new RuntimeException("Windows locale name provider did not return expected localized country name for \"US\". Returned name was \"" + result + "\""); + } + } else { + throw new RuntimeException("Windows Host LocaleProviderAdapter was not selected for English locale."); + } + } + } catch (ParseException pe) { + throw new RuntimeException("Parsing Windows version failed: "+pe.toString()); + } + } + } } diff --git a/jdk/test/java/util/Locale/LocaleProviders.sh b/jdk/test/java/util/Locale/LocaleProviders.sh index 6790f871e79..63a74bbbc81 100644 --- a/jdk/test/java/util/Locale/LocaleProviders.sh +++ b/jdk/test/java/util/Locale/LocaleProviders.sh @@ -1,5 +1,5 @@ # -# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# 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 @@ -23,7 +23,7 @@ #!/bin/sh # # @test -# @bug 6336885 7196799 7197573 7198834 8000245 8000615 8001440 +# @bug 6336885 7196799 7197573 7198834 8000245 8000615 8001440 8010666 # @summary tests for "java.locale.providers" system property # @compile -XDignore.symbol.file LocaleProviders.java # @run shell/timeout=600 LocaleProviders.sh @@ -258,4 +258,15 @@ PARAM2= PARAM3= runTest +# testing 8010666 fix. +if [ "${DEFLANG}" = "en" ] +then + METHODNAME=bug8010666Test + PREFLIST=HOST + PARAM1= + PARAM2= + PARAM3= + runTest +fi + exit $result diff --git a/jdk/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java b/jdk/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java index 046cfa079f1..cb5ffa90ed3 100644 --- a/jdk/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java +++ b/jdk/test/java/util/Spliterator/SpliteratorTraversingAndSplittingTest.java @@ -184,6 +184,8 @@ public class SpliteratorTraversingAndSplittingTest { @Override public boolean tryAdvance(Consumer action) { + if (action == null) + throw new NullPointerException(); if (it.hasNext()) { action.accept(it.next()); return true; @@ -193,7 +195,7 @@ public class SpliteratorTraversingAndSplittingTest { } } } - db.add("new Spliterators.AbstractAdvancingSpliterator()", + db.add("new Spliterators.AbstractSpliterator()", () -> new SpliteratorFromIterator(exp.iterator(), exp.size())); // Collections @@ -370,7 +372,28 @@ public class SpliteratorTraversingAndSplittingTest { db.addCollection(c -> Collections.singletonList(exp.get(0))); } - // @@@ Collections.synchronized/unmodifiable/checked wrappers + // Collections.synchronized/unmodifiable/checked wrappers + db.addCollection(Collections::unmodifiableCollection); + db.addCollection(c -> Collections.unmodifiableSet(new HashSet<>(c))); + db.addCollection(c -> Collections.unmodifiableSortedSet(new TreeSet<>(c))); + db.addList(c -> Collections.unmodifiableList(new ArrayList<>(c))); + db.addMap(Collections::unmodifiableMap); + db.addMap(m -> Collections.unmodifiableSortedMap(new TreeMap<>(m))); + + db.addCollection(Collections::synchronizedCollection); + db.addCollection(c -> Collections.synchronizedSet(new HashSet<>(c))); + db.addCollection(c -> Collections.synchronizedSortedSet(new TreeSet<>(c))); + db.addList(c -> Collections.synchronizedList(new ArrayList<>(c))); + db.addMap(Collections::synchronizedMap); + db.addMap(m -> Collections.synchronizedSortedMap(new TreeMap<>(m))); + + db.addCollection(c -> Collections.checkedCollection(c, Integer.class)); + db.addCollection(c -> Collections.checkedQueue(new ArrayDeque<>(c), Integer.class)); + db.addCollection(c -> Collections.checkedSet(new HashSet<>(c), Integer.class)); + db.addCollection(c -> Collections.checkedSortedSet(new TreeSet<>(c), Integer.class)); + db.addList(c -> Collections.checkedList(new ArrayList<>(c), Integer.class)); + db.addMap(c -> Collections.checkedMap(c, Integer.class, Integer.class)); + db.addMap(m -> Collections.checkedSortedMap(new TreeMap<>(m), Integer.class, Integer.class)); // Maps @@ -400,6 +423,13 @@ public class SpliteratorTraversingAndSplittingTest { return Collections.unmodifiableList(exp); } + @Test(dataProvider = "Spliterator") + @SuppressWarnings({"unchecked", "rawtypes"}) + public void testNullPointerException(String description, Collection exp, Supplier s) { + executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining(null)); + executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance(null)); + } + @Test(dataProvider = "Spliterator") @SuppressWarnings({"unchecked", "rawtypes"}) public void testForEach(String description, Collection exp, Supplier s) { @@ -507,6 +537,8 @@ public class SpliteratorTraversingAndSplittingTest { @Override public boolean tryAdvance(IntConsumer action) { + if (action == null) + throw new NullPointerException(); if (index < a.length) { action.accept(a[index++]); return true; @@ -552,6 +584,12 @@ public class SpliteratorTraversingAndSplittingTest { return b -> new BoxingAdapter(b); } + @Test(dataProvider = "Spliterator.OfInt") + public void testIntNullPointerException(String description, Collection exp, Supplier s) { + executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining((IntConsumer) null)); + executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance((IntConsumer) null)); + } + @Test(dataProvider = "Spliterator.OfInt") public void testIntForEach(String description, Collection exp, Supplier s) { testForEach(exp, s, intBoxingConsumer()); @@ -652,6 +690,8 @@ public class SpliteratorTraversingAndSplittingTest { @Override public boolean tryAdvance(LongConsumer action) { + if (action == null) + throw new NullPointerException(); if (index < a.length) { action.accept(a[index++]); return true; @@ -704,6 +744,12 @@ public class SpliteratorTraversingAndSplittingTest { return b -> new BoxingAdapter(b); } + @Test(dataProvider = "Spliterator.OfLong") + public void testLongNullPointerException(String description, Collection exp, Supplier s) { + executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining((LongConsumer) null)); + executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance((LongConsumer) null)); + } + @Test(dataProvider = "Spliterator.OfLong") public void testLongForEach(String description, Collection exp, Supplier s) { testForEach(exp, s, longBoxingConsumer()); @@ -804,6 +850,8 @@ public class SpliteratorTraversingAndSplittingTest { @Override public boolean tryAdvance(DoubleConsumer action) { + if (action == null) + throw new NullPointerException(); if (index < a.length) { action.accept(a[index++]); return true; @@ -856,6 +904,12 @@ public class SpliteratorTraversingAndSplittingTest { return b -> new BoxingAdapter(b); } + @Test(dataProvider = "Spliterator.OfDouble") + public void testDoubleNullPointerException(String description, Collection exp, Supplier s) { + executeAndCatch(NullPointerException.class, () -> s.get().forEachRemaining((DoubleConsumer) null)); + executeAndCatch(NullPointerException.class, () -> s.get().tryAdvance((DoubleConsumer) null)); + } + @Test(dataProvider = "Spliterator.OfDouble") public void testDoubleForEach(String description, Collection exp, Supplier s) { testForEach(exp, s, doubleBoxingConsumer()); @@ -1057,8 +1111,8 @@ public class SpliteratorTraversingAndSplittingTest { } private static > void visit(int depth, int curLevel, - List dest, S spliterator, UnaryOperator> boxingAdapter, - int rootCharacteristics, boolean useTryAdvance) { + List dest, S spliterator, UnaryOperator> boxingAdapter, + int rootCharacteristics, boolean useTryAdvance) { if (curLevel < depth) { long beforeSize = spliterator.getExactSizeIfKnown(); Spliterator split = spliterator.trySplit(); @@ -1187,13 +1241,13 @@ public class SpliteratorTraversingAndSplittingTest { assertTrue(leftSplit.estimateSize() < parentEstimateSize, String.format("Left split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); assertTrue(parentAndRightSplit.estimateSize() < parentEstimateSize, - String.format("Right split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); + String.format("Right split size estimate %d >= parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); } else { assertTrue(leftSplit.estimateSize() <= parentEstimateSize, - String.format("Left split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); + String.format("Left split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); assertTrue(parentAndRightSplit.estimateSize() <= parentEstimateSize, - String.format("Right split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); + String.format("Right split size estimate %d > parent split size estimate %d", leftSplit.estimateSize(), parentEstimateSize)); } long leftSize = leftSplit.getExactSizeIfKnown(); @@ -1254,4 +1308,22 @@ public class SpliteratorTraversingAndSplittingTest { }); return result; } + + private void executeAndCatch(Class expected, Runnable r) { + Exception caught = null; + try { + r.run(); + } + catch (Exception e) { + caught = e; + } + + assertNotNull(caught, + String.format("No Exception was thrown, expected an Exception of %s to be thrown", + expected.getName())); + assertTrue(expected.isInstance(caught), + String.format("Exception thrown %s not an instance of %s", + caught.getClass().getName(), expected.getName())); + } + } diff --git a/jdk/test/java/util/logging/DrainFindDeadlockTest.java b/jdk/test/java/util/logging/DrainFindDeadlockTest.java new file mode 100644 index 00000000000..13a959c07ff --- /dev/null +++ b/jdk/test/java/util/logging/DrainFindDeadlockTest.java @@ -0,0 +1,196 @@ +/* + * 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.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.lang.Thread.State; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import java.util.Map; + +/** + * @test + * @bug 8010939 + * @summary check for deadlock between findLogger() and drainLoggerRefQueueBounded() + * @author jim.gish@oracle.com + * @build DrainFindDeadlockTest + * @run main/othervm/timeout=10 DrainFindDeadlockTest + */ + +/** + * This test is checking for a deadlock between + * LogManager$LoggerContext.findLogger() and + * LogManager.drainLoggerRefQueueBounded() (which could happen by calling + * Logger.getLogger() and LogManager.readConfiguration() in different threads) + */ +public class DrainFindDeadlockTest { + private LogManager mgr = LogManager.getLogManager(); + private final static int MAX_ITERATIONS = 100; + + // Get a ThreadMXBean so we can check for deadlock. N.B. this may + // not be supported on all platforms, which means we will have to + // resort to the traditional test timeout method. However, if + // we have the support we'll get the deadlock details if one + // is detected. + private final static ThreadMXBean threadMXBean = + ManagementFactory.getThreadMXBean(); + private final boolean threadMXBeanDeadlockSupported = + threadMXBean.isSynchronizerUsageSupported(); + + public static void main(String... args) throws IOException, Exception { + new DrainFindDeadlockTest().testForDeadlock(); + } + + public static void randomDelay() { + int runs = (int) Math.random() * 1000000; + int c = 0; + + for (int i=0; i threadMap = + Thread.getAllStackTraces(); + dumpStack(threadMap.get(x), x); + dumpStack(threadMap.get(y), y); + } + } + + private void dumpStack(StackTraceElement[] aStackElt, Thread aThread) { + if (aStackElt != null) { + System.out.println("Thread:" + aThread.getName() + ": " + + aThread.getState()); + for (StackTraceElement element: aStackElt) { + System.out.println(" " + element); + } + } + } + + @Override + public void run() { + System.out.println("Running " + Thread.currentThread().getName()); + for (int i=0; i < MAX_ITERATIONS*2; i++) { + checkState(t1, t2); + try { + Thread.sleep(10); + } catch (InterruptedException ex) { + }; + } + } + } +} diff --git a/jdk/test/java/util/logging/bundlesearch/ClassPathTestBundle_en.properties b/jdk/test/java/util/logging/bundlesearch/ClassPathTestBundle_en.properties new file mode 100644 index 00000000000..71848703ed2 --- /dev/null +++ b/jdk/test/java/util/logging/bundlesearch/ClassPathTestBundle_en.properties @@ -0,0 +1,25 @@ +# +# 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. +# +sample1=translation #2 for sample1 +sample2=translation #2 for sample2 +supports-test=ResourceBundleSearchTest diff --git a/jdk/test/java/util/logging/bundlesearch/IndirectlyLoadABundle.java b/jdk/test/java/util/logging/bundlesearch/IndirectlyLoadABundle.java new file mode 100644 index 00000000000..76dc1232f22 --- /dev/null +++ b/jdk/test/java/util/logging/bundlesearch/IndirectlyLoadABundle.java @@ -0,0 +1,92 @@ +/* + * 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.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Paths; + +/** + * This class is used to ensure that a resource bundle loadable by a classloader + * is on the caller's stack, but not on the classpath or TCCL to ensure that + * Logger.getLogger() can't load the bundle via a stack search + * + * @author Jim Gish + */ +public class IndirectlyLoadABundle { + + private final static String rbName = "StackSearchableResource"; + + public boolean loadAndTest() throws Throwable { + // Find out where we are running from so we can setup the URLClassLoader URLs + // test.src and test.classes will be set if running in jtreg, but probably + // not otherwise + String testDir = System.getProperty("test.src", System.getProperty("user.dir")); + String testClassesDir = System.getProperty("test.classes", + System.getProperty("user.dir")); + String sep = System.getProperty("file.separator"); + + URL[] urls = new URL[2]; + + // Allow for both jtreg and standalone cases here + urls[0] = Paths.get(testDir, "resources").toUri().toURL(); + urls[1] = Paths.get(testClassesDir).toUri().toURL(); + + System.out.println("INFO: urls[0] = " + urls[0]); + System.out.println("INFO: urls[1] = " + urls[1]); + + // Make sure we can find it via the URLClassLoader + URLClassLoader yetAnotherResourceCL = new URLClassLoader(urls, null); + if (!testForValidResourceSetup(yetAnotherResourceCL)) { + throw new Exception("Couldn't directly load bundle " + rbName + + " as expected. Test config problem"); + } + // But it shouldn't be available via the system classloader + ClassLoader myCL = this.getClass().getClassLoader(); + if (testForValidResourceSetup(myCL)) { + throw new Exception("Was able to directly load bundle " + rbName + + " from " + myCL + " but shouldn't have been" + + " able to. Test config problem"); + } + + Class loadItUpClazz = Class.forName("LoadItUp", true, yetAnotherResourceCL); + ClassLoader actual = loadItUpClazz.getClassLoader(); + if (actual != yetAnotherResourceCL) { + throw new Exception("LoadItUp was loaded by an unexpected CL: " + actual); + } + Object loadItUp = loadItUpClazz.newInstance(); + Method testMethod = loadItUpClazz.getMethod("test", String.class); + try { + return (Boolean) testMethod.invoke(loadItUp, rbName); + } catch (InvocationTargetException ex) { + throw ex.getTargetException(); + } + } + + private boolean testForValidResourceSetup(ClassLoader cl) { + // First make sure the test environment is setup properly and the bundle actually + // exists + return ResourceBundleSearchTest.isOnClassPath(rbName, cl); + } +} diff --git a/jdk/test/java/util/logging/bundlesearch/LoadItUp.java b/jdk/test/java/util/logging/bundlesearch/LoadItUp.java new file mode 100644 index 00000000000..d47b9a3e144 --- /dev/null +++ b/jdk/test/java/util/logging/bundlesearch/LoadItUp.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +import java.util.MissingResourceException; +import java.util.logging.Logger; + +/* + * This class is loaded onto the call stack when the test method is called + * and then its classloader can be used to find a property bundle in the same + * directory as the class. However, Logger is not allowed + * to find the bundle by looking up the stack for this classloader. + * We verify that this cannot happen. + * + * @author Jim Gish + */ +public class LoadItUp { + + private final static boolean DEBUG = false; + + public Boolean test(String rbName) throws Exception { + // we should not be able to find the resource in this directory via + // getLogger calls. The only way that would be possible given this setup + // is that if Logger.getLogger searched up the call stack + return lookupBundle(rbName); + } + + private boolean lookupBundle(String rbName) { + // See if Logger.getLogger can find the resource in this directory + try { + Logger aLogger = Logger.getLogger("NestedLogger", rbName); + } catch (MissingResourceException re) { + if (DEBUG) { + System.out.println( + "As expected, LoadItUp.lookupBundle() did not find the bundle " + + rbName); + } + return false; + } + System.out.println("FAILED: LoadItUp.lookupBundle() found the bundle " + + rbName + " using a stack search."); + return true; + } +} diff --git a/jdk/test/java/util/logging/bundlesearch/ResourceBundleSearchTest.java b/jdk/test/java/util/logging/bundlesearch/ResourceBundleSearchTest.java new file mode 100644 index 00000000000..00f1840042b --- /dev/null +++ b/jdk/test/java/util/logging/bundlesearch/ResourceBundleSearchTest.java @@ -0,0 +1,251 @@ +/* + * 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 8002070 + * @summary Remove the stack search for a resource bundle Logger to use + * @author Jim Gish + * @build ResourceBundleSearchTest IndirectlyLoadABundle LoadItUp + * @run main ResourceBundleSearchTest + */ +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.logging.Logger; + +public class ResourceBundleSearchTest { + + private final static boolean DEBUG = false; + private final static String LOGGER_PREFIX = "myLogger."; + private static int loggerNum = 0; + private final static String PROP_RB_NAME = "ClassPathTestBundle"; + private final static String TCCL_TEST_BUNDLE = "ContextClassLoaderTestBundle"; + + private static int numPass = 0; + private static int numFail = 0; + private static List msgs = new ArrayList<>(); + + public static void main(String[] args) throws Throwable { + ResourceBundleSearchTest test = new ResourceBundleSearchTest(); + test.runTests(); + } + + private void runTests() throws Throwable { + // ensure we are using en as the default Locale so we can find the resource + Locale.setDefault(Locale.ENGLISH); + + String testClasses = System.getProperty("test.classes"); + System.out.println( "test.classes = " + testClasses ); + + ClassLoader myClassLoader = ClassLoader.getSystemClassLoader(); + + // Find out where we are running from so we can setup the URLClassLoader URL + String userDir = System.getProperty("user.dir"); + String testDir = System.getProperty("test.src", userDir); + String sep = System.getProperty("file.separator"); + + URL[] urls = new URL[1]; + + urls[0] = Paths.get(testDir, "resources").toUri().toURL(); + URLClassLoader rbClassLoader = new URLClassLoader(urls); + + // Test 1 - can we find a Logger bundle from doing a stack search? + // We shouldn't be able to + assertFalse(testGetBundleFromStackSearch(), "testGetBundleFromStackSearch"); + + // Test 2 - can we find a Logger bundle off of the Thread context class + // loader? We should be able to. + assertTrue( + testGetBundleFromTCCL(TCCL_TEST_BUNDLE, rbClassLoader), + "testGetBundleFromTCCL"); + + // Test 3 - Can we find a Logger bundle from the classpath? We should be + // able to, but .... + // We check to see if the bundle is on the classpath or not so that this + // will work standalone. In the case of jtreg/samevm, + // the resource bundles are not on the classpath. Running standalone + // (or othervm), they are + if (isOnClassPath(PROP_RB_NAME, myClassLoader)) { + debug("We should be able to see " + PROP_RB_NAME + " on the classpath"); + assertTrue(testGetBundleFromSystemClassLoader(PROP_RB_NAME), + "testGetBundleFromSystemClassLoader"); + } else { + debug("We should not be able to see " + PROP_RB_NAME + " on the classpath"); + assertFalse(testGetBundleFromSystemClassLoader(PROP_RB_NAME), + "testGetBundleFromSystemClassLoader"); + } + + report(); + } + + private void report() throws Exception { + System.out.println("Num passed = " + numPass + " Num failed = " + numFail); + if (numFail > 0) { + // We only care about the messages if they were errors + for (String msg : msgs) { + System.out.println(msg); + } + throw new Exception(numFail + " out of " + (numPass + numFail) + + " tests failed."); + } + } + + public void assertTrue(boolean testResult, String testName) { + if (testResult) { + numPass++; + } else { + numFail++; + System.out.println("FAILED: " + testName + + " was supposed to return true but did NOT!"); + } + } + + public void assertFalse(boolean testResult, String testName) { + if (!testResult) { + numPass++; + } else { + numFail++; + System.out.println("FAILED: " + testName + + " was supposed to return false but did NOT!"); + } + } + + public boolean testGetBundleFromStackSearch() throws Throwable { + // This should fail. This was the old functionality to search up the + // caller's call stack + IndirectlyLoadABundle indirectLoader = new IndirectlyLoadABundle(); + return indirectLoader.loadAndTest(); + } + + public boolean testGetBundleFromTCCL(String bundleName, + ClassLoader setOnTCCL) throws InterruptedException { + // This should succeed. We should be able to get the bundle from the + // thread context class loader + debug("Looking for " + bundleName + " using TCCL"); + LoggingThread lr = new LoggingThread(bundleName, setOnTCCL); + lr.start(); + synchronized (lr) { + try { + lr.wait(); + } catch (InterruptedException ex) { + throw ex; + } + } + msgs.add(lr.msg); + return lr.foundBundle; + } + + /* + * @param String bundleClass + * @param ClassLoader to use for search + * @return true iff bundleClass is on system classpath + */ + public static boolean isOnClassPath(String baseName, ClassLoader cl) { + ResourceBundle rb = null; + try { + rb = ResourceBundle.getBundle(baseName, Locale.getDefault(), cl); + System.out.println("INFO: Found bundle " + baseName + " on " + cl); + } catch (MissingResourceException e) { + System.out.println("INFO: Could not find bundle " + baseName + " on " + cl); + return false; + } + return (rb != null); + } + + private static String newLoggerName() { + // we need a new logger name every time we attempt to find a bundle via + // the Logger.getLogger call, so we'll simply tack on an integer which + // we increment each time this is called + loggerNum++; + return LOGGER_PREFIX + loggerNum; + } + + public boolean testGetBundleFromSystemClassLoader(String bundleName) { + // this should succeed if the bundle is on the system classpath. + try { + Logger aLogger = Logger.getLogger(ResourceBundleSearchTest.newLoggerName(), + bundleName); + } catch (MissingResourceException re) { + msgs.add("INFO: testGetBundleFromSystemClassLoader() did not find bundle " + + bundleName); + return false; + } + msgs.add("INFO: testGetBundleFromSystemClassLoader() found the bundle " + + bundleName); + return true; + } + + public static class LoggingThread extends Thread { + + boolean foundBundle = false; + String msg = null; + ClassLoader clToSetOnTCCL = null; + String bundleName = null; + + public LoggingThread(String bundleName) { + this.bundleName = bundleName; + } + + public LoggingThread(String bundleName, ClassLoader setOnTCCL) { + this.clToSetOnTCCL = setOnTCCL; + this.bundleName = bundleName; + } + + public void run() { + boolean setTCCL = false; + try { + if (clToSetOnTCCL != null) { + Thread.currentThread().setContextClassLoader(clToSetOnTCCL); + setTCCL = true; + } + // this should succeed if the bundle is on the system classpath. + try { + Logger aLogger = Logger.getLogger(ResourceBundleSearchTest.newLoggerName(), + bundleName); + msg = "INFO: LoggingRunnable() found the bundle " + bundleName + + (setTCCL ? " with " : " without ") + "setting the TCCL"; + foundBundle = true; + } catch (MissingResourceException re) { + msg = "INFO: LoggingRunnable() did not find the bundle " + bundleName + + (setTCCL ? " with " : " without ") + "setting the TCCL"; + foundBundle = false; + } + } catch (Throwable e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + private void debug(String msg) { + if (DEBUG) { + System.out.println(msg); + } + } +} diff --git a/jdk/test/java/util/logging/bundlesearch/resources/ContextClassLoaderTestBundle_en.properties b/jdk/test/java/util/logging/bundlesearch/resources/ContextClassLoaderTestBundle_en.properties new file mode 100644 index 00000000000..ad4085ad164 --- /dev/null +++ b/jdk/test/java/util/logging/bundlesearch/resources/ContextClassLoaderTestBundle_en.properties @@ -0,0 +1,25 @@ +# +# 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. +# +sample1=translation #3 for sample1 +sample2=translation #3 for sample2 +supports-test=ResourceBundleSearchTest diff --git a/jdk/test/java/util/logging/bundlesearch/resources/StackSearchableResource_en.properties b/jdk/test/java/util/logging/bundlesearch/resources/StackSearchableResource_en.properties new file mode 100644 index 00000000000..17ba4437d90 --- /dev/null +++ b/jdk/test/java/util/logging/bundlesearch/resources/StackSearchableResource_en.properties @@ -0,0 +1,25 @@ +# +# 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. +# +sample1=translation #4 for sample1 +sample2=translation #4 for sample2 +supports-test=ResourceBundleSearchTest diff --git a/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java b/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java index 208f51943de..237d61f033e 100644 --- a/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java +++ b/jdk/test/sun/security/tools/jarsigner/TimestampCheck.java @@ -260,6 +260,8 @@ public class TimestampCheck { jarsigner(cmd, 7, false); // tsbad2 jarsigner(cmd, 8, false); // tsbad3 jarsigner(cmd, 9, false); // no cert in timestamp + jarsigner(cmd + " -tsapolicyid 1.2.3.4", 0, true); + jarsigner(cmd + " -tsapolicyid 1.2.3.5", 0, false); } else { // Run as a standalone server System.err.println("Press Enter to quit server"); System.in.read(); diff --git a/jdk/test/sun/security/tools/jarsigner/ts.sh b/jdk/test/sun/security/tools/jarsigner/ts.sh index 43a3651f62a..e318ca677e0 100644 --- a/jdk/test/sun/security/tools/jarsigner/ts.sh +++ b/jdk/test/sun/security/tools/jarsigner/ts.sh @@ -22,7 +22,7 @@ # # @test -# @bug 6543842 6543440 6939248 +# @bug 6543842 6543440 6939248 8009636 # @summary checking response of timestamp # # @run shell/timeout=600 ts.sh diff --git a/jdk/test/sun/security/tools/keytool/console.sh b/jdk/test/sun/security/tools/keytool/console.sh index ec0f05961f8..ec338ca373d 100644 --- a/jdk/test/sun/security/tools/keytool/console.sh +++ b/jdk/test/sun/security/tools/keytool/console.sh @@ -1,5 +1,3 @@ -#! /bin/sh - # # Copyright (c) 2006, 2008, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -24,10 +22,11 @@ # # @test -# @bug 6418647 +# @bug 6418647 8005527 # @summary Doc bug 5035358 shows sun.security.util.Password.readPassword() is buggy. # @author Weijun Wang -# +# @ignore unable to test manual tools that have input from stdin, +# and output to stderr and stdout # @run shell/manual console.sh if [ "$ALT_PASS" = "" ]; then