1/*
2 * Copyright (C) 2007 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.io;
18
19import com.google.common.collect.Sets;
20
21import java.io.IOException;
22import java.util.Set;
23
24/**
25 * The purpose of the CheckCloseSupplier is to report when all closeable objects
26 * supplied by the delegate supplier are closed. To do this, the factory method
27 * returns a decorated version of the {@code delegate} supplied in the
28 * constructor. The decoration mechanism is left up to the subclass via the
29 * abstract {@link #wrap} method.
30 *
31 * <p>The decorated object returned from {@link #wrap} should ideally override
32 * its {@code close} method to not only call {@code super.close()} but to also
33 * call {@code callback.delegateClosed()}.
34 *
35 * @author Chris Nokleberg
36 */
37abstract class CheckCloseSupplier<T> {
38  private final Set<Callback> open = Sets.newHashSet();
39
40  abstract static class Input<T> extends CheckCloseSupplier<T>
41      implements InputSupplier<T> {
42    private final InputSupplier<? extends T> delegate;
43
44    public Input(InputSupplier<? extends T> delegate) {
45      this.delegate = delegate;
46    }
47
48    @Override public T getInput() throws IOException {
49      return wrap(delegate.getInput(), newCallback());
50    }
51  }
52
53  abstract static class Output<T> extends CheckCloseSupplier<T>
54      implements OutputSupplier<T> {
55    private final OutputSupplier<? extends T> delegate;
56
57    public Output(OutputSupplier<? extends T> delegate) {
58      this.delegate = delegate;
59    }
60
61    @Override public T getOutput() throws IOException {
62      return wrap(delegate.getOutput(), newCallback());
63    }
64  }
65
66  public final class Callback {
67    public void delegateClosed() {
68      open.remove(this);
69    }
70  }
71
72  protected Callback newCallback() {
73    Callback callback = new Callback();
74    open.add(callback);
75    return callback;
76  }
77
78  /**
79   * Subclasses should wrap the given object and call
80   * {@link Callback#delegateClosed} when the close method of the delegate is
81   * called, to inform the supplier that the underlying
82   * {@code Closeable} is not longer open.
83   *
84   * @param object the object to wrap.
85   * @param callback the object that the wrapper should call to signal that the
86   */
87  protected abstract T wrap(T object, Callback callback);
88
89  /** Returns true if all the closeables have been closed closed */
90  public boolean areClosed() {
91    return open.isEmpty();
92  }
93}
94