/*
* Copyright 2014, The OpenNMS Group
*
* 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 org.opennms.newts.gsod;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Stack;
import java.util.zip.GZIPInputStream;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
public class FileIterable {
private FileIterable() {}
public static FluentIterable<Path> fileTreeWalker(final Path root) {
return FluentIterable.from(Iterables.concat(groupFilesByDir(root)));
}
public static class KeyedIterable<K, T> extends FluentIterable<T> {
private K m_key;
private Iterable<T> m_items;
public KeyedIterable(K key, Iterable<T> items) {
m_key = key;
m_items = items;
}
@Override
public Iterator<T> iterator() {
return m_items.iterator();
}
public K getKey() {
return m_key;
}
}
private static KeyedIterable<Path, Path> children(File dir, FileFilter filter) {
return new KeyedIterable<>(dir.toPath(), toPaths(dir.listFiles(filter)));
}
private static KeyedIterable<Path, Path> files(File dir) {
return children(dir, fileMatcher());
}
private static KeyedIterable<Path, Path> subdirs(File dir) {
return children(dir, directoryMatcher());
}
private static FileFilter fileMatcher() {
return new FileFilter() {
@Override
public boolean accept(File f) {
return f.isFile();
}
};
}
private static FileFilter directoryMatcher() {
return new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory();
}
};
}
public static Iterable<Path> toPaths(File[] files) {
return files == null ? Collections.<Path>emptyList() : toPaths(Arrays.asList(files));
}
public static Iterable<Path> toPaths(Iterable<File> files) {
return Iterables.transform(files, new Function<File, Path>(){
@Override
public Path apply(File input) {
return input.toPath();
}
});
}
public static Iterable<File> toFiles(Iterable<Path> files) {
return Iterables.transform(files, new Function<Path, File>(){
@Override
public File apply(Path input) {
return input.toFile();
}
});
}
public static class GroupedPathIterator extends AbstractIterator<KeyedIterable<Path, Path>> {
Stack<Iterator<Path>> m_dirStack;
File m_root;
GroupedPathIterator(File root) {
m_root = root;
}
@Override
protected KeyedIterable<Path, Path> computeNext() {
if (m_dirStack == null) {
m_dirStack = new Stack<>();
m_dirStack.push(subdirs(m_root).iterator());
return files(m_root);
}
while(!m_dirStack.isEmpty()) {
Iterator<Path> subdirs = m_dirStack.peek();
if (!subdirs.hasNext()) {
m_dirStack.pop();
} else {
File dir = subdirs.next().toFile();
m_dirStack.push(subdirs(dir).iterator());
return files(dir);
}
}
endOfData();
return null;
}
}
public static FluentIterable<KeyedIterable<Path, Path>> groupFilesByDir(final Path root) {
return new FluentIterable<KeyedIterable<Path, Path>>() {
@Override
public Iterator<KeyedIterable<Path, Path>> iterator() {
return new GroupedPathIterator(root.toFile());
}
};
}
private static class LineIterator implements Iterator<String> {
private BufferedReader m_in;
private String m_nextLine;
public LineIterator(Reader r) {
m_in = new BufferedReader(r);
fetchLine();
}
private void fetchLine() {
if (m_in == null) return;
try {
m_nextLine = m_in.readLine();
} catch (IOException e) {
throw Throwables.propagate(e);
} finally {
if (m_nextLine == null) {
close();
}
}
}
private void close() {
try {
if (m_in != null) {
m_in.close();
}
} catch(IOException e) {
throw Throwables.propagate(e);
} finally {
m_in = null;
}
}
@Override
public boolean hasNext() {
return m_nextLine != null;
}
@Override
public String next() {
String line = m_nextLine;
fetchLine();
return line;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Iterator<String>.remove is not yet implemented.");
}
}
public static FluentIterable<String> lines(final Path path) {
return new FluentIterable<String>() {
@Override
public Iterator<String> iterator() {
try {
return new LineIterator(new InputStreamReader(new FileInputStream(path.toFile()), StandardCharsets.UTF_8));
} catch (FileNotFoundException e) {
throw Throwables.propagate(e);
}
}
};
}
public static FluentIterable<String> unzipLines(final Path path, final Charset cs) {
return new FluentIterable<String>() {
@Override
public Iterator<String> iterator() {
try {
return new LineIterator(zippedFileReader(path, cs));
} catch (IOException e) {
throw Throwables.propagate(e);
}
}
};
}
private static Reader zippedFileReader(final Path path, final Charset cs) throws IOException {
InputStream gzipStream = new GZIPInputStream(new FileInputStream(path.toFile()));
return new InputStreamReader(gzipStream, cs);
}
public static <F, T> Function<? super Iterable<F>, Iterable<T>> bind(final Function<? super F, Iterable<T>> f) {
return new Function<Iterable<F>, Iterable<T>>() {
@Override
public Iterable<T> apply(Iterable<F> input) {
return Iterables.concat(Iterables.transform(input, f));
}
};
}
public static <F, T> Function<? super Iterable<F>, Iterable<T>> lift(final Function<F, T> f) {
return new Function<Iterable<F>, Iterable<T>>() {
@Override
public Iterable<T> apply(Iterable<F> input) {
return Iterables.transform(input, f);
}
};
}
}