package com.github.davidmoten.rx; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import com.github.davidmoten.rx.internal.operators.OnSubscribeInputStream; import com.github.davidmoten.rx.util.ZippedEntry; import rx.Observable; import rx.Observable.Transformer; import rx.Observer; import rx.functions.Action1; import rx.functions.Action2; import rx.functions.Func0; import rx.functions.Func1; import rx.observables.SyncOnSubscribe; public final class Bytes { private Bytes() { // prevent instantiation } /** * Returns an Observable stream of byte arrays from the given * {@link InputStream} between 1 and {@code size} bytes. * * @param is * input stream of bytes * @param size * max emitted byte array size * @return a stream of byte arrays */ public static Observable<byte[]> from(InputStream is, int size) { return Observable.create(new OnSubscribeInputStream(is, size)); } public static Observable<byte[]> from(File file) { return from(file, 8192); } public static Observable<byte[]> from(final File file, final int size) { Func0<InputStream> resourceFactory = new Func0<InputStream>() { @Override public InputStream call() { try { return new BufferedInputStream(new FileInputStream(file), size); } catch (FileNotFoundException e) { throw new RuntimeException(e); } } }; Func1<InputStream, Observable<byte[]>> observableFactory = new Func1<InputStream, Observable<byte[]>>() { @Override public Observable<byte[]> call(InputStream is) { return from(is, size); } }; return Observable.using(resourceFactory, observableFactory, InputStreamCloseHolder.INSTANCE, true); } private static class InputStreamCloseHolder { private static final Action1<InputStream> INSTANCE = new Action1<InputStream>() { @Override public void call(InputStream is) { try { is.close(); } catch (IOException e) { throw new RuntimeException(e); } } }; } /** * Returns an Observable stream of byte arrays from the given * {@link InputStream} of {@code 8192} bytes. The final byte array may be * less than {@code 8192} bytes. * * @param is * input stream of bytes * @return a stream of byte arrays */ public static Observable<byte[]> from(InputStream is) { return from(is, 8192); } public static Observable<ZippedEntry> unzip(final File file) { Func0<ZipInputStream> resourceFactory = new Func0<ZipInputStream>() { @Override public ZipInputStream call() { try { return new ZipInputStream(new FileInputStream(file)); } catch (FileNotFoundException e) { throw new RuntimeException(e); } } }; Func1<ZipInputStream, Observable<ZippedEntry>> observableFactory = ZipHolder.OBSERVABLE_FACTORY; Action1<ZipInputStream> disposeAction = ZipHolder.DISPOSER; return Observable.using(resourceFactory, observableFactory, disposeAction); } private static final class ZipHolder { static final Action1<ZipInputStream> DISPOSER = new Action1<ZipInputStream>() { @Override public void call(ZipInputStream zis) { try { zis.close(); } catch (IOException e) { throw new RuntimeException(e); } } }; final static Func1<ZipInputStream, Observable<ZippedEntry>> OBSERVABLE_FACTORY = new Func1<ZipInputStream, Observable<ZippedEntry>>() { @Override public Observable<ZippedEntry> call(ZipInputStream zis) { return unzip(zis); } }; } public static Observable<ZippedEntry> unzip(final InputStream is) { return unzip(new ZipInputStream(is)); } public static Observable<ZippedEntry> unzip(final ZipInputStream zis) { return Observable.create(new SyncOnSubscribe<ZipInputStream, ZippedEntry>() { @Override protected ZipInputStream generateState() { return zis; } @Override protected ZipInputStream next(ZipInputStream zis, Observer<? super ZippedEntry> observer) { try { ZipEntry zipEntry = zis.getNextEntry(); if (zipEntry != null) { observer.onNext(new ZippedEntry(zipEntry, zis)); } else { zis.close(); observer.onCompleted(); } } catch (IOException e) { observer.onError(e); } return zis; } }); } public static Transformer<byte[], byte[]> collect() { return new Transformer<byte[], byte[]>() { @Override public Observable<byte[]> call(Observable<byte[]> source) { return source.collect(BosCreatorHolder.INSTANCE, BosCollectorHolder.INSTANCE) .map(BosToArrayHolder.INSTANCE); } }; } private static final class BosCreatorHolder { static final Func0<ByteArrayOutputStream> INSTANCE = new Func0<ByteArrayOutputStream>() { @Override public ByteArrayOutputStream call() { return new ByteArrayOutputStream(); } }; } private static final class BosCollectorHolder { static final Action2<ByteArrayOutputStream, byte[]> INSTANCE = new Action2<ByteArrayOutputStream, byte[]>() { @Override public void call(ByteArrayOutputStream bos, byte[] bytes) { try { bos.write(bytes); } catch (IOException e) { throw new RuntimeException(e); } } }; } private static final class BosToArrayHolder { static final Func1<ByteArrayOutputStream, byte[]> INSTANCE = new Func1<ByteArrayOutputStream, byte[]>() { @Override public byte[] call(ByteArrayOutputStream bos) { return bos.toByteArray(); } }; } }