| /* |
| * Copyright 2016 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package java.util.stream; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| // package protected, as not part of jre |
| class TerminatableStream<T extends TerminatableStream<T>> { |
| // root-only fields, ignored for non-root instances |
| private boolean terminated = false; |
| private final List<Runnable> onClose; |
| |
| private final TerminatableStream<?> root; |
| |
| public TerminatableStream(TerminatableStream<?> previous) { |
| if (previous == null) { |
| root = null; |
| onClose = new ArrayList<>(); |
| } else { |
| root = previous; |
| onClose = null; |
| } |
| } |
| |
| void throwIfTerminated() { |
| if (root != null) { |
| root.throwIfTerminated(); |
| } else if (terminated) { |
| throw new IllegalStateException("Stream already terminated, can't be modified or used"); |
| } |
| } |
| |
| // note that not all terminals directly call this, but they must use it indirectly |
| void terminate() { |
| if (root == null) { |
| throwIfTerminated(); |
| terminated = true; |
| } else { |
| root.terminate(); |
| } |
| } |
| |
| public T onClose(Runnable closeHandler) { |
| if (root == null) { |
| onClose.add(closeHandler); |
| } else { |
| root.onClose(closeHandler); |
| } |
| |
| return (T) this; |
| } |
| |
| public void close() { |
| if (root == null) { |
| terminated = true; |
| runClosers(); |
| } else { |
| root.close(); |
| } |
| } |
| |
| private void runClosers() { |
| ArrayList<Throwable> throwables = new ArrayList<>(); |
| onClose.forEach( |
| runnable -> { |
| try { |
| runnable.run(); |
| } catch (Throwable e) { |
| throwables.add(e); |
| } |
| }); |
| onClose.clear(); |
| |
| if (!throwables.isEmpty()) { |
| Throwable e = throwables.get(0); |
| for (int i = 1, size = throwables.size(); i < size; ++i) { |
| Throwable suppressed = throwables.get(i); |
| if (suppressed != e) { |
| e.addSuppressed(suppressed); |
| } |
| } |
| |
| if (e instanceof RuntimeException) { |
| throw (RuntimeException) e; |
| } |
| if (e instanceof Error) { |
| throw (Error) e; |
| } |
| assert false : "Couldn't have caught this exception from a Runnable! " + e; |
| } |
| } |
| } |