MumbleCloseable

Jim Mayer jim at pentastich.org
Fri Jun 28 22:42:03 PDT 2013


Alas, I forgot that "generate" creates an infinite sequence and so doesn't
do what I wanted in the code I sent.  The same deferred open trick can be
done with a Spliterator or Iterator though.  I tried the following against
a month-old OpenJDK 8:

  private static class FileReadingSpliterator implements
Spliterator<String>, Closeable {
    private final File file;
    private BufferedReader br;

    public FileReadingSpliterator(File file) throws IOException {
      this.file = file;
      if (! file.canRead()) {
        throw new FileNotFoundException(file.toString());
      }
    }

    @Override
    public void close() throws IOException {
      if (br != null) {
        br.close();
      }
    }

    @Override
    public int characteristics() {
      return Spliterator.ORDERED;
    }

    @Override
    public long estimateSize() {
      return Long.MAX_VALUE;
    }

    @Override
    public boolean tryAdvance(Consumer<? super String> action) {
      try {
        if (br == null) {
          br = new BufferedReader(new FileReader(file));
        }
        String line = br.readLine();
        if (line != null) {
          action.accept(line);
          return true;
        } else {
          return false;
        }
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }

    @Override
    public Spliterator<String> trySplit() {
      return null;
    }
  }

Jim



On Sat, Jun 29, 2013 at 12:36 AM, Jim Mayer <jim at pentastich.org> wrote:

> I agree with Rémi about deferring the open.  I think that the following
> (completely untested) code would defer the file open into the execution
> context of the terminal method and would be resource safe.  This version is
> very specific to file I/O, but I think the basic idea could be easily
> generalized.
>
> private class BufferedReaderSupplier implements Supplier<String>,
> Closeable {
>   private final Path path;
>   private final Charset cs;
>   private BufferedReader br;
>
>   public BufferedReaderSupplier(Path path, Charset cs) throws IOException {
>     this.path = path;
>     this.cs = cs;
>     if (! Files.isReadable(path)) {
>       throw new FileNotFoundException(path.toString());
>     }
>   }
>
>   @Override
>   public String get() {
>     try {
>       if (br == null) {
>         br = Files.newBufferedReader(path, cs);
>       }
>       return br.readLine();
>     } catch (IOException e) {
>       throw new RuntimeException(e);
>     }
>   }
>
>   @Override
>   public void close() throws IOException {
>     if (br != null) {
>       br.close();
>     }
>   }
> }
>
> public static Stream<String> lines(Path path, Charset cs) throws
> IOException {
>   BufferedReaderSupplier supplier = new BufferedReaderSupplier(path, cs);
>   return Stream.generate(supplier).onClose(Closeable.asRunnable(supplier));
> }
>
> Jim Mayer
>
>
> On Fri, Jun 28, 2013 at 5:58 PM, Remi Forax <forax at univ-mlv.fr> wrote:
>
>> On 06/28/2013 10:20 PM, Brian Goetz wrote:
>>
>>> public static Stream<String> lines(Path path, Charset cs) throws
>>> IOException {
>>>         BufferedReader br = Files.newBufferedReader(path, cs);
>>>         return br.lines().onClose(Closeable.**asRunnable(br));
>>>     }
>>>
>>
>> This code, is leaky, it will not call close() on the buffered reader,
>> if lines() or Closeable.asRunnable() throws an unchecked exception.
>>
>> As I said earlier, the only way to deal with that correctly is to
>> open/close the reader only in the terminal method, not before.
>>
>> Rémi
>>
>>
>


More information about the lambda-libs-spec-observers mailing list