@SuppressWarnings("unchecked")
    public static <T> ClickHouseInputStream of(Iterable<T> source, Class<T> clazz, Function<T, byte[]> converter,
            int bufferSize, Runnable postCloseAction) {
        if (source == null) {
            return EmptyInputStream.INSTANCE;
        } else if (converter != null) { // respect custom converter regardless object type
            return new IterableObjectInputStream<>(source, converter, postCloseAction);
        } else if (clazz == byte[].class) {
            return new IterableByteArrayInputStream((Iterable<byte[]>) source, postCloseAction);
        } else if (clazz == ByteBuffer.class) {
            return new IterableByteBufferInputStream((Iterable<ByteBuffer>) source, postCloseAction);
        } else if (clazz == File.class) { // Too many dependencies if we consider VFS(FileObject and FileContent)
            return new IterableMultipleInputStream<>((Iterable<File>) source, f -> {
                if (f == null) {
                    return null;
                }
                try {
                    // TODO decompress as needed ClickHouseCompression.fromFileName(f.getName())
                    return new FileInputStream(f);
                } catch (IOException e) {
                    throw new IllegalArgumentException(e);
                }
            }, bufferSize, postCloseAction);
        } else if (clazz == String.class) {
            return new IterableObjectInputStream<>((Iterable<String>) source,
                    s -> s == null || s.isEmpty() ? ClickHouseByteBuffer.EMPTY_BYTES
                            : s.getBytes(StandardCharsets.UTF_8),
                    postCloseAction);
        } else if (clazz == URL.class) {
            return new IterableMultipleInputStream<>((Iterable<URL>) source, u -> {
                if (u == null) {
                    return null;
                }
                try {
                    return u.openStream();
                } catch (IOException e) {
                    throw new IllegalArgumentException(e);
                }
            }, bufferSize, postCloseAction);
        } else if (InputStream.class.isAssignableFrom(clazz)) {
            return new IterableMultipleInputStream<>((Iterable<InputStream>) source, i -> i, bufferSize,
                    postCloseAction);
        } else {
            throw new IllegalArgumentException("Missing converter for data type: " + clazz);
        }
    }
Wraps the given iterable objects as byte array based binary input stream.
{@code byte[]}, {@link ByteBuffer}, {@link InputStream}, {@link File},
{@link String}, and {@link URL} are all supported by default.

@param <T>             type of the object
@param source          iterable objects(e.g. byte[], ByteBuffer, and
                       String etc.)
@param clazz           class of the object
@param converter       optional transformer to convert each object into byte
                       array
@param bufferSize      read buffer size
@param postCloseAction custom action will be performed right after closing
                       the input stream
@return non-null input stream
clickhouse-data/src/main/java/com/clickhouse/data/ClickHouseInputStream.java