package uk.ac.rhul.cs.cl1.seeding;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import com.sosnoski.util.hashmap.StringIntHashMap;
import uk.ac.rhul.cs.cl1.MutableNodeSet;
import uk.ac.rhul.cs.graph.Graph;
/**
* Seed generator where seeds will be generated from an input stream.
*
* Each line in the stream must contain node names separated by spaces. Unknown
* nodes will silently be ignored.
*
* @see {@link FileBasedSeedGenerator} for reading seeds from a file
* @author tamas
*/
public class StreamBasedSeedGenerator extends SeedGenerator {
/**
* Reader that will be used to read the list of seeds.
*/
BufferedReader reader = null;
/**
* Delimiters to be used when tokenizing the lines from the stream
*/
String delimiters = " \t\r\n";
/**
* Constructs a new seed generator backed by the given stream.
*
* @param stream the stream from which we will read the seeds. The stream
* is assumed to have a default encoding.
*/
public StreamBasedSeedGenerator(Graph graph, InputStream stream) {
this(graph, new BufferedReader(new InputStreamReader(stream)));
}
/**
* Constructs a new seed generator backed by the given reader.
*
* @param reader the reader that will be used to read the list of seeds.
*/
public StreamBasedSeedGenerator(Graph graph, BufferedReader reader) {
super(graph);
this.reader = reader;
}
/**
* Gets the delimiters used when splitting a line into node names
*/
public String getDelimiters() {
return this.delimiters;
}
/**
* Internal iterator class that will be used when calling iterator()
*/
private class IteratorImpl extends SeedIterator {
/** A mutable node set that contains no nodes */
MutableNodeSet emptyNodeSet;
/** The current nodeset that will be returned with the next call to next() */
MutableNodeSet currentNodeSet = null;
/** A map mapping node names to indices */
StringIntHashMap namesToIndices = new StringIntHashMap();
public IteratorImpl() {
/* Populate the mapping from node names to node indices */
int n = graph.getNodeCount();
for (int i = 0; i < n; i++) {
namesToIndices.add(graph.getNodeName(i), i);
}
/* Construct the empty node set */
emptyNodeSet = new MutableNodeSet(graph);
/* Process the first line */
processLine();
}
private void processLine() {
do {
String line;
currentNodeSet = emptyNodeSet.clone();
try {
line = reader.readLine();
} catch (IOException ex) {
ex.printStackTrace();
currentNodeSet = null;
return;
}
if (line == null) {
currentNodeSet = null;
return;
}
StringTokenizer st = new StringTokenizer(line, delimiters);
/* Process current line */
while (st.hasMoreTokens()) {
String name = st.nextToken();
int idx = namesToIndices.get(name);
if (idx >= 0) {
currentNodeSet.add(idx);
}
// TODO: error reporting here
}
} while (currentNodeSet.size() == 0);
}
public boolean hasNext() {
return (currentNodeSet != null);
}
public Seed next() {
Seed result = new Seed(currentNodeSet);
processLine();
return result;
}
}
/**
* Returns an iterator that iterates over the seeds.
*
* This method must be called only once as it is not possible to rewind
* the reader once the seeds have been generated.
*/
public SeedIterator iterator() {
return new IteratorImpl();
}
/**
* Sets the delimiters used when splitting a line into node names
*
* @param delimiters a string containing characters to be used as delimiters
*/
public void setDelimiters(String delimiters) {
this.delimiters = delimiters;
}
/**
* Returns -1 as we cannot know in advance how many seeds there will be.
*/
public int size() {
return -1;
}
}